blob: e69c1f115bb865d8a7c91a558b8061cd4305f55c [file] [log] [blame]
# Copyright 2017 The Chromium Authors
# 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 typing import Any, List
import unittest
from gpu_tests import color_profile_manager
from gpu_tests import common_browser_args as cba
from gpu_tests import common_typing as ct
from gpu_tests import gpu_integration_test
import gpu_path_util
from telemetry.util import image_util
from telemetry.util import rgba_color
class ScreenshotSyncIntegrationTest(gpu_integration_test.GpuIntegrationTest):
"""Tests that screenshots are properly synchronized with the frame on
which they were requested.
"""
@classmethod
def Name(cls) -> str:
"""The name by which this test is invoked on the command line."""
return 'screenshot_sync'
@classmethod
def AddCommandlineArgs(cls, parser: ct.CmdArgParser) -> None:
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) -> None:
super(cls, ScreenshotSyncIntegrationTest).SetUpProcess()
options = cls.GetOriginalFinderOptions()
color_profile_manager.ForceUntilExitSRGB(
options.dont_restore_color_profile_after_test)
cls.CustomizeBrowserArgs([])
cls.StartBrowser()
cls.SetStaticServerDirs([gpu_path_util.GPU_DATA_DIR])
@classmethod
def GenerateBrowserArgs(cls, additional_args: List[str]) -> List[str]:
"""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: ct.ParsedCmdArgs) -> ct.TestGenerator:
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: str) -> None:
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: rgba_color.RgbaColor,
screenshot: ct.Screenshot, x: int,
y: int) -> None:
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) -> None:
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: str, args: ct.TestArgs) -> None:
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) -> List[str]:
return [
os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'test_expectations',
'screenshot_sync_expectations.txt')
]
def load_tests(loader: unittest.TestLoader, tests: Any,
pattern: Any) -> unittest.TestSuite:
del loader, tests, pattern # Unused.
return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__])