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
9 from telemetry
import benchmark
as benchmark_module
10 from telemetry
.core
import exceptions
11 from telemetry
.core
import util
12 from telemetry
.page
import page
13 from telemetry
.page
import page_set
14 from telemetry
.page
import page_test
16 data_path
= os
.path
.join(
17 util
.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu')
19 wait_timeout
= 20 # seconds
22 var domAutomationController = {};
24 domAutomationController._loaded = false;
25 domAutomationController._succeeded = false;
26 domAutomationController._finished = false;
28 domAutomationController.setAutomationId = function(id) {}
30 domAutomationController.send = function(msg) {
31 msg = msg.toLowerCase()
32 if (msg == "loaded") {
33 domAutomationController._loaded = true;
34 } else if (msg == "success") {
35 domAutomationController._succeeded = true;
36 domAutomationController._finished = true;
38 domAutomationController._succeeded = false;
39 domAutomationController._finished = true;
43 domAutomationController.reset = function() {
44 domAutomationController._succeeded = false;
45 domAutomationController._finished = false;
48 window.domAutomationController = domAutomationController;
49 console.log("Harness injected.");
52 class _ContextLostValidator(page_test
.PageTest
):
54 # Strictly speaking this test doesn't yet need a browser restart
55 # after each run, but if more tests are added which crash the GPU
56 # process, then it will.
57 super(_ContextLostValidator
, self
).__init
__(
58 needs_browser_restart_after_each_page
=True)
60 def CustomizeBrowserOptions(self
, options
):
61 options
.AppendExtraBrowserArgs(
62 '--disable-domain-blocking-for-3d-apis')
63 options
.AppendExtraBrowserArgs(
64 '--disable-gpu-process-crash-limit')
65 # Required for about:gpucrash handling from Telemetry.
66 options
.AppendExtraBrowserArgs('--enable-gpu-benchmarking')
68 def ValidateAndMeasurePage(self
, page
, tab
, results
):
69 def WaitForPageToFinish():
70 print "Waiting for page to finish."
72 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
73 'window.domAutomationController._finished'), wait_timeout
)
75 except util
.TimeoutException
:
78 if page
.kill_gpu_process
:
79 # Doing the GPU process kill operation cooperatively -- in the
80 # same page's context -- is much more stressful than restarting
81 # the browser every time.
82 for x
in range(page
.number_of_gpu_process_kills
):
83 if not tab
.browser
.supports_tab_control
:
84 raise page_test
.Failure('Browser must support tab control')
86 expected_kills
= x
+ 1
88 # Reset the test's state.
89 tab
.EvaluateJavaScript(
90 'window.domAutomationController.reset()');
92 # If we're running the GPU process crash test, we need the
93 # test to have fully reset before crashing the GPU process.
94 if page
.check_crash_count
:
95 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
96 'window.domAutomationController._finished'), wait_timeout
)
98 # Crash the GPU process.
99 gpucrash_tab
= tab
.browser
.tabs
.New()
100 # To access these debug URLs from Telemetry, they have to be
101 # written using the chrome:// scheme.
102 # The try/except is a workaround for crbug.com/368107.
104 gpucrash_tab
.Navigate('chrome://gpucrash')
105 except (exceptions
.TabCrashException
, Exception):
106 print 'Tab crashed while navigating to chrome://gpucrash'
107 # Activate the original tab and wait for completion.
109 completed
= WaitForPageToFinish()
111 if page
.check_crash_count
:
112 if not tab
.browser
.supports_system_info
:
113 raise page_test
.Failure('Browser must support system info')
115 if not tab
.EvaluateJavaScript(
116 'window.domAutomationController._succeeded'):
117 raise page_test
.Failure(
118 'Test failed (didn\'t render content properly?)')
120 number_of_crashes
= -1
121 # To allow time for a gpucrash to complete, wait up to 20s,
122 # polling repeatedly.
123 start_time
= time
.time()
124 current_time
= time
.time()
125 while current_time
- start_time
< 20:
126 system_info
= tab
.browser
.GetSystemInfo()
127 number_of_crashes
= \
128 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
129 if number_of_crashes
>= expected_kills
:
132 current_time
= time
.time()
134 # Wait 5 more seconds and re-read process_crash_count, in
135 # attempt to catch latent process crashes.
137 system_info
= tab
.browser
.GetSystemInfo()
138 number_of_crashes
= \
139 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
141 if number_of_crashes
< expected_kills
:
142 raise page_test
.Failure(
143 'Timed out waiting for a gpu process crash')
144 elif number_of_crashes
!= expected_kills
:
145 raise page_test
.Failure(
146 'Expected %d gpu process crashes; got: %d' %
147 (expected_kills
, number_of_crashes
))
149 # The try/except is a workaround for crbug.com/368107.
152 except (exceptions
.TabCrashException
, Exception):
153 print 'Tab crashed while closing chrome://gpucrash'
155 raise page_test
.Failure(
156 'Test didn\'t complete (no context lost event?)')
157 if not tab
.EvaluateJavaScript(
158 'window.domAutomationController._succeeded'):
159 raise page_test
.Failure(
160 'Test failed (context not restored properly?)')
161 elif page
.force_garbage_collection
:
162 # Try to corce GC to clean up any contexts not attached to the page.
163 # This method seem unreliable, so the page will also attempt to force
164 # GC through excessive allocations.
166 completed
= WaitForPageToFinish()
169 raise page_test
.Failure(
170 'Test didn\'t complete (no context restored event?)')
171 if not tab
.EvaluateJavaScript(
172 'window.domAutomationController._succeeded'):
173 raise page_test
.Failure(
174 'Test failed (context not restored properly?)')
175 elif page
.hide_tab_and_lose_context
:
176 if not tab
.browser
.supports_tab_control
:
177 raise page_test
.Failure('Browser must support tab control')
179 # Test losing a context in a hidden tab. This test passes if the tab
181 dummy_tab
= tab
.browser
.tabs
.New()
182 tab
.EvaluateJavaScript('loseContextUsingExtension()')
185 completed
= WaitForPageToFinish()
188 raise page_test
.Failure('Test didn\'t complete')
189 if not tab
.EvaluateJavaScript(
190 'window.domAutomationController._succeeded'):
191 raise page_test
.Failure('Test failed')
193 completed
= WaitForPageToFinish()
196 raise page_test
.Failure('Test didn\'t complete')
197 if not tab
.EvaluateJavaScript(
198 'window.domAutomationController._succeeded'):
199 raise page_test
.Failure('Test failed')
201 # Test that navigating to chrome://gpucrash causes the GPU process to crash
202 # exactly one time per navigation.
203 class GPUProcessCrashesExactlyOnce(page
.Page
):
204 def __init__(self
, page_set
, base_dir
):
205 super(GPUProcessCrashesExactlyOnce
, self
).__init
__(
206 url
='file://gpu_process_crash.html',
209 name
='GpuCrash.GPUProcessCrashesExactlyOnce')
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 RunNavigateSteps(self
, action_runner
):
218 action_runner
.NavigateToPage(self
)
219 action_runner
.WaitForJavaScriptCondition(
220 'window.domAutomationController._loaded')
222 class WebGLContextLostFromGPUProcessExitPage(page
.Page
):
223 def __init__(self
, page_set
, base_dir
):
224 super(WebGLContextLostFromGPUProcessExitPage
, self
).__init
__(
225 url
='file://webgl.html?query=kill_after_notification',
228 name
='ContextLost.WebGLContextLostFromGPUProcessExit')
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 RunNavigateSteps(self
, action_runner
):
237 action_runner
.NavigateToPage(self
)
238 action_runner
.WaitForJavaScriptCondition(
239 'window.domAutomationController._loaded')
242 class WebGLContextLostFromLoseContextExtensionPage(page
.Page
):
243 def __init__(self
, page_set
, base_dir
):
244 super(WebGLContextLostFromLoseContextExtensionPage
, self
).__init
__(
245 url
='file://webgl.html?query=WEBGL_lose_context',
248 name
='ContextLost.WebGLContextLostFromLoseContextExtension')
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 RunNavigateSteps(self
, action_runner
):
256 action_runner
.NavigateToPage(self
)
257 action_runner
.WaitForJavaScriptCondition(
258 'window.domAutomationController._finished')
261 class WebGLContextLostInHiddenTabPage(page
.Page
):
262 def __init__(self
, page_set
, base_dir
):
263 super(WebGLContextLostInHiddenTabPage
, self
).__init
__(
264 url
='file://webgl.html?query=kill_after_notification',
267 name
='ContextLost.WebGLContextLostInHiddenTab')
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 RunNavigateSteps(self
, action_runner
):
275 action_runner
.NavigateToPage(self
)
276 action_runner
.WaitForJavaScriptCondition(
277 'window.domAutomationController._loaded')
280 class WebGLContextLostFromQuantityPage(page
.Page
):
281 def __init__(self
, page_set
, base_dir
):
282 super(WebGLContextLostFromQuantityPage
, self
).__init
__(
283 url
='file://webgl.html?query=forced_quantity_loss',
286 name
='ContextLost.WebGLContextLostFromQuantity')
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 RunNavigateSteps(self
, action_runner
):
294 action_runner
.NavigateToPage(self
)
295 action_runner
.WaitForJavaScriptCondition(
296 'window.domAutomationController._loaded')
298 class WebGLContextLostFromSelectElementPage(page
.Page
):
299 def __init__(self
, page_set
, base_dir
):
300 super(WebGLContextLostFromSelectElementPage
, self
).__init
__(
301 url
='file://webgl_with_select_element.html',
304 name
='ContextLost.WebGLContextLostFromSelectElement')
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 RunNavigateSteps(self
, action_runner
):
312 action_runner
.NavigateToPage(self
)
313 action_runner
.WaitForJavaScriptCondition(
314 'window.domAutomationController._loaded')
316 class ContextLost(benchmark_module
.Benchmark
):
318 test
= _ContextLostValidator
320 def CreateExpectations(self
, page_set
):
321 return context_lost_expectations
.ContextLostExpectations()
323 # For the record, this would have been another way to get the pages
324 # to repeat. pageset_repeat would be another option.
325 # options = {'page_repeat': 5}
326 def CreatePageSet(self
, options
):
327 ps
= page_set
.PageSet(
329 user_agent_type
='desktop',
330 serving_dirs
=set(['']))
331 ps
.AddPage(GPUProcessCrashesExactlyOnce(ps
, ps
.base_dir
))
332 ps
.AddPage(WebGLContextLostFromGPUProcessExitPage(ps
, ps
.base_dir
))
333 ps
.AddPage(WebGLContextLostFromLoseContextExtensionPage(ps
, ps
.base_dir
))
334 ps
.AddPage(WebGLContextLostFromQuantityPage(ps
, ps
.base_dir
))
335 ps
.AddPage(WebGLContextLostFromSelectElementPage(ps
, ps
.base_dir
))
336 ps
.AddPage(WebGLContextLostInHiddenTabPage(ps
, ps
.base_dir
))