8253820: Save test images and dumps with timestamps from client sanity suite

Reviewed-by: serb
This commit is contained in:
Alexandre Iline 2020-11-13 02:27:32 +00:00
parent dff26a489d
commit e32a4ea4ef
3 changed files with 86 additions and 95 deletions

View File

@ -22,22 +22,16 @@
*/ */
import com.sun.swingset3.demos.button.ButtonDemo; import com.sun.swingset3.demos.button.ButtonDemo;
import org.jemmy2ext.JemmyExt;
import org.jtregext.GuiTestListener; import org.jtregext.GuiTestListener;
import org.netbeans.jemmy.ClassReference; import org.netbeans.jemmy.ClassReference;
import org.netbeans.jemmy.ComponentChooser;
import org.netbeans.jemmy.image.StrictImageComparator;
import org.netbeans.jemmy.operators.JButtonOperator; import org.netbeans.jemmy.operators.JButtonOperator;
import org.netbeans.jemmy.operators.JFrameOperator; import org.netbeans.jemmy.operators.JFrameOperator;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners; import org.testng.annotations.Listeners;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -65,25 +59,18 @@ import static org.jemmy2ext.JemmyExt.*;
public class ButtonDemoScreenshotTest { public class ButtonDemoScreenshotTest {
private static final int[] BUTTONS = {0, 1, 2, 3, 4, 5}; // "open browser" buttons (6, 7) open a browser, so ignore private static final int[] BUTTONS = {0, 1, 2, 3, 4, 5}; // "open browser" buttons (6, 7) open a browser, so ignore
private static StrictImageComparator sComparator = null;
@BeforeClass
public void init() {
sComparator = new StrictImageComparator();
}
@Test(dataProvider = "availableLookAndFeels", dataProviderClass = TestHelpers.class) @Test(dataProvider = "availableLookAndFeels", dataProviderClass = TestHelpers.class)
public void test(String lookAndFeel) throws Exception { public void test(String lookAndFeel) throws Exception {
UIManager.setLookAndFeel(lookAndFeel); UIManager.setLookAndFeel(lookAndFeel);
Robot rob = new Robot();
//capture some of the background //capture some of the background
Dimension screeSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension screeSize = Toolkit.getDefaultToolkit().getScreenSize();
Point screenCenter = new Point(screeSize.width / 2, screeSize.height / 2); Point screenCenter = new Point(screeSize.width / 2, screeSize.height / 2);
Rectangle center = new Rectangle( Rectangle center = new Rectangle(
screenCenter.x - 50, screenCenter.y - 50, screenCenter.x - 50, screenCenter.y - 50,
screenCenter.x + 50, screenCenter.y + 50); 100, 100);
BufferedImage background = rob.createScreenCapture(center); BufferedImage background = getRobot().createScreenCapture(center);
new ClassReference(ButtonDemo.class.getCanonicalName()).startApplication(); new ClassReference(ButtonDemo.class.getCanonicalName()).startApplication();
@ -91,18 +78,18 @@ public class ButtonDemoScreenshotTest {
mainFrame.waitComponentShowing(true); mainFrame.waitComponentShowing(true);
//make sure the frame is already painted //make sure the frame is already painted
waitChangedImage(rob, () -> rob.createScreenCapture(center), waitChangedImage(() -> getRobot().createScreenCapture(center),
background, mainFrame.getTimeouts(), "background.png"); background, mainFrame.getTimeouts(), "background");
//make sure the frame is painted completely //make sure the frame is painted completely
waitStillImage(rob, mainFrame, "frame.png"); waitStillImage(mainFrame, "frame");
// Check all the buttons // Check all the buttons
for (int i : BUTTONS) { for (int i : BUTTONS) {
checkButton(mainFrame, i, rob); checkButton(mainFrame, i);
} }
} }
private void checkButton(JFrameOperator jfo, int i, Robot rob) throws InterruptedException { private void checkButton(JFrameOperator jfo, int i) throws InterruptedException {
JButtonOperator button = new JButtonOperator(jfo, i); JButtonOperator button = new JButtonOperator(jfo, i);
//additional instrumentation for JDK-8198920. To be removed after the bug is fixed //additional instrumentation for JDK-8198920. To be removed after the bug is fixed
@ -113,9 +100,7 @@ public class ButtonDemoScreenshotTest {
button.moveMouse(button.getCenterX(), button.getCenterY()); button.moveMouse(button.getCenterX(), button.getCenterY());
BufferedImage notPressed, pressed = null; BufferedImage notPressed, pressed = null;
notPressed = waitStillImage(rob, button, "not-pressed-" + i + ".png"); notPressed = waitStillImage(button, "not-pressed-" + i);
BufferedImage[] pressedImage = new BufferedImage[1];
button.pressMouse(); button.pressMouse();
//additional instrumentation for JDK-8198920. To be removed after the bug is fixed //additional instrumentation for JDK-8198920. To be removed after the bug is fixed
@ -126,14 +111,14 @@ public class ButtonDemoScreenshotTest {
//additional instrumentation for JDK-8198920. To be removed after the bug is fixed //additional instrumentation for JDK-8198920. To be removed after the bug is fixed
button.getOutput().printTrace("JDK-8198920: Button press confirmed by " + System.currentTimeMillis()); button.getOutput().printTrace("JDK-8198920: Button press confirmed by " + System.currentTimeMillis());
//end of instrumentation for JDK-8198920 //end of instrumentation for JDK-8198920
waitChangedImage(rob, () -> capture(rob, button), notPressed, waitChangedImage(() -> capture(button), notPressed,
button.getTimeouts(), "pressed-" + i + ".png"); button.getTimeouts(), "after-press-" + i);
pressed = waitStillImage(rob, button, "pressed.png"); pressed = waitStillImage(button, "pressed-" + i);
} finally { } finally {
button.releaseMouse(); button.releaseMouse();
if(pressed != null) { if(pressed != null) {
waitChangedImage(rob, () -> capture(rob, button), pressed, waitChangedImage(() -> capture(button), pressed,
button.getTimeouts(), "released-" + i + ".png"); button.getTimeouts(), "released-" + i);
} }
//additional instrumentation for JDK-8198920. To be removed after the bug is fixed //additional instrumentation for JDK-8198920. To be removed after the bug is fixed
button.getOutput().printTrace("JDK-8198920: Button released at " + System.currentTimeMillis()); button.getOutput().printTrace("JDK-8198920: Button released at " + System.currentTimeMillis());

View File

@ -149,7 +149,7 @@ public class EditorPaneDemoTest {
final int xGap = 100, yGap = 40, columns = 2, rows = 5; final int xGap = 100, yGap = 40, columns = 2, rows = 5;
editorPaneOperator.waitState(comp -> { editorPaneOperator.waitState(comp -> {
BufferedImage capturedImage = ImageTool.getImage(imageRect); BufferedImage capturedImage = ImageTool.getImage(imageRect);
save(capturedImage, "editor.png"); save(capturedImage, "editor");
assertFalse(isBlack(capturedImage), "image blackness"); assertFalse(isBlack(capturedImage), "image blackness");
int x = 0, y = 0, i = 0, j; int x = 0, y = 0, i = 0, j;
for (; i < columns; i++) { for (; i < columns; i++) {
@ -159,8 +159,7 @@ public class EditorPaneDemoTest {
y += yGap; y += yGap;
if(capturedImage.getRGB(x, y) == Color.WHITE.getRGB()) { if(capturedImage.getRGB(x, y) == Color.WHITE.getRGB()) {
// saving image for failure case // saving image for failure case
JemmyExt.save(capturedImage, "capturedimage_" + pageName + "_" + save(capturedImage, "capturedimage-" + pageName);
UIManager.getLookAndFeel().getClass().getSimpleName() + ".png");
return false; return false;
} }
} }

View File

@ -22,12 +22,14 @@
*/ */
package org.jemmy2ext; package org.jemmy2ext;
import java.awt.AWTException;
import java.awt.Component; import java.awt.Component;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Robot; import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window; import java.awt.Window;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -36,8 +38,11 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -83,6 +88,19 @@ import static org.testng.AssertJUnit.*;
*/ */
public class JemmyExt { public class JemmyExt {
private static Robot robot = null;
public static Robot getRobot() {
try {
if(robot == null) {
robot = new Robot();
}
return robot;
} catch (AWTException e) {
throw new RuntimeException(e);
}
}
/** /**
* Statically referencing all the classes that are needed by tests so that * Statically referencing all the classes that are needed by tests so that
* they're compiled by jtreg * they're compiled by jtreg
@ -151,78 +169,62 @@ public class JemmyExt {
}); });
} }
public static void assertEquals(String string, StrictImageComparator comparator, BufferedImage expected, BufferedImage actual) { private static final DateFormat timestampFormat = new SimpleDateFormat("yyyyMMddHHmmss");
try { private static String timeStamp() {
assertTrue(string, comparator.compare(expected, actual)); return timestampFormat.format(new Date());
} catch (Error err) {
save(expected, "expected.png");
save(actual, "actual.png");
throw err;
}
} }
public static void assertNotEquals(String string, StrictImageComparator comparator, BufferedImage notExpected, BufferedImage actual) { /**
try { * Constructs filename with a timestamp.
assertFalse(string, comparator.compare(notExpected, actual)); * @param name File name or a path without the extension
} catch (Error err) { * @param extension File extension (without the dot). Could be null,
save(notExpected, "notExpected.png"); * in which case timestamp is simply added to the filename (no trailing dot).
save(actual, "actual.png"); * @return file name
throw err; */
} public static String timeStamp(String name, String extension) {
return name + "-" + timeStamp() +
((extension != null) ? ("." + extension) : "");
} }
public static void save(BufferedImage image, String filename) { /**
String filepath = filename; * Saves an image into a file. Filename will be constructed from the given fileID and
* a timestamp.
* @param image
* @param fileID
*/
public static void save(BufferedImage image, String fileID) {
doSave(image, timeStamp(fileID + "-" + lafShortName(), "png"));
}
//Saves an image into a file with the provided filename
private static void doSave(BufferedImage image, String filename) {
try { try {
filepath = new File(filename).getCanonicalPath(); String filepath = new File(filename).getCanonicalPath();
System.out.println("Saving screenshot to " + filepath); System.out.println("Saving screenshot to " + filepath);
BufferedOutputStream file = new BufferedOutputStream(new FileOutputStream(filepath)); BufferedOutputStream file = new BufferedOutputStream(new FileOutputStream(filepath));
new PNGEncoder(file, PNGEncoder.COLOR_MODE).encode(image); new PNGEncoder(file, PNGEncoder.COLOR_MODE).encode(image);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new RuntimeException("Failed to save image to " + filepath, ioe); throw new RuntimeException("Failed to save image to " + filename, ioe);
} }
} }
/**
* Waits for a screen area taken by a component to not be completely black rectangle.
* @return last (non-black) image
* @throws TimeoutExpiredException if the waiting is unsuccessful
*/
public static BufferedImage waitNotBlack(Robot rob, ComponentOperator operator, String imageName) {
class NonBlackImageChooser implements ComponentChooser {
private BufferedImage image = null;
@Override
public boolean checkComponent(Component comp) {
image = capture(rob, operator);
save(image, imageName);
return !isBlack(image);
}
@Override
public String getDescription() {
return "A non-black Image of " + operator;
}
}
NonBlackImageChooser chooser = new NonBlackImageChooser();
operator.waitState(chooser);
return chooser.image;
}
/** /**
* Waits for the displayed image to be still. * Waits for the displayed image to be still.
* @param imageID an image ID with no extension. Timestamp and LAF information is added to the ID when saving.
* @return last still image * @return last still image
* @throws TimeoutExpiredException if the waiting is unsuccessful * @throws TimeoutExpiredException if the waiting is unsuccessful
*/ */
public static BufferedImage waitStillImage(Robot rob, ComponentOperator operator, String imageName) { public static BufferedImage waitStillImage(ComponentOperator operator, String imageID) {
operator.getTimeouts().setTimeout("Waiter.TimeDelta", 1000); operator.getTimeouts().setTimeout("Waiter.TimeDelta", 1000);
String timestampName = timeStamp(imageID + "-" + lafShortName(), "png");
class StillImageChooser implements ComponentChooser { class StillImageChooser implements ComponentChooser {
private BufferedImage previousImage = null; private BufferedImage previousImage = null;
private final StrictImageComparator sComparator = new StrictImageComparator(); private final StrictImageComparator sComparator = new StrictImageComparator();
@Override @Override
public boolean checkComponent(Component comp) { public boolean checkComponent(Component comp) {
BufferedImage currentImage = capture(rob, operator); BufferedImage currentImage = capture(operator);
save(currentImage, imageName); doSave(currentImage, timestampName);
boolean compareResult = previousImage == null ? false : sComparator.compare(currentImage, previousImage); boolean compareResult = previousImage == null ? false : sComparator.compare(currentImage, previousImage);
previousImage = currentImage; previousImage = currentImage;
return compareResult; return compareResult;
@ -241,22 +243,23 @@ public class JemmyExt {
/** /**
* Waits for the displayed image to change. * Waits for the displayed image to change.
* @param reference image to compare to * @param reference image to compare to
* @param imageID an image ID with no extension. Timestamp and LAF information is added to the ID when saving.
* @return last (changed) image * @return last (changed) image
* @throws TimeoutExpiredException if the waiting is unsuccessful * @throws TimeoutExpiredException if the waiting is unsuccessful
*/ */
public static BufferedImage waitChangedImage(Robot rob, public static BufferedImage waitChangedImage(Supplier<BufferedImage> supplier,
Supplier<BufferedImage> supplier,
BufferedImage reference, BufferedImage reference,
Timeouts timeouts, Timeouts timeouts,
String imageName) throws InterruptedException { String imageID) throws InterruptedException {
ImageComparator comparator = new StrictImageComparator(); ImageComparator comparator = new StrictImageComparator();
String timestampName = timeStamp(imageID + "-" + lafShortName(), "png");
class ImageWaitable implements Waitable { class ImageWaitable implements Waitable {
BufferedImage image; BufferedImage image;
@Override @Override
public Object actionProduced(Object obj) { public Object actionProduced(Object obj) {
image = supplier.get(); image = supplier.get();
save(image, imageName); doSave(image, timestampName);
return comparator.compare(reference, image) ? null : image; return comparator.compare(reference, image) ? null : image;
} }
@ -316,10 +319,10 @@ public class JemmyExt {
} }
} }
public static BufferedImage capture(Robot rob, ComponentOperator operator) { public static BufferedImage capture(ComponentOperator operator) {
Rectangle boundary = new Rectangle(operator.getLocationOnScreen(), Rectangle boundary = new Rectangle(operator.getLocationOnScreen(),
operator.getSize()); operator.getSize());
return rob.createScreenCapture(boundary); return getRobot().createScreenCapture(boundary);
} }
/** /**
@ -400,26 +403,26 @@ public class JemmyExt {
} }
} }
private static String lafShortName() { return UIManager.getLookAndFeel().getClass().getSimpleName(); }
/** /**
* Trying to capture as much information as possible. Currently it includes * Trying to capture as much information as possible. Currently it includes
* full dump and a screenshot of the whole screen. * full dump and a screenshot of the whole screen.
*/ */
public static void captureAll() { public static void captureAll() {
String lookAndFeelClassName = UIManager.getLookAndFeel().getClass().getSimpleName(); save(getRobot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "failure");
PNGEncoder.captureScreen("failure_" + lookAndFeelClassName + ".png", PNGEncoder.COLOR_MODE);
try { try {
Dumper.dumpAll("dumpAll_" + lookAndFeelClassName + ".xml"); Dumper.dumpAll(timeStamp("dumpAll-" + lafShortName(), "xml"));
} catch (FileNotFoundException ex) { } catch (FileNotFoundException ex) {
Logger.getLogger(JemmyExt.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(JemmyExt.class.getName()).log(Level.SEVERE, null, ex);
} }
captureWindows(lookAndFeelClassName); captureWindows();
} }
/** /**
* Captures each showing window image using Window.paint() method. * Captures each showing window image using Window.paint() method.
* @param lookAndFeelClassName
*/ */
private static void captureWindows(String lookAndFeelClassName) { private static void captureWindows() {
try { try {
EventQueue.invokeAndWait(() -> { EventQueue.invokeAndWait(() -> {
Window[] windows = Window.getWindows(); Window[] windows = Window.getWindows();
@ -434,10 +437,14 @@ public class JemmyExt {
g.dispose(); g.dispose();
try { try {
ImageIO.write(img, "png", new File("window_" + lookAndFeelClassName save(img, "window-" + index++);
+ "_" + index++ + ".png")); } catch (RuntimeException e) {
} catch (IOException e) { if (e.getCause() instanceof IOException) {
e.printStackTrace(); System.err.println("Failed to save screen images");
e.printStackTrace();
} else {
throw e;
}
} }
} }
}); });