| # 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. |
| |
| from __future__ import print_function |
| |
| import math |
| import os |
| import random |
| import sys |
| |
| from gpu_tests import color_profile_manager |
| from gpu_tests import common_browser_args as cba |
| from gpu_tests import gpu_integration_test |
| from gpu_tests import path_util |
| |
| 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.StartBrowser() |
| cls.SetStaticServerDirs([data_path]) |
| |
| @classmethod |
| def GenerateBrowserArgs(cls, additional_args): |
| """Adds default arguments to |additional_args|. |
| |
| See the parent class' method documentation for additional information. |
| """ |
| default_args = super(ScreenshotSyncIntegrationTest, |
| cls).GenerateBrowserArgs(additional_args) |
| default_args.extend([ |
| cba.FORCE_COLOR_PROFILE_SRGB, |
| cba.ENSURE_FORCED_COLOR_PROFILE, |
| # --test-type=gpu is used to suppress the "Google API Keys are |
| # missing" infobar, which causes flakiness in tests. |
| cba.TEST_TYPE_GPU, |
| ]) |
| return default_args |
| |
| @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', |
| (cba.ENABLE_GPU_RASTERIZATION)) |
| yield ('ScreenshotSync_GPURasterWithDivs', 'screenshot_sync_divs.html', |
| (cba.ENABLE_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 |
| # Pixel 4 devices require a slightly higher tolerance. See |
| # crbug.com/1166379. |
| if self.tab.browser.platform.GetDeviceTypeName() == 'Pixel 4': |
| tolerance = 7 |
| 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-rel). |
| 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([browser_arg]) |
| self._Navigate(test_path) |
| repetitions = 20 |
| for _ in range(0, repetitions): |
| self._CheckScreenshot() |
| |
| @classmethod |
| def ExpectationsFiles(cls): |
| return [ |
| os.path.join( |
| os.path.dirname(os.path.abspath(__file__)), 'test_expectations', |
| 'screenshot_sync_expectations.txt') |
| ] |
| |
| |
| def load_tests(loader, tests, pattern): |
| del loader, tests, pattern # Unused. |
| return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__]) |