| # Copyright 2017 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import math |
| import os |
| import random |
| import sys |
| |
| from gpu_tests import color_profile_manager |
| from gpu_tests import gpu_integration_test |
| from gpu_tests import path_util |
| from gpu_tests import screenshot_sync_expectations |
| |
| from telemetry.util import image_util |
| from telemetry.util import rgba_color |
| |
| data_path = os.path.join( |
| path_util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu') |
| |
| class ScreenshotSyncIntegrationTest(gpu_integration_test.GpuIntegrationTest): |
| """Tests that screenshots are properly synchronized with the frame on |
| which they were requested. |
| """ |
| |
| @classmethod |
| def Name(cls): |
| """The name by which this test is invoked on the command line.""" |
| return 'screenshot_sync' |
| |
| # The command line options (which are passed to subclasses' |
| # GenerateGpuTests) *must* be configured here, via a call to |
| # SetParsedCommandLineOptions. If they are not, an error will be |
| # raised when running the tests. |
| _parsed_command_line_options = None |
| |
| @classmethod |
| def SetParsedCommandLineOptions(cls, options): |
| cls._parsed_command_line_options = options |
| |
| @classmethod |
| def GetParsedCommandLineOptions(cls): |
| return cls._parsed_command_line_options |
| |
| @classmethod |
| def AddCommandlineArgs(cls, parser): |
| super(ScreenshotSyncIntegrationTest, cls).AddCommandlineArgs(parser) |
| parser.add_option( |
| '--dont-restore-color-profile-after-test', |
| dest='dont_restore_color_profile_after_test', |
| action='store_true', default=False, |
| help='(Mainly on Mac) don\'t restore the system\'s original color ' |
| 'profile after the test completes; leave the system using the sRGB color ' |
| 'profile. See http://crbug.com/784456.') |
| |
| @classmethod |
| def SetUpProcess(cls): |
| options = cls.GetParsedCommandLineOptions() |
| color_profile_manager.ForceUntilExitSRGB( |
| options.dont_restore_color_profile_after_test) |
| super(cls, ScreenshotSyncIntegrationTest).SetUpProcess() |
| cls.CustomizeBrowserArgs(cls._AddDefaultArgs([])) |
| cls.StartBrowser() |
| cls.SetStaticServerDirs([data_path]) |
| |
| @staticmethod |
| def _AddDefaultArgs(browser_args): |
| # --test-type=gpu is used to suppress the "Google API Keys are |
| # missing" infobar, which causes flakiness in tests. |
| return [ |
| '--force-color-profile=srgb', |
| '--ensure-forced-color-profile', |
| '--test-type=gpu'] + browser_args |
| |
| @classmethod |
| def _CreateExpectations(cls): |
| return screenshot_sync_expectations.ScreenshotSyncExpectations() |
| |
| @classmethod |
| def GenerateGpuTests(cls, options): |
| cls.SetParsedCommandLineOptions(options) |
| yield('ScreenshotSync_SWRasterWithCanvas', |
| 'screenshot_sync_canvas.html', |
| ('--disable-gpu-rasterization')) |
| yield('ScreenshotSync_SWRasterWithDivs', |
| 'screenshot_sync_divs.html', |
| ('--disable-gpu-rasterization')) |
| yield('ScreenshotSync_GPURasterWithCanvas', |
| 'screenshot_sync_canvas.html', |
| ('--force-gpu-rasterization')) |
| yield('ScreenshotSync_GPURasterWithDivs', |
| 'screenshot_sync_divs.html', |
| ('--force-gpu-rasterization')) |
| |
| def _Navigate(self, test_path): |
| url = self.UrlOfStaticFilePath(test_path) |
| # It's crucial to use the action_runner, rather than the tab's |
| # Navigate method directly. It waits for the document ready state |
| # to become interactive or better, avoiding critical race |
| # conditions. |
| self.tab.action_runner.Navigate(url) |
| |
| def _CheckColorMatchAtLocation(self, expectedRGB, screenshot, x, y): |
| pixel_value = image_util.GetPixelColor(screenshot, x, y) |
| # Allow for off-by-one errors due to color conversion. |
| tolerance = 1 |
| if not expectedRGB.IsEqual(pixel_value, tolerance): |
| error_message = ('Color mismatch at (%d, %d): expected (%d, %d, %d), ' + |
| 'got (%d, %d, %d)') % ( |
| x, y, expectedRGB.r, expectedRGB.g, expectedRGB.b, |
| pixel_value.r, pixel_value.g, pixel_value.b) |
| self.fail(error_message) |
| |
| def _CheckScreenshot(self): |
| canvasRGB = rgba_color.RgbaColor(random.randint(0, 255), |
| random.randint(0, 255), |
| random.randint(0, 255), |
| 255) |
| tab = self.tab |
| tab.EvaluateJavaScript( |
| "window.draw({{ red }}, {{ green }}, {{ blue }});", |
| red=canvasRGB.r, green=canvasRGB.g, blue=canvasRGB.b) |
| screenshot = tab.Screenshot(10) |
| # Avoid checking along antialiased boundary due to limited Adreno 3xx |
| # interpolation precision (crbug.com/847984). We inset by one CSS pixel |
| # adjusted by the device pixel ratio. |
| inset = int(math.ceil(tab.EvaluateJavaScript('window.devicePixelRatio'))) |
| # It seems that we should be able to set start_x to 2 * inset (one to |
| # account for the inner div having left=1 and one to avoid sampling the |
| # aa edge). For reasons not fully understood this is insufficent on |
| # several bots (N9, 6P, mac_chromium_rel_ng). |
| start_x = 10 |
| start_y = inset |
| outer_size = 256 - inset |
| skip = 10 |
| for y in range(start_y, outer_size, skip): |
| for x in range(start_x, outer_size, skip): |
| self._CheckColorMatchAtLocation(canvasRGB, screenshot, x, y) |
| |
| def RunActualGpuTest(self, test_path, *args): |
| browser_arg = args[0] |
| self.RestartBrowserIfNecessaryWithArgs(self._AddDefaultArgs([browser_arg])) |
| self._Navigate(test_path) |
| repetitions = 20 |
| for _ in range(0, repetitions): |
| self._CheckScreenshot() |
| |
| def load_tests(loader, tests, pattern): |
| del loader, tests, pattern # Unused. |
| return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__]) |