1 # Copyright (c) 2013 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.
7 import context_lost_expectations
10 from telemetry
.core
import exceptions
11 from telemetry
.core
import util
12 from telemetry
.page
import page_test
13 from telemetry
.story
import story_set
as story_set_module
15 data_path
= os
.path
.join(
16 util
.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu')
18 wait_timeout
= 60 # seconds
21 var domAutomationController = {};
23 domAutomationController._loaded = false;
24 domAutomationController._succeeded = false;
25 domAutomationController._finished = false;
27 domAutomationController.setAutomationId = function(id) {}
29 domAutomationController.send = function(msg) {
30 msg = msg.toLowerCase()
31 if (msg == "loaded") {
32 domAutomationController._loaded = true;
33 } else if (msg == "success") {
34 domAutomationController._succeeded = true;
35 domAutomationController._finished = true;
37 domAutomationController._succeeded = false;
38 domAutomationController._finished = true;
42 domAutomationController.reset = function() {
43 domAutomationController._succeeded = false;
44 domAutomationController._finished = false;
47 window.domAutomationController = domAutomationController;
48 console.log("Harness injected.");
51 class ContextLostValidator(gpu_test_base
.ValidatorBase
):
53 # Strictly speaking this test doesn't yet need a browser restart
54 # after each run, but if more tests are added which crash the GPU
55 # process, then it will.
56 super(ContextLostValidator
, self
).__init
__(
57 needs_browser_restart_after_each_page
=True)
59 def CustomizeBrowserOptions(self
, options
):
60 options
.AppendExtraBrowserArgs(
61 '--disable-domain-blocking-for-3d-apis')
62 options
.AppendExtraBrowserArgs(
63 '--disable-gpu-process-crash-limit')
64 # Required for about:gpucrash handling from Telemetry.
65 options
.AppendExtraBrowserArgs('--enable-gpu-benchmarking')
67 def ValidateAndMeasurePageInner(self
, page
, tab
, results
):
68 def WaitForPageToFinish():
69 print "Waiting for page to finish."
71 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
72 'window.domAutomationController._finished'), wait_timeout
)
74 except exceptions
.TimeoutException
:
77 if page
.kill_gpu_process
:
78 # Doing the GPU process kill operation cooperatively -- in the
79 # same page's context -- is much more stressful than restarting
80 # the browser every time.
81 for x
in range(page
.number_of_gpu_process_kills
):
82 if not tab
.browser
.supports_tab_control
:
83 raise page_test
.Failure('Browser must support tab control')
85 expected_kills
= x
+ 1
87 # Reset the test's state.
88 tab
.EvaluateJavaScript(
89 'window.domAutomationController.reset()');
91 # If we're running the GPU process crash test, we need the
92 # test to have fully reset before crashing the GPU process.
93 if page
.check_crash_count
:
94 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
95 'window.domAutomationController._finished'), wait_timeout
)
97 # Crash the GPU process.
98 gpucrash_tab
= tab
.browser
.tabs
.New()
99 # To access these debug URLs from Telemetry, they have to be
100 # written using the chrome:// scheme.
101 # The try/except is a workaround for crbug.com/368107.
103 gpucrash_tab
.Navigate('chrome://gpucrash')
105 print 'Tab crashed while navigating to chrome://gpucrash'
106 # Activate the original tab and wait for completion.
108 completed
= WaitForPageToFinish()
110 if page
.check_crash_count
:
111 if not tab
.browser
.supports_system_info
:
112 raise page_test
.Failure('Browser must support system info')
114 if not tab
.EvaluateJavaScript(
115 'window.domAutomationController._succeeded'):
116 raise page_test
.Failure(
117 'Test failed (didn\'t render content properly?)')
119 number_of_crashes
= -1
120 # To allow time for a gpucrash to complete, wait up to 20s,
121 # polling repeatedly.
122 start_time
= time
.time()
123 current_time
= time
.time()
124 while current_time
- start_time
< 20:
125 system_info
= tab
.browser
.GetSystemInfo()
126 number_of_crashes
= \
127 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
128 if number_of_crashes
>= expected_kills
:
131 current_time
= time
.time()
133 # Wait 5 more seconds and re-read process_crash_count, in
134 # attempt to catch latent process crashes.
136 system_info
= tab
.browser
.GetSystemInfo()
137 number_of_crashes
= \
138 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
140 if number_of_crashes
< expected_kills
:
141 raise page_test
.Failure(
142 'Timed out waiting for a gpu process crash')
143 elif number_of_crashes
!= expected_kills
:
144 raise page_test
.Failure(
145 'Expected %d gpu process crashes; got: %d' %
146 (expected_kills
, number_of_crashes
))
148 # The try/except is a workaround for crbug.com/368107.
152 print 'Tab crashed while closing chrome://gpucrash'
154 raise page_test
.Failure(
155 'Test didn\'t complete (no context lost event?)')
156 if not tab
.EvaluateJavaScript(
157 'window.domAutomationController._succeeded'):
158 raise page_test
.Failure(
159 'Test failed (context not restored properly?)')
160 elif page
.force_garbage_collection
:
161 # Try to corce GC to clean up any contexts not attached to the page.
162 # This method seem unreliable, so the page will also attempt to force
163 # GC through excessive allocations.
165 completed
= WaitForPageToFinish()
168 raise page_test
.Failure(
169 'Test didn\'t complete (no context restored event?)')
170 if not tab
.EvaluateJavaScript(
171 'window.domAutomationController._succeeded'):
172 raise page_test
.Failure(
173 'Test failed (context not restored properly?)')
174 elif page
.hide_tab_and_lose_context
:
175 if not tab
.browser
.supports_tab_control
:
176 raise page_test
.Failure('Browser must support tab control')
178 # Test losing a context in a hidden tab. This test passes if the tab
180 dummy_tab
= tab
.browser
.tabs
.New()
181 tab
.EvaluateJavaScript('loseContextUsingExtension()')
184 completed
= WaitForPageToFinish()
187 raise page_test
.Failure('Test didn\'t complete')
188 if not tab
.EvaluateJavaScript(
189 'window.domAutomationController._succeeded'):
190 raise page_test
.Failure('Test failed')
192 completed
= WaitForPageToFinish()
195 raise page_test
.Failure('Test didn\'t complete')
196 if not tab
.EvaluateJavaScript(
197 'window.domAutomationController._succeeded'):
198 raise page_test
.Failure('Test failed')
200 # Test that navigating to chrome://gpucrash causes the GPU process to crash
201 # exactly one time per navigation.
202 class GPUProcessCrashesExactlyOnce(gpu_test_base
.PageBase
):
203 def __init__(self
, story_set
, base_dir
, expectations
):
204 super(GPUProcessCrashesExactlyOnce
, self
).__init
__(
205 url
='file://gpu_process_crash.html',
208 name
='GpuCrash.GPUProcessCrashesExactlyOnce',
209 expectations
=expectations
)
210 self
.script_to_evaluate_on_commit
= harness_script
211 self
.kill_gpu_process
= True
212 self
.number_of_gpu_process_kills
= 2
213 self
.check_crash_count
= True
214 self
.force_garbage_collection
= False
215 self
.hide_tab_and_lose_context
= False
217 def RunNavigateStepsInner(self
, action_runner
):
218 action_runner
.WaitForJavaScriptCondition(
219 'window.domAutomationController._loaded')
221 class WebGLContextLostFromGPUProcessExitPage(gpu_test_base
.PageBase
):
222 def __init__(self
, story_set
, base_dir
, expectations
):
223 super(WebGLContextLostFromGPUProcessExitPage
, self
).__init
__(
224 url
='file://webgl.html?query=kill_after_notification',
227 name
='ContextLost.WebGLContextLostFromGPUProcessExit',
228 expectations
=expectations
)
229 self
.script_to_evaluate_on_commit
= harness_script
230 self
.kill_gpu_process
= True
231 self
.check_crash_count
= False
232 self
.number_of_gpu_process_kills
= 1
233 self
.force_garbage_collection
= False
234 self
.hide_tab_and_lose_context
= False
236 def RunNavigateStepsInner(self
, action_runner
):
237 action_runner
.WaitForJavaScriptCondition(
238 'window.domAutomationController._loaded')
241 class WebGLContextLostFromLoseContextExtensionPage(gpu_test_base
.PageBase
):
242 def __init__(self
, story_set
, base_dir
, expectations
):
243 super(WebGLContextLostFromLoseContextExtensionPage
, self
).__init
__(
244 url
='file://webgl.html?query=WEBGL_lose_context',
247 name
='ContextLost.WebGLContextLostFromLoseContextExtension',
248 expectations
=expectations
)
249 self
.script_to_evaluate_on_commit
= harness_script
250 self
.kill_gpu_process
= False
251 self
.check_crash_count
= False
252 self
.force_garbage_collection
= False
253 self
.hide_tab_and_lose_context
= False
255 def RunNavigateStepsInner(self
, action_runner
):
256 action_runner
.WaitForJavaScriptCondition(
257 'window.domAutomationController._finished')
260 class WebGLContextLostInHiddenTabPage(gpu_test_base
.PageBase
):
261 def __init__(self
, story_set
, base_dir
, expectations
):
262 super(WebGLContextLostInHiddenTabPage
, self
).__init
__(
263 url
='file://webgl.html?query=kill_after_notification',
266 name
='ContextLost.WebGLContextLostInHiddenTab',
267 expectations
=expectations
)
268 self
.script_to_evaluate_on_commit
= harness_script
269 self
.kill_gpu_process
= False
270 self
.check_crash_count
= False
271 self
.force_garbage_collection
= False
272 self
.hide_tab_and_lose_context
= True
274 def RunNavigateStepsInner(self
, action_runner
):
275 action_runner
.WaitForJavaScriptCondition(
276 'window.domAutomationController._loaded')
279 class WebGLContextLostFromQuantityPage(gpu_test_base
.PageBase
):
280 def __init__(self
, story_set
, base_dir
, expectations
):
281 super(WebGLContextLostFromQuantityPage
, self
).__init
__(
282 url
='file://webgl.html?query=forced_quantity_loss',
285 name
='ContextLost.WebGLContextLostFromQuantity',
286 expectations
=expectations
)
287 self
.script_to_evaluate_on_commit
= harness_script
288 self
.kill_gpu_process
= False
289 self
.check_crash_count
= False
290 self
.force_garbage_collection
= True
291 self
.hide_tab_and_lose_context
= False
293 def RunNavigateStepsInner(self
, action_runner
):
294 action_runner
.WaitForJavaScriptCondition(
295 'window.domAutomationController._loaded')
297 class WebGLContextLostFromSelectElementPage(gpu_test_base
.PageBase
):
298 def __init__(self
, story_set
, base_dir
, expectations
):
299 super(WebGLContextLostFromSelectElementPage
, self
).__init
__(
300 url
='file://webgl_with_select_element.html',
303 name
='ContextLost.WebGLContextLostFromSelectElement',
304 expectations
=expectations
)
305 self
.script_to_evaluate_on_commit
= harness_script
306 self
.kill_gpu_process
= False
307 self
.check_crash_count
= False
308 self
.force_garbage_collection
= False
309 self
.hide_tab_and_lose_context
= False
311 def RunNavigateStepsInner(self
, action_runner
):
312 action_runner
.WaitForJavaScriptCondition(
313 'window.domAutomationController._loaded')
315 class ContextLost(gpu_test_base
.TestBase
):
317 test
= ContextLostValidator
321 return 'context_lost'
323 def _CreateExpectations(self
):
324 return context_lost_expectations
.ContextLostExpectations()
326 # For the record, this would have been another way to get the pages
327 # to repeat. pageset_repeat would be another option.
328 # options = {'page_repeat': 5}
329 def CreateStorySet(self
, options
):
330 ps
= story_set_module
.StorySet(
332 serving_dirs
=set(['']))
333 ps
.AddStory(GPUProcessCrashesExactlyOnce(
334 ps
, ps
.base_dir
, self
.GetExpectations()))
335 ps
.AddStory(WebGLContextLostFromGPUProcessExitPage(
336 ps
, ps
.base_dir
, self
.GetExpectations()))
337 ps
.AddStory(WebGLContextLostFromLoseContextExtensionPage(
338 ps
, ps
.base_dir
, self
.GetExpectations()))
339 ps
.AddStory(WebGLContextLostFromQuantityPage(
340 ps
, ps
.base_dir
, self
.GetExpectations()))
341 ps
.AddStory(WebGLContextLostFromSelectElementPage(
342 ps
, ps
.base_dir
, self
.GetExpectations()))
343 ps
.AddStory(WebGLContextLostInHiddenTabPage(
344 ps
, ps
.base_dir
, self
.GetExpectations()))