| # 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 os |
| import sys |
| |
| from gpu_tests import gpu_integration_test |
| from gpu_tests import gpu_process_expectations |
| from gpu_tests import path_util |
| |
| data_path = os.path.join( |
| path_util.GetChromiumSrcDir(), 'content', 'test', 'data') |
| |
| test_harness_script = r""" |
| var domAutomationController = {}; |
| domAutomationController._finished = false; |
| domAutomationController._succeeded = false; |
| domAutomationController.send = function(msg) { |
| domAutomationController._finished = true; |
| if (msg.toLowerCase() == "finished") { |
| domAutomationController._succeeded = true; |
| } else { |
| domAutomationController._succeeded = false; |
| } |
| } |
| |
| window.domAutomationController = domAutomationController; |
| |
| function GetDriverBugWorkarounds() { |
| var query_result = document.querySelector('.workarounds-list'); |
| var browser_list = [] |
| for (var i=0; i < query_result.childElementCount; i++) |
| browser_list.push(query_result.children[i].textContent); |
| return browser_list; |
| }; |
| """ |
| |
| class GpuProcessIntegrationTest(gpu_integration_test.GpuIntegrationTest): |
| @classmethod |
| def Name(cls): |
| """The name by which this test is invoked on the command line.""" |
| return 'gpu_process' |
| |
| @classmethod |
| def SetUpProcess(cls): |
| super(GpuProcessIntegrationTest, cls).SetUpProcess() |
| cls.CustomizeBrowserArgs(cls._AddDefaultArgs([])) |
| cls.StartBrowser() |
| cls.SetStaticServerDirs([data_path]) |
| |
| @staticmethod |
| def _AddDefaultArgs(browser_args): |
| # All tests receive the following options. |
| return [ |
| '--enable-gpu-benchmarking', |
| # TODO(kbr): figure out why the following option seems to be |
| # needed on Android for robustness. |
| # https://github.com/catapult-project/catapult/issues/3122 |
| '--no-first-run'] + browser_args |
| |
| @classmethod |
| def _CreateExpectations(cls): |
| return gpu_process_expectations.GpuProcessExpectations() |
| |
| @classmethod |
| def GenerateGpuTests(cls, options): |
| # The browser test runner synthesizes methods with the exact name |
| # given in GenerateGpuTests, so in order to hand-write our tests but |
| # also go through the _RunGpuTest trampoline, the test needs to be |
| # slightly differently named. |
| |
| # Also note that since functional_video.html refers to files in |
| # ../media/ , the serving dir must be the common parent directory. |
| tests = (('GpuProcess_canvas2d', 'gpu/functional_canvas_demo.html'), |
| ('GpuProcess_css3d', 'gpu/functional_3d_css.html'), |
| ('GpuProcess_webgl', 'gpu/functional_webgl.html'), |
| ('GpuProcess_video', 'gpu/functional_video.html'), |
| ('GpuProcess_gpu_info_complete', 'gpu/functional_3d_css.html'), |
| ('GpuProcess_driver_bug_workarounds_in_gpu_process', 'chrome:gpu'), |
| ('GpuProcess_readback_webgl_gpu_process', 'chrome:gpu'), |
| ('GpuProcess_only_one_workaround', 'chrome:gpu'), |
| ('GpuProcess_disable_gpu', 'gpu/functional_webgl.html'), |
| ('GpuProcess_disable_gpu_and_swiftshader', |
| 'gpu/functional_webgl.html'), |
| ('GpuProcess_disabling_workarounds_works', 'chrome:gpu'), |
| ('GpuProcess_swiftshader_for_webgl', 'gpu/functional_webgl.html'), |
| ('GpuProcess_webgl_disabled_extension', |
| 'gpu/functional_webgl_disabled_extension.html')) |
| |
| # The earlier has_transparent_visuals_gpu_process and |
| # no_transparent_visuals_gpu_process tests became no-ops in |
| # http://crrev.com/2347383002 and were deleted. |
| |
| for t in tests: |
| yield (t[0], t[1], ('_' + t[0])) |
| |
| def RunActualGpuTest(self, test_path, *args): |
| test_name = args[0] |
| getattr(self, test_name)(test_path) |
| |
| ###################################### |
| # Helper functions for the tests below |
| |
| 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, script_to_evaluate_on_commit=test_harness_script) |
| |
| def _NavigateAndWait(self, test_path): |
| self._Navigate(test_path) |
| tab = self.tab |
| tab.action_runner.WaitForJavaScriptCondition( |
| 'window.domAutomationController._finished', timeout=10) |
| if not tab.EvaluateJavaScript('window.domAutomationController._succeeded'): |
| self.fail('Test reported that it failed') |
| |
| def _VerifyGpuProcessPresent(self): |
| tab = self.tab |
| if not tab.EvaluateJavaScript('chrome.gpuBenchmarking.hasGpuChannel()'): |
| self.fail('No GPU channel detected') |
| |
| def _ValidateDriverBugWorkaroundsImpl(self, is_expected, workaround_name): |
| tab = self.tab |
| gpu_driver_bug_workarounds = tab.EvaluateJavaScript( |
| 'chrome.gpuBenchmarking.getGpuDriverBugWorkarounds()') |
| |
| is_present = workaround_name in gpu_driver_bug_workarounds |
| failure = False |
| if is_expected and not is_present: |
| failure = True |
| error_message = "is missing" |
| elif not is_expected and is_present: |
| failure = True |
| error_message = "is not expected" |
| |
| if failure: |
| print 'Test failed. Printing page contents:' |
| print tab.EvaluateJavaScript('document.body.innerHTML') |
| self.fail('%s %s workarounds: %s' |
| % (workaround_name, error_message, gpu_driver_bug_workarounds)) |
| |
| def _ValidateDriverBugWorkarounds(self, expected_workaround, |
| unexpected_workaround): |
| if not expected_workaround and not unexpected_workaround: |
| return |
| if expected_workaround: |
| self._ValidateDriverBugWorkaroundsImpl(True, expected_workaround) |
| if unexpected_workaround: |
| self._ValidateDriverBugWorkaroundsImpl(False, unexpected_workaround) |
| |
| # This can only be called from one of the tests, i.e., after the |
| # browser's been brought up once. |
| def _RunningOnAndroid(self): |
| options = self.__class__._original_finder_options.browser_options |
| return options.browser_type.startswith('android') |
| |
| def _SupportsSwiftShader(self): |
| # Currently we only enables SwiftShader on Windows and Linux. |
| return (sys.platform in ('cygwin', 'win32') or |
| (sys.platform.startswith('linux') and |
| not self._RunningOnAndroid())) |
| |
| def _CompareAndCaptureDriverBugWorkarounds(self): |
| tab = self.tab |
| if not tab.EvaluateJavaScript('chrome.gpuBenchmarking.hasGpuProcess()'): |
| self.fail('No GPU process detected') |
| |
| if not tab.EvaluateJavaScript('chrome.gpuBenchmarking.hasGpuChannel()'): |
| self.fail('No GPU channel detected') |
| |
| browser_list = tab.EvaluateJavaScript('GetDriverBugWorkarounds()') |
| gpu_list = tab.EvaluateJavaScript( |
| 'chrome.gpuBenchmarking.getGpuDriverBugWorkarounds()') |
| |
| diff = set(browser_list).symmetric_difference(set(gpu_list)) |
| if len(diff) > 0: |
| print 'Test failed. Printing page contents:' |
| print tab.EvaluateJavaScript('document.body.innerHTML') |
| self.fail('Browser and GPU process list of driver bug' |
| 'workarounds are not equal: %s != %s, diff: %s' % |
| (browser_list, gpu_list, list(diff))) |
| |
| basic_infos = tab.EvaluateJavaScript('browserBridge.gpuInfo.basic_info') |
| disabled_gl_extensions = None |
| for info in basic_infos: |
| if info['description'].startswith('Disabled Extensions'): |
| disabled_gl_extensions = info['value'] |
| break |
| |
| return gpu_list, disabled_gl_extensions |
| |
| ###################################### |
| # The actual tests |
| |
| def _GpuProcess_canvas2d(self, test_path): |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._NavigateAndWait(test_path) |
| self._VerifyGpuProcessPresent() |
| |
| def _GpuProcess_css3d(self, test_path): |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._NavigateAndWait(test_path) |
| self._VerifyGpuProcessPresent() |
| |
| def _GpuProcess_webgl(self, test_path): |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._NavigateAndWait(test_path) |
| self._VerifyGpuProcessPresent() |
| |
| def _GpuProcess_video(self, test_path): |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._NavigateAndWait(test_path) |
| self._VerifyGpuProcessPresent() |
| |
| def _GpuProcess_gpu_info_complete(self, test_path): |
| # Regression test for crbug.com/454906 |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._NavigateAndWait(test_path) |
| tab = self.tab |
| if not tab.browser.supports_system_info: |
| self.fail('Browser must support system info') |
| system_info = tab.browser.GetSystemInfo() |
| if not system_info.gpu: |
| self.fail('Target machine must have a GPU') |
| if not system_info.gpu.aux_attributes: |
| self.fail('Browser must support GPU aux attributes') |
| if not 'gl_renderer' in system_info.gpu.aux_attributes: |
| self.fail('Browser must have gl_renderer in aux attribs') |
| if (sys.platform != 'darwin' and |
| len(system_info.gpu.aux_attributes['gl_renderer']) <= 0): |
| # On MacOSX we don't create a context to collect GL strings.1 |
| self.fail('Must have a non-empty gl_renderer string') |
| |
| def _GpuProcess_driver_bug_workarounds_in_gpu_process(self, test_path): |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--use_gpu_driver_workaround_for_testing']) |
| self._Navigate(test_path) |
| self._ValidateDriverBugWorkarounds( |
| 'use_gpu_driver_workaround_for_testing', None) |
| |
| def _GpuProcess_readback_webgl_gpu_process(self, test_path): |
| # Hit test group 1 with entry 152 from kSoftwareRenderingListEntries. |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--gpu-blacklist-test-group=1']) |
| self._Navigate(test_path) |
| feature_status_list = self.tab.EvaluateJavaScript( |
| 'browserBridge.gpuInfo.featureStatus.featureStatus') |
| result = True |
| for name, status in feature_status_list.items(): |
| if name == 'webgl': |
| result = result and status == 'enabled_readback' |
| elif name == 'webgl2': |
| result = result and status == 'unavailable_off' |
| else: |
| pass |
| if not result: |
| self.fail('WebGL readback setup failed: %s' % feature_status_list) |
| |
| def _GpuProcess_only_one_workaround(self, test_path): |
| # Start this test by launching the browser with no command line |
| # arguments. |
| self.RestartBrowserIfNecessaryWithArgs([]) |
| self._Navigate(test_path) |
| self._VerifyGpuProcessPresent() |
| recorded_workarounds, recorded_disabled_gl_extensions = ( |
| self._CompareAndCaptureDriverBugWorkarounds()) |
| # Relaunch the browser with OS-specific command line arguments. |
| # Trigger test group 1 with entry 215, where only |
| # use_gpu_driver_workaround_for_testing is enabled. |
| browser_args = ['--gpu-driver-bug-list-test-group=1'] |
| for workaround in recorded_workarounds: |
| browser_args.append('--' + workaround) |
| # Add the testing workaround to the recorded workarounds. |
| recorded_workarounds.append('use_gpu_driver_workaround_for_testing') |
| browser_args.append('--disable-gl-extensions=' + |
| recorded_disabled_gl_extensions) |
| self.RestartBrowserIfNecessaryWithArgs(browser_args) |
| self._Navigate(test_path) |
| self._VerifyGpuProcessPresent() |
| new_workarounds, new_disabled_gl_extensions = ( |
| self._CompareAndCaptureDriverBugWorkarounds()) |
| diff = set(recorded_workarounds).symmetric_difference(new_workarounds) |
| tab = self.tab |
| if len(diff) > 0: |
| print 'Test failed. Printing page contents:' |
| print tab.EvaluateJavaScript('document.body.innerHTML') |
| self.fail( |
| 'GPU process and expected list of driver bug ' |
| 'workarounds are not equal: %s != %s, diff: %s' % |
| (recorded_workarounds, new_workarounds, list(diff))) |
| if recorded_disabled_gl_extensions != new_disabled_gl_extensions: |
| print 'Test failed. Printing page contents:' |
| print tab.EvaluateJavaScript('document.body.innerHTML') |
| self.fail( |
| 'The expected disabled gl extensions are ' |
| 'incorrect: %s != %s:' % |
| (recorded_disabled_gl_extensions, new_disabled_gl_extensions)) |
| |
| def _GpuProcess_disable_gpu(self, test_path): |
| # This test loads functional_webgl.html so that there is a |
| # deliberate attempt to use an API which would start the GPU |
| # process. |
| if self._RunningOnAndroid(): |
| # Chrome on Android doesn't support software fallback, skip it. |
| # TODO(zmo): If this test runs on ChromeOS, we also need to skip it. |
| return |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--disable-gpu', |
| '--skip-gpu-data-loading']) |
| self._NavigateAndWait(test_path) |
| # On Windows or Linux, SwiftShader is enabled, so GPU process will still |
| # launch with SwiftShader. |
| supports_swiftshader = self._SupportsSwiftShader() |
| has_gpu_process = self.tab.EvaluateJavaScript( |
| 'chrome.gpuBenchmarking.hasGpuProcess()') |
| if supports_swiftshader and not has_gpu_process: |
| self.fail('GPU process not detected') |
| elif not supports_swiftshader and has_gpu_process: |
| self.fail('GPU process detected') |
| |
| def _GpuProcess_disable_gpu_and_swiftshader(self, test_path): |
| # Disable SwiftShader, so GPU process should not launch anywhere. |
| if self._RunningOnAndroid(): |
| # Chrome on Android doesn't support software fallback, skip it. |
| # TODO(zmo): If this test runs on ChromeOS, we also need to skip it. |
| return |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--disable-gpu', |
| '--skip-gpu-data-loading', |
| '--disable-software-rasterizer']) |
| self._NavigateAndWait(test_path) |
| if self.tab.EvaluateJavaScript('chrome.gpuBenchmarking.hasGpuProcess()'): |
| self.fail('GPU process detected') |
| |
| def _GpuProcess_disabling_workarounds_works(self, test_path): |
| # Hit exception from id 215 from kGpuDriverBugListEntries. |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--gpu-driver-bug-list-test-group=1', |
| '--use_gpu_driver_workaround_for_testing=0']) |
| self._Navigate(test_path) |
| workarounds, _ = ( |
| self._CompareAndCaptureDriverBugWorkarounds()) |
| if 'use_gpu_driver_workaround_for_testing' in workarounds: |
| self.fail('use_gpu_driver_workaround_for_testing erroneously present') |
| |
| def _GpuProcess_swiftshader_for_webgl(self, test_path): |
| # This test loads functional_webgl.html so that there is a deliberate |
| # attempt to use an API which would start the GPU process. |
| # On platforms where SwiftShader is not supported, skip this test. |
| if not self._SupportsSwiftShader(): |
| return |
| args_list = ( |
| # Triggering test_group 2 where WebGL is blacklisted. |
| ['--gpu-blacklist-test-group=2'], |
| # Explicitly disable GPU access. |
| ['--disable-gpu']) |
| for args in args_list: |
| self.RestartBrowserIfNecessaryWithArgs(args) |
| self._NavigateAndWait(test_path) |
| # Validate the WebGL unmasked renderer string. |
| renderer = self.tab.EvaluateJavaScript('gl_renderer') |
| if not renderer: |
| self.fail('getParameter(UNMASKED_RENDERER_WEBGL) was null') |
| if 'SwiftShader' not in renderer: |
| self.fail('Expected SwiftShader renderer; instead got ' + renderer) |
| # Validate GPU info. |
| if not self.browser.supports_system_info: |
| self.fail("Browser doesn't support GetSystemInfo") |
| gpu = self.browser.GetSystemInfo().gpu |
| if not gpu: |
| self.fail('Target machine must have a GPU') |
| if not gpu.aux_attributes: |
| self.fail('Browser must support GPU aux attributes') |
| if not gpu.aux_attributes['software_rendering']: |
| self.fail("Software rendering was disabled") |
| if 'SwiftShader' not in gpu.aux_attributes['gl_renderer']: |
| self.fail("Expected 'SwiftShader' in GPU info GL renderer string") |
| if 'Google' not in gpu.aux_attributes['gl_vendor']: |
| self.fail("Expected 'Google' in GPU info GL vendor string") |
| device = gpu.devices[0] |
| if not device: |
| self.fail("System Info doesn't have a device") |
| # Validate extensions. |
| ext_list = [ |
| 'ANGLE_instanced_arrays', |
| 'EXT_blend_minmax', |
| 'EXT_texture_filter_anisotropic', |
| 'WEBKIT_EXT_texture_filter_anisotropic', |
| 'OES_element_index_uint', |
| 'OES_standard_derivatives', |
| 'OES_texture_float', |
| 'OES_texture_float_linear', |
| 'OES_texture_half_float', |
| 'OES_texture_half_float_linear', |
| 'OES_vertex_array_object', |
| 'WEBGL_compressed_texture_etc1', |
| 'WEBGL_debug_renderer_info', |
| 'WEBGL_debug_shaders', |
| 'WEBGL_depth_texture', |
| 'WEBKIT_WEBGL_depth_texture', |
| 'WEBGL_draw_buffers', |
| 'WEBGL_lose_context', |
| 'WEBKIT_WEBGL_lose_context', |
| ] |
| tab = self.tab |
| for ext in ext_list: |
| if tab.EvaluateJavaScript('!gl_context.getExtension("' + ext + '")'): |
| self.fail("Expected " + ext + " support") |
| |
| def _GpuProcess_webgl_disabled_extension(self, test_path): |
| # Hit exception from id 257 from kGpuDriverBugListEntries. |
| self.RestartBrowserIfNecessaryWithArgs([ |
| '--gpu-driver-bug-list-test-group=2', |
| ]) |
| self._NavigateAndWait(test_path) |
| |
| def load_tests(loader, tests, pattern): |
| del loader, tests, pattern # Unused. |
| return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__]) |