Chromium Code Reviews| Index: content/test/gpu/gpu_tests/context_lost_integration_test.py |
| diff --git a/content/test/gpu/gpu_tests/context_lost_integration_test.py b/content/test/gpu/gpu_tests/context_lost_integration_test.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a90ed33b4d06b00356147901b3277a7dd6f11341 |
| --- /dev/null |
| +++ b/content/test/gpu/gpu_tests/context_lost_integration_test.py |
| @@ -0,0 +1,254 @@ |
| +# Copyright 2016 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 time |
| + |
| +from gpu_tests import gpu_integration_test |
| +from gpu_tests import context_lost_expectations |
| +from gpu_tests import path_util |
| + |
| +from telemetry.core import exceptions |
| +from telemetry.core import util |
| + |
| +data_path = os.path.join( |
| + path_util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu') |
| + |
| +wait_timeout = 60 # seconds |
| + |
| +harness_script = r""" |
| + var domAutomationController = {}; |
| + |
| + domAutomationController._loaded = false; |
| + domAutomationController._succeeded = false; |
| + domAutomationController._finished = false; |
| + |
| + domAutomationController.setAutomationId = function(id) {} |
| + |
| + domAutomationController.send = function(msg) { |
| + msg = msg.toLowerCase() |
| + if (msg == "loaded") { |
| + domAutomationController._loaded = true; |
| + } else if (msg == "success") { |
| + /* Don't squelch earlier failures! */ |
| + if (!domAutomationController._finished) { |
| + domAutomationController._succeeded = true; |
| + } |
| + domAutomationController._finished = true; |
| + } else { |
| + /* Always record failures. */ |
| + domAutomationController._succeeded = false; |
| + domAutomationController._finished = true; |
| + } |
| + } |
| + |
| + domAutomationController.reset = function() { |
| + domAutomationController._succeeded = false; |
| + domAutomationController._finished = false; |
| + } |
| + |
| + window.domAutomationController = domAutomationController; |
| + console.log("Harness injected."); |
| +""" |
| + |
| +class ContextLostIntegrationTest(gpu_integration_test.GpuIntegrationTest): |
| + |
| + @classmethod |
| + def Name(cls): |
| + return 'context_lost' |
| + |
| + @classmethod |
| + def CustomizeOptions(cls): |
| + options = cls._finder_options.browser_options |
| + options.AppendExtraBrowserArgs( |
| + '--disable-domain-blocking-for-3d-apis') |
| + options.AppendExtraBrowserArgs( |
| + '--disable-gpu-process-crash-limit') |
| + # Required for about:gpucrash handling from Telemetry. |
| + options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') |
| + |
| + @classmethod |
| + def GenerateGpuTests(cls, options): |
| + tests = (('GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash', |
| + 'gpu_process_crash.html'), |
| + ('WebGLContextLostFromGPUProcessExit', |
| + 'webgl.html?query=kill_after_notification'), |
| + ('WebGLContextLostFromLoseContextExtension', |
| + 'webgl.html?query=WEBGL_lose_context'), |
| + ('WebGLContextLostFromQuantity', |
| + 'webgl.html?query=forced_quantity_loss'), |
| + ('WebGLContextLostFromSelectElement', |
| + 'webgl_with_select_element.html'), |
| + ('WebGLContextLostInHiddenTab', |
| + 'webgl.html?query=kill_after_notification')) |
| + for t in tests: |
| + yield (t[0], t[1], ('_' + t[0])) |
| + |
| + def RunActualGpuTest(self, test_path, *args): |
| + test_name = args[0] |
| + tab = self.tab |
| + if not tab.browser.supports_tab_control: |
| + self.fail('Browser must support tab control') |
| + getattr(self, test_name)(test_path) |
| + |
| + @classmethod |
| + def _CreateExpectations(cls): |
| + return context_lost_expectations.ContextLostExpectations() |
| + |
| + @classmethod |
| + def setUpClass(cls): |
| + super(cls, ContextLostIntegrationTest).setUpClass() |
| + cls.CustomizeOptions() |
| + cls.SetBrowserOptions(cls._finder_options) |
| + cls.StartBrowser() |
| + cls.SetStaticServerDir(data_path) |
| + |
| + def _WaitForPageToFinish(self, tab): |
| + try: |
| + util.WaitFor(lambda: tab.EvaluateJavaScript( |
| + 'window.domAutomationController._finished'), wait_timeout) |
| + return True |
| + except exceptions.TimeoutException: |
| + return False |
| + |
| + def _KillGPUProcess(self, tab, number_of_gpu_process_kills, |
| + check_crash_count): |
| + # Doing the GPU process kill operation cooperatively -- in the |
| + # same page's context -- is much more stressful than restarting |
| + # the browser every time. |
| + for x in range(number_of_gpu_process_kills): |
| + expected_kills = x + 1 |
| + |
| + # Reset the test's state. |
| + tab.EvaluateJavaScript( |
| + 'window.domAutomationController.reset()') |
| + |
| + # If we're running the GPU process crash test, we need the test |
| + # to have fully reset before crashing the GPU process. |
| + if check_crash_count: |
| + util.WaitFor(lambda: tab.EvaluateJavaScript( |
| + 'window.domAutomationController._finished'), wait_timeout) |
| + |
| + # Crash the GPU process. |
| + gpucrash_tab = tab.browser.tabs.New() |
| + # To access these debug URLs from Telemetry, they have to be |
| + # written using the chrome:// scheme. |
| + # The try/except is a workaround for crbug.com/368107. |
| + try: |
| + gpucrash_tab.Navigate('chrome://gpucrash') |
| + except Exception: |
| + print 'Tab crashed while navigating to chrome://gpucrash' |
| + # Activate the original tab and wait for completion. |
| + tab.Activate() |
| + completed = self._WaitForPageToFinish(tab) |
| + |
| + if check_crash_count: |
| + self._CheckCrashCount(tab, expected_kills) |
| + |
| + # The try/except is a workaround for crbug.com/368107. |
| + try: |
| + gpucrash_tab.Close() |
| + except Exception: |
| + print 'Tab crashed while closing chrome://gpucrash' |
| + if not completed: |
| + self.fail('Test didn\'t complete (no context lost event?)') |
| + if not tab.EvaluateJavaScript( |
| + 'window.domAutomationController._succeeded'): |
| + self.fail('Test failed (context not restored properly?)') |
| + |
| + def _CheckCrashCount(self, tab, expected_kills): |
| + if not tab.browser.supports_system_info: |
| + self.fail('Browser must support system info') |
| + |
| + if not tab.EvaluateJavaScript( |
| + 'window.domAutomationController._succeeded'): |
| + self.fail('Test failed (didn\'t render content properly?)') |
| + |
| + number_of_crashes = -1 |
| + # To allow time for a gpucrash to complete, wait up to 20s, |
| + # polling repeatedly. |
| + start_time = time.time() |
| + current_time = time.time() |
| + while current_time - start_time < 20: |
| + system_info = tab.browser.GetSystemInfo() |
| + number_of_crashes = \ |
| + system_info.gpu.aux_attributes[u'process_crash_count'] |
| + if number_of_crashes >= expected_kills: |
| + break |
| + time.sleep(1) |
| + current_time = time.time() |
| + |
| + # Wait 5 more seconds and re-read process_crash_count, in |
| + # attempt to catch latent process crashes. |
| + time.sleep(5) |
| + system_info = tab.browser.GetSystemInfo() |
| + number_of_crashes = \ |
| + system_info.gpu.aux_attributes[u'process_crash_count'] |
| + |
| + if number_of_crashes < expected_kills: |
| + self.fail('Timed out waiting for a gpu process crash') |
| + elif number_of_crashes != expected_kills: |
| + self.fail('Expected %d gpu process crashes; got: %d' % |
| + (expected_kills, number_of_crashes)) |
| + |
| + def _NavigateAndWaitForLoad(self, tab, test_path): |
| + url = self.UrlOfStaticFilePath(test_path) |
| + tab = self.tab |
| + tab.Navigate(url, script_to_evaluate_on_commit=harness_script) |
| + tab.action_runner.WaitForJavaScriptCondition( |
| + 'window.domAutomationController._loaded') |
| + |
| + def _WaitForTabAndCheckCompletion(self, tab): |
| + completed = self._WaitForPageToFinish(tab) |
| + if not completed: |
| + self.fail('Test didn\'t complete (no context restored event?)') |
| + if not tab.EvaluateJavaScript('window.domAutomationController._succeeded'): |
| + self.fail('Test failed (context not restored properly?)') |
| + |
| + # 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. |
| + def _GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash(self, test_path): |
| + tab = self.tab |
| + self._NavigateAndWaitForLoad(tab, test_path) |
| + self._KillGPUProcess(tab, 2, True) |
| + self._RestartBrowser('must restart after tests that kill the GPU process') |
| + |
| + def _WebGLContextLostFromGPUProcessExit(self, test_path): |
| + tab = self.tab |
|
eyaich1
2016/08/24 13:28:53
remove this line, done in _NavigateAndWaitForLoad
Ken Russell (switch to Gerrit)
2016/08/25 01:41:01
Done.
|
| + self._NavigateAndWaitForLoad(tab, test_path) |
| + self._KillGPUProcess(tab, 1, False) |
| + self._RestartBrowser('must restart after tests that kill the GPU process') |
| + |
| + def _WebGLContextLostFromLoseContextExtension(self, test_path): |
| + url = self.UrlOfStaticFilePath(test_path) |
| + tab = self.tab |
| + tab.Navigate(url, script_to_evaluate_on_commit=harness_script) |
| + tab.action_runner.WaitForJavaScriptCondition( |
| + 'window.domAutomationController._finished') |
|
eyaich1
2016/08/24 13:28:53
Can't you replace this entire function with self._
Ken Russell (switch to Gerrit)
2016/08/25 01:41:01
No, because this one waits for _finished rather th
|
| + |
| + def _WebGLContextLostFromQuantity(self, test_path): |
| + tab = self.tab |
|
eyaich1
2016/08/24 13:28:53
remove this line, done in _NavigateAndWaitForLoad
Ken Russell (switch to Gerrit)
2016/08/25 01:41:01
This method calls self.tab.CollectGarbage(), but I
|
| + self._NavigateAndWaitForLoad(tab, test_path) |
| + # Try to corce GC to clean up any contexts not attached to the page. |
| + # This method seems unreliable, so the page will also attempt to |
| + # force GC through excessive allocations. |
| + tab.CollectGarbage() |
| + self._WaitForTabAndCheckCompletion(tab) |
| + |
| + def _WebGLContextLostFromSelectElement(self, test_path): |
| + tab = self.tab |
|
eyaich1
2016/08/24 13:28:53
remove this line, done in _NavigateAndWaitForLoad
Ken Russell (switch to Gerrit)
2016/08/25 01:41:01
Done.
|
| + self._NavigateAndWaitForLoad(tab, test_path) |
| + self._WaitForTabAndCheckCompletion(tab) |
| + |
| + def _WebGLContextLostInHiddenTab(self, test_path): |
| + tab = self.tab |
|
eyaich1
2016/08/24 13:28:53
remove this line, done in _NavigateAndWaitForLoad
Ken Russell (switch to Gerrit)
2016/08/25 01:41:01
This one uses "tab" locally in a couple of places,
|
| + self._NavigateAndWaitForLoad(tab, test_path) |
| + # Test losing a context in a hidden tab. This test passes if the tab |
| + # doesn't crash. |
| + dummy_tab = tab.browser.tabs.New() |
| + tab.EvaluateJavaScript('loseContextUsingExtension()') |
| + tab.Activate() |
| + self._WaitForTabAndCheckCompletion(tab) |