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