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 5import logging 6 7from telemetry.core import exceptions 8from telemetry.page import action_runner as action_runner_module 9 10# Export story_test.Failure to this page_test module 11from telemetry.web_perf.story_test import Failure 12 13 14class TestNotSupportedOnPlatformError(Exception): 15 """PageTest Exception raised when a required feature is unavailable. 16 17 The feature required to run the test could be part of the platform, 18 hardware configuration, or browser. 19 """ 20 21 22class MultiTabTestAppCrashError(Exception): 23 """PageTest Exception raised after browser or tab crash for multi-tab tests. 24 25 Used to abort the test rather than try to recover from an unknown state. 26 """ 27 28 29class MeasurementFailure(Failure): 30 """PageTest Exception raised when an undesired but designed-for problem.""" 31 32 33class PageTest(object): 34 """A class styled on unittest.TestCase for creating page-specific tests. 35 36 Test should override ValidateAndMeasurePage to perform test 37 validation and page measurement as necessary. 38 39 class BodyChildElementMeasurement(PageTest): 40 def ValidateAndMeasurePage(self, page, tab, results): 41 body_child_count = tab.EvaluateJavaScript( 42 'document.body.children.length') 43 results.AddValue(scalar.ScalarValue( 44 page, 'body_children', 'count', body_child_count)) 45 """ 46 47 def __init__(self, 48 needs_browser_restart_after_each_page=False, 49 clear_cache_before_each_run=False): 50 super(PageTest, self).__init__() 51 52 self.options = None 53 self._needs_browser_restart_after_each_page = ( 54 needs_browser_restart_after_each_page) 55 self._clear_cache_before_each_run = clear_cache_before_each_run 56 self._close_tabs_before_run = True 57 58 @property 59 def is_multi_tab_test(self): 60 """Returns True if the test opens multiple tabs. 61 62 If the test overrides TabForPage, it is deemed a multi-tab test. 63 Multi-tab tests do not retry after tab or browser crashes, whereas, 64 single-tab tests too. That is because the state of multi-tab tests 65 (e.g., how many tabs are open, etc.) is unknown after crashes. 66 """ 67 return self.TabForPage.__func__ is not PageTest.TabForPage.__func__ 68 69 @property 70 def clear_cache_before_each_run(self): 71 """When set to True, the browser's disk and memory cache will be cleared 72 before each run.""" 73 return self._clear_cache_before_each_run 74 75 @property 76 def close_tabs_before_run(self): 77 """When set to True, all tabs are closed before running the test for the 78 first time.""" 79 return self._close_tabs_before_run 80 81 @close_tabs_before_run.setter 82 def close_tabs_before_run(self, close_tabs): 83 self._close_tabs_before_run = close_tabs 84 85 def RestartBrowserBeforeEachPage(self): 86 """ Should the browser be restarted for the page? 87 88 This returns true if the test needs to unconditionally restart the 89 browser for each page. It may be called before the browser is started. 90 """ 91 return self._needs_browser_restart_after_each_page 92 93 def StopBrowserAfterPage(self, browser, page): 94 """Should the browser be stopped after the page is run? 95 96 This is called after a page is run to decide whether the browser needs to 97 be stopped to clean up its state. If it is stopped, then it will be 98 restarted to run the next page. 99 100 A test that overrides this can look at both the page and the browser to 101 decide whether it needs to stop the browser. 102 """ 103 del browser, page # unused 104 return False 105 106 def CustomizeBrowserOptions(self, options): 107 """Override to add test-specific options to the BrowserOptions object""" 108 109 def WillStartBrowser(self, platform): 110 """Override to manipulate the browser environment before it launches.""" 111 112 def DidStartBrowser(self, browser): 113 """Override to customize the browser right after it has launched.""" 114 115 def SetOptions(self, options): 116 """Sets the BrowserFinderOptions instance to use.""" 117 self.options = options 118 119 def WillNavigateToPage(self, page, tab): 120 """Override to do operations before the page is navigated, notably Telemetry 121 will already have performed the following operations on the browser before 122 calling this function: 123 * Ensure only one tab is open. 124 * Call WaitForDocumentReadyStateToComplete on the tab.""" 125 126 def DidNavigateToPage(self, page, tab): 127 """Override to do operations right after the page is navigated and after 128 all waiting for completion has occurred.""" 129 130 def DidRunPage(self, platform): 131 """Called after the test run method was run, even if it failed.""" 132 133 def TabForPage(self, page, browser): # pylint: disable=unused-argument 134 """Override to select a different tab for the page. For instance, to 135 create a new tab for every page, return browser.tabs.New().""" 136 try: 137 return browser.tabs[0] 138 # The tab may have gone away in some case, so we create a new tab and retry 139 # (See crbug.com/496280) 140 except exceptions.DevtoolsTargetCrashException as e: 141 logging.error('Tab may have crashed: %s' % str(e)) 142 browser.tabs.New() 143 # See comment in shared_page_state.WillRunStory for why this waiting 144 # is needed. 145 browser.tabs[0].WaitForDocumentReadyStateToBeComplete() 146 return browser.tabs[0] 147 148 def ValidateAndMeasurePage(self, page, tab, results): 149 """Override to check test assertions and perform measurement. 150 151 When adding measurement results, call results.AddValue(...) for 152 each result. Raise an exception or add a failure.FailureValue on 153 failure. page_test.py also provides several base exception classes 154 to use. 155 156 Prefer metric value names that are in accordance with python 157 variable style. e.g., metric_name. The name 'url' must not be used. 158 159 Put together: 160 def ValidateAndMeasurePage(self, page, tab, results): 161 res = tab.EvaluateJavaScript('2+2') 162 if res != 4: 163 raise Exception('Oh, wow.') 164 results.AddValue(scalar.ScalarValue( 165 page, 'two_plus_two', 'count', res)) 166 167 Args: 168 page: A telemetry.page.Page instance. 169 tab: A telemetry.core.Tab instance. 170 results: A telemetry.results.PageTestResults instance. 171 """ 172 raise NotImplementedError 173 174 # Deprecated: do not use this hook. (crbug.com/470147) 175 def RunNavigateSteps(self, page, tab): 176 """Navigates the tab to the page URL attribute. 177 178 Runs the 'navigate_steps' page attribute as a compound action. 179 """ 180 action_runner = action_runner_module.ActionRunner( 181 tab, skip_waits=page.skip_waits) 182 page.RunNavigateSteps(action_runner) 183