| 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 |