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. 4import os 5 6from telemetry import test as test_module 7from telemetry.core import exceptions 8from telemetry.core import util 9from telemetry.page import page 10from telemetry.page import page_set 11# pylint: disable=W0401,W0614 12from telemetry.page import page_test 13from telemetry.page.actions.all_page_actions import * 14 15data_path = os.path.join( 16 util.GetChromiumSrcDir(), 'content', 'test', 'data', 'gpu') 17 18wait_timeout = 20 # seconds 19 20harness_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 domAutomationController._succeeded = true; 35 domAutomationController._finished = true; 36 } else { 37 domAutomationController._succeeded = false; 38 domAutomationController._finished = true; 39 } 40 } 41 42 window.domAutomationController = domAutomationController; 43 console.log("Harness injected."); 44""" 45 46class _ContextLostValidator(page_test.PageTest): 47 def __init__(self): 48 # Strictly speaking this test doesn't yet need a browser restart 49 # after each run, but if more tests are added which crash the GPU 50 # process, then it will. 51 super(_ContextLostValidator, self).__init__( 52 needs_browser_restart_after_each_page=True) 53 54 def CustomizeBrowserOptions(self, options): 55 options.AppendExtraBrowserArgs( 56 '--disable-domain-blocking-for-3d-apis') 57 options.AppendExtraBrowserArgs( 58 '--disable-gpu-process-crash-limit') 59 # Required for about:gpucrash handling from Telemetry. 60 options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') 61 62 def ValidatePage(self, page, tab, results): 63 if page.kill_gpu_process: 64 # Doing the GPU process kill operation cooperatively -- in the 65 # same page's context -- is much more stressful than restarting 66 # the browser every time. 67 for x in range(page.number_of_gpu_process_kills): 68 if not tab.browser.supports_tab_control: 69 raise page_test.Failure('Browser must support tab control') 70 # Reset the test's state. 71 tab.EvaluateJavaScript( 72 'window.domAutomationController._succeeded = false'); 73 tab.EvaluateJavaScript( 74 'window.domAutomationController._finished = false'); 75 # Crash the GPU process. 76 new_tab = tab.browser.tabs.New() 77 # To access these debug URLs from Telemetry, they have to be 78 # written using the chrome:// scheme. 79 # The try/except is a workaround for crbug.com/368107. 80 try: 81 new_tab.Navigate('chrome://gpucrash') 82 except (exceptions.TabCrashException, Exception): 83 print 'Tab crashed while navigating to chrome://gpucrash' 84 # Activate the original tab and wait for completion. 85 tab.Activate() 86 completed = False 87 try: 88 util.WaitFor(lambda: tab.EvaluateJavaScript( 89 'window.domAutomationController._finished'), wait_timeout) 90 completed = True 91 except util.TimeoutException: 92 pass 93 # The try/except is a workaround for crbug.com/368107. 94 try: 95 new_tab.Close() 96 except (exceptions.TabCrashException, Exception): 97 print 'Tab crashed while closing chrome://gpucrash' 98 if not completed: 99 raise page_test.Failure( 100 'Test didn\'t complete (no context lost event?)') 101 if not tab.EvaluateJavaScript( 102 'window.domAutomationController._succeeded'): 103 raise page_test.Failure( 104 'Test failed (context not restored properly?)') 105 elif page.force_garbage_collection: 106 # Try to corce GC to clean up any contexts not attached to the page. 107 # This method seem unreliable, so the page will also attempt to force 108 # GC through excessive allocations. 109 tab.CollectGarbage() 110 completed = False 111 try: 112 print "Waiting for page to finish." 113 util.WaitFor(lambda: tab.EvaluateJavaScript( 114 'window.domAutomationController._finished'), wait_timeout) 115 completed = True 116 except util.TimeoutException: 117 pass 118 119 if not completed: 120 raise page_test.Failure( 121 'Test didn\'t complete (no context restored event?)') 122 if not tab.EvaluateJavaScript( 123 'window.domAutomationController._succeeded'): 124 raise page_test.Failure( 125 'Test failed (context not restored properly?)') 126 else: 127 completed = False 128 try: 129 print "Waiting for page to finish." 130 util.WaitFor(lambda: tab.EvaluateJavaScript( 131 'window.domAutomationController._finished'), wait_timeout) 132 completed = True 133 except util.TimeoutException: 134 pass 135 136 if not completed: 137 raise page_test.Failure('Test didn\'t complete') 138 if not tab.EvaluateJavaScript( 139 'window.domAutomationController._succeeded'): 140 raise page_test.Failure('Test failed') 141 142class WebGLContextLostFromGPUProcessExitPage(page.Page): 143 def __init__(self, page_set, base_dir): 144 super(WebGLContextLostFromGPUProcessExitPage, self).__init__( 145 url='file://webgl.html?query=kill_after_notification', 146 page_set=page_set, 147 base_dir=base_dir, 148 name='ContextLost.WebGLContextLostFromGPUProcessExit') 149 self.script_to_evaluate_on_commit = harness_script 150 self.kill_gpu_process = True 151 self.number_of_gpu_process_kills = 1 152 self.force_garbage_collection = False 153 154 def RunNavigateSteps(self, action_runner): 155 action_runner.NavigateToPage(self) 156 action_runner.WaitForJavaScriptCondition( 157 'window.domAutomationController._loaded') 158 159 160class WebGLContextLostFromLoseContextExtensionPage(page.Page): 161 def __init__(self, page_set, base_dir): 162 super(WebGLContextLostFromLoseContextExtensionPage, self).__init__( 163 url='file://webgl.html?query=WEBGL_lose_context', 164 page_set=page_set, 165 base_dir=base_dir, 166 name='ContextLost.WebGLContextLostFromLoseContextExtension') 167 self.script_to_evaluate_on_commit = harness_script 168 self.kill_gpu_process = False 169 self.force_garbage_collection = False 170 171 def RunNavigateSteps(self, action_runner): 172 action_runner.NavigateToPage(self) 173 action_runner.WaitForJavaScriptCondition( 174 'window.domAutomationController._finished') 175 176class WebGLContextLostFromQuantityPage(page.Page): 177 def __init__(self, page_set, base_dir): 178 super(WebGLContextLostFromQuantityPage, self).__init__( 179 url='file://webgl.html?query=forced_quantity_loss', 180 page_set=page_set, 181 base_dir=base_dir, 182 name='ContextLost.WebGLContextLostFromQuantity') 183 self.script_to_evaluate_on_commit = harness_script 184 self.kill_gpu_process = False 185 self.force_garbage_collection = True 186 187 def RunNavigateSteps(self, action_runner): 188 action_runner.NavigateToPage(self) 189 action_runner.WaitForJavaScriptCondition( 190 'window.domAutomationController._loaded') 191 192class WebGLContextLostFromSelectElementPage(page.Page): 193 def __init__(self, page_set, base_dir): 194 super(WebGLContextLostFromSelectElementPage, self).__init__( 195 url='file://webgl_with_select_element.html', 196 page_set=page_set, 197 base_dir=base_dir, 198 name='ContextLost.WebGLContextLostFromSelectElement') 199 self.script_to_evaluate_on_commit = harness_script 200 self.kill_gpu_process = False 201 self.force_garbage_collection = False 202 203 def RunNavigateSteps(self, action_runner): 204 action_runner.NavigateToPage(self) 205 action_runner.WaitForJavaScriptCondition( 206 'window.domAutomationController._loaded') 207 208class ContextLost(test_module.Test): 209 enabled = True 210 test = _ContextLostValidator 211 # For the record, this would have been another way to get the pages 212 # to repeat. pageset_repeat would be another option. 213 # options = {'page_repeat': 5} 214 def CreatePageSet(self, options): 215 ps = page_set.PageSet( 216 file_path=data_path, 217 user_agent_type='desktop', 218 serving_dirs=set([''])) 219 ps.AddPage(WebGLContextLostFromGPUProcessExitPage(ps, ps.base_dir)) 220 ps.AddPage(WebGLContextLostFromLoseContextExtensionPage(ps, ps.base_dir)) 221 ps.AddPage(WebGLContextLostFromQuantityPage(ps, ps.base_dir)) 222 ps.AddPage(WebGLContextLostFromSelectElementPage(ps, ps.base_dir)) 223 return ps 224