OLD | NEW |
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 the current CL.""" |
6 | |
7 This command interacts with the Rietveld API to get information about try jobs | |
8 with layout test results. | |
9 """ | |
10 | 6 |
11 import json | 7 import json |
12 import logging | 8 import logging |
13 import optparse | 9 import optparse |
14 | 10 |
15 from webkitpy.common.net.git_cl import GitCL | 11 from webkitpy.common.net.git_cl import GitCL |
16 from webkitpy.common.net.rietveld import Rietveld | |
17 from webkitpy.common.net.web import Web | |
18 from webkitpy.layout_tests.models.test_expectations import BASELINE_SUFFIX_LIST | 12 from webkitpy.layout_tests.models.test_expectations import BASELINE_SUFFIX_LIST |
19 from webkitpy.tool.commands.rebaseline import AbstractParallelRebaselineCommand | 13 from webkitpy.tool.commands.rebaseline import AbstractParallelRebaselineCommand |
20 | 14 |
21 _log = logging.getLogger(__name__) | 15 _log = logging.getLogger(__name__) |
22 | 16 |
23 | 17 |
24 class RebaselineCL(AbstractParallelRebaselineCommand): | 18 class RebaselineCL(AbstractParallelRebaselineCommand): |
25 name = "rebaseline-cl" | 19 name = "rebaseline-cl" |
26 help_text = "Fetches new baselines for a CL from test runs on try bots." | 20 help_text = "Fetches new baselines for a CL from test runs on try bots." |
27 long_help = ("By default, this command will check the latest try job results
" | 21 long_help = ("By default, this command will check the latest try job results
" |
28 "for all platforms, and start try jobs for platforms with no " | 22 "for all platforms, and start try jobs for platforms with no " |
29 "try jobs. Then, new baselines are downloaded for any tests " | 23 "try jobs. Then, new baselines are downloaded for any tests " |
30 "that are being rebaselined. After downloading, the baselines " | 24 "that are being rebaselined. After downloading, the baselines " |
31 "for different platforms will be optimized (consolidated).") | 25 "for different platforms will be optimized (consolidated).") |
32 show_in_main_help = True | 26 show_in_main_help = True |
33 | 27 |
34 def __init__(self): | 28 def __init__(self): |
35 super(RebaselineCL, self).__init__(options=[ | 29 super(RebaselineCL, self).__init__(options=[ |
36 optparse.make_option( | 30 optparse.make_option( |
37 '--issue', type='int', default=None, | |
38 help='Rietveld issue number; if none given, this will be obtaine
d via `git cl issue`.'), | |
39 optparse.make_option( | |
40 '--dry-run', action='store_true', default=False, | 31 '--dry-run', action='store_true', default=False, |
41 help='Dry run mode; list actions that would be performed but do
not do anything.'), | 32 help='Dry run mode; list actions that would be performed but do
not do anything.'), |
42 optparse.make_option( | 33 optparse.make_option( |
43 '--only-changed-tests', action='store_true', default=False, | 34 '--only-changed-tests', action='store_true', default=False, |
44 help='Only download new baselines for tests that are changed in
the CL.'), | 35 help='Only download new baselines for tests that are changed in
the CL.'), |
45 optparse.make_option( | 36 optparse.make_option( |
46 '--no-trigger-jobs', dest='trigger_jobs', action='store_false',
default=True, | 37 '--no-trigger-jobs', dest='trigger_jobs', action='store_false',
default=True, |
47 help='Do not trigger any try jobs.'), | 38 help='Do not trigger any try jobs.'), |
48 self.no_optimize_option, | 39 self.no_optimize_option, |
49 self.results_directory_option, | 40 self.results_directory_option, |
50 ]) | 41 ]) |
51 self.rietveld = Rietveld(Web()) | |
52 | 42 |
53 def execute(self, options, args, tool): | 43 def execute(self, options, args, tool): |
54 self._tool = tool | 44 self._tool = tool |
55 | 45 |
56 unstaged_baselines = self.unstaged_baselines() | 46 unstaged_baselines = self.unstaged_baselines() |
57 if unstaged_baselines: | 47 if unstaged_baselines: |
58 _log.error('Aborting: there are unstaged baselines:') | 48 _log.error('Aborting: there are unstaged baselines:') |
59 for path in unstaged_baselines: | 49 for path in unstaged_baselines: |
60 _log.error(' %s', path) | 50 _log.error(' %s', path) |
61 return 1 | 51 return 1 |
62 | 52 |
63 issue_number = self._get_issue_number(options) | 53 issue_number = self._get_issue_number() |
64 if not issue_number: | 54 if not issue_number: |
65 return 1 | 55 return 1 |
66 | 56 |
67 # TODO(qyearsley): Remove dependency on Rietveld. See crbug.com/671684. | 57 builds = self.git_cl().latest_try_jobs(self._try_bots()) |
68 if options.issue: | |
69 builds = self.rietveld.latest_try_jobs(issue_number, self._try_bots(
)) | |
70 else: | |
71 builds = self.git_cl().latest_try_jobs(self._try_bots()) | |
72 | 58 |
73 if options.trigger_jobs: | 59 if options.trigger_jobs: |
74 if self.trigger_jobs_for_missing_builds(builds): | 60 if self.trigger_jobs_for_missing_builds(builds): |
75 _log.info('Please re-run webkit-patch rebaseline-cl once all pen
ding try jobs have finished.') | 61 _log.info('Please re-run webkit-patch rebaseline-cl once all pen
ding try jobs have finished.') |
76 return 1 | 62 return 1 |
77 if not builds: | 63 if not builds: |
78 _log.info('No builds to download baselines from.') | 64 _log.info('No builds to download baselines from.') |
79 | 65 |
80 _log.debug('Getting results for Rietveld issue %d.', issue_number) | 66 _log.debug('Getting results for issue %d.', issue_number) |
81 builds_to_results = self._fetch_results(builds) | 67 builds_to_results = self._fetch_results(builds) |
82 if builds_to_results is None: | 68 if builds_to_results is None: |
83 return 1 | 69 return 1 |
84 | 70 |
85 test_prefix_list = {} | 71 test_prefix_list = {} |
86 if args: | 72 if args: |
87 for test in args: | 73 for test in args: |
88 test_prefix_list[test] = {b: BASELINE_SUFFIX_LIST for b in build
s} | 74 test_prefix_list[test] = {b: BASELINE_SUFFIX_LIST for b in build
s} |
89 else: | 75 else: |
90 test_prefix_list = self._test_prefix_list( | 76 test_prefix_list = self._test_prefix_list( |
91 issue_number, | 77 issue_number, |
92 builds_to_results, | 78 builds_to_results, |
93 only_changed_tests=options.only_changed_tests) | 79 only_changed_tests=options.only_changed_tests) |
94 | 80 |
95 self._log_test_prefix_list(test_prefix_list) | 81 self._log_test_prefix_list(test_prefix_list) |
96 | 82 |
97 if not options.dry_run: | 83 if not options.dry_run: |
98 self.rebaseline(options, test_prefix_list) | 84 self.rebaseline(options, test_prefix_list) |
99 return 0 | 85 return 0 |
100 | 86 |
101 | 87 def _get_issue_number(self): |
102 def _get_issue_number(self, options): | 88 """Returns the current CL number, or None if there is none.""" |
103 """Gets the Rietveld CL number from either |options| or from the current
local branch.""" | |
104 if options.issue: | |
105 return options.issue | |
106 issue_number = self.git_cl().get_issue_number() | 89 issue_number = self.git_cl().get_issue_number() |
107 _log.debug('Issue number for current branch: %s', issue_number) | 90 _log.debug('Issue number for current branch: %s', issue_number) |
108 if not issue_number.isdigit(): | 91 if not issue_number.isdigit(): |
109 _log.error('No issue number given and no issue for current branch. T
his tool requires a CL\n' | 92 _log.error('No CL number for current branch.') |
110 'to operate on; please run `git cl upload` on this branch
first, or use the --issue\n' | |
111 'option to download baselines for another existing CL.') | |
112 return None | 93 return None |
113 return int(issue_number) | 94 return int(issue_number) |
114 | 95 |
115 def git_cl(self): | 96 def git_cl(self): |
116 """Returns a GitCL instance; can be overridden for tests.""" | 97 """Returns a GitCL instance; can be overridden for tests.""" |
117 return GitCL(self._tool) | 98 return GitCL(self._tool) |
118 | 99 |
119 def trigger_jobs_for_missing_builds(self, builds): | 100 def trigger_jobs_for_missing_builds(self, builds): |
120 """Triggers try jobs for any builders that have no builds started. | 101 """Triggers try jobs for any builders that have no builds started. |
121 | 102 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 are changed in this CL. If False, all new baselines for failing | 168 are changed in this CL. If False, all new baselines for failing |
188 tests will be downloaded, even for tests that were not modified. | 169 tests will be downloaded, even for tests that were not modified. |
189 | 170 |
190 Returns: | 171 Returns: |
191 A dict containing information about which new baselines to download. | 172 A dict containing information about which new baselines to download. |
192 """ | 173 """ |
193 builds_to_tests = {} | 174 builds_to_tests = {} |
194 for build, results in builds_to_results.iteritems(): | 175 for build, results in builds_to_results.iteritems(): |
195 builds_to_tests[build] = self._tests_to_rebaseline(build, results) | 176 builds_to_tests[build] = self._tests_to_rebaseline(build, results) |
196 if only_changed_tests: | 177 if only_changed_tests: |
197 files_in_cl = self.rietveld.changed_files(issue_number) | 178 files_in_cl = self._tool.git().changed_files(diff_filter='AM') |
198 # Note, in the changed files list from Rietveld, paths always | 179 # Note, in the changed files list from Rietveld, paths always |
199 # use / as the separator, and they're always relative to repo root. | 180 # use / as the separator, and they're always relative to repo root. |
200 # TODO(qyearsley): Do this without using a hard-coded constant. | 181 # TODO(qyearsley): Do this without using a hard-coded constant. |
201 test_base = 'third_party/WebKit/LayoutTests/' | 182 test_base = 'third_party/WebKit/LayoutTests/' |
202 tests_in_cl = [f[len(test_base):] for f in files_in_cl if f.startswi
th(test_base)] | 183 tests_in_cl = [f[len(test_base):] for f in files_in_cl if f.startswi
th(test_base)] |
203 result = {} | 184 result = {} |
204 for build, tests in builds_to_tests.iteritems(): | 185 for build, tests in builds_to_tests.iteritems(): |
205 for test in tests: | 186 for test in tests: |
206 if only_changed_tests and test not in tests_in_cl: | 187 if only_changed_tests and test not in tests_in_cl: |
207 continue | 188 continue |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
240 def _log_test_prefix_list(test_prefix_list): | 221 def _log_test_prefix_list(test_prefix_list): |
241 """Logs the tests to download new baselines for.""" | 222 """Logs the tests to download new baselines for.""" |
242 if not test_prefix_list: | 223 if not test_prefix_list: |
243 _log.info('No tests to rebaseline; exiting.') | 224 _log.info('No tests to rebaseline; exiting.') |
244 return | 225 return |
245 _log.debug('Tests to rebaseline:') | 226 _log.debug('Tests to rebaseline:') |
246 for test, builds in test_prefix_list.iteritems(): | 227 for test, builds in test_prefix_list.iteritems(): |
247 _log.debug(' %s:', test) | 228 _log.debug(' %s:', test) |
248 for build in sorted(builds): | 229 for build in sorted(builds): |
249 _log.debug(' %s', build) | 230 _log.debug(' %s', build) |
OLD | NEW |