page_runner.py revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import codecs
5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)import glob
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import logging
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import os
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import sys
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import time
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import traceback
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import urlparse
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import random
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import util
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import wpr_modes
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import exceptions
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.page import page_filter as page_filter_module
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.page import page_test
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PageState(object):
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self):
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.did_login = False
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class _RunState(object):
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self):
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.first_browser = True
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.browser = None
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.tab = None
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.is_tracing = False
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Close(self):
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.is_tracing = False
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self.tab:
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.tab.Disconnect()
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.tab = None
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self.browser:
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.browser.Close()
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.browser = None
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def _ShuffleAndFilterPageSet(page_set, options):
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if options.pageset_shuffle_order_file and not options.pageset_shuffle:
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    raise Exception('--pageset-shuffle-order-file requires --pageset-shuffle.')
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if options.pageset_shuffle_order_file:
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return page_set.ReorderPageSet(options.pageset_shuffle_order_file)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  page_filter = page_filter_module.PageFilter(options)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pages = [page for page in page_set.pages[:]
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           if not page.disabled and page_filter.IsSelected(page)]
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if options.pageset_shuffle:
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    random.Random().shuffle(pages)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return [page
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for _ in xrange(int(options.pageset_repeat))
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for page in pages
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for _ in xrange(int(options.page_repeat))]
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PageRunner(object):
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """Runs a given test against a given test."""
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __init__(self, page_set):
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.page_set = page_set
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    self.has_called_will_run_page_set = False
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __enter__(self):
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __exit__(self, *args):
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.Close()
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  def Run(self, options, possible_browser, test, out_results):
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Reorder page set based on options.
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pages = _ShuffleAndFilterPageSet(self.page_set, options)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if not options.allow_live_sites:
7790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      pages = self._CheckArchives(options, pages, out_results)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Verify credentials path.
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    credentials_path = None
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self.page_set.credentials_path:
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      credentials_path = os.path.join(os.path.dirname(self.page_set.file_path),
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      self.page_set.credentials_path)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if not os.path.exists(credentials_path):
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        credentials_path = None
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Set up user agent.
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self.page_set.user_agent_type:
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      options.browser_user_agent_type = self.page_set.user_agent_type
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for page in pages:
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      test.CustomizeBrowserOptionsForPage(page, possible_browser.options)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    self._ValidateOrCreateEmptyDirectory('--trace-dir', options.trace_dir)
95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    self._ValidateOrCreateEmptyDirectory('--profiler-dir', options.profiler_dir)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state = _RunState()
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_archive_path = None
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    results_for_current_run = out_results
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for page in pages:
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if options.wpr_mode != wpr_modes.WPR_RECORD:
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if page.archive_path and os.path.isfile(page.archive_path):
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            possible_browser.options.wpr_mode = wpr_modes.WPR_REPLAY
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          else:
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            possible_browser.options.wpr_mode = wpr_modes.WPR_OFF
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if last_archive_path != page.archive_path:
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          state.Close()
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          state = _RunState()
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          last_archive_path = page.archive_path
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (test.discard_first_result and
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            not self.has_called_will_run_page_set):
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          # If discarding results, substitute a dummy object.
11590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          results_for_current_run = type(out_results)()
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        else:
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          results_for_current_run = out_results
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        results_for_current_run.StartTest(page)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        tries = 3
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        while tries:
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          try:
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if not state.browser:
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              self._SetupBrowser(state, test, possible_browser,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 credentials_path, page.archive_path)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if not state.tab:
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if len(state.browser.tabs) == 0:
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                state.browser.tabs.New()
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              state.tab = state.browser.tabs[0]
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if state.browser.supports_tab_control:
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              while len(state.browser.tabs) > 1:
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                state.browser.tabs[-1].Close()
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
134b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            self._WaitForThermalThrottlingIfNeeded(state.browser.platform)
135b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if options.trace_dir:
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              self._SetupTracingTab(state)
138b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            if options.profiler_dir:
139b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)              self._SetupProfiling(state, options, page)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            try:
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              self._RunPage(options, page, state.tab, test,
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                            results_for_current_run)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              self._CheckThermalThrottling(state.browser.platform)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            except exceptions.TabCrashException:
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              stdout = ''
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              if not options.show_stdout:
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                stdout = state.browser.GetStandardOutput()
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                stdout = (('\nStandard Output:\n') +
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ('*' * 80) +
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          '\n\t' + stdout.replace('\n', '\n\t') + '\n' +
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ('*' * 80))
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              logging.warning('Tab crashed: %s%s', page.url, stdout)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              state.Close()
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if options.trace_dir:
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              self._EndTracing(state, options, page)
158b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)            if options.profiler_dir:
159b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)              self._EndProfiling(state)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if test.needs_browser_restart_after_each_run:
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              state.Close()
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          except exceptions.BrowserGoneException:
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            logging.warning('Lost connection to browser. Retrying.')
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            state.Close()
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            tries -= 1
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if not tries:
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              logging.error('Lost connection to browser 3 times. Failing.')
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              raise
17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        results_for_current_run.StopTest(page)
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      test.DidRunPageSet(state.tab, results_for_current_run)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    finally:
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.Close()
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  def _CheckArchives(self, options, pages, out_results):
17890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    """Returns a subset of pages that are local or have WPR archives.
17990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
18090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    Logs warnings if any are missing."""
18190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    pages_without_archive_path = []
18290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    pages_missing_archive_file = []
18390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
18490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for page in pages:
18590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      parsed_url = urlparse.urlparse(page.url)
18690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if parsed_url.scheme == 'chrome' or parsed_url.scheme == 'file':
18790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        continue
18890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
18990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if not page.archive_path:
19090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        pages_without_archive_path.append(page)
19190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      elif options.wpr_mode != wpr_modes.WPR_RECORD:
19290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        if not os.path.isfile(page.archive_path):
19390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          pages_missing_archive_file.append(page)
19490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for page in pages_without_archive_path:
19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.StartTest(page)
19790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.AddErrorMessage(page, 'Page set archive not defined.')
19890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.StopTest(page)
19990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for page in pages_missing_archive_file:
20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.StartTest(page)
20190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.AddErrorMessage(page, 'Page set archive doesn\'t exist.')
20290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      out_results.StopTest(page)
20390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
20490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if pages_without_archive_path:
20590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('No page set archive provided for some pages. '
20690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      'Skipping those pages. To run against live sites, '
20790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      'pass the flag --allow-live-sites.')
20890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('First 5 such pages:\n%s' % '\n'.join(
20990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          [page.url for page in pages_without_archive_path][:5]))
21090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
21190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if pages_missing_archive_file:
21290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('The page set archives some pages do not exist, '
21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      'not running those pages. '
21490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      'To fix this, either add svn-internal to your '
21590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      '.gclient using http://goto/read-src-internal, '
21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      'or create a new archive using record_wpr.')
21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('First 5 such pages:\n%s' % '\n'.join(
21890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          [page.url for page in pages_missing_archive_file][:5]))
21990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
22090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return [page for page in pages if page not in
22190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            pages_without_archive_path + pages_missing_archive_file]
22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _RunPage(self, options, page, tab, test, results):
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not test.CanRunForPage(page):
22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('Skipping test: it cannot run for %s', page.url)
22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      results.AddSkip(page, 'Test cannot run')
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    logging.info('Running %s' % page.url)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    page_state = PageState()
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      self._PreparePage(page, tab, page_state, test, results)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      test.Run(options, page, tab, results)
23690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      util.CloseConnections(tab)
23790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    except page_test.Failure:
23890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.warning('%s:\n%s', page.url, traceback.format_exc())
23990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      results.AddFailure(page, sys.exc_info())
24090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    except (util.TimeoutException, exceptions.LoginException):
24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.error('%s:\n%s', page.url, traceback.format_exc())
24290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      results.AddError(page, sys.exc_info())
24390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    except (exceptions.TabCrashException, exceptions.BrowserGoneException):
24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.error('%s:\n%s', page.url, traceback.format_exc())
24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      results.AddError(page, sys.exc_info())
24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      # Run() catches these exceptions to relaunch the tab/browser, so re-raise.
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise
24890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    except Exception:
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise
25090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    else:
25190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      results.AddSuccess(page)
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    finally:
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._CleanUpPage(page, tab, page_state)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Close(self):
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pass
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
258b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def _GetSequentialFileName(self, base_name):
259b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    """Returns the next sequential file name based on |base_name| and the
260b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    existing files."""
261b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    index = 0
262b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    while True:
263b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      output_name = '%s_%03d' % (base_name, index)
264b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      if not glob.glob(output_name + '.*'):
265b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        break
266b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      index = index + 1
267b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return output_name
268b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
269b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def _ValidateOrCreateEmptyDirectory(self, name, path):
270b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if not path:
271b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      return
272b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if not os.path.exists(path):
273b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      os.mkdir(path)
274b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if not os.path.isdir(path):
275b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      raise Exception('%s isn\'t a directory: %s' % (name, path))
276b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    elif os.listdir(path):
277b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      raise Exception('%s isn\'t empty: %s' % (name, path))
278b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _SetupBrowser(self, state, test, possible_browser, credentials_path,
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    archive_path):
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    assert not state.tab
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state.browser = possible_browser.Create()
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state.browser.credentials.credentials_path = credentials_path
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    test.SetUpBrowser(state.browser)
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if state.first_browser:
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.browser.credentials.WarnIfMissingCredentials(self.page_set)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.first_browser = False
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state.browser.SetReplayArchivePath(archive_path)
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def _SetupProfiling(self, state, options, page):
293b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    output_file = os.path.join(options.profiler_dir, page.url_as_file_safe_name)
294b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if options.page_repeat != 1 or options.pageset_repeat != 1:
295b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      output_file = self._GetSequentialFileName(output_file)
296b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    state.browser.StartProfiling(options, output_file)
297b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
298b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def _EndProfiling(self, state):
299b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    state.browser.StopProfiling()
300b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _SetupTracingTab(self, state):
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if state.browser.supports_tracing:
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.is_tracing = True
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.browser.StartTracing()
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _EndTracing(self, state, options, page):
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if state.is_tracing:
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      assert state.browser
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.is_tracing = False
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state.browser.StopTracing()
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      trace_result = state.browser.GetTraceResultAndReset()
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      logging.info('Processing trace...')
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
314b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      trace_file = os.path.join(options.trace_dir, page.url_as_file_safe_name)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if options.page_repeat != 1 or options.pageset_repeat != 1:
316b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        trace_file = self._GetSequentialFileName(trace_file)
317b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      trace_file += '.json'
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      with codecs.open(trace_file, 'w',
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       encoding='utf-8') as trace_file:
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        trace_result.Serialize(trace_file)
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      logging.info('Trace saved.')
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _PreparePage(self, page, tab, page_state, test, results):
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    parsed_url = urlparse.urlparse(page.url)
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if parsed_url[0] == 'file':
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      serving_dirs, filename = page.serving_dirs_and_file
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab.browser.SetHTTPServerDirectories(serving_dirs)
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      target_side_url = tab.browser.http_server.UrlOf(filename)
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      target_side_url = page.url
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if page.credentials:
33490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if not tab.browser.credentials.LoginNeeded(tab, page.credentials):
33590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        raise page_test.Failure('Login as ' + page.credentials + ' failed')
33690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      page_state.did_login = True
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if not self.has_called_will_run_page_set:
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      self.has_called_will_run_page_set = True
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      test.WillRunPageSet(tab, results)
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    test.WillNavigateToPage(page, tab)
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab.Navigate(target_side_url, page.script_to_evaluate_on_commit)
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    test.DidNavigateToPage(page, tab)
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    page.WaitToLoad(tab, 60)
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _CleanUpPage(self, page, tab, page_state): # pylint: disable=R0201
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if page.credentials and page_state.did_login:
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab.browser.credentials.LoginNoLongerNeeded(tab, page.credentials)
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _WaitForThermalThrottlingIfNeeded(self, platform):
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not platform.CanMonitorThermalThrottling():
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    thermal_throttling_retry = 0
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    while (platform.IsThermallyThrottled() and
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           thermal_throttling_retry < 3):
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      logging.warning('Thermally throttled, waiting (%d)...',
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      thermal_throttling_retry)
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      thermal_throttling_retry += 1
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      time.sleep(thermal_throttling_retry * 2)
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if platform.IsThermallyThrottled():
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      logging.error('Device is thermally throttled before running '
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    'performance tests, results will vary.')
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def _CheckThermalThrottling(self, platform):
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not platform.CanMonitorThermalThrottling():
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if platform.HasBeenThermallyThrottled():
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      logging.error('Device has been thermally throttled during '
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    'performance tests, results will vary.')
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  @staticmethod
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def AddCommandLineOptions(parser):
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    page_filter_module.PageFilter.AddCommandLineOptions(parser)
378