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