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 if page
.kill_gpu_process
:
70 # Doing the GPU process kill operation cooperatively -- in the
71 # same page's context -- is much more stressful than restarting
72 # the browser every time.
73 for x
in range(page
.number_of_gpu_process_kills
):
74 if not tab
.browser
.supports_tab_control
:
75 raise page_test
.Failure('Browser must support tab control')
77 expected_kills
= x
+ 1
79 # Reset the test's state.
80 tab
.EvaluateJavaScript(
81 'window.domAutomationController.reset()');
83 # If we're running the GPU process crash test, we need the
84 # test to have fully reset before crashing the GPU process.
85 if page
.check_crash_count
:
86 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
87 'window.domAutomationController._finished'), wait_timeout
)
89 # Crash the GPU process.
90 gpucrash_tab
= tab
.browser
.tabs
.New()
91 # To access these debug URLs from Telemetry, they have to be
92 # written using the chrome:// scheme.
93 # The try/except is a workaround for crbug.com/368107.
95 gpucrash_tab
.Navigate('chrome://gpucrash')
96 except (exceptions
.TabCrashException
, Exception):
97 print 'Tab crashed while navigating to chrome://gpucrash'
98 # Activate the original tab and wait for completion.
102 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
103 'window.domAutomationController._finished'), wait_timeout
)
105 except util
.TimeoutException
:
108 if page
.check_crash_count
:
109 if not tab
.browser
.supports_system_info
:
110 raise page_test
.Failure('Browser must support system info')
112 if not tab
.EvaluateJavaScript(
113 'window.domAutomationController._succeeded'):
114 raise page_test
.Failure(
115 'Test failed (didn\'t render content properly?)')
117 number_of_crashes
= -1
118 # To allow time for a gpucrash to complete, wait up to 20s,
119 # polling repeatedly.
120 start_time
= time
.time()
121 current_time
= time
.time()
122 while current_time
- start_time
< 20:
123 system_info
= tab
.browser
.GetSystemInfo()
124 number_of_crashes
= \
125 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
126 if number_of_crashes
>= expected_kills
:
129 current_time
= time
.time()
131 # Wait 5 more seconds and re-read process_crash_count, in
132 # attempt to catch latent process crashes.
134 system_info
= tab
.browser
.GetSystemInfo()
135 number_of_crashes
= \
136 system_info
.gpu
.aux_attributes
[u
'process_crash_count']
138 if number_of_crashes
< expected_kills
:
139 raise page_test
.Failure(
140 'Timed out waiting for a gpu process crash')
141 elif number_of_crashes
!= expected_kills
:
142 raise page_test
.Failure(
143 'Expected %d gpu process crashes; got: %d' %
144 (expected_kills
, number_of_crashes
))
146 # The try/except is a workaround for crbug.com/368107.
149 except (exceptions
.TabCrashException
, Exception):
150 print 'Tab crashed while closing chrome://gpucrash'
152 raise page_test
.Failure(
153 'Test didn\'t complete (no context lost event?)')
154 if not tab
.EvaluateJavaScript(
155 'window.domAutomationController._succeeded'):
156 raise page_test
.Failure(
157 'Test failed (context not restored properly?)')
158 elif page
.force_garbage_collection
:
159 # Try to corce GC to clean up any contexts not attached to the page.
160 # This method seem unreliable, so the page will also attempt to force
161 # GC through excessive allocations.
165 print "Waiting for page to finish."
166 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
167 'window.domAutomationController._finished'), wait_timeout
)
169 except util
.TimeoutException
:
173 raise page_test
.Failure(
174 'Test didn\'t complete (no context restored event?)')
175 if not tab
.EvaluateJavaScript(
176 'window.domAutomationController._succeeded'):
177 raise page_test
.Failure(
178 'Test failed (context not restored properly?)')
182 print "Waiting for page to finish."
183 util
.WaitFor(lambda: tab
.EvaluateJavaScript(
184 'window.domAutomationController._finished'), wait_timeout
)
186 except util
.TimeoutException
:
190 raise page_test
.Failure('Test didn\'t complete')
191 if not tab
.EvaluateJavaScript(
192 'window.domAutomationController._succeeded'):
193 raise page_test
.Failure('Test failed')
195 # Test that navigating to chrome://gpucrash causes the GPU process to crash
196 # exactly one time per navigation.
197 class GPUProcessCrashesExactlyOnce(page
.Page
):
198 def __init__(self
, page_set
, base_dir
):
199 super(GPUProcessCrashesExactlyOnce
, self
).__init
__(
200 url
='file://gpu_process_crash.html',
203 name
='GpuCrash.GPUProcessCrashesExactlyOnce')
204 self
.script_to_evaluate_on_commit
= harness_script
205 self
.kill_gpu_process
= True
206 self
.number_of_gpu_process_kills
= 2
207 self
.check_crash_count
= True
208 self
.force_garbage_collection
= False
210 def RunNavigateSteps(self
, action_runner
):
211 action_runner
.NavigateToPage(self
)
212 action_runner
.WaitForJavaScriptCondition(
213 'window.domAutomationController._loaded')
215 class WebGLContextLostFromGPUProcessExitPage(page
.Page
):
216 def __init__(self
, page_set
, base_dir
):
217 super(WebGLContextLostFromGPUProcessExitPage
, self
).__init
__(
218 url
='file://webgl.html?query=kill_after_notification',
221 name
='ContextLost.WebGLContextLostFromGPUProcessExit')
222 self
.script_to_evaluate_on_commit
= harness_script
223 self
.kill_gpu_process
= True
224 self
.check_crash_count
= False
225 self
.number_of_gpu_process_kills
= 1
226 self
.force_garbage_collection
= False
228 def RunNavigateSteps(self
, action_runner
):
229 action_runner
.NavigateToPage(self
)
230 action_runner
.WaitForJavaScriptCondition(
231 'window.domAutomationController._loaded')
234 class WebGLContextLostFromLoseContextExtensionPage(page
.Page
):
235 def __init__(self
, page_set
, base_dir
):
236 super(WebGLContextLostFromLoseContextExtensionPage
, self
).__init
__(
237 url
='file://webgl.html?query=WEBGL_lose_context',
240 name
='ContextLost.WebGLContextLostFromLoseContextExtension')
241 self
.script_to_evaluate_on_commit
= harness_script
242 self
.kill_gpu_process
= False
243 self
.check_crash_count
= False
244 self
.force_garbage_collection
= False
246 def RunNavigateSteps(self
, action_runner
):
247 action_runner
.NavigateToPage(self
)
248 action_runner
.WaitForJavaScriptCondition(
249 'window.domAutomationController._finished')
251 class WebGLContextLostFromQuantityPage(page
.Page
):
252 def __init__(self
, page_set
, base_dir
):
253 super(WebGLContextLostFromQuantityPage
, self
).__init
__(
254 url
='file://webgl.html?query=forced_quantity_loss',
257 name
='ContextLost.WebGLContextLostFromQuantity')
258 self
.script_to_evaluate_on_commit
= harness_script
259 self
.kill_gpu_process
= False
260 self
.check_crash_count
= False
261 self
.force_garbage_collection
= True
263 def RunNavigateSteps(self
, action_runner
):
264 action_runner
.NavigateToPage(self
)
265 action_runner
.WaitForJavaScriptCondition(
266 'window.domAutomationController._loaded')
268 class WebGLContextLostFromSelectElementPage(page
.Page
):
269 def __init__(self
, page_set
, base_dir
):
270 super(WebGLContextLostFromSelectElementPage
, self
).__init
__(
271 url
='file://webgl_with_select_element.html',
274 name
='ContextLost.WebGLContextLostFromSelectElement')
275 self
.script_to_evaluate_on_commit
= harness_script
276 self
.kill_gpu_process
= False
277 self
.check_crash_count
= False
278 self
.force_garbage_collection
= False
280 def RunNavigateSteps(self
, action_runner
):
281 action_runner
.NavigateToPage(self
)
282 action_runner
.WaitForJavaScriptCondition(
283 'window.domAutomationController._loaded')
285 class ContextLost(benchmark_module
.Benchmark
):
287 test
= _ContextLostValidator
289 def CreateExpectations(self
, page_set
):
290 return context_lost_expectations
.ContextLostExpectations()
292 # For the record, this would have been another way to get the pages
293 # to repeat. pageset_repeat would be another option.
294 # options = {'page_repeat': 5}
295 def CreatePageSet(self
, options
):
296 ps
= page_set
.PageSet(
298 user_agent_type
='desktop',
299 serving_dirs
=set(['']))
300 ps
.AddPage(GPUProcessCrashesExactlyOnce(ps
, ps
.base_dir
))
301 ps
.AddPage(WebGLContextLostFromGPUProcessExitPage(ps
, ps
.base_dir
))
302 ps
.AddPage(WebGLContextLostFromLoseContextExtensionPage(ps
, ps
.base_dir
))
303 ps
.AddPage(WebGLContextLostFromQuantityPage(ps
, ps
.base_dir
))
304 ps
.AddPage(WebGLContextLostFromSelectElementPage(ps
, ps
.base_dir
))