def _take_and_transfer_screenshot(self, renderer, imageName, screenshotNumber, testClassName, testRunName, displayNumber, useSystemCompositorForScreenshot, minWidth, minHeight): splittedImageName = os.path.splitext(imageName) localScreenshotName = "{}_{}_{}{}".format(splittedImageName[0], screenshotNumber, self.name, splittedImageName[1]) targetScreenshotName = "{}{}_{:04d}_{}".format(self.fixed_screenshot_prefix, self.unique_screenshot_prefix, self.target_screenshot_counter, localScreenshotName) self.target_screenshot_counter += 1 # trigger screenshot(s) if useSystemCompositorForScreenshot: self._take_system_compositor_screenshot(targetScreenshotName, renderer) else: self._take_renderer_screenshot(targetScreenshotName, renderer, displayNumber) resultDirForTest = helper.get_result_dir_subdirectory(self.resultDir, testClassName+"/"+testRunName) self._transfer_screenshots(targetScreenshotName, self.tmpDir, resultDirForTest) localScreenshotFile = os.path.join(resultDirForTest, localScreenshotName) log.info("Store remote {} as local {}".format(targetScreenshotName, localScreenshotFile)) os.rename(os.path.join(resultDirForTest, targetScreenshotName), localScreenshotFile) #check image size pilImage = Image.open(localScreenshotFile) if (pilImage.size[0] < minWidth) or (pilImage.size[1] < minHeight): log.errorAndAssert("Screenshot too small: expected >= {}x{}, got {}x{}".format(minWidth, minHeight, pilImage.size[0], pilImage.size[1])) return localScreenshotFile
def take_screenshot_and_compare(self, renderer, imageName, testClassName, testRunName, displayNumber, screenshotNumber, percentageOfWrongPixelsAllowed, percentageOfRGBDifferenceAllowedPerPixel, numberOfRequiredUnequalPixels, useSystemCompositorForScreenshot, compareForEquality): log.info("taking screenshot...") referenceImagePath = self._find_reference_image(imageName) refImage = Image.open(referenceImagePath) minWidth = refImage.size[0] minHeight = refImage.size[1] pathToResultScreenshot = self._take_and_transfer_screenshot(renderer, imageName, screenshotNumber, testClassName, testRunName, displayNumber, useSystemCompositorForScreenshot, minWidth, minHeight) if not os.path.isfile(pathToResultScreenshot): log.errorAndAssert("Screenshot not found at {}".format(pathToResultScreenshot)) else: log.info("comparing images...") if (self.percentageOfRGBDifferenceAllowedPerPixelOnTarget > percentageOfRGBDifferenceAllowedPerPixel): log.important_info("Allowing higher difference per pixel because of target value: {}%".format(self.percentageOfRGBDifferenceAllowedPerPixelOnTarget*100)) percentageOfRGBDifferenceAllowedPerPixel = self.percentageOfRGBDifferenceAllowedPerPixelOnTarget if (self.percentageOfWrongPixelsAllowedOnTarget > percentageOfWrongPixelsAllowed): log.important_info("Allowing higher number of wrong pixels because of target value: {}%".format(self.percentageOfWrongPixelsAllowedOnTarget*100)) percentageOfWrongPixelsAllowed = self.percentageOfWrongPixelsAllowedOnTarget helper.compare_images(pathToResultScreenshot, referenceImagePath, percentageOfWrongPixelsAllowed, percentageOfRGBDifferenceAllowedPerPixel, numberOfRequiredUnequalPixels, self.imageDiffScaleFactor, compareForEquality)
def compare_images(screenshotPath, desiredImagePath, percentageOfWrongPixelsAllowed, percentageOfRGBDifferenceAllowedPerPixel, numberOfRequiredUnequalPixels, imageDiffScaleFactor, compareForEquality): try: image = Image.open(screenshotPath) desiredImage = Image.open(desiredImagePath) except IOError as e: log.errorAndAssert("Image file for comparison could not be opened ({} {})".format(e.strerror, e.filename)) #crop image if bigger than desiredImage if (image.size[0] > desiredImage.size[0] and image.size[1] >= desiredImage.size[1]) or \ (image.size[1] > desiredImage.size[1] and image.size[0] >= desiredImage.size[0]): log.info("Image is bigger than desired image, using cropped image") splittedImagePath = os.path.splitext(screenshotPath) croppedImagePath = splittedImagePath[0]+"_cropped"+splittedImagePath[1] image = image.crop((0, 0, desiredImage.size[0], desiredImage.size[1])) image.save(croppedImagePath) if compareForEquality: log.info("compareEqual: Bitmap compare of {0} with {1}".format(screenshotPath, desiredImagePath)) result = image_utils.compareEqual(image, desiredImage, percentageOfWrongPixelsAllowed, percentageOfRGBDifferenceAllowedPerPixel) else: log.info("compareUnequal: Bitmap compare of {0} with {1}".format(screenshotPath, desiredImagePath)) result = image_utils.compareUnequal(image, desiredImage, numberOfRequiredUnequalPixels, percentageOfRGBDifferenceAllowedPerPixel) if not result: image_utils.create_diff_images(image, desiredImage, screenshotPath, imageDiffScaleFactor) log.errorAndAssert("Result of screenshot image comparison was false, created diff image for screenshot: {})".format(screenshotPath))
def _find_reference_image(self, imageName): result = None for d in self.imagesDesiredDirs: path = os.path.normpath(d + '/' + imageName) if os.path.isfile(path): if result is None: result = path else: log.errorAndAssert("Multiple reference images found for image name '{}'".format(imageName)) if result is None: log.errorAndAssert("No reference image found for image name '{}'".format(imageName)) return result
def _take_and_transfer_screenshot(self, renderer, imageName, testClassName, testRunName, displayNumber, useSystemCompositorForScreenshot, minWidth, minHeight): splittedImageName = os.path.splitext( imageName) # split into image name and extension extendedImageNameBase = splittedImageName[ 0] + '_' + self.name # extend name with target name to make unique # Add wildcard, because the system compositor outputs multiple screenshots. One for each screen, that has the suffix "_<number>". screenshotNamePattern = extendedImageNameBase + "*.*" #delete old screenshot files on target if existing self._delete_files_on_target(screenshotNamePattern, self.tmpDir) # trigger screenshot(s) if useSystemCompositorForScreenshot: self._take_system_compositor_screenshot( extendedImageNameBase + ".png", renderer) else: self._take_renderer_screenshot(extendedImageNameBase + ".bmp", renderer, displayNumber) resultDirForTest = helper.get_result_dir_subdirectory( self.resultDir, testClassName + "/" + testRunName) self._transfer_screenshots(screenshotNamePattern, self.tmpDir, resultDirForTest) # delete screenshots on target (disk space is precious these days) self._delete_files_on_target(screenshotNamePattern, self.tmpDir) #search for suitable screenshot suitableScreenshotFound = False screenShotFiles = glob.glob( os.path.normcase(resultDirForTest + '/' + screenshotNamePattern)) folderForUnusedPath = helper.get_result_dir_subdirectory( resultDirForTest, 'unused_screenshots') if len(screenShotFiles) == 0: log.errorAndAssert("No screenshot files found") for f in screenShotFiles: if not suitableScreenshotFound: #check image size pilImage = Image.open(f) if (pilImage.size[0] >= minWidth) and (pilImage.size[1] >= minHeight): suitableScreenshotFound = True returnFile = f else: #too small, move to unused folder log.info( "Screenshot too small: expected >= {}x{}, got {}x{}". format(minWidth, minHeight, pilImage.size[0], pilImage.size[1])) os.rename( f, os.path.join(folderForUnusedPath, os.path.basename(f))) else: #suitable file already found, move others to unused foler os.rename( f, os.path.join(folderForUnusedPath, os.path.basename(f))) if not suitableScreenshotFound: log.errorAndAssert("No suitable screenshot found") return returnFile