Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / page / page_test.py
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
5 from telemetry.core import command_line
6 from telemetry.page import test_expectations
7 from telemetry.page.actions import action_runner as action_runner_module
8
9
10 class Failure(Exception):
11   """Exception that can be thrown from PageTest to indicate an
12   undesired but designed-for problem."""
13
14
15 class TestNotSupportedOnPlatformFailure(Failure):
16   """Exception that can be thrown to indicate that a certain feature required
17   to run the test is not available on the platform, hardware configuration, or
18   browser version."""
19
20
21 class MeasurementFailure(Failure):
22   """Exception that can be thrown from MeasurePage to indicate an undesired but
23   designed-for problem."""
24
25
26 class PageTest(command_line.Command):
27   """A class styled on unittest.TestCase for creating page-specific tests.
28
29   Test should override ValidateAndMeasurePage to perform test
30   validation and page measurement as necessary.
31
32      class BodyChildElementMeasurement(PageTest):
33        def ValidateAndMeasurePage(self, page, tab, results):
34          body_child_count = tab.EvaluateJavaScript(
35              'document.body.children.length')
36          results.AddValue(scalar.ScalarValue(
37              page, 'body_children', 'count', body_child_count))
38
39   The class also provide hooks to add test-specific options. Here is
40   an example:
41
42      class BodyChildElementMeasurement(PageTest):
43        def AddCommandLineArgs(parser):
44          parser.add_option('--element', action='store', default='body')
45
46        def ValidateAndMeasurePage(self, page, tab, results):
47          body_child_count = tab.EvaluateJavaScript(
48              'document.querySelector('%s').children.length')
49          results.AddValue(scalar.ScalarValue(
50              page, 'children', 'count', child_count))
51
52   Args:
53     action_name_to_run: This is the method name in telemetry.page.Page
54         subclasses to run.
55     discard_first_run: Discard the first run of this page. This is
56         usually used with page_repeat and pageset_repeat options.
57     attempts: The number of attempts to run if we encountered
58         infrastructure problems (as opposed to test issues), such as
59         losing a browser.
60     max_failures: The number of page failures allowed before we stop
61         running other pages.
62     is_action_name_to_run_optional: Determines what to do if
63         action_name_to_run is not empty but the page doesn't have that
64         action. The page will run (without any action) if
65         is_action_name_to_run_optional is True, otherwise the page
66         will fail.
67   """
68
69   options = {}
70
71   def __init__(self,
72                action_name_to_run='',
73                needs_browser_restart_after_each_page=False,
74                discard_first_result=False,
75                clear_cache_before_each_run=False,
76                attempts=3,
77                max_failures=None,
78                is_action_name_to_run_optional=False):
79     super(PageTest, self).__init__()
80
81     self.options = None
82     if action_name_to_run:
83       assert action_name_to_run.startswith('Run') \
84           and '_' not in action_name_to_run, \
85           ('Wrong way of naming action_name_to_run. By new convention,'
86            'action_name_to_run must start with Run- prefix and in CamelCase.')
87     self._action_name_to_run = action_name_to_run
88     self._needs_browser_restart_after_each_page = (
89         needs_browser_restart_after_each_page)
90     self._discard_first_result = discard_first_result
91     self._clear_cache_before_each_run = clear_cache_before_each_run
92     self._close_tabs_before_run = True
93     self._attempts = attempts
94     self._max_failures = max_failures
95     self._is_action_name_to_run_optional = is_action_name_to_run_optional
96     assert self._attempts > 0, 'Test attempts must be greater than 0'
97     # If the test overrides the TabForPage method, it is considered a multi-tab
98     # test.  The main difference between this and a single-tab test is that we
99     # do not attempt recovery for the former if a tab or the browser crashes,
100     # because we don't know the current state of tabs (how many are open, etc.)
101     self.is_multi_tab_test = (self.__class__ is not PageTest and
102                               self.TabForPage.__func__ is not
103                               self.__class__.__bases__[0].TabForPage.__func__)
104     # _exit_requested is set to true when the test requests an early exit.
105     self._exit_requested = False
106
107   @classmethod
108   def SetArgumentDefaults(cls, parser):
109     parser.set_defaults(**cls.options)
110
111   @property
112   def discard_first_result(self):
113     """When set to True, the first run of the test is discarded.  This is
114     useful for cases where it's desirable to have some test resource cached so
115     the first run of the test can warm things up. """
116     return self._discard_first_result
117
118   @discard_first_result.setter
119   def discard_first_result(self, discard):
120     self._discard_first_result = discard
121
122   @property
123   def clear_cache_before_each_run(self):
124     """When set to True, the browser's disk and memory cache will be cleared
125     before each run."""
126     return self._clear_cache_before_each_run
127
128   @property
129   def close_tabs_before_run(self):
130     """When set to True, all tabs are closed before running the test for the
131     first time."""
132     return self._close_tabs_before_run
133
134   @close_tabs_before_run.setter
135   def close_tabs_before_run(self, close_tabs):
136     self._close_tabs_before_run = close_tabs
137
138   @property
139   def attempts(self):
140     """Maximum number of times test will be attempted."""
141     return self._attempts
142
143   @attempts.setter
144   def attempts(self, count):
145     assert self._attempts > 0, 'Test attempts must be greater than 0'
146     self._attempts = count
147
148   @property
149   def max_failures(self):
150     """Maximum number of failures allowed for the page set."""
151     return self._max_failures
152
153   @max_failures.setter
154   def max_failures(self, count):
155     self._max_failures = count
156
157   def Run(self, args):
158     # Define this method to avoid pylint errors.
159     # TODO(dtu): Make this actually run the test with args.page_set.
160     pass
161
162   def RestartBrowserBeforeEachPage(self):
163     """ Should the browser be restarted for the page?
164
165     This returns true if the test needs to unconditionally restart the
166     browser for each page. It may be called before the browser is started.
167     """
168     return self._needs_browser_restart_after_each_page
169
170   def StopBrowserAfterPage(self, browser, page):  # pylint: disable=W0613
171     """Should the browser be stopped after the page is run?
172
173     This is called after a page is run to decide whether the browser needs to
174     be stopped to clean up its state. If it is stopped, then it will be
175     restarted to run the next page.
176
177     A test that overrides this can look at both the page and the browser to
178     decide whether it needs to stop the browser.
179     """
180     return False
181
182   def CustomizeBrowserOptions(self, options):
183     """Override to add test-specific options to the BrowserOptions object"""
184
185   def CustomizeBrowserOptionsForSinglePage(self, page, options):
186     """Set options specific to the test and the given page.
187
188     This will be called with the current page when the browser is (re)started.
189     Changing options at this point only makes sense if the browser is being
190     restarted for each page. Note that if page has a startup_url, the browser
191     will always be restarted for each run.
192     """
193     if page.startup_url:
194       options.browser_options.startup_url = page.startup_url
195
196   def WillStartBrowser(self, platform):
197     """Override to manipulate the browser environment before it launches."""
198
199   def DidStartBrowser(self, browser):
200     """Override to customize the browser right after it has launched."""
201
202   def CanRunForPage(self, page):  # pylint: disable=W0613
203     """Override to customize if the test can be ran for the given page."""
204     if self._action_name_to_run and not self._is_action_name_to_run_optional:
205       return hasattr(page, self._action_name_to_run)
206     return True
207
208   def WillRunTest(self, options):
209     """Override to do operations before the page set(s) are navigated."""
210     self.options = options
211
212   def DidRunTest(self, browser, results): # pylint: disable=W0613
213     """Override to do operations after all page set(s) are completed.
214
215     This will occur before the browser is torn down.
216     """
217     self.options = None
218
219   def WillNavigateToPage(self, page, tab):
220     """Override to do operations before the page is navigated, notably Telemetry
221     will already have performed the following operations on the browser before
222     calling this function:
223     * Ensure only one tab is open.
224     * Call WaitForDocumentReadyStateToComplete on the tab."""
225
226   def DidNavigateToPage(self, page, tab):
227     """Override to do operations right after the page is navigated and after
228     all waiting for completion has occurred."""
229
230   def WillRunActions(self, page, tab):
231     """Override to do operations before running the actions on the page."""
232
233   def DidRunActions(self, page, tab):
234     """Override to do operations after running the actions on the page."""
235
236   def CleanUpAfterPage(self, page, tab):
237     """Called after the test run method was run, even if it failed."""
238
239   def CreateExpectations(self, page_set):   # pylint: disable=W0613
240     """Override to make this test generate its own expectations instead of
241     any that may have been defined in the page set."""
242     return test_expectations.TestExpectations()
243
244   def TabForPage(self, page, browser):   # pylint: disable=W0613
245     """Override to select a different tab for the page.  For instance, to
246     create a new tab for every page, return browser.tabs.New()."""
247     return browser.tabs[0]
248
249   def ValidatePageSet(self, page_set):
250     """Override to examine the page set before the test run.  Useful for
251     example to validate that the pageset can be used with the test."""
252
253   def ValidateAndMeasurePage(self, page, tab, results):
254     """Override to check test assertions and perform measurement.
255
256     When adding measurement results, call results.AddValue(...) for
257     each result. Raise an exception or add a failure.FailureValue on
258     failure. page_test.py also provides several base exception classes
259     to use.
260
261     Prefer metric value names that are in accordance with python
262     variable style. e.g., metric_name. The name 'url' must not be used.
263
264     Put together:
265       def ValidateAndMeasurePage(self, page, tab, results):
266         res = tab.EvaluateJavaScript('2+2')
267         if res != 4:
268           raise Exception('Oh, wow.')
269         results.AddValue(scalar.ScalarValue(
270             page, 'two_plus_two', 'count', res))
271
272     Args:
273       page: A telemetry.page.Page instance.
274       tab: A telemetry.core.Tab instance.
275       results: A telemetry.results.PageTestResults instance.
276     """
277     # TODO(chrishenry): Switch to raise NotImplementedError() when
278     # subclasses no longer override ValidatePage/MeasurePage.
279     self.ValidatePage(page, tab, results)
280
281   def ValidatePage(self, page, tab, results):
282     """DEPRECATED: Use ValidateAndMeasurePage instead."""
283     self.MeasurePage(page, tab, results)
284
285   def MeasurePage(self, page, tab, results):
286     """DEPRECATED: Use ValidateAndMeasurePage instead."""
287
288   def RunPage(self, page, tab, results):
289     # Run actions.
290     interactive = self.options and self.options.interactive
291     action_runner = action_runner_module.ActionRunner(
292         tab, skip_waits=page.skip_waits)
293     self.WillRunActions(page, tab)
294     if interactive:
295       action_runner.PauseInteractive()
296     else:
297       self._RunMethod(page, self._action_name_to_run, action_runner)
298     self.DidRunActions(page, tab)
299
300     self.ValidateAndMeasurePage(page, tab, results)
301
302   def _RunMethod(self, page, method_name, action_runner):
303     if hasattr(page, method_name):
304       run_method = getattr(page, method_name)
305       run_method(action_runner)
306
307   def RunNavigateSteps(self, page, tab):
308     """Navigates the tab to the page URL attribute.
309
310     Runs the 'navigate_steps' page attribute as a compound action.
311     """
312     action_runner = action_runner_module.ActionRunner(
313         tab, skip_waits=page.skip_waits)
314     page.RunNavigateSteps(action_runner)
315
316   def IsExiting(self):
317     return self._exit_requested
318
319   def RequestExit(self):
320     self._exit_requested = True
321
322   @property
323   def action_name_to_run(self):
324     return self._action_name_to_run