Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / page / page_test.py
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.
4 import logging
5
6 from telemetry.page import test_expectations
7 from telemetry.page.actions import all_page_actions
8 from telemetry.page.actions import interact
9 from telemetry.page.actions import navigate
10 from telemetry.page.actions import page_action
11
12
13 def _GetActionFromData(action_data):
14   action_name = action_data['action']
15   action = all_page_actions.FindClassWithName(action_name)
16   if not action:
17     logging.critical('Could not find an action named %s.', action_name)
18     logging.critical('Check the page set for a typo and check the error '
19                      'log for possible Python loading/compilation errors.')
20     raise Exception('Action "%s" not found.' % action_name)
21   return action(action_data)
22
23
24 def GetSubactionFromData(page, subaction_data, interactive):
25   subaction_name = subaction_data['action']
26   if hasattr(page, subaction_name):
27     return GetCompoundActionFromPage(page, subaction_name, interactive)
28   else:
29     return [_GetActionFromData(subaction_data)]
30
31
32 def GetCompoundActionFromPage(page, action_name, interactive=False):
33   if interactive:
34     return [interact.InteractAction()]
35
36   if not action_name:
37     return []
38
39   action_data_list = getattr(page, action_name)
40   if not isinstance(action_data_list, list):
41     action_data_list = [action_data_list]
42
43   action_list = []
44   for subaction_data in action_data_list:
45     for _ in xrange(subaction_data.get('repeat', 1)):
46       action_list += GetSubactionFromData(page, subaction_data, interactive)
47   return action_list
48
49
50 class Failure(Exception):
51   """Exception that can be thrown from PageMeasurement to indicate an
52   undesired but designed-for problem."""
53   pass
54
55
56 class PageTest(object):
57   """A class styled on unittest.TestCase for creating page-specific tests."""
58
59   def __init__(self,
60                test_method_name,
61                action_name_to_run='',
62                needs_browser_restart_after_each_run=False,
63                discard_first_result=False,
64                clear_cache_before_each_run=False,
65                attempts=3):
66     self.options = None
67     try:
68       self._test_method = getattr(self, test_method_name)
69     except AttributeError:
70       raise ValueError, 'No such method %s.%s' % (
71         self.__class_, test_method_name)  # pylint: disable=E1101
72     self._action_name_to_run = action_name_to_run
73     self._needs_browser_restart_after_each_run = (
74         needs_browser_restart_after_each_run)
75     self._discard_first_result = discard_first_result
76     self._clear_cache_before_each_run = clear_cache_before_each_run
77     self._close_tabs_before_run = True
78     self._attempts = attempts
79     assert self._attempts > 0, 'Test attempts must be greater than 0'
80     # If the test overrides the TabForPage method, it is considered a multi-tab
81     # test.  The main difference between this and a single-tab test is that we
82     # do not attempt recovery for the former if a tab or the browser crashes,
83     # because we don't know the current state of tabs (how many are open, etc.)
84     self.is_multi_tab_test = (self.__class__ is not PageTest and
85                               self.TabForPage.__func__ is not
86                               self.__class__.__bases__[0].TabForPage.__func__)
87     # _exit_requested is set to true when the test requests an early exit.
88     self._exit_requested = False
89
90   @property
91   def discard_first_result(self):
92     """When set to True, the first run of the test is discarded.  This is
93     useful for cases where it's desirable to have some test resource cached so
94     the first run of the test can warm things up. """
95     return self._discard_first_result
96
97   @discard_first_result.setter
98   def discard_first_result(self, discard):
99     self._discard_first_result = discard
100
101   @property
102   def clear_cache_before_each_run(self):
103     """When set to True, the browser's disk and memory cache will be cleared
104     before each run."""
105     return self._clear_cache_before_each_run
106
107   @property
108   def close_tabs_before_run(self):
109     """When set to True, all tabs are closed before running the test for the
110     first time."""
111     return self._close_tabs_before_run
112
113   @close_tabs_before_run.setter
114   def close_tabs_before_run(self, close_tabs):
115     self._close_tabs_before_run = close_tabs
116
117   @property
118   def attempts(self):
119     """Maximum number of times test will be attempted."""
120     return self._attempts
121
122   @attempts.setter
123   def attempts(self, count):
124     assert self._attempts > 0, 'Test attempts must be greater than 0'
125     self._attempts = count
126
127   def RestartBrowserBeforeEachPage(self):
128     """ Should the browser be restarted for the page?
129
130     This returns true if the test needs to unconditionally restart the
131     browser for each page. It may be called before the browser is started.
132     """
133     return self._needs_browser_restart_after_each_run
134
135   def StopBrowserAfterPage(self, browser, page):  # pylint: disable=W0613
136     """Should the browser be stopped after the page is run?
137
138     This is called after a page is run to decide whether the browser needs to
139     be stopped to clean up its state. If it is stopped, then it will be
140     restarted to run the next page.
141
142     A test that overrides this can look at both the page and the browser to
143     decide whether it needs to stop the browser.
144     """
145     return False
146
147   def AddCommandLineOptions(self, parser):
148     """Override to expose command-line options for this test.
149
150     The provided parser is an optparse.OptionParser instance and accepts all
151     normal results. The parsed options are available in Run as
152     self.options."""
153     pass
154
155   def CustomizeBrowserOptions(self, options):
156     """Override to add test-specific options to the BrowserOptions object"""
157     pass
158
159   def CustomizeBrowserOptionsForPageSet(self, page_set, options):
160     """Set options required for this page set.
161
162     These options will be used every time the browser is started while running
163     this page set. They may, however, be further modified by
164     CustomizeBrowserOptionsForSinglePage or by the profiler.
165     """
166     for page in page_set:
167       if not self.CanRunForPage(page):
168         return
169       interactive = options and options.interactive
170       for action in GetCompoundActionFromPage(
171           page, self._action_name_to_run, interactive):
172         action.CustomizeBrowserOptionsForPageSet(options)
173
174   def CustomizeBrowserOptionsForSinglePage(self, page, options):
175     """Set options specific to the test and the given page.
176
177     This will be called with the current page when the browser is (re)started.
178     Changing options at this point only makes sense if the browser is being
179     restarted for each page.
180     """
181     interactive = options and options.interactive
182     for action in GetCompoundActionFromPage(
183         page, self._action_name_to_run, interactive):
184       action.CustomizeBrowserOptionsForSinglePage(options)
185
186   def WillStartBrowser(self, browser):
187     """Override to manipulate the browser environment before it launches."""
188     pass
189
190   def DidStartBrowser(self, browser):
191     """Override to customize the browser right after it has launched."""
192     pass
193
194   def CanRunForPage(self, page):  # pylint: disable=W0613
195     """Override to customize if the test can be ran for the given page."""
196     return True
197
198   def WillRunTest(self):
199     """Override to do operations before the page set(s) are navigated."""
200     pass
201
202   def DidRunTest(self, browser, results):
203     """Override to do operations after all page set(s) are completed.
204
205     This will occur before the browser is torn down.
206     """
207     pass
208
209   def WillRunPageRepeats(self, page):
210     """Override to do operations before each page is iterated over."""
211     pass
212
213   def DidRunPageRepeats(self, page):
214     """Override to do operations after each page is iterated over."""
215     pass
216
217   def DidStartHTTPServer(self, tab):
218     """Override to do operations after the HTTP server is started."""
219     pass
220
221   def WillNavigateToPage(self, page, tab):
222     """Override to do operations before the page is navigated, notably Telemetry
223     will already have performed the following operations on the browser before
224     calling this function:
225     * Ensure only one tab is open.
226     * Call WaitForDocumentReadyStateToComplete on the tab."""
227     pass
228
229   def DidNavigateToPage(self, page, tab):
230     """Override to do operations right after the page is navigated and after
231     all waiting for completion has occurred."""
232     pass
233
234   def WillRunActions(self, page, tab):
235     """Override to do operations before running the actions on the page."""
236     pass
237
238   def DidRunActions(self, page, tab):
239     """Override to do operations after running the actions on the page."""
240     pass
241
242   def WillRunAction(self, page, tab, action):
243     """Override to do operations before running the action on the page."""
244     pass
245
246   def DidRunAction(self, page, tab, action):
247     """Override to do operations after running the action on the page."""
248     pass
249
250   def CreatePageSet(self, args, options):   # pylint: disable=W0613
251     """Override to make this test generate its own page set instead of
252     allowing arbitrary page sets entered from the command-line."""
253     return None
254
255   def CreateExpectations(self, page_set):   # pylint: disable=W0613
256     """Override to make this test generate its own expectations instead of
257     any that may have been defined in the page set."""
258     return test_expectations.TestExpectations()
259
260   def TabForPage(self, page, browser):   # pylint: disable=W0613
261     """Override to select a different tab for the page.  For instance, to
262     create a new tab for every page, return browser.tabs.New()."""
263     return browser.tabs[0]
264
265   def ValidatePageSet(self, page_set):
266     """Override to examine the page set before the test run.  Useful for
267     example to validate that the pageset can be used with the test."""
268     pass
269
270   def Run(self, options, page, tab, results):
271     self.options = options
272     interactive = options and options.interactive
273     compound_action = GetCompoundActionFromPage(
274         page, self._action_name_to_run, interactive)
275     self.WillRunActions(page, tab)
276     self._RunCompoundAction(page, tab, compound_action)
277     self.DidRunActions(page, tab)
278     try:
279       self._test_method(page, tab, results)
280     finally:
281       self.options = None
282
283   def _RunCompoundAction(self, page, tab, actions, run_setup_methods=True):
284     for i, action in enumerate(actions):
285       prev_action = actions[i - 1] if i > 0 else None
286       next_action = actions[i + 1] if i < len(actions) - 1 else None
287
288       if (action.RunsPreviousAction() and
289           next_action and next_action.RunsPreviousAction()):
290         raise page_action.PageActionFailed('Consecutive actions cannot both '
291                                            'have RunsPreviousAction() == True.')
292
293       if not (next_action and next_action.RunsPreviousAction()):
294         action.WillRunAction(page, tab)
295         if run_setup_methods:
296           self.WillRunAction(page, tab, action)
297         try:
298           action.RunAction(page, tab, prev_action)
299         finally:
300           if run_setup_methods:
301             self.DidRunAction(page, tab, action)
302
303       # Note that we must not call util.CloseConnections here. Many tests
304       # navigate to a URL in the first action and then wait for a condition
305       # in the second action. Calling util.CloseConnections here often
306       # aborts resource loads performed by the page.
307
308   def RunNavigateSteps(self, page, tab):
309     """Navigates the tab to the page URL attribute.
310
311     Runs the 'navigate_steps' page attribute as a compound action.
312     """
313     navigate_actions = GetCompoundActionFromPage(page, 'navigate_steps')
314     if not any(isinstance(action, navigate.NavigateAction)
315         for action in navigate_actions):
316       raise page_action.PageActionFailed(
317           'No NavigateAction in navigate_steps')
318
319     self._RunCompoundAction(page, tab, navigate_actions, False)
320
321   def IsExiting(self):
322     return self._exit_requested
323
324   def RequestExit(self):
325     self._exit_requested = True
326
327   @property
328   def action_name_to_run(self):
329     return self._action_name_to_run