Chromium Code Reviews| Index: tools/telemetry/telemetry/user_story/user_story_runner.py |
| diff --git a/tools/telemetry/telemetry/page/page_runner.py b/tools/telemetry/telemetry/user_story/user_story_runner.py |
| similarity index 60% |
| rename from tools/telemetry/telemetry/page/page_runner.py |
| rename to tools/telemetry/telemetry/user_story/user_story_runner.py |
| index de95853894889c78ff2eba9d3b24726eae245de2..c6b44cdb81a9215fb1776b12943b475e23d6d884 100644 |
| --- a/tools/telemetry/telemetry/page/page_runner.py |
| +++ b/tools/telemetry/telemetry/user_story/user_story_runner.py |
| @@ -14,6 +14,7 @@ from telemetry.core import exceptions |
| from telemetry.core import util |
| from telemetry.core import wpr_modes |
| from telemetry.page import shared_page_state |
| +from telemetry.page import page_set as page_set_module |
| from telemetry.page import page_test |
| from telemetry.page.actions import page_action |
| from telemetry.results import results_options |
| @@ -75,25 +76,25 @@ def ProcessCommandLineArgs(parser, args): |
| parser.error('--pageset-repeat must be a positive integer.') |
| -def _RunPageAndHandleExceptionIfNeeded(test, page_set, expectations, |
| - page, results, state): |
| +def _RunUserStoryAndProcessErrorIfNeeded( |
| + test, expectations, user_story, results, state): |
| expectation = None |
| def ProcessError(): |
| if expectation == 'fail': |
| - msg = 'Expected exception while running %s' % page.url |
| + msg = 'Expected exception while running %s' % user_story.display_name |
| exception_formatter.PrintFormattedException(msg=msg) |
| else: |
| - msg = 'Exception while running %s' % page.url |
| - results.AddValue(failure.FailureValue(page, sys.exc_info())) |
| + msg = 'Exception while running %s' % user_story.display_name |
| + results.AddValue(failure.FailureValue(user_story, sys.exc_info())) |
| try: |
| - state.WillRunPage(page, page_set) |
| - expectation, skip_value = state.GetPageExpectationAndSkipValue(expectations) |
| + state.WillRunUserStory(user_story) |
| + expectation, skip_value = state.GetTestExpectationAndSkipValue(expectations) |
| if expectation == 'skip': |
| assert skip_value |
| results.AddValue(skip_value) |
| return |
| - state.RunPage(results) |
| + state.RunUserStory(results) |
| except page_test.TestNotSupportedOnPlatformFailure: |
| raise |
| except (page_test.Failure, util.TimeoutException, exceptions.LoginException, |
| @@ -104,24 +105,26 @@ def _RunPageAndHandleExceptionIfNeeded(test, page_set, expectations, |
| state.TearDown(results) |
| if test.is_multi_tab_test: |
| logging.error('Aborting multi-tab test after browser or tab crashed at ' |
| - 'page %s' % page.url) |
| + 'user story %s' % user_story.display_name) |
| test.RequestExit() |
| return |
| except page_action.PageActionNotSupported as e: |
| - results.AddValue(skip.SkipValue(page, 'Unsupported page action: %s' % e)) |
| + results.AddValue( |
| + skip.SkipValue(user_story, 'Unsupported page action: %s' % e)) |
| except Exception: |
| exception_formatter.PrintFormattedException( |
| - msg='Unhandled exception while running %s' % page.url) |
| - results.AddValue(failure.FailureValue(page, sys.exc_info())) |
| + msg='Unhandled exception while running %s' % user_story.display_name) |
| + results.AddValue(failure.FailureValue(user_story, sys.exc_info())) |
| else: |
| if expectation == 'fail': |
| - logging.warning('%s was expected to fail, but passed.\n', page.url) |
| + logging.warning( |
| + '%s was expected to fail, but passed.\n', user_story.display_name) |
| finally: |
| - state.DidRunPage(results) |
| + state.DidRunUserStory(results) |
| @decorators.Cache |
| -def _UpdatePageSetArchivesIfChanged(page_set): |
| +def _UpdateUserStoryArchivesIfChanged(page_set): |
| # Scan every serving directory for .sha1 files |
| # and download them from Cloud Storage. Assume all data is public. |
| all_serving_dirs = page_set.serving_dirs.copy() |
| @@ -142,76 +145,125 @@ def _UpdatePageSetArchivesIfChanged(page_set): |
| cloud_storage.GetIfChanged(path, page_set.bucket) |
| -def Run(test, page_set, expectations, finder_options, results): |
| +class UserStoryGroup(object): |
| + def __init__(self, shared_user_story_state_class): |
| + self._shared_user_story_state_class = shared_user_story_state_class |
| + self._user_stories = [] |
| + |
| + @property |
| + def shared_user_story_state_class(self): |
| + return self._shared_user_story_state_class |
| + |
| + @property |
| + def user_stories(self): |
| + return self._user_stories |
| + |
| + def AddUserStory(self, user_story): |
| + assert (user_story.shared_user_story_state_class is |
| + self._shared_user_story_state_class) |
| + self._user_stories.append(user_story) |
| + |
| + |
| +def GetUserStoryGroupsWithSameSharedUserStoryClass(user_story_set): |
| + """ Returns a list of user story groups which each contains user stories with |
| + the same shared_user_story_state_class. """ |
| + user_story_groups = [] |
| + user_story_groups.append( |
| + UserStoryGroup(user_story_set[0].shared_user_story_state_class)) |
| + for user_story in user_story_set: |
| + if (user_story.shared_user_story_state_class is not |
|
chrishenry
2014/11/20 19:01:24
Btw, it might be a nice syntax to use decorator to
nednguyen
2014/11/20 21:08:27
Err, I would avoid innovating the syntax for now.
chrishenry
2014/11/20 21:54:27
Yeah, it's a comment for future, not for this patc
|
| + user_story_groups[-1].shared_user_story_state_class): |
| + user_story_groups.append( |
| + UserStoryGroup(user_story.shared_user_story_state_class)) |
| + user_story_groups[-1].AddUserStory(user_story) |
|
chrishenry
2014/11/20 19:01:24
if you have (A and B are the user story state)
A A
chrishenry
2014/11/20 21:54:27
Ping?
nednguyen
2014/11/20 22:15:02
I added documentation. We will add some check in p
|
| + return user_story_groups |
| + |
| + |
| +def Run(test, user_story_set, expectations, finder_options, results): |
| """Runs a given test against a given page_set with the given options.""" |
| - test.ValidatePageSet(page_set) |
| + test.ValidatePageSet(user_story_set) |
| # Reorder page set based on options. |
| - pages = _ShuffleAndFilterPageSet(page_set, finder_options) |
| - |
| - if not finder_options.use_live_sites: |
| - if finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD: |
| - _UpdatePageSetArchivesIfChanged(page_set) |
| - pages = _CheckArchives(page_set, pages, results) |
| - |
| - for page in list(pages): |
| - if not test.CanRunForPage(page): |
| - results.WillRunPage(page) |
| - logging.debug('Skipping test: it cannot run for %s', page.url) |
| - results.AddValue(skip.SkipValue(page, 'Test cannot run')) |
| - results.DidRunPage(page) |
| - pages.remove(page) |
| - |
| - if not pages: |
| + user_stories = _ShuffleAndFilterUserStorySet(user_story_set, finder_options) |
| + |
| + if (not finder_options.use_live_sites and |
| + finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD and |
| + # TODO(nednguyen): also handle these logic for user_story_set in next |
| + # patch. |
| + isinstance(user_story_set, page_set_module.PageSet)): |
| + _UpdateUserStoryArchivesIfChanged(user_story_set) |
| + user_stories = _CheckArchives(user_story_set, user_stories, results) |
| + |
| + for user_story in list(user_stories): |
| + if not test.CanRunForPage(user_story): |
| + results.WillRunPage(user_story) |
| + logging.debug('Skipping test: it cannot run for %s', |
| + user_story.display_name) |
| + results.AddValue(skip.SkipValue(user_story, 'Test cannot run')) |
| + results.DidRunPage(user_story) |
| + user_stories.remove(user_story) |
| + |
| + if not user_stories: |
| return |
| - state = shared_page_state.SharedPageState(test, finder_options, page_set) |
| - pages_with_discarded_first_result = set() |
| + user_story_with_discarded_first_results = set() |
| max_failures = finder_options.max_failures # command-line gets priority |
| if max_failures is None: |
| max_failures = test.max_failures # may be None |
| - |
| - try: |
| - test.WillRunTest(finder_options) |
| - for _ in xrange(finder_options.pageset_repeat): |
| - for page in pages: |
| - if test.IsExiting(): |
| - break |
| - for _ in xrange(finder_options.page_repeat): |
| - results.WillRunPage(page) |
| - try: |
| - _WaitForThermalThrottlingIfNeeded(state.platform) |
| - _RunPageAndHandleExceptionIfNeeded( |
| - test, page_set, expectations, page, results, state) |
| - except Exception: |
| - # Tear down & restart the state for unhandled exceptions thrown by |
| - # _RunPageAndHandleExceptionIfNeeded. |
| - results.AddValue(failure.FailureValue(page, sys.exc_info())) |
| - state.TearDown(results) |
| - state = shared_page_state.SharedPageState( |
| - test, finder_options, page_set) |
| - finally: |
| - _CheckThermalThrottling(state.platform) |
| - discard_run = (test.discard_first_result and |
| - page not in pages_with_discarded_first_result) |
| - if discard_run: |
| - pages_with_discarded_first_result.add(page) |
| - results.DidRunPage(page, discard_run=discard_run) |
| - if max_failures is not None and len(results.failures) > max_failures: |
| - logging.error('Too many failures. Aborting.') |
| - test.RequestExit() |
| - finally: |
| - state.TearDown(results) |
| - |
| - |
| -def _ShuffleAndFilterPageSet(page_set, finder_options): |
| + user_story_groups = GetUserStoryGroupsWithSameSharedUserStoryClass( |
| + user_stories) |
| + |
| + test.WillRunTest(finder_options) |
| + state = None |
| + for _ in xrange(finder_options.pageset_repeat): |
| + for group in user_story_groups: |
| + try: |
| + state = group.shared_user_story_state_class( |
| + test, finder_options, user_story_set) |
| + for user_story in group.user_stories: |
| + if test.IsExiting(): |
| + break |
| + for _ in xrange(finder_options.page_repeat): |
| + results.WillRunPage(user_story) |
| + try: |
| + _WaitForThermalThrottlingIfNeeded(state.platform) |
| + _RunUserStoryAndProcessErrorIfNeeded( |
| + test, expectations, user_story, results, state) |
| + except Exception: |
| + # Tear down & restart the state for unhandled exceptions thrown by |
| + # _RunUserStoryAndProcessErrorIfNeeded. |
| + results.AddValue(failure.FailureValue(user_story, sys.exc_info())) |
| + state.TearDown(results) |
| + state = group.shared_user_story_state_class( |
| + test, finder_options, user_story_set) |
| + finally: |
| + _CheckThermalThrottling(state.platform) |
| + discard_run = (test.discard_first_result and |
| + user_story not in |
| + user_story_with_discarded_first_results) |
| + if discard_run: |
| + user_story_with_discarded_first_results.add(user_story) |
| + results.DidRunPage(user_story, discard_run=discard_run) |
| + if max_failures is not None and len(results.failures) > max_failures: |
| + logging.error('Too many failures. Aborting.') |
| + test.RequestExit() |
| + finally: |
| + if state: |
| + state.TearDown(results) |
| + |
| +def _ShuffleAndFilterUserStorySet(user_story_set, finder_options): |
| if finder_options.pageset_shuffle_order_file: |
| - return page_set.ReorderPageSet(finder_options.pageset_shuffle_order_file) |
| - pages = [page for page in page_set.pages[:] |
| - if user_story_filter.UserStoryFilter.IsSelected(page)] |
| + if isinstance(user_story_set, page_set_module.PageSet): |
| + return page_set_module.ReorderPageSet( |
| + finder_options.pageset_shuffle_order_file) |
| + else: |
| + raise Exception( |
| + 'pageset-shuffle-order-file flag can only be used with page set') |
| + user_stories = [u for u in user_story_set[:] |
| + if user_story_filter.UserStoryFilter.IsSelected(u)] |
| if finder_options.pageset_shuffle: |
| - random.shuffle(pages) |
| - return pages |
| + random.shuffle(user_stories) |
| + return user_stories |
| def _CheckArchives(page_set, pages, results): |