3

I am performing the cross browser testing of a GWT web application on different PCs using Selenium Grid. The problem which I am facing is that the size of generated screenshots (taken during the tests) is different for different PCs and I want to make my code generic i.e. either to set some default size of the screenshots. Here is my code which I am using to take the screenshot and then to compare the generated image with the image stored locally on my PC.

Firstly I will call the CallScreenshotAndCompareImage method with arguments (canvas,className).

Here canvas is the WebElement representing the HTML5 canvas in the GWT application.

FileBase is the file stored locally in my project and fileActual is the generated screenshot image.

public class Browser {
    //ThreadLocal will provide thread-safe tests
        protected ThreadLocal<RemoteWebDriver> threadLocal = null;
        String serverMachine = "xxx.xxx.xxx.xxx:xxx/hub"; //IP Address of hub
        @BeforeTest
        @Parameters("browser")
        public void setup(String browser) throws MalformedURLException{

            if(browser.equalsIgnoreCase("chrome")) {

                System.setProperty("webdriver.chrome.driver", ".src/Drivers/chromedriver.exe");
                DesiredCapabilities capability = null;
                capability = DesiredCapabilities.chrome();
                capability.setPlatform(Platform.WINDOWS);
                capability.setBrowserName("chrome");
                createRemoteWebDriver(capability);
            }
            else if(browser.equalsIgnoreCase("firefox")) {

                System.setProperty("webdriver.gecko.driver", ".src/Drivers/geckodriver.exe");
                DesiredCapabilities capability = null;
                capability = DesiredCapabilities.firefox();
                capability.setPlatform(Platform.WINDOWS);
                capability.setBrowserName("firefox");
                createRemoteWebDriver(capability);
            }

        }
        public void createRemoteWebDriver(DesiredCapabilities capability) throws MalformedURLException {
            threadLocal = new ThreadLocal<RemoteWebDriver>();
            threadLocal.set(new RemoteWebDriver(new URL(serverMachine), capability));
        }

        public WebDriver getDriver() {
            return threadLocal.get();
        }

public void CallScreenshotAndCompareImage(WebElement element, String className) throws IOException, InterruptedException {
            File fileBase1 = new File("./src/Images/baseDrawing"+className+"Chrome.png");
            File fileBase2 = new File("./src/Images/baseDrawing"+className+"Firefox.png");
            Capabilities cap = ((RemoteWebDriver) getDriver()).getCapabilities();
            String browserName = cap.getBrowserName().toLowerCase();
            File fileActual = new File("./src/Images/actualDrawing"+className+browserName+".png");
            File elementImage = this.takeElementScreenshot(element,"png");
            FileUtils.copyFile(elementImage, fileActual);
            if(browserName.equalsIgnoreCase("chrome"))
                this.compareImage(fileBase1,fileActual);
            else if(browserName.equalsIgnoreCase("firefox"))
                this.compareImage(fileBase2,fileActual);
            Thread.sleep(3000);
        }

public File takeElementScreenshot(WebElement element, String imageFormat) throws IOException{

            Point elementXY = element.getLocation();
            int elementHeight = element.getSize().getHeight();
            int elementWidth = element.getSize().getWidth();
            Rectangle elementRectArea = new Rectangle(elementWidth,elementHeight);
            WrapsDriver wrapsDriver = (WrapsDriver) element;
            File pageImage = ((TakesScreenshot)wrapsDriver.getWrappedDriver()).getScreenshotAs(OutputType.FILE);
            BufferedImage bufferedImage = ImageIO.read(pageImage);
            BufferedImage elementImage = bufferedImage.getSubimage(elementXY.getX(), elementXY.getY(), elementRectArea.width, elementRectArea.height);
            ImageIO.write(elementImage, imageFormat, pageImage);
            return pageImage; 
        }

        public void compareImage(File fileBase, File fileActual) {
            /* 
         STEPS:
         1) For first image file, recognize the contents of the file and decodes it into a BufferedImage which can be directly used by Java 2D.
         2) Get the raster from the bufferedImage object which is a copy of image data.
         3) Get the DataBuffer associated with the raster.
         4) Get the size of the all the banks(data arrays) for the DataBuffer object.
         5) Repeat steps 1-4 for the second image file.
         6) If sizes of both of the images are different, then images won't be same.
         7) If sizes are same, then compare all the data array elements for both of the DataBuffer objects. If they are same. then both images will be same.
             */

            try {

                BufferedImage bufferedImage = ImageIO.read(fileBase); 
                DataBuffer dataBufferFirst = bufferedImage.getData().getDataBuffer(); 
                int sizeFirst = dataBufferFirst.getSize();              
                BufferedImage bufferedImage2 = ImageIO.read(fileActual);
                DataBuffer dataBufferSecond = bufferedImage2.getData().getDataBuffer();
                int sizeSecond = dataBufferSecond.getSize();
                int count=0;
                Assert.assertEquals(sizeFirst, sizeSecond,"Size of Base Drawing and actual Drawing is not same");
                if(sizeFirst == sizeSecond) 
                {
                    for(int i=0; i<sizeFirst; i++) 
                    { 
                        if(dataBufferFirst.getElem(i) != dataBufferSecond.getElem(i)) //getElem() returns the data array element at the specified index.
                        {
                            count++;
                        }

                    }
                    Assert.assertEquals(count, 0,"Both images are not same");
                }
            } catch (Exception e) { 
                Assert.fail("Failed to compare image files...!!!");
            }
        }
}

After running this code, when I compared the properties of both the base (local) image and the actual (generated) image, then there was a little difference between both of those images.

Base Drawing: Size -> 253 KB, Bit Depth -> 32, Dimensions -> 1570 x 873 pixels

Actual Drawing: Size -> 232 KB, Bit Depth -> 24, Dimensions -> 1570 x 873 pixels

Everything else was same in the properties of both of these images.

What should I add in my code so that the generated screenshots from different PCs will always be of the same size?

KhiladiBhaiyya
  • 633
  • 1
  • 14
  • 43

3 Answers3

1

The issue is not the size of the image, but the Alpha channel (transparency) which is present in the base drawing and missing in the screenshot.

The driver is supposed to return a PNG Base64 encoded string. But it's not specified whether it should be a 24-bit RGB or 32-bit RGBA PNG. So to be able to compare the buffers, you'll first have to convert each one of them to the desired color space.

Here's an example:

public static void main(String[] args) throws Exception {
    BufferedImage imageA = ImageIO.read(new File("C:\\temp\\img_24bits.png"));
    BufferedImage imageB = ImageIO.read(new File("C:\\temp\\img_32bits.png"));
    boolean same = isSameImage(imageA, imageB);
}

public static boolean isSameImage(BufferedImage imageA, BufferedImage imageB) throws IOException {
    DataBufferInt bufferA = getImageBuffer(imageA);
    DataBufferInt bufferB = getImageBuffer(imageB);

    if (bufferA.getSize() != bufferB.getSize() || bufferA.getNumBanks() != bufferB.getNumBanks())
        return false;

    for (int i = 0; i < bufferA.getNumBanks(); ++i) {
        if (!Arrays.equals(bufferA.getData(i), bufferB.getData(i)))
            return false;
    }

    return true;
}

private static DataBufferInt getImageBuffer(BufferedImage img) throws IOException {
    BufferedImage  bi  = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
    ColorConvertOp cco = new ColorConvertOp(bi.getColorModel().getColorSpace(), img.getColorModel().getColorSpace(), null);
    cco.filter(img, bi);
    return (DataBufferInt)bi.getRaster().getDataBuffer();
}
Florent B.
  • 41,537
  • 7
  • 86
  • 101
  • I tried your method but still it is saying that both of the images are different. I checked that by printing the boolean value of the same variable. – KhiladiBhaiyya Oct 17 '17 at 06:19
  • If the method returns false, then the images are different with a least one pixel with a different color. It's probably invisible to a naked eye, but the difference is there. It's probably due to a different anti-aliasing. Strict pixel comparison should only be used to compare screenshots from the same browser. To compare screenshots between browsers you'll have to apply some filters to narrow the features you are trying to validate. Have a look at image magic. – Florent B. Oct 17 '17 at 13:08
  • What kind of filters are you talking about? Like permitting at least one or two different pixels in the images or some other filters? – KhiladiBhaiyya Oct 17 '17 at 13:54
  • It depends on what the differences are and on what you are trying to validate. It could be edge, sharpen, gaussian... You could also use a specific algorithm to ignore antialiasing: https://github.com/mapbox/pixelmatch – Florent B. Oct 17 '17 at 14:12
0

Although @Florent B.'s answer gave me a direction to approach the solution, but here's what I did to solve this issue.

  1. Checked whether the bit depth of the image is 24 or 32.
  2. If it is 32, then removed all the alpha channels from it.
  3. Next, I set a range that if the difference between the RGB values of the pixels of two corresponding images was less than 10, then consider those images as same.
KhiladiBhaiyya
  • 633
  • 1
  • 14
  • 43
-1

I think you must use a command to set the resolution of the executed driver. To do so add in Java

driver.manage().window().setSize(new Dimension (1280, 1024));

before your teststeps. Now the screenshot will always have the same resolution.

  • But the resolution of my images is same and that is not my problem. The main issue is concerned with the sizes (and bit depth) of the images which are different and not the resolution. Please read what I have specified in the block-quote in the end of my question's details. – KhiladiBhaiyya Oct 11 '17 at 08:58