• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2012 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.
4
5from telemetry.core import command_line
6
7from telemetry.page import test_expectations
8from telemetry.page.actions import action_runner as action_runner_module
9
10
11class Failure(Exception):
12  """Exception that can be thrown from PageMeasurement to indicate an
13  undesired but designed-for problem."""
14
15
16class TestNotSupportedOnPlatformFailure(Failure):
17  """Exception that can be thrown to indicate that a certain feature required
18  to run the test is not available on the platform, hardware configuration, or
19  browser version."""
20
21
22class PageTest(command_line.Command):
23  """A class styled on unittest.TestCase for creating page-specific tests."""
24
25  options = {}
26
27  def __init__(self,
28               action_name_to_run='',
29               needs_browser_restart_after_each_page=False,
30               discard_first_result=False,
31               clear_cache_before_each_run=False,
32               attempts=3,
33               max_failures=None,
34               max_errors=None,
35               is_action_name_to_run_optional=False):
36    super(PageTest, self).__init__()
37
38    self.options = None
39    if action_name_to_run:
40      assert action_name_to_run.startswith('Run') \
41          and '_' not in action_name_to_run, \
42          ('Wrong way of naming action_name_to_run. By new convention,'
43           'action_name_to_run must start with Run- prefix and in CamelCase.')
44    self._action_name_to_run = action_name_to_run
45    self._needs_browser_restart_after_each_page = (
46        needs_browser_restart_after_each_page)
47    self._discard_first_result = discard_first_result
48    self._clear_cache_before_each_run = clear_cache_before_each_run
49    self._close_tabs_before_run = True
50    self._attempts = attempts
51    self._max_failures = max_failures
52    self._max_errors = max_errors
53    self._is_action_name_to_run_optional = is_action_name_to_run_optional
54    assert self._attempts > 0, 'Test attempts must be greater than 0'
55    # If the test overrides the TabForPage method, it is considered a multi-tab
56    # test.  The main difference between this and a single-tab test is that we
57    # do not attempt recovery for the former if a tab or the browser crashes,
58    # because we don't know the current state of tabs (how many are open, etc.)
59    self.is_multi_tab_test = (self.__class__ is not PageTest and
60                              self.TabForPage.__func__ is not
61                              self.__class__.__bases__[0].TabForPage.__func__)
62    # _exit_requested is set to true when the test requests an early exit.
63    self._exit_requested = False
64
65  @classmethod
66  def SetArgumentDefaults(cls, parser):
67    parser.set_defaults(**cls.options)
68
69  @property
70  def discard_first_result(self):
71    """When set to True, the first run of the test is discarded.  This is
72    useful for cases where it's desirable to have some test resource cached so
73    the first run of the test can warm things up. """
74    return self._discard_first_result
75
76  @discard_first_result.setter
77  def discard_first_result(self, discard):
78    self._discard_first_result = discard
79
80  @property
81  def clear_cache_before_each_run(self):
82    """When set to True, the browser's disk and memory cache will be cleared
83    before each run."""
84    return self._clear_cache_before_each_run
85
86  @property
87  def close_tabs_before_run(self):
88    """When set to True, all tabs are closed before running the test for the
89    first time."""
90    return self._close_tabs_before_run
91
92  @close_tabs_before_run.setter
93  def close_tabs_before_run(self, close_tabs):
94    self._close_tabs_before_run = close_tabs
95
96  @property
97  def attempts(self):
98    """Maximum number of times test will be attempted."""
99    return self._attempts
100
101  @attempts.setter
102  def attempts(self, count):
103    assert self._attempts > 0, 'Test attempts must be greater than 0'
104    self._attempts = count
105
106  @property
107  def max_failures(self):
108    """Maximum number of failures allowed for the page set."""
109    return self._max_failures
110
111  @max_failures.setter
112  def max_failures(self, count):
113    self._max_failures = count
114
115  @property
116  def max_errors(self):
117    """Maximum number of errors allowed for the page set."""
118    return self._max_errors
119
120  @max_errors.setter
121  def max_errors(self, count):
122    self._max_errors = count
123
124  def Run(self, args):
125    # Define this method to avoid pylint errors.
126    # TODO(dtu): Make this actually run the test with args.page_set.
127    pass
128
129  def RestartBrowserBeforeEachPage(self):
130    """ Should the browser be restarted for the page?
131
132    This returns true if the test needs to unconditionally restart the
133    browser for each page. It may be called before the browser is started.
134    """
135    return self._needs_browser_restart_after_each_page
136
137  def StopBrowserAfterPage(self, browser, page):  # pylint: disable=W0613
138    """Should the browser be stopped after the page is run?
139
140    This is called after a page is run to decide whether the browser needs to
141    be stopped to clean up its state. If it is stopped, then it will be
142    restarted to run the next page.
143
144    A test that overrides this can look at both the page and the browser to
145    decide whether it needs to stop the browser.
146    """
147    return False
148
149  def CustomizeBrowserOptions(self, options):
150    """Override to add test-specific options to the BrowserOptions object"""
151
152  def CustomizeBrowserOptionsForSinglePage(self, page, options):
153    """Set options specific to the test and the given page.
154
155    This will be called with the current page when the browser is (re)started.
156    Changing options at this point only makes sense if the browser is being
157    restarted for each page. Note that if page has a startup_url, the browser
158    will always be restarted for each run.
159    """
160    if page.startup_url:
161      options.browser_options.startup_url = page.startup_url
162
163  def WillStartBrowser(self, browser):
164    """Override to manipulate the browser environment before it launches."""
165
166  def DidStartBrowser(self, browser):
167    """Override to customize the browser right after it has launched."""
168
169  def CanRunForPage(self, page):  # pylint: disable=W0613
170    """Override to customize if the test can be ran for the given page."""
171    if self._action_name_to_run and not self._is_action_name_to_run_optional:
172      return hasattr(page, self._action_name_to_run)
173    return True
174
175  def WillRunTest(self, options):
176    """Override to do operations before the page set(s) are navigated."""
177    self.options = options
178
179  def DidRunTest(self, browser, results): # pylint: disable=W0613
180    """Override to do operations after all page set(s) are completed.
181
182    This will occur before the browser is torn down.
183    """
184    self.options = None
185
186  def WillRunPageRepeats(self, page):
187    """Override to do operations before each page is iterated over."""
188
189  def DidRunPageRepeats(self, page):
190    """Override to do operations after each page is iterated over."""
191
192  def DidStartHTTPServer(self, tab):
193    """Override to do operations after the HTTP server is started."""
194
195  def WillNavigateToPage(self, page, tab):
196    """Override to do operations before the page is navigated, notably Telemetry
197    will already have performed the following operations on the browser before
198    calling this function:
199    * Ensure only one tab is open.
200    * Call WaitForDocumentReadyStateToComplete on the tab."""
201
202  def DidNavigateToPage(self, page, tab):
203    """Override to do operations right after the page is navigated and after
204    all waiting for completion has occurred."""
205
206  def WillRunActions(self, page, tab):
207    """Override to do operations before running the actions on the page."""
208
209  def DidRunActions(self, page, tab):
210    """Override to do operations after running the actions on the page."""
211
212  def CleanUpAfterPage(self, page, tab):
213    """Called after the test run method was run, even if it failed."""
214
215  def CreateExpectations(self, page_set):   # pylint: disable=W0613
216    """Override to make this test generate its own expectations instead of
217    any that may have been defined in the page set."""
218    return test_expectations.TestExpectations()
219
220  def TabForPage(self, page, browser):   # pylint: disable=W0613
221    """Override to select a different tab for the page.  For instance, to
222    create a new tab for every page, return browser.tabs.New()."""
223    return browser.tabs[0]
224
225  def ValidatePageSet(self, page_set):
226    """Override to examine the page set before the test run.  Useful for
227    example to validate that the pageset can be used with the test."""
228
229  def ValidatePage(self, page, tab, results):
230    """Override to check the actual test assertions.
231
232    This is where most your test logic should go."""
233    raise NotImplementedError()
234
235  def RunPage(self, page, tab, results):
236    # Run actions.
237    interactive = self.options and self.options.interactive
238    action_runner = action_runner_module.ActionRunner(tab)
239    self.WillRunActions(page, tab)
240    if interactive:
241      action_runner.PauseInteractive()
242    else:
243      self._RunMethod(page, self._action_name_to_run, action_runner)
244    self.DidRunActions(page, tab)
245
246    # Run validator.
247    self.ValidatePage(page, tab, results)
248
249  def _RunMethod(self, page, method_name, action_runner):
250    if hasattr(page, method_name):
251      run_method = getattr(page, method_name)
252      run_method(action_runner)
253
254  def RunNavigateSteps(self, page, tab):
255    """Navigates the tab to the page URL attribute.
256
257    Runs the 'navigate_steps' page attribute as a compound action.
258    """
259    action_runner = action_runner_module.ActionRunner(tab)
260    page.RunNavigateSteps(action_runner)
261
262  def IsExiting(self):
263    return self._exit_requested
264
265  def RequestExit(self):
266    self._exit_requested = True
267
268  @property
269  def action_name_to_run(self):
270    return self._action_name_to_run
271