OLD | NEW |
(Empty) | |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import os |
| 6 import time |
| 7 |
| 8 from gpu_tests import gpu_integration_test |
| 9 from gpu_tests import context_lost_expectations |
| 10 from gpu_tests import path_util |
| 11 |
| 12 from telemetry.core import exceptions |
| 13 from telemetry.core import util |
| 14 |
| 15 data_path = os.path.join( |
| 16 path_util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu') |
| 17 |
| 18 wait_timeout = 60 # seconds |
| 19 |
| 20 harness_script = r""" |
| 21 var domAutomationController = {}; |
| 22 |
| 23 domAutomationController._loaded = false; |
| 24 domAutomationController._succeeded = false; |
| 25 domAutomationController._finished = false; |
| 26 |
| 27 domAutomationController.setAutomationId = function(id) {} |
| 28 |
| 29 domAutomationController.send = function(msg) { |
| 30 msg = msg.toLowerCase() |
| 31 if (msg == "loaded") { |
| 32 domAutomationController._loaded = true; |
| 33 } else if (msg == "success") { |
| 34 /* Don't squelch earlier failures! */ |
| 35 if (!domAutomationController._finished) { |
| 36 domAutomationController._succeeded = true; |
| 37 } |
| 38 domAutomationController._finished = true; |
| 39 } else { |
| 40 /* Always record failures. */ |
| 41 domAutomationController._succeeded = false; |
| 42 domAutomationController._finished = true; |
| 43 } |
| 44 } |
| 45 |
| 46 domAutomationController.reset = function() { |
| 47 domAutomationController._succeeded = false; |
| 48 domAutomationController._finished = false; |
| 49 } |
| 50 |
| 51 window.domAutomationController = domAutomationController; |
| 52 console.log("Harness injected."); |
| 53 """ |
| 54 |
| 55 class ContextLostIntegrationTest(gpu_integration_test.GpuIntegrationTest): |
| 56 |
| 57 @classmethod |
| 58 def Name(cls): |
| 59 return 'context_lost' |
| 60 |
| 61 @classmethod |
| 62 def CustomizeOptions(cls): |
| 63 options = cls._finder_options.browser_options |
| 64 options.AppendExtraBrowserArgs( |
| 65 '--disable-domain-blocking-for-3d-apis') |
| 66 options.AppendExtraBrowserArgs( |
| 67 '--disable-gpu-process-crash-limit') |
| 68 # Required for about:gpucrash handling from Telemetry. |
| 69 options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') |
| 70 |
| 71 @classmethod |
| 72 def GenerateGpuTests(cls, options): |
| 73 tests = (('GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash', |
| 74 'gpu_process_crash.html'), |
| 75 ('WebGLContextLostFromGPUProcessExit', |
| 76 'webgl.html?query=kill_after_notification'), |
| 77 ('WebGLContextLostFromLoseContextExtension', |
| 78 'webgl.html?query=WEBGL_lose_context'), |
| 79 ('WebGLContextLostFromQuantity', |
| 80 'webgl.html?query=forced_quantity_loss'), |
| 81 ('WebGLContextLostFromSelectElement', |
| 82 'webgl_with_select_element.html'), |
| 83 ('WebGLContextLostInHiddenTab', |
| 84 'webgl.html?query=kill_after_notification')) |
| 85 for t in tests: |
| 86 yield (t[0], t[1], ('_' + t[0])) |
| 87 |
| 88 def RunActualGpuTest(self, test_path, *args): |
| 89 test_name = args[0] |
| 90 tab = self.tab |
| 91 if not tab.browser.supports_tab_control: |
| 92 self.fail('Browser must support tab control') |
| 93 getattr(self, test_name)(test_path) |
| 94 |
| 95 @classmethod |
| 96 def _CreateExpectations(cls): |
| 97 return context_lost_expectations.ContextLostExpectations() |
| 98 |
| 99 @classmethod |
| 100 def setUpClass(cls): |
| 101 super(cls, ContextLostIntegrationTest).setUpClass() |
| 102 cls.CustomizeOptions() |
| 103 cls.SetBrowserOptions(cls._finder_options) |
| 104 cls.StartBrowser() |
| 105 cls.SetStaticServerDir(data_path) |
| 106 |
| 107 def _WaitForPageToFinish(self, tab): |
| 108 try: |
| 109 util.WaitFor(lambda: tab.EvaluateJavaScript( |
| 110 'window.domAutomationController._finished'), wait_timeout) |
| 111 return True |
| 112 except exceptions.TimeoutException: |
| 113 return False |
| 114 |
| 115 def _KillGPUProcess(self, number_of_gpu_process_kills, |
| 116 check_crash_count): |
| 117 tab = self.tab |
| 118 # Doing the GPU process kill operation cooperatively -- in the |
| 119 # same page's context -- is much more stressful than restarting |
| 120 # the browser every time. |
| 121 for x in range(number_of_gpu_process_kills): |
| 122 expected_kills = x + 1 |
| 123 |
| 124 # Reset the test's state. |
| 125 tab.EvaluateJavaScript( |
| 126 'window.domAutomationController.reset()') |
| 127 |
| 128 # If we're running the GPU process crash test, we need the test |
| 129 # to have fully reset before crashing the GPU process. |
| 130 if check_crash_count: |
| 131 util.WaitFor(lambda: tab.EvaluateJavaScript( |
| 132 'window.domAutomationController._finished'), wait_timeout) |
| 133 |
| 134 # Crash the GPU process. |
| 135 gpucrash_tab = tab.browser.tabs.New() |
| 136 # To access these debug URLs from Telemetry, they have to be |
| 137 # written using the chrome:// scheme. |
| 138 # The try/except is a workaround for crbug.com/368107. |
| 139 try: |
| 140 gpucrash_tab.Navigate('chrome://gpucrash') |
| 141 except Exception: |
| 142 print 'Tab crashed while navigating to chrome://gpucrash' |
| 143 # Activate the original tab and wait for completion. |
| 144 tab.Activate() |
| 145 completed = self._WaitForPageToFinish(tab) |
| 146 |
| 147 if check_crash_count: |
| 148 self._CheckCrashCount(tab, expected_kills) |
| 149 |
| 150 # The try/except is a workaround for crbug.com/368107. |
| 151 try: |
| 152 gpucrash_tab.Close() |
| 153 except Exception: |
| 154 print 'Tab crashed while closing chrome://gpucrash' |
| 155 if not completed: |
| 156 self.fail('Test didn\'t complete (no context lost event?)') |
| 157 if not tab.EvaluateJavaScript( |
| 158 'window.domAutomationController._succeeded'): |
| 159 self.fail('Test failed (context not restored properly?)') |
| 160 |
| 161 def _CheckCrashCount(self, tab, expected_kills): |
| 162 if not tab.browser.supports_system_info: |
| 163 self.fail('Browser must support system info') |
| 164 |
| 165 if not tab.EvaluateJavaScript( |
| 166 'window.domAutomationController._succeeded'): |
| 167 self.fail('Test failed (didn\'t render content properly?)') |
| 168 |
| 169 number_of_crashes = -1 |
| 170 # To allow time for a gpucrash to complete, wait up to 20s, |
| 171 # polling repeatedly. |
| 172 start_time = time.time() |
| 173 current_time = time.time() |
| 174 while current_time - start_time < 20: |
| 175 system_info = tab.browser.GetSystemInfo() |
| 176 number_of_crashes = \ |
| 177 system_info.gpu.aux_attributes[u'process_crash_count'] |
| 178 if number_of_crashes >= expected_kills: |
| 179 break |
| 180 time.sleep(1) |
| 181 current_time = time.time() |
| 182 |
| 183 # Wait 5 more seconds and re-read process_crash_count, in |
| 184 # attempt to catch latent process crashes. |
| 185 time.sleep(5) |
| 186 system_info = tab.browser.GetSystemInfo() |
| 187 number_of_crashes = \ |
| 188 system_info.gpu.aux_attributes[u'process_crash_count'] |
| 189 |
| 190 if number_of_crashes < expected_kills: |
| 191 self.fail('Timed out waiting for a gpu process crash') |
| 192 elif number_of_crashes != expected_kills: |
| 193 self.fail('Expected %d gpu process crashes; got: %d' % |
| 194 (expected_kills, number_of_crashes)) |
| 195 |
| 196 def _NavigateAndWaitForLoad(self, test_path): |
| 197 url = self.UrlOfStaticFilePath(test_path) |
| 198 tab = self.tab |
| 199 tab.Navigate(url, script_to_evaluate_on_commit=harness_script) |
| 200 tab.action_runner.WaitForJavaScriptCondition( |
| 201 'window.domAutomationController._loaded') |
| 202 |
| 203 def _WaitForTabAndCheckCompletion(self): |
| 204 tab = self.tab |
| 205 completed = self._WaitForPageToFinish(tab) |
| 206 if not completed: |
| 207 self.fail('Test didn\'t complete (no context restored event?)') |
| 208 if not tab.EvaluateJavaScript('window.domAutomationController._succeeded'): |
| 209 self.fail('Test failed (context not restored properly?)') |
| 210 |
| 211 # The browser test runner synthesizes methods with the exact name |
| 212 # given in GenerateGpuTests, so in order to hand-write our tests but |
| 213 # also go through the _RunGpuTest trampoline, the test needs to be |
| 214 # slightly differently named. |
| 215 def _GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash(self, test_path): |
| 216 self._NavigateAndWaitForLoad(test_path) |
| 217 self._KillGPUProcess(2, True) |
| 218 self._RestartBrowser('must restart after tests that kill the GPU process') |
| 219 |
| 220 def _WebGLContextLostFromGPUProcessExit(self, test_path): |
| 221 self._NavigateAndWaitForLoad(test_path) |
| 222 self._KillGPUProcess(1, False) |
| 223 self._RestartBrowser('must restart after tests that kill the GPU process') |
| 224 |
| 225 def _WebGLContextLostFromLoseContextExtension(self, test_path): |
| 226 url = self.UrlOfStaticFilePath(test_path) |
| 227 tab = self.tab |
| 228 tab.Navigate(url, script_to_evaluate_on_commit=harness_script) |
| 229 tab.action_runner.WaitForJavaScriptCondition( |
| 230 'window.domAutomationController._finished') |
| 231 |
| 232 def _WebGLContextLostFromQuantity(self, test_path): |
| 233 self._NavigateAndWaitForLoad(test_path) |
| 234 # Try to coerce GC to clean up any contexts not attached to the page. |
| 235 # This method seems unreliable, so the page will also attempt to |
| 236 # force GC through excessive allocations. |
| 237 self.tab.CollectGarbage() |
| 238 self._WaitForTabAndCheckCompletion() |
| 239 |
| 240 def _WebGLContextLostFromSelectElement(self, test_path): |
| 241 self._NavigateAndWaitForLoad(test_path) |
| 242 self._WaitForTabAndCheckCompletion() |
| 243 |
| 244 def _WebGLContextLostInHiddenTab(self, test_path): |
| 245 self._NavigateAndWaitForLoad(test_path) |
| 246 # Test losing a context in a hidden tab. This test passes if the tab |
| 247 # doesn't crash. |
| 248 tab = self.tab |
| 249 dummy_tab = tab.browser.tabs.New() |
| 250 tab.EvaluateJavaScript('loseContextUsingExtension()') |
| 251 tab.Activate() |
| 252 self._WaitForTabAndCheckCompletion() |
OLD | NEW |