Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Side by Side Diff: tools/telemetry/telemetry/user_story/user_story_runner.py

Issue 733303003: [Telemetry] Introduce shared_user_story_state for real. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging 5 import logging
6 import optparse 6 import optparse
7 import os 7 import os
8 import random 8 import random
9 import sys 9 import sys
10 import time 10 import time
11 11
12 from telemetry import decorators 12 from telemetry import decorators
13 from telemetry.core import exceptions 13 from telemetry.core import exceptions
14 from telemetry.core import util 14 from telemetry.core import util
15 from telemetry.core import wpr_modes 15 from telemetry.core import wpr_modes
16 from telemetry.page import shared_page_state 16 from telemetry.page import shared_page_state
17 from telemetry.page import page_set as page_set_module
17 from telemetry.page import page_test 18 from telemetry.page import page_test
18 from telemetry.page.actions import page_action 19 from telemetry.page.actions import page_action
19 from telemetry.results import results_options 20 from telemetry.results import results_options
20 from telemetry.user_story import user_story_filter 21 from telemetry.user_story import user_story_filter
21 from telemetry.util import cloud_storage 22 from telemetry.util import cloud_storage
22 from telemetry.util import exception_formatter 23 from telemetry.util import exception_formatter
23 from telemetry.value import failure 24 from telemetry.value import failure
24 from telemetry.value import skip 25 from telemetry.value import skip
25 26
26 27
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 # Page set options 69 # Page set options
69 if args.pageset_shuffle_order_file and not args.pageset_shuffle: 70 if args.pageset_shuffle_order_file and not args.pageset_shuffle:
70 parser.error('--pageset-shuffle-order-file requires --pageset-shuffle.') 71 parser.error('--pageset-shuffle-order-file requires --pageset-shuffle.')
71 72
72 if args.page_repeat < 1: 73 if args.page_repeat < 1:
73 parser.error('--page-repeat must be a positive integer.') 74 parser.error('--page-repeat must be a positive integer.')
74 if args.pageset_repeat < 1: 75 if args.pageset_repeat < 1:
75 parser.error('--pageset-repeat must be a positive integer.') 76 parser.error('--pageset-repeat must be a positive integer.')
76 77
77 78
78 def _RunPageAndHandleExceptionIfNeeded(test, page_set, expectations, 79 def _RunUserStoryAndProcessErrorIfNeeded(
79 page, results, state): 80 test, expectations, user_story, results, state):
80 expectation = None 81 expectation = None
81 def ProcessError(): 82 def ProcessError():
82 if expectation == 'fail': 83 if expectation == 'fail':
83 msg = 'Expected exception while running %s' % page.url 84 msg = 'Expected exception while running %s' % user_story.display_name
84 exception_formatter.PrintFormattedException(msg=msg) 85 exception_formatter.PrintFormattedException(msg=msg)
85 else: 86 else:
86 msg = 'Exception while running %s' % page.url 87 msg = 'Exception while running %s' % user_story.display_name
87 results.AddValue(failure.FailureValue(page, sys.exc_info())) 88 results.AddValue(failure.FailureValue(user_story, sys.exc_info()))
88 89
89 try: 90 try:
90 state.WillRunPage(page, page_set) 91 state.WillRunUserStory(user_story)
91 expectation, skip_value = state.GetPageExpectationAndSkipValue(expectations) 92 expectation, skip_value = state.GetTestExpectationAndSkipValue(expectations)
92 if expectation == 'skip': 93 if expectation == 'skip':
93 assert skip_value 94 assert skip_value
94 results.AddValue(skip_value) 95 results.AddValue(skip_value)
95 return 96 return
96 state.RunPage(results) 97 state.RunUserStory(results)
97 except page_test.TestNotSupportedOnPlatformFailure: 98 except page_test.TestNotSupportedOnPlatformFailure:
98 raise 99 raise
99 except (page_test.Failure, util.TimeoutException, exceptions.LoginException, 100 except (page_test.Failure, util.TimeoutException, exceptions.LoginException,
100 exceptions.ProfilingException): 101 exceptions.ProfilingException):
101 ProcessError() 102 ProcessError()
102 except exceptions.AppCrashException: 103 except exceptions.AppCrashException:
103 ProcessError() 104 ProcessError()
104 state.TearDown(results) 105 state.TearDownState(results)
105 if test.is_multi_tab_test: 106 if test.is_multi_tab_test:
106 logging.error('Aborting multi-tab test after browser or tab crashed at ' 107 logging.error('Aborting multi-tab test after browser or tab crashed at '
107 'page %s' % page.url) 108 'user story %s' % user_story.display_name)
108 test.RequestExit() 109 test.RequestExit()
109 return 110 return
110 except page_action.PageActionNotSupported as e: 111 except page_action.PageActionNotSupported as e:
111 results.AddValue(skip.SkipValue(page, 'Unsupported page action: %s' % e)) 112 results.AddValue(
113 skip.SkipValue(user_story, 'Unsupported page action: %s' % e))
112 except Exception: 114 except Exception:
113 exception_formatter.PrintFormattedException( 115 exception_formatter.PrintFormattedException(
114 msg='Unhandled exception while running %s' % page.url) 116 msg='Unhandled exception while running %s' % user_story.display_name)
115 results.AddValue(failure.FailureValue(page, sys.exc_info())) 117 results.AddValue(failure.FailureValue(user_story, sys.exc_info()))
116 else: 118 else:
117 if expectation == 'fail': 119 if expectation == 'fail':
118 logging.warning('%s was expected to fail, but passed.\n', page.url) 120 logging.warning(
121 '%s was expected to fail, but passed.\n', user_story.display_name)
119 finally: 122 finally:
120 state.DidRunPage(results) 123 state.DidRunUserStory(results)
121 124
122 125
123 @decorators.Cache 126 @decorators.Cache
124 def _UpdatePageSetArchivesIfChanged(page_set): 127 def _UpdateUserStoryArchivesIfChanged(page_set):
125 # Scan every serving directory for .sha1 files 128 # Scan every serving directory for .sha1 files
126 # and download them from Cloud Storage. Assume all data is public. 129 # and download them from Cloud Storage. Assume all data is public.
127 all_serving_dirs = page_set.serving_dirs.copy() 130 all_serving_dirs = page_set.serving_dirs.copy()
128 # Add individual page dirs to all serving dirs. 131 # Add individual page dirs to all serving dirs.
129 for page in page_set: 132 for page in page_set:
130 if page.is_file: 133 if page.is_file:
131 all_serving_dirs.add(page.serving_dir) 134 all_serving_dirs.add(page.serving_dir)
132 # Scan all serving dirs. 135 # Scan all serving dirs.
133 for serving_dir in all_serving_dirs: 136 for serving_dir in all_serving_dirs:
134 if os.path.splitdrive(serving_dir)[1] == '/': 137 if os.path.splitdrive(serving_dir)[1] == '/':
135 raise ValueError('Trying to serve root directory from HTTP server.') 138 raise ValueError('Trying to serve root directory from HTTP server.')
136 for dirpath, _, filenames in os.walk(serving_dir): 139 for dirpath, _, filenames in os.walk(serving_dir):
137 for filename in filenames: 140 for filename in filenames:
138 path, extension = os.path.splitext( 141 path, extension = os.path.splitext(
139 os.path.join(dirpath, filename)) 142 os.path.join(dirpath, filename))
140 if extension != '.sha1': 143 if extension != '.sha1':
141 continue 144 continue
142 cloud_storage.GetIfChanged(path, page_set.bucket) 145 cloud_storage.GetIfChanged(path, page_set.bucket)
143 146
144 147
145 def Run(test, page_set, expectations, finder_options, results): 148 class UserStoryGroup(object):
149 def __init__(self, shared_user_story_state_class):
150 self._shared_user_story_state_class = shared_user_story_state_class
151 self._user_stories = []
152
153 @property
154 def shared_user_story_state_class(self):
155 return self._shared_user_story_state_class
156
157 @property
158 def user_stories(self):
159 return self._user_stories
160
161 def AddUserStory(self, user_story):
162 assert (user_story.shared_user_story_state_class is
163 self._shared_user_story_state_class)
164 self._user_stories.append(user_story)
165
166
167 def GetUserStoryGroupsWithSameSharedUserStoryClass(user_story_set):
168 """ Returns a list of user story groups which each contains user stories with
169 the same shared_user_story_state_class.
170
171 Example:
172 Assume A1, A2, A3 are user stories with same shared user story class, and
173 similar for B1, B2.
174 If their orders in user story set is A1 A2 B1 B2 A3, then the grouping will
175 be [A1 A2] [B1 B2] [A3].
176
177 It's purposefully done this way to make sure that order of user
178 stories are the same of that defined in user_story_set. It's recommended that
179 user stories with the same states should be arranged next to each others in
180 user story sets to reduce the overhead of setting up & tearing down the
181 shared user story state.
182 """
183 user_story_groups = []
184 user_story_groups.append(
185 UserStoryGroup(user_story_set[0].shared_user_story_state_class))
186 for user_story in user_story_set:
187 if (user_story.shared_user_story_state_class is not
188 user_story_groups[-1].shared_user_story_state_class):
189 user_story_groups.append(
190 UserStoryGroup(user_story.shared_user_story_state_class))
191 user_story_groups[-1].AddUserStory(user_story)
192 return user_story_groups
193
194
195 def Run(test, user_story_set, expectations, finder_options, results):
146 """Runs a given test against a given page_set with the given options.""" 196 """Runs a given test against a given page_set with the given options."""
147 test.ValidatePageSet(page_set) 197 test.ValidatePageSet(user_story_set)
148 198
149 # Reorder page set based on options. 199 # Reorder page set based on options.
150 pages = _ShuffleAndFilterPageSet(page_set, finder_options) 200 user_stories = _ShuffleAndFilterUserStorySet(user_story_set, finder_options)
151 201
152 if not finder_options.use_live_sites: 202 if (not finder_options.use_live_sites and
153 if finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD: 203 finder_options.browser_options.wpr_mode != wpr_modes.WPR_RECORD and
154 _UpdatePageSetArchivesIfChanged(page_set) 204 # TODO(nednguyen): also handle these logic for user_story_set in next
155 pages = _CheckArchives(page_set, pages, results) 205 # patch.
206 isinstance(user_story_set, page_set_module.PageSet)):
207 _UpdateUserStoryArchivesIfChanged(user_story_set)
208 user_stories = _CheckArchives(user_story_set, user_stories, results)
156 209
157 for page in list(pages): 210 for user_story in list(user_stories):
158 if not test.CanRunForPage(page): 211 if not test.CanRunForPage(user_story):
159 results.WillRunPage(page) 212 results.WillRunPage(user_story)
160 logging.debug('Skipping test: it cannot run for %s', page.url) 213 logging.debug('Skipping test: it cannot run for %s',
161 results.AddValue(skip.SkipValue(page, 'Test cannot run')) 214 user_story.display_name)
162 results.DidRunPage(page) 215 results.AddValue(skip.SkipValue(user_story, 'Test cannot run'))
163 pages.remove(page) 216 results.DidRunPage(user_story)
217 user_stories.remove(user_story)
164 218
165 if not pages: 219 if not user_stories:
166 return 220 return
167 221
168 state = shared_page_state.SharedPageState(test, finder_options, page_set) 222 user_story_with_discarded_first_results = set()
169 pages_with_discarded_first_result = set()
170 max_failures = finder_options.max_failures # command-line gets priority 223 max_failures = finder_options.max_failures # command-line gets priority
171 if max_failures is None: 224 if max_failures is None:
172 max_failures = test.max_failures # may be None 225 max_failures = test.max_failures # may be None
226 user_story_groups = GetUserStoryGroupsWithSameSharedUserStoryClass(
227 user_stories)
173 228
174 try: 229 test.WillRunTest(finder_options)
175 test.WillRunTest(finder_options) 230 state = None
176 for _ in xrange(finder_options.pageset_repeat): 231 for _ in xrange(finder_options.pageset_repeat):
177 for page in pages: 232 for group in user_story_groups:
178 if test.IsExiting(): 233 try:
179 break 234 state = group.shared_user_story_state_class(
180 for _ in xrange(finder_options.page_repeat): 235 test, finder_options, user_story_set)
181 results.WillRunPage(page) 236 for user_story in group.user_stories:
182 try: 237 if test.IsExiting():
183 _WaitForThermalThrottlingIfNeeded(state.platform) 238 break
184 _RunPageAndHandleExceptionIfNeeded( 239 for _ in xrange(finder_options.page_repeat):
185 test, page_set, expectations, page, results, state) 240 results.WillRunPage(user_story)
186 except Exception: 241 try:
187 # Tear down & restart the state for unhandled exceptions thrown by 242 _WaitForThermalThrottlingIfNeeded(state.platform)
188 # _RunPageAndHandleExceptionIfNeeded. 243 _RunUserStoryAndProcessErrorIfNeeded(
189 results.AddValue(failure.FailureValue(page, sys.exc_info())) 244 test, expectations, user_story, results, state)
190 state.TearDown(results) 245 except Exception:
191 state = shared_page_state.SharedPageState( 246 # Tear down & restart the state for unhandled exceptions thrown by
192 test, finder_options, page_set) 247 # _RunUserStoryAndProcessErrorIfNeeded.
193 finally: 248 results.AddValue(failure.FailureValue(user_story, sys.exc_info()))
194 _CheckThermalThrottling(state.platform) 249 state.TearDownState(results)
195 discard_run = (test.discard_first_result and 250 state = group.shared_user_story_state_class(
196 page not in pages_with_discarded_first_result) 251 test, finder_options, user_story_set)
197 if discard_run: 252 finally:
198 pages_with_discarded_first_result.add(page) 253 _CheckThermalThrottling(state.platform)
199 results.DidRunPage(page, discard_run=discard_run) 254 discard_run = (test.discard_first_result and
200 if max_failures is not None and len(results.failures) > max_failures: 255 user_story not in
201 logging.error('Too many failures. Aborting.') 256 user_story_with_discarded_first_results)
202 test.RequestExit() 257 if discard_run:
203 finally: 258 user_story_with_discarded_first_results.add(user_story)
204 state.TearDown(results) 259 results.DidRunPage(user_story, discard_run=discard_run)
260 if max_failures is not None and len(results.failures) > max_failures:
261 logging.error('Too many failures. Aborting.')
262 test.RequestExit()
263 finally:
264 if state:
265 state.TearDownState(results)
205 266
206 267 def _ShuffleAndFilterUserStorySet(user_story_set, finder_options):
207 def _ShuffleAndFilterPageSet(page_set, finder_options):
208 if finder_options.pageset_shuffle_order_file: 268 if finder_options.pageset_shuffle_order_file:
209 return page_set.ReorderPageSet(finder_options.pageset_shuffle_order_file) 269 if isinstance(user_story_set, page_set_module.PageSet):
210 pages = [page for page in page_set.pages[:] 270 return page_set_module.ReorderPageSet(
211 if user_story_filter.UserStoryFilter.IsSelected(page)] 271 finder_options.pageset_shuffle_order_file)
272 else:
273 raise Exception(
274 'pageset-shuffle-order-file flag can only be used with page set')
275 user_stories = [u for u in user_story_set[:]
276 if user_story_filter.UserStoryFilter.IsSelected(u)]
212 if finder_options.pageset_shuffle: 277 if finder_options.pageset_shuffle:
213 random.shuffle(pages) 278 random.shuffle(user_stories)
214 return pages 279 return user_stories
215 280
216 281
217 def _CheckArchives(page_set, pages, results): 282 def _CheckArchives(page_set, pages, results):
218 """Returns a subset of pages that are local or have WPR archives. 283 """Returns a subset of pages that are local or have WPR archives.
219 284
220 Logs warnings if any are missing. 285 Logs warnings if any are missing.
221 """ 286 """
222 # Warn of any problems with the entire page set. 287 # Warn of any problems with the entire page set.
223 if any(not p.is_local for p in pages): 288 if any(not p.is_local for p in pages):
224 if not page_set.archive_data_file: 289 if not page_set.archive_data_file:
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 logging.warning('Device is thermally throttled before running ' 341 logging.warning('Device is thermally throttled before running '
277 'performance tests, results will vary.') 342 'performance tests, results will vary.')
278 343
279 344
280 def _CheckThermalThrottling(platform): 345 def _CheckThermalThrottling(platform):
281 if not platform.CanMonitorThermalThrottling(): 346 if not platform.CanMonitorThermalThrottling():
282 return 347 return
283 if platform.HasBeenThermallyThrottled(): 348 if platform.HasBeenThermallyThrottled():
284 logging.warning('Device has been thermally throttled during ' 349 logging.warning('Device has been thermally throttled during '
285 'performance tests, results will vary.') 350 'performance tests, results will vary.')
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698