com.vaadin.testbench.screenshot.ScreenShotFailureReporter Maven / Gradle / Ivy
/**
* Copyright (C) 2000-2022 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.testbench.screenshot;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ScreenShotFailureReporter {
private final BufferedImage referenceImage;
private final boolean[][] falseBlocks;
private final int xBlocks;
private final int yBlocks;
public ScreenShotFailureReporter(BufferedImage referenceImage,
boolean[][] falseBlocks) {
this.referenceImage = referenceImage;
this.falseBlocks = falseBlocks;
xBlocks = ImageComparisonUtil.getNrBlocks(referenceImage.getWidth());
yBlocks = ImageComparisonUtil.getNrBlocks(referenceImage.getHeight());
}
public void createErrorImageAndHTML(String fileName,
BufferedImage screenshotImage) {
try {
// Write the screenshot into the error directory
ImageIO.write(screenshotImage, "png",
ImageFileUtil.getErrorScreenshotFile(fileName));
} catch (IOException e) {
getLogger().error("Error writing screenshot to "
+ ImageFileUtil.getErrorScreenshotFile(fileName).getPath(),
e);
}
// collect big error blocks of differences
List errorAreas = collectErrorsToList(xBlocks, yBlocks);
// Draw boxes around blocks that differ
drawErrorsToImage(errorAreas, screenshotImage);
createDiffHtml(errorAreas, fileName, screenshotImage, referenceImage);
}
private Logger getLogger() {
return LoggerFactory.getLogger(getClass());
}
/**
* Runs through the marked false macroblocks and collects them to bigger
* blocks
*
* @param xBlocks
* Amount of macroblocks in x direction
* @param yBlocks
* Amount of macroblocks in y direction
* @return List of ErrorBlocks
*/
private List collectErrorsToList(int xBlocks, int yBlocks) {
List errorAreas = new LinkedList<>();
// run through blocks for marked errors for macroblocks.
for (int y = 0; y < yBlocks; y++) {
for (int x = 0; x < xBlocks; x++) {
// if found error make new ErrorBlock and collect
// connected error blocks and mark them false so
// that they won't trigger new errors
if (falseBlocks[x][y]) {
ErrorBlock newBlock = new ErrorBlock();
newBlock.setX(x * 16);
newBlock.setY(y * 16);
int x1 = x, xmin = x, y1 = y, maxSteps = xBlocks * yBlocks,
steps = 0;
falseBlocks[x][y] = false;
// This'll confirm logic errors.
while (true) {
x1++;
// if x1 out of bounds set x1 to xmin where
// xmin == smallest error block found for
// this error
if (x1 >= xBlocks) {
x1 = xmin;
}
// if x1,y1 marked true add width to ErrorBlock
if (falseBlocks[x1][y1]) {
newBlock.addXBlock();
falseBlocks[x1][y1] = false;
} else if (y1 < yBlocks) {
x1 = xmin;
// If next row has a false block
// connected to our block
boolean foundConnectedBlock = false;
for (int foundX = x1; foundX < x1
+ newBlock.getXBlocks(); foundX++) {
if (foundX == xBlocks || y1 + 1 == yBlocks) {
break;
}
if (falseBlocks[foundX][y1 + 1]) {
foundConnectedBlock = true;
}
}
// If connected error to ErrorBlock add
// height to error block
if (foundConnectedBlock) {
y1++;
newBlock.addYBlock();
// while stepping back on this
// row is false change block x
// position
if (x1 - 1 >= 0) {
while (falseBlocks[x1 - 1][y1]) {
falseBlocks[x1 - 1][y1] = false;
newBlock.addXBlock();
x1 = x1 - 1;
newBlock.setX(newBlock.getX() - 16);
if (x1 == 0) {
break;
}
}
xmin = x1;
}
// Skip blocks inside main error
// block for this error
x1 = x1 + newBlock.getXBlocks() - 1;
} else {
x1 = newBlock.getX() / 16;
y1 = newBlock.getY() / 16;
// Set all blocks to false
// inside found box
for (int j = 0; j < newBlock
.getYBlocks(); j++) {
for (int i = 0; i < newBlock
.getXBlocks(); i++) {
if (x1 + i < xBlocks
&& y1 + j < yBlocks) {
falseBlocks[x1 + i][y1 + j] = false;
}
}
}
break;
}
}
// In case something goes wrong we won't get stuck in
// the loop forever
if (++steps == maxSteps) {
break;
}
}
errorAreas.add(newBlock);
}
}
}
return errorAreas;
}
private void drawErrorsToImage(List errorAreas,
BufferedImage screenshotImage) {
// Draw lines around false ErrorBlocks before saving _diff
// file.
Graphics2D drawToPicture = screenshotImage.createGraphics();
drawToPicture.setColor(Color.MAGENTA);
int width = screenshotImage.getWidth();
int height = screenshotImage.getHeight();
for (ErrorBlock error : errorAreas) {
int offsetX = 0, offsetY = 0;
if (error.getX() > 0) {
offsetX = 1;
}
if (error.getY() > 0) {
offsetY = 1;
}
int toX = error.getXBlocks() * 16 + offsetX;
int toY = error.getYBlocks() * 16 + offsetY;
// Draw lines inside canvas
if ((error.getX() + (error.getXBlocks() * 16) + offsetX) > width) {
toX = width - error.getX();
}
if ((error.getY() + (error.getYBlocks() * 16) + offsetY) > height) {
toY = height - error.getY();
}
// draw error to image
drawToPicture.drawRect(error.getX() - offsetX,
error.getY() - offsetY, toX, toY);
}
// release resources
drawToPicture.dispose();
}
/**
* Build a small html file that has mouse over picture change for fast
* checking of errors and click on picture to switch between reference and
* diff pictures.
*
* @param blocks
* List of ErrorBlock
* @param fileId
* fileName for html file
*/
private void createDiffHtml(List blocks, String fileId,
BufferedImage screenshotImage, BufferedImage referenceImage) {
String image = ImageUtil.encodeImageToBase64(screenshotImage);
String ref_image = ImageUtil.encodeImageToBase64(referenceImage);
try {
PrintWriter writer = new PrintWriter(
ImageFileUtil.getErrorScreenshotFile(fileId + ".html"));
// Write head
writer.println("");
writer.println("");
writer.println(
"");
writer.println("");
writer.println(
"");
writer.println(
"Image for this run");
writer.println(
"");
int add = 0;
for (ErrorBlock error : blocks) {
int offsetX = 0, offsetY = 0;
if (error.getX() > 0) {
offsetX = 1;
}
if (error.getY() > 0) {
offsetY = 1;
}
String id = "popUpDiv_" + (error.getX() + add) + "_"
+ (error.getY() + add);
// position stars so that it's not out of screen.
writer.println("");
// Start "popup" div
writer.println(
"");
writer.println("");
// End popup div
writer.println("");
add++;
}
// End file
writer.println("");
writer.flush();
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}