|
Lines 123-149
class PatchAnalysisTask(object):
a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py_sec1
|
| 123 |
"Patch does not apply") |
123 |
"Patch does not apply") |
| 124 |
|
124 |
|
| 125 |
def _build(self): |
125 |
def _build(self): |
| 126 |
return self._run_command([ |
126 |
args = [ |
| 127 |
"build", |
127 |
"build", |
| 128 |
"--no-clean", |
128 |
"--no-clean", |
| 129 |
"--no-update", |
129 |
"--no-update", |
| 130 |
"--build-style=%s" % self._delegate.build_style(), |
130 |
"--build-style=%s" % self._delegate.build_style(), |
| 131 |
], |
131 |
] |
| 132 |
"Built patch", |
132 |
|
| 133 |
"Patch does not build") |
133 |
try: |
|
|
134 |
args.append("--group=%s" % self._delegate.group()) |
| 135 |
finally: |
| 136 |
return self._run_command( |
| 137 |
args, |
| 138 |
"Built patch", |
| 139 |
"Patch does not build") |
| 134 |
|
140 |
|
| 135 |
def _build_without_patch(self): |
141 |
def _build_without_patch(self): |
| 136 |
return self._run_command([ |
142 |
args = [ |
| 137 |
"build", |
143 |
"build", |
| 138 |
"--force-clean", |
144 |
"--force-clean", |
| 139 |
"--no-update", |
145 |
"--no-update", |
| 140 |
"--build-style=%s" % self._delegate.build_style(), |
146 |
"--build-style=%s" % self._delegate.build_style(), |
| 141 |
], |
147 |
] |
| 142 |
"Able to build without patch", |
148 |
|
| 143 |
"Unable to build without patch") |
149 |
try: |
|
|
150 |
args.append("--group=%s" % self._delegate.group()) |
| 151 |
finally: |
| 152 |
return self._run_command( |
| 153 |
args, |
| 154 |
"Able to build without patch", |
| 155 |
"Unable to build without patch") |
| 144 |
|
156 |
|
| 145 |
def _test(self): |
157 |
def _test(self): |
| 146 |
return self._run_command([ |
158 |
args = [ |
| 147 |
"build-and-test", |
159 |
"build-and-test", |
| 148 |
"--no-clean", |
160 |
"--no-clean", |
| 149 |
"--no-update", |
161 |
"--no-update", |
|
Lines 151-162
class PatchAnalysisTask(object):
a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py_sec2
|
| 151 |
"--test", |
163 |
"--test", |
| 152 |
"--non-interactive", |
164 |
"--non-interactive", |
| 153 |
"--build-style=%s" % self._delegate.build_style(), |
165 |
"--build-style=%s" % self._delegate.build_style(), |
| 154 |
], |
166 |
] |
| 155 |
"Passed tests", |
167 |
|
| 156 |
"Patch does not pass tests") |
168 |
try: |
|
|
169 |
args.append("--group=%s" % self._delegate.group()) |
| 170 |
finally: |
| 171 |
return self._run_command( |
| 172 |
args, |
| 173 |
"Passed tests", |
| 174 |
"Patch does not pass tests") |
| 157 |
|
175 |
|
| 158 |
def _build_and_test_without_patch(self): |
176 |
def _build_and_test_without_patch(self): |
| 159 |
return self._run_command([ |
177 |
args = [ |
| 160 |
"build-and-test", |
178 |
"build-and-test", |
| 161 |
"--force-clean", |
179 |
"--force-clean", |
| 162 |
"--no-update", |
180 |
"--no-update", |
|
Lines 164-172
class PatchAnalysisTask(object):
a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py_sec3
|
| 164 |
"--test", |
182 |
"--test", |
| 165 |
"--non-interactive", |
183 |
"--non-interactive", |
| 166 |
"--build-style=%s" % self._delegate.build_style(), |
184 |
"--build-style=%s" % self._delegate.build_style(), |
| 167 |
], |
185 |
] |
| 168 |
"Able to pass tests without patch", |
186 |
|
| 169 |
"Unable to pass tests without patch (tree is red?)") |
187 |
try: |
|
|
188 |
args.append("--group=%s" % self._delegate.group()) |
| 189 |
finally: |
| 190 |
return self._run_command( |
| 191 |
args, |
| 192 |
"Able to pass tests without patch", |
| 193 |
"Unable to pass tests without patch (tree is red?)") |
| 170 |
|
194 |
|
| 171 |
def _land(self): |
195 |
def _land(self): |
| 172 |
# Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should. |
196 |
# Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should. |
|
Lines 210-278
class PatchAnalysisTask(object):
a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py_sec4
|
| 210 |
if self._test(): |
234 |
if self._test(): |
| 211 |
return True |
235 |
return True |
| 212 |
|
236 |
|
| 213 |
# Note: archive_last_test_results deletes the results directory, making these calls order-sensitve. |
237 |
if hasattr(self._delegate, 'group') and self._delegate.group() == "jsc": |
| 214 |
# We could remove this dependency by building the test_results from the archive. |
238 |
first_results = self._delegate.test_results() |
| 215 |
first_results = self._delegate.test_results() |
239 |
first_script_error = self._script_error |
| 216 |
first_results_archive = self._delegate.archive_last_test_results(self._patch) |
240 |
if first_results.all_api_tests_passed() is True and len(first_results.stress_test_failures()) == 0: |
| 217 |
first_script_error = self._script_error |
241 |
return True |
| 218 |
first_failure_status_id = self.failure_status_id |
242 |
else: |
| 219 |
|
243 |
# TODO svijayaraghavan@apple.com: Comment this, and uncomment the below lines so it actually builds without patch |
| 220 |
if self._test() and not first_results.did_exceed_test_failure_limit(): |
244 |
#clean_tree_results = first_results |
| 221 |
# Only report flaky tests if we were successful at parsing results.json and archiving results. |
245 |
#clean_tree_results['allApiTestsPassed'] = False |
| 222 |
if first_results and first_results_archive: |
|
|
| 223 |
self._report_flaky_tests(first_results.failing_test_results(), first_results_archive) |
| 224 |
return True |
| 225 |
|
246 |
|
| 226 |
second_results = self._delegate.test_results() |
247 |
self._build_and_test_without_patch() |
| 227 |
second_results_archive = self._delegate.archive_last_test_results(self._patch) |
248 |
clean_tree_results = self._delegate.test_results() |
| 228 |
second_script_error = self._script_error |
|
|
| 229 |
second_failure_status_id = self.failure_status_id |
| 230 |
|
249 |
|
| 231 |
if second_results.did_exceed_test_failure_limit() and first_results.did_exceed_test_failure_limit(): |
250 |
if clean_tree_results == first_results: |
| 232 |
self._build_and_test_without_patch() |
251 |
return True |
| 233 |
clean_tree_results = self._delegate.test_results() |
|
|
| 234 |
|
252 |
|
| 235 |
if (len(first_results.failing_tests()) - len(clean_tree_results.failing_tests())) <= 5: |
253 |
return self.report_failure(None, first_results, first_script_error) |
| 236 |
return False |
|
|
| 237 |
|
254 |
|
| 238 |
self.failure_status_id = first_failure_status_id |
255 |
else: |
|
|
256 |
# Note: archive_last_test_results deletes the results directory, making these calls order-sensitve. |
| 257 |
# We could remove this dependency by building the test_results from the archive. |
| 258 |
first_results = self._delegate.test_results() |
| 259 |
first_results_archive = self._delegate.archive_last_test_results(self._patch) |
| 260 |
first_script_error = self._script_error |
| 261 |
first_failure_status_id = self.failure_status_id |
| 239 |
|
262 |
|
| 240 |
return self.report_failure(first_results_archive, first_results, first_script_error) |
263 |
if self._test() and not first_results.did_exceed_test_failure_limit(): |
|
|
264 |
# Only report flaky tests if we were successful at parsing results.json and archiving results. |
| 265 |
if first_results and first_results_archive: |
| 266 |
self._report_flaky_tests(first_results.failing_test_results(), first_results_archive) |
| 267 |
return True |
| 241 |
|
268 |
|
| 242 |
if second_results.did_exceed_test_failure_limit(): |
269 |
second_results = self._delegate.test_results() |
| 243 |
self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id) |
270 |
second_results_archive = self._delegate.archive_last_test_results(self._patch) |
| 244 |
return False |
271 |
second_script_error = self._script_error |
|
|
272 |
second_failure_status_id = self.failure_status_id |
| 245 |
|
273 |
|
| 246 |
if first_results.did_exceed_test_failure_limit(): |
274 |
if second_results.did_exceed_test_failure_limit() and first_results.did_exceed_test_failure_limit(): |
| 247 |
self._should_defer_patch_or_throw(second_results.failing_test_results(), second_results_archive, second_script_error, second_failure_status_id) |
275 |
self._build_and_test_without_patch() |
| 248 |
return False |
276 |
clean_tree_results = self._delegate.test_results() |
|
|
277 |
|
| 278 |
if (len(first_results.failing_tests()) - len(clean_tree_results.failing_tests())) <= 5: |
| 279 |
return False |
| 249 |
|
280 |
|
| 250 |
if self._results_failed_different_tests(first_results, second_results): |
281 |
self.failure_status_id = first_failure_status_id |
| 251 |
first_failing_results_set = frozenset(first_results.failing_test_results()) |
|
|
| 252 |
second_failing_results_set = frozenset(second_results.failing_test_results()) |
| 253 |
|
282 |
|
| 254 |
tests_that_only_failed_first = first_failing_results_set.difference(second_failing_results_set) |
283 |
return self.report_failure(first_results_archive, first_results, first_script_error) |
| 255 |
self._report_flaky_tests(tests_that_only_failed_first, first_results_archive) |
|
|
| 256 |
|
284 |
|
| 257 |
tests_that_only_failed_second = second_failing_results_set.difference(first_failing_results_set) |
285 |
if second_results.did_exceed_test_failure_limit(): |
| 258 |
self._report_flaky_tests(tests_that_only_failed_second, second_results_archive) |
286 |
self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id) |
|
|
287 |
return False |
| 288 |
|
| 289 |
if first_results.did_exceed_test_failure_limit(): |
| 290 |
self._should_defer_patch_or_throw(second_results.failing_test_results(), second_results_archive, second_script_error, second_failure_status_id) |
| 291 |
return False |
| 292 |
|
| 293 |
if self._results_failed_different_tests(first_results, second_results): |
| 294 |
first_failing_results_set = frozenset(first_results.failing_test_results()) |
| 295 |
second_failing_results_set = frozenset(second_results.failing_test_results()) |
| 259 |
|
296 |
|
| 260 |
tests_that_consistently_failed = first_failing_results_set.intersection(second_failing_results_set) |
297 |
tests_that_only_failed_first = first_failing_results_set.difference(second_failing_results_set) |
| 261 |
if tests_that_consistently_failed: |
298 |
self._report_flaky_tests(tests_that_only_failed_first, first_results_archive) |
| 262 |
if self._should_defer_patch_or_throw(tests_that_consistently_failed, first_results_archive, first_script_error, first_failure_status_id): |
|
|
| 263 |
return False # Defer patch |
| 264 |
|
299 |
|
| 265 |
# At this point we know that at least one test flaked, but no consistent failures |
300 |
tests_that_only_failed_second = second_failing_results_set.difference(first_failing_results_set) |
| 266 |
# were introduced. This is a bit of a grey-zone. |
301 |
self._report_flaky_tests(tests_that_only_failed_second, second_results_archive) |
| 267 |
return False # Defer patch |
|
|
| 268 |
|
302 |
|
| 269 |
if self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id): |
303 |
tests_that_consistently_failed = first_failing_results_set.intersection(second_failing_results_set) |
| 270 |
return False # Defer patch |
304 |
if tests_that_consistently_failed: |
|
|
305 |
if self._should_defer_patch_or_throw(tests_that_consistently_failed, first_results_archive, first_script_error, first_failure_status_id): |
| 306 |
return False # Defer patch |
| 271 |
|
307 |
|
| 272 |
# At this point, we know that the first and second runs had the exact same failures, |
308 |
# At this point we know that at least one test flaked, but no consistent failures |
| 273 |
# and that those failures are all present on the clean tree, so we can say with certainty |
309 |
# were introduced. This is a bit of a grey-zone. |
| 274 |
# that the patch is good. |
310 |
return False # Defer patch |
| 275 |
return True |
311 |
|
|
|
312 |
if self._should_defer_patch_or_throw(first_results.failing_test_results(), first_results_archive, first_script_error, first_failure_status_id): |
| 313 |
return False # Defer patch |
| 314 |
|
| 315 |
# At this point, we know that the first and second runs had the exact same failures, |
| 316 |
# and that those failures are all present on the clean tree, so we can say with certainty |
| 317 |
# that the patch is good. |
| 318 |
return True |
| 276 |
|
319 |
|
| 277 |
def results_archive_from_patch_test_run(self, patch): |
320 |
def results_archive_from_patch_test_run(self, patch): |
| 278 |
assert(self._patch.id() == patch.id()) # PatchAnalysisTask is not currently re-useable. |
321 |
assert(self._patch.id() == patch.id()) # PatchAnalysisTask is not currently re-useable. |