OLD | NEW |
1 # Copyright (c) 2009 Google Inc. All rights reserved. | 1 # Copyright (c) 2009 Google Inc. All rights reserved. |
2 # Copyright (c) 2009 Apple Inc. All rights reserved. | 2 # Copyright (c) 2009 Apple Inc. All rights reserved. |
3 # Copyright (c) 2012 Intel Corporation. All rights reserved. | 3 # Copyright (c) 2012 Intel Corporation. All rights reserved. |
4 # | 4 # |
5 # Redistribution and use in source and binary forms, with or without | 5 # Redistribution and use in source and binary forms, with or without |
6 # modification, are permitted provided that the following conditions are | 6 # modification, are permitted provided that the following conditions are |
7 # met: | 7 # met: |
8 # | 8 # |
9 # * Redistributions of source code must retain the above copyright | 9 # * Redistributions of source code must retain the above copyright |
10 # notice, this list of conditions and the following disclaimer. | 10 # notice, this list of conditions and the following disclaimer. |
(...skipping 19 matching lines...) Expand all Loading... |
30 | 30 |
31 import fnmatch | 31 import fnmatch |
32 import logging | 32 import logging |
33 import re | 33 import re |
34 | 34 |
35 from datetime import datetime | 35 from datetime import datetime |
36 from optparse import make_option | 36 from optparse import make_option |
37 | 37 |
38 from webkitpy.tool import steps | 38 from webkitpy.tool import steps |
39 | 39 |
40 from webkitpy.common.checkout.commitinfo import CommitInfo | |
41 from webkitpy.common.config.committers import CommitterList | |
42 import webkitpy.common.config.urls as config_urls | 40 import webkitpy.common.config.urls as config_urls |
43 from webkitpy.common.net.buildbot import BuildBot | 41 from webkitpy.common.net.buildbot import BuildBot |
44 from webkitpy.common.net.regressionwindow import RegressionWindow | 42 from webkitpy.common.net.regressionwindow import RegressionWindow |
45 from webkitpy.common.system.crashlogs import CrashLogs | 43 from webkitpy.common.system.crashlogs import CrashLogs |
46 from webkitpy.common.system.user import User | 44 from webkitpy.common.system.user import User |
47 from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCom
mand | 45 from webkitpy.tool.commands.abstractsequencedcommand import AbstractSequencedCom
mand |
48 from webkitpy.tool.grammar import pluralize | 46 from webkitpy.tool.grammar import pluralize |
49 from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand | 47 from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand |
50 from webkitpy.layout_tests.models.test_expectations import TestExpectations | 48 from webkitpy.layout_tests.models.test_expectations import TestExpectations |
51 from webkitpy.layout_tests.port import platform_options, configuration_options | 49 from webkitpy.layout_tests.port import platform_options, configuration_options |
52 | 50 |
53 _log = logging.getLogger(__name__) | 51 _log = logging.getLogger(__name__) |
54 | 52 |
55 | 53 |
56 class BugsToCommit(AbstractDeclarativeCommand): | |
57 name = "bugs-to-commit" | |
58 help_text = "List bugs in the commit-queue" | |
59 | |
60 def execute(self, options, args, tool): | |
61 # FIXME: This command is poorly named. It's fetching the commit-queue l
ist here. The name implies it's fetching pending-commit (all r+'d patches). | |
62 bug_ids = tool.bugs.queries.fetch_bug_ids_from_commit_queue() | |
63 for bug_id in bug_ids: | |
64 print "%s" % bug_id | |
65 | |
66 | |
67 class PatchesInCommitQueue(AbstractDeclarativeCommand): | |
68 name = "patches-in-commit-queue" | |
69 help_text = "List patches in the commit-queue" | |
70 | |
71 def execute(self, options, args, tool): | |
72 patches = tool.bugs.queries.fetch_patches_from_commit_queue() | |
73 _log.info("Patches in commit queue:") | |
74 for patch in patches: | |
75 print patch.url() | |
76 | |
77 | |
78 class PatchesToCommitQueue(AbstractDeclarativeCommand): | |
79 name = "patches-to-commit-queue" | |
80 help_text = "Patches which should be added to the commit queue" | |
81 def __init__(self): | |
82 options = [ | |
83 make_option("--bugs", action="store_true", dest="bugs", help="Output
bug links instead of patch links"), | |
84 ] | |
85 AbstractDeclarativeCommand.__init__(self, options=options) | |
86 | |
87 @staticmethod | |
88 def _needs_commit_queue(patch): | |
89 if patch.commit_queue() == "+": # If it's already cq+, ignore the patch. | |
90 _log.info("%s already has cq=%s" % (patch.id(), patch.commit_queue()
)) | |
91 return False | |
92 | |
93 # We only need to worry about patches from contributers who are not yet
committers. | |
94 committer_record = CommitterList().committer_by_email(patch.attacher_ema
il()) | |
95 if committer_record: | |
96 _log.info("%s committer = %s" % (patch.id(), committer_record)) | |
97 return not committer_record | |
98 | |
99 def execute(self, options, args, tool): | |
100 patches = tool.bugs.queries.fetch_patches_from_pending_commit_list() | |
101 patches_needing_cq = filter(self._needs_commit_queue, patches) | |
102 if options.bugs: | |
103 bugs_needing_cq = map(lambda patch: patch.bug_id(), patches_needing_
cq) | |
104 bugs_needing_cq = sorted(set(bugs_needing_cq)) | |
105 for bug_id in bugs_needing_cq: | |
106 print "%s" % tool.bugs.bug_url_for_bug_id(bug_id) | |
107 else: | |
108 for patch in patches_needing_cq: | |
109 print "%s" % tool.bugs.attachment_url_for_id(patch.id(), action=
"edit") | |
110 | |
111 | |
112 class PatchesToReview(AbstractDeclarativeCommand): | |
113 name = "patches-to-review" | |
114 help_text = "List bugs which have attachments pending review" | |
115 | |
116 def __init__(self): | |
117 options = [ | |
118 make_option("--all", action="store_true", | |
119 help="Show all bugs regardless of who is on CC (it might
take a while)"), | |
120 make_option("--include-cq-denied", action="store_true", | |
121 help="By default, r? patches with cq- are omitted unless
this option is set"), | |
122 make_option("--cc-email", | |
123 help="Specifies the email on the CC field (defaults to y
our bugzilla login email)"), | |
124 ] | |
125 AbstractDeclarativeCommand.__init__(self, options=options) | |
126 | |
127 def _print_report(self, report, cc_email, print_all): | |
128 if print_all: | |
129 print "Bugs with attachments pending review:" | |
130 else: | |
131 print "Bugs with attachments pending review that has %s in the CC li
st:" % cc_email | |
132 | |
133 print "http://webkit.org/b/bugid Description (age in days)" | |
134 for row in report: | |
135 print "%s (%d)" % (row[1], row[0]) | |
136 | |
137 print "Total: %d" % len(report) | |
138 | |
139 def _generate_report(self, bugs, include_cq_denied): | |
140 report = [] | |
141 | |
142 for bug in bugs: | |
143 patch = bug.unreviewed_patches()[-1] | |
144 | |
145 if not include_cq_denied and patch.commit_queue() == "-": | |
146 continue | |
147 | |
148 age_in_days = (datetime.today() - patch.attach_date()).days | |
149 report.append((age_in_days, "http://webkit.org/b/%-7s %s" % (bug.id(
), bug.title()))) | |
150 | |
151 report.sort() | |
152 return report | |
153 | |
154 def execute(self, options, args, tool): | |
155 tool.bugs.authenticate() | |
156 | |
157 cc_email = options.cc_email | |
158 if not cc_email and not options.all: | |
159 cc_email = tool.bugs.username | |
160 | |
161 bugs = tool.bugs.queries.fetch_bugs_from_review_queue(cc_email=cc_email) | |
162 report = self._generate_report(bugs, options.include_cq_denied) | |
163 self._print_report(report, cc_email, options.all) | |
164 | |
165 class WhatBroke(AbstractDeclarativeCommand): | |
166 name = "what-broke" | |
167 help_text = "Print failing buildbots (%s) and what revisions broke them" % c
onfig_urls.buildbot_url | |
168 | |
169 def _print_builder_line(self, builder_name, max_name_width, status_message): | |
170 print "%s : %s" % (builder_name.ljust(max_name_width), status_message) | |
171 | |
172 def _print_blame_information_for_builder(self, builder_status, name_width, a
void_flakey_tests=True): | |
173 builder = self._tool.buildbot.builder_with_name(builder_status["name"]) | |
174 red_build = builder.build(builder_status["build_number"]) | |
175 regression_window = builder.find_regression_window(red_build) | |
176 if not regression_window.failing_build(): | |
177 self._print_builder_line(builder.name(), name_width, "FAIL (error lo
ading build information)") | |
178 return | |
179 if not regression_window.build_before_failure(): | |
180 self._print_builder_line(builder.name(), name_width, "FAIL (blame-li
st: sometime before %s?)" % regression_window.failing_build().revision()) | |
181 return | |
182 | |
183 revisions = regression_window.revisions() | |
184 first_failure_message = "" | |
185 if (regression_window.failing_build() == builder.build(builder_status["b
uild_number"])): | |
186 first_failure_message = " FIRST FAILURE, possibly a flaky test" | |
187 self._print_builder_line(builder.name(), name_width, "FAIL (blame-list:
%s%s)" % (revisions, first_failure_message)) | |
188 for revision in revisions: | |
189 commit_info = self._tool.checkout().commit_info_for_revision(revisio
n) | |
190 if commit_info: | |
191 print commit_info.blame_string(self._tool.bugs) | |
192 else: | |
193 print "FAILED to fetch CommitInfo for r%s, likely missing Change
Log" % revision | |
194 | |
195 def execute(self, options, args, tool): | |
196 builder_statuses = tool.buildbot.builder_statuses() | |
197 longest_builder_name = max(map(len, map(lambda builder: builder["name"],
builder_statuses))) | |
198 failing_builders = 0 | |
199 for builder_status in builder_statuses: | |
200 # If the builder is green, print OK, exit. | |
201 if builder_status["is_green"]: | |
202 continue | |
203 self._print_blame_information_for_builder(builder_status, name_width
=longest_builder_name) | |
204 failing_builders += 1 | |
205 if failing_builders: | |
206 print "%s of %s are failing" % (failing_builders, pluralize("builder
", len(builder_statuses))) | |
207 else: | |
208 print "All builders are passing!" | |
209 | |
210 | |
211 class ResultsFor(AbstractDeclarativeCommand): | 54 class ResultsFor(AbstractDeclarativeCommand): |
212 name = "results-for" | 55 name = "results-for" |
213 help_text = "Print a list of failures for the passed revision from bots on %
s" % config_urls.buildbot_url | 56 help_text = "Print a list of failures for the passed revision from bots on %
s" % config_urls.buildbot_url |
214 argument_names = "REVISION" | 57 argument_names = "REVISION" |
215 | 58 |
216 def _print_layout_test_results(self, results): | 59 def _print_layout_test_results(self, results): |
217 if not results: | 60 if not results: |
218 print " No results." | 61 print " No results." |
219 return | 62 return |
220 for title, files in results.parsed_results().items(): | 63 for title, files in results.parsed_results().items(): |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 print "%s,%s,%s,%s,%s,%s" % (port_name, test_name, self._pla
tform_for_path(test_name), | 398 print "%s,%s,%s,%s,%s,%s" % (port_name, test_name, self._pla
tform_for_path(test_name), |
556 extension[1:], baseline_locatio
n, self._platform_for_path(baseline_location)) | 399 extension[1:], baseline_locatio
n, self._platform_for_path(baseline_location)) |
557 else: | 400 else: |
558 print baseline_location | 401 print baseline_location |
559 | 402 |
560 def _platform_for_path(self, relpath): | 403 def _platform_for_path(self, relpath): |
561 platform_matchobj = self._platform_regexp.match(relpath) | 404 platform_matchobj = self._platform_regexp.match(relpath) |
562 if platform_matchobj: | 405 if platform_matchobj: |
563 return platform_matchobj.group(1) | 406 return platform_matchobj.group(1) |
564 return None | 407 return None |
OLD | NEW |