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

Side by Side Diff: third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py

Issue 2547153002: In webkit-patch rebaseline-cl, abort if results are missing. (Closed)
Patch Set: Add unit test Created 4 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 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 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 """A command to fetch new baselines from try jobs for a Rietveld issue. 5 """A command to fetch new baselines from try jobs for a Rietveld issue.
6 6
7 This command interacts with the Rietveld API to get information about try jobs 7 This command interacts with the Rietveld API to get information about try jobs
8 with layout test results. 8 with layout test results.
9 """ 9 """
10 10
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 ]) 50 ])
51 self.rietveld = Rietveld(Web()) 51 self.rietveld = Rietveld(Web())
52 52
53 def execute(self, options, args, tool): 53 def execute(self, options, args, tool):
54 self._tool = tool 54 self._tool = tool
55 issue_number = self._get_issue_number(options) 55 issue_number = self._get_issue_number(options)
56 if not issue_number: 56 if not issue_number:
57 return 57 return
58 58
59 builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots()) 59 builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots())
60
60 if options.trigger_jobs: 61 if options.trigger_jobs:
61 if self.trigger_jobs_for_missing_builds(builds): 62 if self.trigger_jobs_for_missing_builds(builds):
62 _log.info('Please re-run webkit-patch rebaseline-cl once all pen ding try jobs have finished.') 63 _log.info('Please re-run webkit-patch rebaseline-cl once all pen ding try jobs have finished.')
63 return 64 return
64 if not builds: 65 if not builds:
65 _log.info('No builds to download baselines from.') 66 _log.info('No builds to download baselines from.')
66 67
68 try:
69 _log.debug('Getting results for Rietveld issue %d.', issue_number)
wkorman 2016/12/05 05:58:35 Q for whomever may know: when are we moving to Ger
qyearsley 2016/12/05 17:55:21 There are ramifications for this tool, but not so
wkorman 2016/12/05 18:56:25 Sounds reasonable, perhaps worth checking in with
qyearsley 2016/12/07 00:42:27 Aye - just filed a bug for this: http://crbug.com/
70 builds_to_results = self._fetch_results(builds)
71 except _MissingResults as error:
72 _log.error(error.message)
73 return
74
75 test_prefix_list = {}
67 if args: 76 if args:
68 test_prefix_list = {}
69 for test in args: 77 for test in args:
70 test_prefix_list[test] = {b: BASELINE_SUFFIX_LIST for b in build s} 78 test_prefix_list[test] = {b: BASELINE_SUFFIX_LIST for b in build s}
71 else: 79 else:
72 test_prefix_list = self._test_prefix_list( 80 test_prefix_list = self._test_prefix_list(
73 issue_number, only_changed_tests=options.only_changed_tests) 81 issue_number,
82 builds_to_results,
83 only_changed_tests=options.only_changed_tests)
74 84
75 self._log_test_prefix_list(test_prefix_list) 85 self._log_test_prefix_list(test_prefix_list)
76 86
77 if options.dry_run: 87 if options.dry_run:
78 return 88 return
79 self.rebaseline(options, test_prefix_list) 89 self.rebaseline(options, test_prefix_list)
80 90
81 def _get_issue_number(self, options): 91 def _get_issue_number(self, options):
82 """Gets the Rietveld CL number from either |options| or from the current local branch.""" 92 """Gets the Rietveld CL number from either |options| or from the current local branch."""
83 if options.issue: 93 if options.issue:
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
117 if builders_without_builds: 127 if builders_without_builds:
118 _log.info('Triggering try jobs for:') 128 _log.info('Triggering try jobs for:')
119 command = ['try'] 129 command = ['try']
120 for builder in sorted(builders_without_builds): 130 for builder in sorted(builders_without_builds):
121 _log.info(' %s', builder) 131 _log.info(' %s', builder)
122 command.extend(['-b', builder]) 132 command.extend(['-b', builder])
123 self.git_cl().run(command) 133 self.git_cl().run(command)
124 134
125 return bool(builders_with_pending_builds or builders_without_builds) 135 return bool(builders_with_pending_builds or builders_without_builds)
126 136
127 def _test_prefix_list(self, issue_number, only_changed_tests): 137 def _try_bots(self):
128 """Returns a collection of test, builder and file extensions to get new baselines for. 138 """Returns a collection of try bot builders to fetch results for."""
139 return self._tool.builders.all_try_builder_names()
140
141 def _fetch_results(self, builds):
142 """Fetch results for each build.
143
144 There should be a one-to-one correspondence between Builds, supported
145 platforms, and try bots. If not all of the builds can be fetched, then
146 continuing with rebaselining may yield incorrect results, when the new
147 baselines are deduped, an old baseline may be kept for the platform
148 that's missing results.
149
150 Returns:
151 A dict mapping Build to LayoutTestResults.
152 """
153 buildbot = self._tool.buildbot
154 results = {}
155 for build in builds:
156 results_url = buildbot.results_url(build.builder_name, build.build_n umber)
157 layout_test_results = buildbot.fetch_results(build)
158 if layout_test_results is None:
159 raise _MissingResults(
wkorman 2016/12/05 05:58:35 Worth documenting exceptions a method may raise in
qyearsley 2016/12/05 17:55:21 Added a "Raises:" part to the docstring.
160 'Failed to fetch results from "%s".\n'
161 'Try starting a new job for %s by running :\n'
162 ' git cl try -b %s' %
163 (results_url, build.builder_name, build.builder_name))
164 results[build] = layout_test_results
165 return results
166
167 def _test_prefix_list(self, issue_number, builds_to_results, only_changed_te sts):
168 """Returns a collection of tests, builders and file extensions to get ne w baselines for.
129 169
130 Args: 170 Args:
131 issue_number: The CL number of the change which needs new baselines. 171 issue_number: The CL number of the change which needs new baselines.
172 builds_to_results: A dict mapping Builds to LayoutTestResults.
132 only_changed_tests: Whether to only include baselines for tests that 173 only_changed_tests: Whether to only include baselines for tests that
133 are changed in this CL. If False, all new baselines for failing 174 are changed in this CL. If False, all new baselines for failing
134 tests will be downloaded, even for tests that were not modified. 175 tests will be downloaded, even for tests that were not modified.
135 176
136 Returns: 177 Returns:
137 A dict containing information about which new baselines to download. 178 A dict containing information about which new baselines to download.
138 """ 179 """
139 builds_to_tests = self._builds_to_tests(issue_number) 180 builds_to_tests = {}
181 for build, results in builds_to_results.iteritems():
182 builds_to_tests[build] = self._tests_to_rebaseline(build, results)
140 if only_changed_tests: 183 if only_changed_tests:
141 files_in_cl = self.rietveld.changed_files(issue_number) 184 files_in_cl = self.rietveld.changed_files(issue_number)
142 # Note, in the changed files list from Rietveld, paths always 185 # Note, in the changed files list from Rietveld, paths always
143 # use / as the separator, and they're always relative to repo root. 186 # use / as the separator, and they're always relative to repo root.
144 # TODO(qyearsley): Do this without using a hard-coded constant. 187 # TODO(qyearsley): Do this without using a hard-coded constant.
145 test_base = 'third_party/WebKit/LayoutTests/' 188 test_base = 'third_party/WebKit/LayoutTests/'
146 tests_in_cl = [f[len(test_base):] for f in files_in_cl if f.startswi th(test_base)] 189 tests_in_cl = [f[len(test_base):] for f in files_in_cl if f.startswi th(test_base)]
147 result = {} 190 result = {}
148 for build, tests in builds_to_tests.iteritems(): 191 for build, tests in builds_to_tests.iteritems():
149 for test in tests: 192 for test in tests:
150 if only_changed_tests and test not in tests_in_cl: 193 if only_changed_tests and test not in tests_in_cl:
151 continue 194 continue
152 if test not in result: 195 if test not in result:
153 result[test] = {} 196 result[test] = {}
154 result[test][build] = BASELINE_SUFFIX_LIST 197 result[test][build] = BASELINE_SUFFIX_LIST
155 return result 198 return result
156 199
157 def _builds_to_tests(self, issue_number): 200 def _tests_to_rebaseline(self, build, layout_test_results):
158 """Fetches a list of try bots, and for each, fetches tests with new base lines.""" 201 """Fetches a list of tests that should be rebaselined for some build ."" "
159 _log.debug('Getting results for Rietveld issue %d.', issue_number)
160 builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots())
161 if not builds:
162 _log.debug('No try job results for builders in: %r.', self._try_bots ())
163 return {build: self._tests_to_rebaseline(build) for build in builds}
164
165 def _try_bots(self):
166 """Returns a collection of try bot builders to fetch results for."""
167 return self._tool.builders.all_try_builder_names()
168
169 def _tests_to_rebaseline(self, build):
170 """Fetches a list of tests that should be rebaselined."""
171 buildbot = self._tool.buildbot
172 results_url = buildbot.results_url(build.builder_name, build.build_numbe r)
173
174 layout_test_results = buildbot.fetch_layout_test_results(results_url)
175 if layout_test_results is None:
176 _log.warning('Failed to request layout test results from "%s".', res ults_url)
177 return []
178
179 unexpected_results = layout_test_results.didnt_run_as_expected_results() 202 unexpected_results = layout_test_results.didnt_run_as_expected_results()
180 tests = sorted(r.test_name() for r in unexpected_results 203 tests = sorted(r.test_name() for r in unexpected_results
181 if r.is_missing_baseline() or r.has_mismatch_result()) 204 if r.is_missing_baseline() or r.has_mismatch_result())
182 205
183 new_failures = self._fetch_tests_with_new_failures(build) 206 new_failures = self._fetch_tests_with_new_failures(build)
184 if new_failures is None: 207 if new_failures is None:
185 _log.warning('No retry summary available for build %s.', build) 208 _log.warning('No retry summary available for build %s.', build)
186 else: 209 else:
187 tests = [t for t in tests if t in new_failures] 210 tests = [t for t in tests if t in new_failures]
188 return tests 211 return tests
189 212
190 def _fetch_tests_with_new_failures(self, build): 213 def _fetch_tests_with_new_failures(self, build):
191 """Fetches a list of tests that failed with a patch in a given try job b ut not without.""" 214 """Fetches a list of tests that failed with a patch in a given try job b ut not without."""
192 buildbot = self._tool.buildbot 215 buildbot = self._tool.buildbot
193 content = buildbot.fetch_retry_summary_json(build) 216 content = buildbot.fetch_retry_summary_json(build)
194 if content is None: 217 if content is None:
195 return None 218 return None
196 try: 219 try:
197 retry_summary = json.loads(content) 220 retry_summary = json.loads(content)
198 return retry_summary['failures'] 221 return retry_summary['failures']
199 except (ValueError, KeyError): 222 except (ValueError, KeyError):
200 _log.warning('Unexepected retry summary content:\n%s', content) 223 _log.warning('Unexpected retry summary content:\n%s', content)
201 return None 224 return None
202 225
203 @staticmethod 226 @staticmethod
204 def _log_test_prefix_list(test_prefix_list): 227 def _log_test_prefix_list(test_prefix_list):
205 """Logs the tests to download new baselines for.""" 228 """Logs the tests to download new baselines for."""
206 if not test_prefix_list: 229 if not test_prefix_list:
207 _log.info('No tests to rebaseline; exiting.') 230 _log.info('No tests to rebaseline; exiting.')
208 return 231 return
209 _log.debug('Tests to rebaseline:') 232 _log.debug('Tests to rebaseline:')
210 for test, builds in test_prefix_list.iteritems(): 233 for test, builds in test_prefix_list.iteritems():
211 _log.debug(' %s:', test) 234 _log.debug(' %s:', test)
212 for build in sorted(builds): 235 for build in sorted(builds):
213 _log.debug(' %s', build) 236 _log.debug(' %s', build)
237
238
239 class _MissingResults(Exception):
wkorman 2016/12/05 05:58:35 Probably worth a doc comment or something in name
qyearsley 2016/12/05 17:55:21 Changed name to _MissingLayoutTestResults. My rat
wkorman 2016/12/05 18:56:25 Yes, that sounds preferable.
qyearsley 2016/12/07 00:42:27 Now done :-)
240 pass
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698