• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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