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, tab, number_of_gpu_process_kills, | |
116 check_crash_count): | |
117 # Doing the GPU process kill operation cooperatively -- in the | |
118 # same page's context -- is much more stressful than restarting | |
119 # the browser every time. | |
120 for x in range(number_of_gpu_process_kills): | |
121 expected_kills = x + 1 | |
122 | |
123 # Reset the test's state. | |
124 tab.EvaluateJavaScript( | |
125 'window.domAutomationController.reset()') | |
126 | |
127 # If we're running the GPU process crash test, we need the test | |
128 # to have fully reset before crashing the GPU process. | |
129 if check_crash_count: | |
130 util.WaitFor(lambda: tab.EvaluateJavaScript( | |
131 'window.domAutomationController._finished'), wait_timeout) | |
132 | |
133 # Crash the GPU process. | |
134 gpucrash_tab = tab.browser.tabs.New() | |
135 # To access these debug URLs from Telemetry, they have to be | |
136 # written using the chrome:// scheme. | |
137 # The try/except is a workaround for crbug.com/368107. | |
138 try: | |
139 gpucrash_tab.Navigate('chrome://gpucrash') | |
140 except Exception: | |
141 print 'Tab crashed while navigating to chrome://gpucrash' | |
142 # Activate the original tab and wait for completion. | |
143 tab.Activate() | |
144 completed = self._WaitForPageToFinish(tab) | |
145 | |
146 if check_crash_count: | |
147 self._CheckCrashCount(tab, expected_kills) | |
148 | |
149 # The try/except is a workaround for crbug.com/368107. | |
150 try: | |
151 gpucrash_tab.Close() | |
152 except Exception: | |
153 print 'Tab crashed while closing chrome://gpucrash' | |
154 if not completed: | |
155 self.fail('Test didn\'t complete (no context lost event?)') | |
156 if not tab.EvaluateJavaScript( | |
157 'window.domAutomationController._succeeded'): | |
158 self.fail('Test failed (context not restored properly?)') | |
159 | |
160 def _CheckCrashCount(self, tab, expected_kills): | |
161 if not tab.browser.supports_system_info: | |
162 self.fail('Browser must support system info') | |
163 | |
164 if not tab.EvaluateJavaScript( | |
165 'window.domAutomationController._succeeded'): | |
166 self.fail('Test failed (didn\'t render content properly?)') | |
167 | |
168 number_of_crashes = -1 | |
169 # To allow time for a gpucrash to complete, wait up to 20s, | |
170 # polling repeatedly. | |
171 start_time = time.time() | |
172 current_time = time.time() | |
173 while current_time - start_time < 20: | |
174 system_info = tab.browser.GetSystemInfo() | |
175 number_of_crashes = \ | |
176 system_info.gpu.aux_attributes[u'process_crash_count'] | |
177 if number_of_crashes >= expected_kills: | |
178 break | |
179 time.sleep(1) | |
180 current_time = time.time() | |
181 | |
182 # Wait 5 more seconds and re-read process_crash_count, in | |
183 # attempt to catch latent process crashes. | |
184 time.sleep(5) | |
185 system_info = tab.browser.GetSystemInfo() | |
186 number_of_crashes = \ | |
187 system_info.gpu.aux_attributes[u'process_crash_count'] | |
188 | |
189 if number_of_crashes < expected_kills: | |
190 self.fail('Timed out waiting for a gpu process crash') | |
191 elif number_of_crashes != expected_kills: | |
192 self.fail('Expected %d gpu process crashes; got: %d' % | |
193 (expected_kills, number_of_crashes)) | |
194 | |
195 def _NavigateAndWaitForLoad(self, tab, test_path): | |
196 url = self.UrlOfStaticFilePath(test_path) | |
197 tab = self.tab | |
198 tab.Navigate(url, script_to_evaluate_on_commit=harness_script) | |
199 tab.action_runner.WaitForJavaScriptCondition( | |
200 'window.domAutomationController._loaded') | |
201 | |
202 def _WaitForTabAndCheckCompletion(self, tab): | |
203 completed = self._WaitForPageToFinish(tab) | |
204 if not completed: | |
205 self.fail('Test didn\'t complete (no context restored event?)') | |
206 if not tab.EvaluateJavaScript('window.domAutomationController._succeeded'): | |
207 self.fail('Test failed (context not restored properly?)') | |
208 | |
209 # The browser test runner synthesizes methods with the exact name | |
210 # given in GenerateGpuTests, so in order to hand-write our tests but | |
211 # also go through the _RunGpuTest trampoline, the test needs to be | |
212 # slightly differently named. | |
213 def _GPUProcessCrashesExactlyOncePerVisitToAboutGpuCrash(self, test_path): | |
214 tab = self.tab | |
215 self._NavigateAndWaitForLoad(tab, test_path) | |
216 self._KillGPUProcess(tab, 2, True) | |
217 self._RestartBrowser('must restart after tests that kill the GPU process') | |
218 | |
219 def _WebGLContextLostFromGPUProcessExit(self, test_path): | |
220 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.
| |
221 self._NavigateAndWaitForLoad(tab, test_path) | |
222 self._KillGPUProcess(tab, 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') | |
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
| |
231 | |
232 def _WebGLContextLostFromQuantity(self, test_path): | |
233 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
| |
234 self._NavigateAndWaitForLoad(tab, test_path) | |
235 # Try to corce GC to clean up any contexts not attached to the page. | |
236 # This method seems unreliable, so the page will also attempt to | |
237 # force GC through excessive allocations. | |
238 tab.CollectGarbage() | |
239 self._WaitForTabAndCheckCompletion(tab) | |
240 | |
241 def _WebGLContextLostFromSelectElement(self, test_path): | |
242 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.
| |
243 self._NavigateAndWaitForLoad(tab, test_path) | |
244 self._WaitForTabAndCheckCompletion(tab) | |
245 | |
246 def _WebGLContextLostInHiddenTab(self, test_path): | |
247 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,
| |
248 self._NavigateAndWaitForLoad(tab, test_path) | |
249 # Test losing a context in a hidden tab. This test passes if the tab | |
250 # doesn't crash. | |
251 dummy_tab = tab.browser.tabs.New() | |
252 tab.EvaluateJavaScript('loseContextUsingExtension()') | |
253 tab.Activate() | |
254 self._WaitForTabAndCheckCompletion(tab) | |
OLD | NEW |