1 # Copyright (c) 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.
5 from telemetry.core import command_line
7 from telemetry.page import test_expectations
8 from telemetry.page.actions import action_runner as action_runner_module
9 from telemetry.page.actions import interact
12 class Failure(Exception):
13 """Exception that can be thrown from PageMeasurement to indicate an
14 undesired but designed-for problem."""
17 class TestNotSupportedOnPlatformFailure(Failure):
18 """Exception that can be thrown to indicate that a certain feature required
19 to run the test is not available on the platform, hardware configuration, or
23 class PageTest(command_line.Command):
24 """A class styled on unittest.TestCase for creating page-specific tests."""
29 action_name_to_run='',
30 needs_browser_restart_after_each_page=False,
31 discard_first_result=False,
32 clear_cache_before_each_run=False,
36 super(PageTest, self).__init__()
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 assert self._attempts > 0, 'Test attempts must be greater than 0'
54 # If the test overrides the TabForPage method, it is considered a multi-tab
55 # test. The main difference between this and a single-tab test is that we
56 # do not attempt recovery for the former if a tab or the browser crashes,
57 # because we don't know the current state of tabs (how many are open, etc.)
58 self.is_multi_tab_test = (self.__class__ is not PageTest and
59 self.TabForPage.__func__ is not
60 self.__class__.__bases__[0].TabForPage.__func__)
61 # _exit_requested is set to true when the test requests an early exit.
62 self._exit_requested = False
65 def SetArgumentDefaults(cls, parser):
66 parser.set_defaults(**cls.options)
69 def discard_first_result(self):
70 """When set to True, the first run of the test is discarded. This is
71 useful for cases where it's desirable to have some test resource cached so
72 the first run of the test can warm things up. """
73 return self._discard_first_result
75 @discard_first_result.setter
76 def discard_first_result(self, discard):
77 self._discard_first_result = discard
80 def clear_cache_before_each_run(self):
81 """When set to True, the browser's disk and memory cache will be cleared
83 return self._clear_cache_before_each_run
86 def close_tabs_before_run(self):
87 """When set to True, all tabs are closed before running the test for the
89 return self._close_tabs_before_run
91 @close_tabs_before_run.setter
92 def close_tabs_before_run(self, close_tabs):
93 self._close_tabs_before_run = close_tabs
97 """Maximum number of times test will be attempted."""
101 def attempts(self, count):
102 assert self._attempts > 0, 'Test attempts must be greater than 0'
103 self._attempts = count
106 def max_failures(self):
107 """Maximum number of failures allowed for the page set."""
108 return self._max_failures
111 def max_failures(self, count):
112 self._max_failures = count
115 def max_errors(self):
116 """Maximum number of errors allowed for the page set."""
117 return self._max_errors
120 def max_errors(self, count):
121 self._max_errors = count
124 # Define this method to avoid pylint errors.
125 # TODO(dtu): Make this actually run the test with args.page_set.
128 def RestartBrowserBeforeEachPage(self):
129 """ Should the browser be restarted for the page?
131 This returns true if the test needs to unconditionally restart the
132 browser for each page. It may be called before the browser is started.
134 return self._needs_browser_restart_after_each_page
136 def StopBrowserAfterPage(self, browser, page): # pylint: disable=W0613
137 """Should the browser be stopped after the page is run?
139 This is called after a page is run to decide whether the browser needs to
140 be stopped to clean up its state. If it is stopped, then it will be
141 restarted to run the next page.
143 A test that overrides this can look at both the page and the browser to
144 decide whether it needs to stop the browser.
148 def CustomizeBrowserOptions(self, options):
149 """Override to add test-specific options to the BrowserOptions object"""
151 def CustomizeBrowserOptionsForSinglePage(self, page, options):
152 """Set options specific to the test and the given page.
154 This will be called with the current page when the browser is (re)started.
155 Changing options at this point only makes sense if the browser is being
156 restarted for each page. Note that if page has a startup_url, the browser
157 will always be restarted for each run.
160 options.browser_options.startup_url = page.startup_url
162 def WillStartBrowser(self, browser):
163 """Override to manipulate the browser environment before it launches."""
165 def DidStartBrowser(self, browser):
166 """Override to customize the browser right after it has launched."""
168 def CanRunForPage(self, page): # pylint: disable=W0613
169 """Override to customize if the test can be ran for the given page."""
170 if self._action_name_to_run:
171 return hasattr(page, self._action_name_to_run)
174 def WillRunTest(self, options):
175 """Override to do operations before the page set(s) are navigated."""
176 self.options = options
178 def DidRunTest(self, browser, results): # pylint: disable=W0613
179 """Override to do operations after all page set(s) are completed.
181 This will occur before the browser is torn down.
185 def WillRunPageRepeats(self, page):
186 """Override to do operations before each page is iterated over."""
188 def DidRunPageRepeats(self, page):
189 """Override to do operations after each page is iterated over."""
191 def DidStartHTTPServer(self, tab):
192 """Override to do operations after the HTTP server is started."""
194 def WillNavigateToPage(self, page, tab):
195 """Override to do operations before the page is navigated, notably Telemetry
196 will already have performed the following operations on the browser before
197 calling this function:
198 * Ensure only one tab is open.
199 * Call WaitForDocumentReadyStateToComplete on the tab."""
201 def DidNavigateToPage(self, page, tab):
202 """Override to do operations right after the page is navigated and after
203 all waiting for completion has occurred."""
205 def WillRunActions(self, page, tab):
206 """Override to do operations before running the actions on the page."""
208 def DidRunActions(self, page, tab):
209 """Override to do operations after running the actions on the page."""
211 def CleanUpAfterPage(self, page, tab):
212 """Called after the test run method was run, even if it failed."""
214 def CreateExpectations(self, page_set): # pylint: disable=W0613
215 """Override to make this test generate its own expectations instead of
216 any that may have been defined in the page set."""
217 return test_expectations.TestExpectations()
219 def TabForPage(self, page, browser): # pylint: disable=W0613
220 """Override to select a different tab for the page. For instance, to
221 create a new tab for every page, return browser.tabs.New()."""
222 return browser.tabs[0]
224 def ValidatePageSet(self, page_set):
225 """Override to examine the page set before the test run. Useful for
226 example to validate that the pageset can be used with the test."""
228 def ValidatePage(self, page, tab, results):
229 """Override to check the actual test assertions.
231 This is where most your test logic should go."""
232 raise NotImplementedError()
234 def RunPage(self, page, tab, results):
236 interactive = self.options and self.options.interactive
237 action_runner = action_runner_module.ActionRunner(page, tab, self)
238 self.WillRunActions(page, tab)
240 action_runner.RunAction(interact.InteractAction())
242 self._RunMethod(page, self._action_name_to_run, action_runner)
243 self.DidRunActions(page, tab)
246 self.ValidatePage(page, tab, results)
248 def _RunMethod(self, page, method_name, action_runner):
249 if hasattr(page, method_name):
250 run_method = getattr(page, method_name)
251 run_method(action_runner)
253 def RunNavigateSteps(self, page, tab):
254 """Navigates the tab to the page URL attribute.
256 Runs the 'navigate_steps' page attribute as a compound action.
258 action_runner = action_runner_module.ActionRunner(page, tab, None)
259 page.RunNavigateSteps(action_runner)
262 return self._exit_requested
264 def RequestExit(self):
265 self._exit_requested = True
268 def action_name_to_run(self):
269 return self._action_name_to_run