| OLD | NEW |
| (Empty) |
| 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 codecs | |
| 5 import logging | |
| 6 import os | |
| 7 import time | |
| 8 import traceback | |
| 9 import urlparse | |
| 10 import random | |
| 11 | |
| 12 from telemetry.core import util | |
| 13 from telemetry.core import wpr_modes | |
| 14 from telemetry.core import exceptions | |
| 15 from telemetry.page import page_filter as page_filter_module | |
| 16 from telemetry.page import page_test | |
| 17 | |
| 18 class PageState(object): | |
| 19 def __init__(self): | |
| 20 self.did_login = False | |
| 21 | |
| 22 class _RunState(object): | |
| 23 def __init__(self): | |
| 24 self.first_browser = True | |
| 25 self.browser = None | |
| 26 self.tab = None | |
| 27 self.is_tracing = False | |
| 28 | |
| 29 def Close(self): | |
| 30 self.is_tracing = False | |
| 31 | |
| 32 if self.tab: | |
| 33 self.tab.Disconnect() | |
| 34 self.tab = None | |
| 35 | |
| 36 if self.browser: | |
| 37 self.browser.Close() | |
| 38 self.browser = None | |
| 39 | |
| 40 def _ShuffleAndFilterPageSet(page_set, options): | |
| 41 if options.pageset_shuffle_order_file and not options.pageset_shuffle: | |
| 42 raise Exception('--pageset-shuffle-order-file requires --pageset-shuffle.') | |
| 43 | |
| 44 if options.pageset_shuffle_order_file: | |
| 45 return page_set.ReorderPageSet(options.pageset_shuffle_order_file) | |
| 46 | |
| 47 page_filter = page_filter_module.PageFilter(options) | |
| 48 pages = [page for page in page_set.pages[:] | |
| 49 if not page.disabled and page_filter.IsSelected(page)] | |
| 50 | |
| 51 if options.pageset_shuffle: | |
| 52 random.Random().shuffle(pages) | |
| 53 return [page | |
| 54 for _ in xrange(int(options.pageset_repeat)) | |
| 55 for page in pages | |
| 56 for _ in xrange(int(options.page_repeat))] | |
| 57 | |
| 58 class PageRunner(object): | |
| 59 """Runs a given test against a given test.""" | |
| 60 def __init__(self, page_set): | |
| 61 self.page_set = page_set | |
| 62 | |
| 63 def __enter__(self): | |
| 64 return self | |
| 65 | |
| 66 def __exit__(self, *args): | |
| 67 self.Close() | |
| 68 | |
| 69 def Run(self, options, possible_browser, test, results): | |
| 70 # Check if we can run against WPR. | |
| 71 for page in self.page_set.pages: | |
| 72 parsed_url = urlparse.urlparse(page.url) | |
| 73 if parsed_url.scheme == 'file': | |
| 74 continue | |
| 75 if not page.archive_path: | |
| 76 logging.warning(""" | |
| 77 No page set archive provided for the page %s. Benchmarking against live sites! | |
| 78 Results won't be repeatable or comparable. | |
| 79 """, page.url) | |
| 80 elif options.wpr_mode != wpr_modes.WPR_RECORD: | |
| 81 # The page has an archive, and we're not recording. | |
| 82 if not os.path.isfile(page.archive_path): | |
| 83 logging.warning(""" | |
| 84 The page set archive %s for page %s does not exist, benchmarking against live | |
| 85 sites! Results won't be repeatable or comparable. | |
| 86 | |
| 87 To fix this, either add svn-internal to your .gclient using | |
| 88 http://goto/read-src-internal, or create a new archive using record_wpr. | |
| 89 """, os.path.relpath(page.archive_path), page.url) | |
| 90 | |
| 91 # Verify credentials path. | |
| 92 credentials_path = None | |
| 93 if self.page_set.credentials_path: | |
| 94 credentials_path = os.path.join(os.path.dirname(self.page_set.file_path), | |
| 95 self.page_set.credentials_path) | |
| 96 if not os.path.exists(credentials_path): | |
| 97 credentials_path = None | |
| 98 | |
| 99 # Set up user agent. | |
| 100 if self.page_set.user_agent_type: | |
| 101 options.browser_user_agent_type = self.page_set.user_agent_type | |
| 102 | |
| 103 for page in self.page_set: | |
| 104 test.CustomizeBrowserOptionsForPage(page, possible_browser.options) | |
| 105 | |
| 106 # Check tracing directory. | |
| 107 if options.trace_dir: | |
| 108 if not os.path.exists(options.trace_dir): | |
| 109 os.mkdir(options.trace_dir) | |
| 110 if not os.path.isdir(options.trace_dir): | |
| 111 raise Exception('--trace-dir isn\'t a directory: %s' % | |
| 112 options.trace_dir) | |
| 113 elif os.listdir(options.trace_dir): | |
| 114 raise Exception('Trace directory isn\'t empty: %s' % options.trace_dir) | |
| 115 | |
| 116 # Reorder page set based on options. | |
| 117 pages = _ShuffleAndFilterPageSet(self.page_set, options) | |
| 118 | |
| 119 state = _RunState() | |
| 120 last_archive_path = None | |
| 121 try: | |
| 122 for page in pages: | |
| 123 if options.wpr_mode != wpr_modes.WPR_RECORD: | |
| 124 if page.archive_path and os.path.isfile(page.archive_path): | |
| 125 possible_browser.options.wpr_mode = wpr_modes.WPR_REPLAY | |
| 126 else: | |
| 127 possible_browser.options.wpr_mode = wpr_modes.WPR_OFF | |
| 128 if last_archive_path != page.archive_path: | |
| 129 state.Close() | |
| 130 state = _RunState() | |
| 131 last_archive_path = page.archive_path | |
| 132 tries = 3 | |
| 133 while tries: | |
| 134 try: | |
| 135 if not state.browser: | |
| 136 self._SetupBrowser(state, test, possible_browser, | |
| 137 credentials_path, page.archive_path) | |
| 138 if not state.tab: | |
| 139 if len(state.browser.tabs) == 0: | |
| 140 state.browser.tabs.New() | |
| 141 state.tab = state.browser.tabs[0] | |
| 142 if options.trace_dir: | |
| 143 self._SetupTracingTab(state) | |
| 144 | |
| 145 try: | |
| 146 self._RunPage(options, page, state.tab, test, results) | |
| 147 except exceptions.TabCrashException: | |
| 148 stdout = '' | |
| 149 if not options.show_stdout: | |
| 150 stdout = state.browser.GetStandardOutput() | |
| 151 stdout = (('\nStandard Output:\n') + | |
| 152 ('*' * 80) + | |
| 153 '\n\t' + stdout.replace('\n', '\n\t') + '\n' + | |
| 154 ('*' * 80)) | |
| 155 logging.warning('Tab crashed: %s%s', page.url, stdout) | |
| 156 state.Close() | |
| 157 | |
| 158 if options.trace_dir: | |
| 159 self._EndTracing(state, options, page) | |
| 160 | |
| 161 if test.needs_browser_restart_after_each_run: | |
| 162 state.Close() | |
| 163 | |
| 164 break | |
| 165 except exceptions.BrowserGoneException: | |
| 166 logging.warning('Lost connection to browser. Retrying.') | |
| 167 state.Close() | |
| 168 tries -= 1 | |
| 169 if not tries: | |
| 170 logging.error('Lost connection to browser 3 times. Failing.') | |
| 171 raise | |
| 172 finally: | |
| 173 state.Close() | |
| 174 | |
| 175 def _RunPage(self, options, page, tab, test, results): | |
| 176 if not test.CanRunForPage(page): | |
| 177 logging.warning('Skiping test: it cannot run for %s', page.url) | |
| 178 results.AddSkippedPage(page, 'Test cannot run', '') | |
| 179 return | |
| 180 | |
| 181 logging.info('Running %s' % page.url) | |
| 182 | |
| 183 page_state = PageState() | |
| 184 try: | |
| 185 did_prepare = self._PreparePage(page, tab, page_state, test, results) | |
| 186 except util.TimeoutException, ex: | |
| 187 logging.warning('Timed out waiting for reply on %s. This is unusual.', | |
| 188 page.url) | |
| 189 results.AddFailure(page, ex, traceback.format_exc()) | |
| 190 return | |
| 191 except exceptions.TabCrashException, ex: | |
| 192 results.AddFailure(page, ex, traceback.format_exc()) | |
| 193 raise | |
| 194 except exceptions.BrowserGoneException: | |
| 195 raise | |
| 196 except Exception, ex: | |
| 197 logging.error('Unexpected failure while running %s: %s', | |
| 198 page.url, traceback.format_exc()) | |
| 199 self._CleanUpPage(page, tab, page_state) | |
| 200 raise | |
| 201 | |
| 202 if not did_prepare: | |
| 203 self._CleanUpPage(page, tab, page_state) | |
| 204 return | |
| 205 | |
| 206 try: | |
| 207 test.Run(options, page, tab, results) | |
| 208 except page_test.Failure, ex: | |
| 209 logging.info('%s: %s', ex, page.url) | |
| 210 results.AddFailure(page, ex, traceback.format_exc()) | |
| 211 return | |
| 212 except util.TimeoutException, ex: | |
| 213 logging.warning('Timed out while running %s', page.url) | |
| 214 results.AddFailure(page, ex, traceback.format_exc()) | |
| 215 return | |
| 216 except exceptions.TabCrashException, ex: | |
| 217 results.AddFailure(page, ex, traceback.format_exc()) | |
| 218 raise | |
| 219 except exceptions.BrowserGoneException: | |
| 220 raise | |
| 221 except Exception, ex: | |
| 222 logging.error('Unexpected failure while running %s: %s', | |
| 223 page.url, traceback.format_exc()) | |
| 224 raise | |
| 225 finally: | |
| 226 self._CleanUpPage(page, tab, page_state) | |
| 227 | |
| 228 results.AddSuccess(page) | |
| 229 | |
| 230 def Close(self): | |
| 231 pass | |
| 232 | |
| 233 def _SetupBrowser(self, state, test, possible_browser, credentials_path, | |
| 234 archive_path): | |
| 235 assert not state.tab | |
| 236 state.browser = possible_browser.Create() | |
| 237 state.browser.credentials.credentials_path = credentials_path | |
| 238 test.SetUpBrowser(state.browser) | |
| 239 | |
| 240 if state.first_browser: | |
| 241 state.browser.credentials.WarnIfMissingCredentials(self.page_set) | |
| 242 state.first_browser = False | |
| 243 | |
| 244 state.browser.SetReplayArchivePath(archive_path) | |
| 245 | |
| 246 def _SetupTracingTab(self, state): | |
| 247 if state.browser.supports_tracing: | |
| 248 state.is_tracing = True | |
| 249 state.browser.StartTracing() | |
| 250 | |
| 251 def _EndTracing(self, state, options, page): | |
| 252 if state.is_tracing: | |
| 253 assert state.browser | |
| 254 state.is_tracing = False | |
| 255 state.browser.StopTracing() | |
| 256 trace_result = state.browser.GetTraceResultAndReset() | |
| 257 logging.info('Processing trace...') | |
| 258 | |
| 259 trace_file_base = os.path.join( | |
| 260 options.trace_dir, page.url_as_file_safe_name) | |
| 261 | |
| 262 if options.page_repeat != 1 or options.pageset_repeat != 1: | |
| 263 trace_file_index = 0 | |
| 264 | |
| 265 while True: | |
| 266 trace_file = '%s_%03d.json' % (trace_file_base, trace_file_index) | |
| 267 if not os.path.exists(trace_file): | |
| 268 break | |
| 269 trace_file_index = trace_file_index + 1 | |
| 270 else: | |
| 271 trace_file = '%s.json' % trace_file_base | |
| 272 with codecs.open(trace_file, 'w', | |
| 273 encoding='utf-8') as trace_file: | |
| 274 trace_result.Serialize(trace_file) | |
| 275 logging.info('Trace saved.') | |
| 276 | |
| 277 def _PreparePage(self, page, tab, page_state, test, results): | |
| 278 parsed_url = urlparse.urlparse(page.url) | |
| 279 if parsed_url[0] == 'file': | |
| 280 dirname, filename = page.url_base_dir_and_file | |
| 281 tab.browser.SetHTTPServerDirectory(dirname) | |
| 282 target_side_url = tab.browser.http_server.UrlOf(filename) | |
| 283 else: | |
| 284 target_side_url = page.url | |
| 285 | |
| 286 if page.credentials: | |
| 287 page_state.did_login = tab.browser.credentials.LoginNeeded( | |
| 288 tab, page.credentials) | |
| 289 if not page_state.did_login: | |
| 290 msg = 'Could not login to %s on %s' % (page.credentials, | |
| 291 target_side_url) | |
| 292 logging.info(msg) | |
| 293 results.AddFailure(page, msg, "") | |
| 294 return False | |
| 295 | |
| 296 test.WillNavigateToPage(page, tab) | |
| 297 tab.Navigate(target_side_url) | |
| 298 test.DidNavigateToPage(page, tab) | |
| 299 | |
| 300 # Wait for unpredictable redirects. | |
| 301 if page.wait_time_after_navigate: | |
| 302 time.sleep(page.wait_time_after_navigate) | |
| 303 page.WaitToLoad(tab, 60) | |
| 304 tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() | |
| 305 | |
| 306 return True | |
| 307 | |
| 308 def _CleanUpPage(self, page, tab, page_state): # pylint: disable=R0201 | |
| 309 if page.credentials and page_state.did_login: | |
| 310 tab.browser.credentials.LoginNoLongerNeeded(tab, page.credentials) | |
| 311 try: | |
| 312 tab.EvaluateJavaScript("""window.chrome && chrome.benchmarking && | |
| 313 chrome.benchmarking.closeConnections()""") | |
| 314 except Exception: | |
| 315 pass | |
| 316 | |
| 317 @staticmethod | |
| 318 def AddCommandLineOptions(parser): | |
| 319 page_filter_module.PageFilter.AddCommandLineOptions(parser) | |
| OLD | NEW |