| Index: tools/telemetry/telemetry/user_story/user_story_runner.py
|
| diff --git a/tools/telemetry/telemetry/user_story/user_story_runner.py b/tools/telemetry/telemetry/user_story/user_story_runner.py
|
| deleted file mode 100644
|
| index a69b888fc76a5a6e36ac786fd4d2fca13abde8b3..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/telemetry/user_story/user_story_runner.py
|
| +++ /dev/null
|
| @@ -1,341 +0,0 @@
|
| -# Copyright 2014 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed by a BSD-style license that can be
|
| -# found in the LICENSE file.
|
| -
|
| -import logging
|
| -import optparse
|
| -import os
|
| -import sys
|
| -import time
|
| -
|
| -from telemetry.core import exceptions
|
| -from telemetry.core import wpr_modes
|
| -from telemetry.internal.actions import page_action
|
| -from telemetry.page import page_test
|
| -from telemetry.results import results_options
|
| -from telemetry.story import story_filter
|
| -from telemetry.util import cloud_storage
|
| -from telemetry.util import exception_formatter
|
| -from telemetry.value import failure
|
| -from telemetry.value import skip
|
| -
|
| -
|
| -class ArchiveError(Exception):
|
| - pass
|
| -
|
| -
|
| -def AddCommandLineArgs(parser):
|
| - story_filter.StoryFilter.AddCommandLineArgs(parser)
|
| - results_options.AddResultsOptions(parser)
|
| -
|
| - # Page set options
|
| - group = optparse.OptionGroup(parser, 'Page set repeat options')
|
| - group.add_option('--page-repeat', default=1, type='int',
|
| - help='Number of times to repeat each individual page '
|
| - 'before proceeding with the next page in the pageset.')
|
| - group.add_option('--pageset-repeat', default=1, type='int',
|
| - help='Number of times to repeat the entire pageset.')
|
| - group.add_option('--max-failures', default=None, type='int',
|
| - help='Maximum number of test failures before aborting '
|
| - 'the run. Defaults to the number specified by the '
|
| - 'PageTest.')
|
| - parser.add_option_group(group)
|
| -
|
| - # WPR options
|
| - group = optparse.OptionGroup(parser, 'Web Page Replay options')
|
| - group.add_option('--use-live-sites',
|
| - dest='use_live_sites', action='store_true',
|
| - help='Run against live sites and ignore the Web Page Replay archives.')
|
| - parser.add_option_group(group)
|
| -
|
| - parser.add_option('-d', '--also-run-disabled-tests',
|
| - dest='run_disabled_tests',
|
| - action='store_true', default=False,
|
| - help='Ignore @Disabled and @Enabled restrictions.')
|
| -
|
| -def ProcessCommandLineArgs(parser, args):
|
| - story_filter.StoryFilter.ProcessCommandLineArgs(parser, args)
|
| - results_options.ProcessCommandLineArgs(parser, args)
|
| -
|
| - # Page set options
|
| - if args.page_repeat < 1:
|
| - parser.error('--page-repeat must be a positive integer.')
|
| - if args.pageset_repeat < 1:
|
| - parser.error('--pageset-repeat must be a positive integer.')
|
| -
|
| -
|
| -def _RunUserStoryAndProcessErrorIfNeeded(expectations, user_story, results,
|
| - state):
|
| - def ProcessError():
|
| - if expectation == 'fail':
|
| - msg = 'Expected exception while running %s' % user_story.display_name
|
| - exception_formatter.PrintFormattedException(msg=msg)
|
| - else:
|
| - msg = 'Exception while running %s' % user_story.display_name
|
| - results.AddValue(failure.FailureValue(user_story, sys.exc_info()))
|
| - try:
|
| - expectation = None
|
| - state.WillRunUserStory(user_story)
|
| - expectation, skip_value = state.GetTestExpectationAndSkipValue(expectations)
|
| - if expectation == 'skip':
|
| - assert skip_value
|
| - results.AddValue(skip_value)
|
| - return
|
| - state.RunUserStory(results)
|
| - except (page_test.Failure, exceptions.TimeoutException,
|
| - exceptions.LoginException, exceptions.ProfilingException):
|
| - ProcessError()
|
| - except exceptions.Error:
|
| - ProcessError()
|
| - raise
|
| - except page_action.PageActionNotSupported as e:
|
| - results.AddValue(
|
| - skip.SkipValue(user_story, 'Unsupported page action: %s' % e))
|
| - except Exception:
|
| - results.AddValue(
|
| - failure.FailureValue(
|
| - user_story, sys.exc_info(), 'Unhandlable exception raised.'))
|
| - raise
|
| - else:
|
| - if expectation == 'fail':
|
| - logging.warning(
|
| - '%s was expected to fail, but passed.\n', user_story.display_name)
|
| - finally:
|
| - has_existing_exception = sys.exc_info() is not None
|
| - try:
|
| - state.DidRunUserStory(results)
|
| - except Exception:
|
| - if not has_existing_exception:
|
| - raise
|
| - # Print current exception and propagate existing exception.
|
| - exception_formatter.PrintFormattedException(
|
| - msg='Exception from DidRunUserStory: ')
|
| -
|
| -class UserStoryGroup(object):
|
| - def __init__(self, shared_state_class):
|
| - self._shared_state_class = shared_state_class
|
| - self._user_stories = []
|
| -
|
| - @property
|
| - def shared_state_class(self):
|
| - return self._shared_state_class
|
| -
|
| - @property
|
| - def user_stories(self):
|
| - return self._user_stories
|
| -
|
| - def AddUserStory(self, user_story):
|
| - assert (user_story.shared_state_class is
|
| - self._shared_state_class)
|
| - self._user_stories.append(user_story)
|
| -
|
| -
|
| -def StoriesGroupedByStateClass(user_story_set, allow_multiple_groups):
|
| - """ Returns a list of user story groups which each contains user stories with
|
| - the same shared_state_class.
|
| -
|
| - Example:
|
| - Assume A1, A2, A3 are user stories with same shared user story class, and
|
| - similar for B1, B2.
|
| - If their orders in user story set is A1 A2 B1 B2 A3, then the grouping will
|
| - be [A1 A2] [B1 B2] [A3].
|
| -
|
| - It's purposefully done this way to make sure that order of user
|
| - stories are the same of that defined in user_story_set. It's recommended that
|
| - user stories with the same states should be arranged next to each others in
|
| - user story sets to reduce the overhead of setting up & tearing down the
|
| - shared user story state.
|
| - """
|
| - user_story_groups = []
|
| - user_story_groups.append(
|
| - UserStoryGroup(user_story_set[0].shared_state_class))
|
| - for user_story in user_story_set:
|
| - if (user_story.shared_state_class is not
|
| - user_story_groups[-1].shared_state_class):
|
| - if not allow_multiple_groups:
|
| - raise ValueError('This UserStorySet is only allowed to have one '
|
| - 'SharedState but contains the following '
|
| - 'SharedState classes: %s, %s.\n Either '
|
| - 'remove the extra SharedStates or override '
|
| - 'allow_mixed_story_states.' % (
|
| - user_story_groups[-1].shared_state_class,
|
| - user_story.shared_state_class))
|
| - user_story_groups.append(
|
| - UserStoryGroup(user_story.shared_state_class))
|
| - user_story_groups[-1].AddUserStory(user_story)
|
| - return user_story_groups
|
| -
|
| -
|
| -def Run(test, user_story_set, expectations, finder_options, results,
|
| - max_failures=None):
|
| - """Runs a given test against a given page_set with the given options.
|
| -
|
| - Stop execution for unexpected exceptions such as KeyboardInterrupt.
|
| - We "white list" certain exceptions for which the user story runner
|
| - can continue running the remaining user stories.
|
| - """
|
| - # Filter page set based on options.
|
| - user_stories = filter(story_filter.StoryFilter.IsSelected,
|
| - user_story_set)
|
| -
|
| - if (not finder_options.use_live_sites and user_story_set.bucket and
|
| - finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD):
|
| - serving_dirs = user_story_set.serving_dirs
|
| - for directory in serving_dirs:
|
| - cloud_storage.GetFilesInDirectoryIfChanged(directory,
|
| - user_story_set.bucket)
|
| - if not _UpdateAndCheckArchives(
|
| - user_story_set.archive_data_file, user_story_set.wpr_archive_info,
|
| - user_stories):
|
| - return
|
| -
|
| - if not user_stories:
|
| - return
|
| -
|
| - # Effective max failures gives priority to command-line flag value.
|
| - effective_max_failures = finder_options.max_failures
|
| - if effective_max_failures is None:
|
| - effective_max_failures = max_failures
|
| -
|
| - user_story_groups = StoriesGroupedByStateClass(
|
| - user_stories,
|
| - user_story_set.allow_mixed_story_states)
|
| -
|
| - for group in user_story_groups:
|
| - state = None
|
| - try:
|
| - for _ in xrange(finder_options.pageset_repeat):
|
| - for user_story in group.user_stories:
|
| - for _ in xrange(finder_options.page_repeat):
|
| - if not state:
|
| - state = group.shared_state_class(
|
| - test, finder_options, user_story_set)
|
| - results.WillRunPage(user_story)
|
| - try:
|
| - _WaitForThermalThrottlingIfNeeded(state.platform)
|
| - _RunUserStoryAndProcessErrorIfNeeded(
|
| - expectations, user_story, results, state)
|
| - except exceptions.Error:
|
| - # Catch all Telemetry errors to give the story a chance to retry.
|
| - # The retry is enabled by tearing down the state and creating
|
| - # a new state instance in the next iteration.
|
| - try:
|
| - # If TearDownState raises, do not catch the exception.
|
| - # (The Error was saved as a failure value.)
|
| - state.TearDownState(results)
|
| - finally:
|
| - # Later finally-blocks use state, so ensure it is cleared.
|
| - state = None
|
| - finally:
|
| - has_existing_exception = sys.exc_info() is not None
|
| - try:
|
| - if state:
|
| - _CheckThermalThrottling(state.platform)
|
| - results.DidRunPage(user_story)
|
| - except Exception:
|
| - if not has_existing_exception:
|
| - raise
|
| - # Print current exception and propagate existing exception.
|
| - exception_formatter.PrintFormattedException(
|
| - msg='Exception from result processing:')
|
| - if (effective_max_failures is not None and
|
| - len(results.failures) > effective_max_failures):
|
| - logging.error('Too many failures. Aborting.')
|
| - return
|
| - finally:
|
| - if state:
|
| - has_existing_exception = sys.exc_info() is not None
|
| - try:
|
| - state.TearDownState(results)
|
| - except Exception:
|
| - if not has_existing_exception:
|
| - raise
|
| - # Print current exception and propagate existing exception.
|
| - exception_formatter.PrintFormattedException(
|
| - msg='Exception from TearDownState:')
|
| -
|
| -
|
| -def _UpdateAndCheckArchives(archive_data_file, wpr_archive_info,
|
| - filtered_user_stories):
|
| - """Verifies that all user stories are local or have WPR archives.
|
| -
|
| - Logs warnings and returns False if any are missing.
|
| - """
|
| - # Report any problems with the entire user story set.
|
| - if any(not user_story.is_local for user_story in filtered_user_stories):
|
| - if not archive_data_file:
|
| - logging.error('The user story set is missing an "archive_data_file" '
|
| - 'property.\nTo run from live sites pass the flag '
|
| - '--use-live-sites.\nTo create an archive file add an '
|
| - 'archive_data_file property to the user story set and then '
|
| - 'run record_wpr.')
|
| - raise ArchiveError('No archive data file.')
|
| - if not wpr_archive_info:
|
| - logging.error('The archive info file is missing.\n'
|
| - 'To fix this, either add svn-internal to your '
|
| - '.gclient using http://goto/read-src-internal, '
|
| - 'or create a new archive using record_wpr.')
|
| - raise ArchiveError('No archive info file.')
|
| - wpr_archive_info.DownloadArchivesIfNeeded()
|
| -
|
| - # Report any problems with individual user story.
|
| - user_stories_missing_archive_path = []
|
| - user_stories_missing_archive_data = []
|
| - for user_story in filtered_user_stories:
|
| - if not user_story.is_local:
|
| - archive_path = wpr_archive_info.WprFilePathForUserStory(user_story)
|
| - if not archive_path:
|
| - user_stories_missing_archive_path.append(user_story)
|
| - elif not os.path.isfile(archive_path):
|
| - user_stories_missing_archive_data.append(user_story)
|
| - if user_stories_missing_archive_path:
|
| - logging.error(
|
| - 'The user story set archives for some user stories do not exist.\n'
|
| - 'To fix this, record those user stories using record_wpr.\n'
|
| - 'To ignore this warning and run against live sites, '
|
| - 'pass the flag --use-live-sites.')
|
| - logging.error(
|
| - 'User stories without archives: %s',
|
| - ', '.join(user_story.display_name
|
| - for user_story in user_stories_missing_archive_path))
|
| - if user_stories_missing_archive_data:
|
| - logging.error(
|
| - 'The user story set archives for some user stories are missing.\n'
|
| - 'Someone forgot to check them in, uploaded them to the '
|
| - 'wrong cloud storage bucket, or they were deleted.\n'
|
| - 'To fix this, record those user stories using record_wpr.\n'
|
| - 'To ignore this warning and run against live sites, '
|
| - 'pass the flag --use-live-sites.')
|
| - logging.error(
|
| - 'User stories missing archives: %s',
|
| - ', '.join(user_story.display_name
|
| - for user_story in user_stories_missing_archive_data))
|
| - if user_stories_missing_archive_path or user_stories_missing_archive_data:
|
| - raise ArchiveError('Archive file is missing user stories.')
|
| - # Only run valid user stories if no problems with the user story set or
|
| - # individual user stories.
|
| - return True
|
| -
|
| -
|
| -def _WaitForThermalThrottlingIfNeeded(platform):
|
| - if not platform.CanMonitorThermalThrottling():
|
| - return
|
| - thermal_throttling_retry = 0
|
| - while (platform.IsThermallyThrottled() and
|
| - thermal_throttling_retry < 3):
|
| - logging.warning('Thermally throttled, waiting (%d)...',
|
| - thermal_throttling_retry)
|
| - thermal_throttling_retry += 1
|
| - time.sleep(thermal_throttling_retry * 2)
|
| -
|
| - if thermal_throttling_retry and platform.IsThermallyThrottled():
|
| - logging.warning('Device is thermally throttled before running '
|
| - 'performance tests, results will vary.')
|
| -
|
| -
|
| -def _CheckThermalThrottling(platform):
|
| - if not platform.CanMonitorThermalThrottling():
|
| - return
|
| - if platform.HasBeenThermallyThrottled():
|
| - logging.warning('Device has been thermally throttled during '
|
| - 'performance tests, results will vary.')
|
|
|