| 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 import logging |
| 6 |
| 5 from common.pipeline_wrapper import BasePipeline | 7 from common.pipeline_wrapper import BasePipeline |
| 6 from model.wf_analysis import WfAnalysis | 8 from common.waterfall import failure_type |
| 7 from waterfall import try_job_util | 9 from waterfall import try_job_util |
| 10 from waterfall.identify_try_job_culprit_pipeline import ( |
| 11 IdentifyTryJobCulpritPipeline) |
| 12 from waterfall.monitor_try_job_pipeline import MonitorTryJobPipeline |
| 13 from waterfall.process_swarming_task_result_pipeline import ( |
| 14 ProcessSwarmingTaskResultPipeline) |
| 15 from waterfall.schedule_compile_try_job_pipeline import ( |
| 16 ScheduleCompileTryJobPipeline) |
| 17 from waterfall.schedule_test_try_job_pipeline import ( |
| 18 ScheduleTestTryJobPipeline) |
| 19 |
| 20 |
| 21 def _GetLastPassCompile(build_number, failed_steps): |
| 22 if (failed_steps.get('compile') and |
| 23 failed_steps['compile']['first_failure'] == build_number and |
| 24 failed_steps['compile'].get('last_pass') is not None): |
| 25 return failed_steps['compile']['last_pass'] |
| 26 return None |
| 27 |
| 28 |
| 29 def _GetLastPassTest(build_number, failed_steps): |
| 30 for step_failure in failed_steps.itervalues(): |
| 31 for test_failure in step_failure['tests'].itervalues(): |
| 32 if (test_failure['first_failure'] == build_number and |
| 33 test_failure.get('last_pass') is not None): |
| 34 return test_failure['last_pass'] |
| 35 return None |
| 36 |
| 37 |
| 38 def _GetLastPass(build_number, failure_info, try_job_type): |
| 39 if try_job_type == failure_type.COMPILE: |
| 40 return _GetLastPassCompile(build_number, failure_info['failed_steps']) |
| 41 elif try_job_type == failure_type.TEST: |
| 42 return _GetLastPassTest(build_number, failure_info['failed_steps']) |
| 43 else: |
| 44 return None |
| 45 |
| 46 |
| 47 def _GetSuspectsFromHeuristicResult(heuristic_result): |
| 48 if not heuristic_result: |
| 49 return [] |
| 50 |
| 51 suspected_revisions = set() |
| 52 for failure in heuristic_result.get('failures', []): |
| 53 for cl in failure['suspected_cls']: |
| 54 suspected_revisions.add(cl['revision']) |
| 55 return list(suspected_revisions) |
| 56 |
| 57 |
| 58 def _HasFirstTimeFailure(tests, build_number): |
| 59 for test_failure in tests.itervalues(): |
| 60 if test_failure['first_failure'] == build_number: |
| 61 return True |
| 62 return False |
| 8 | 63 |
| 9 | 64 |
| 10 class StartTryJobOnDemandPipeline(BasePipeline): | 65 class StartTryJobOnDemandPipeline(BasePipeline): |
| 11 | 66 |
| 12 # Arguments number differs from overridden method - pylint: disable=W0221 | 67 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 13 def run(self, failure_info, signals, build_completed, force_try_job, | 68 def run(self, master_name, builder_name, build_number, failure_info, |
| 14 heuristic_result): | 69 signals, heuristic_result, build_completed, force_try_job): |
| 15 """Starts a try job if one is needed for the given failure.""" | 70 """Starts a try job if one is needed for the given failure.""" |
| 71 |
| 16 if not build_completed: # Only start try-jobs for completed builds. | 72 if not build_completed: # Only start try-jobs for completed builds. |
| 17 return False | 73 return |
| 18 | 74 |
| 19 failure_result_map = try_job_util.ScheduleTryJobIfNeeded( | 75 need_try_job = try_job_util.NeedANewTryJob( |
| 20 failure_info, signals=signals, heuristic_result=heuristic_result, | 76 master_name, builder_name, build_number, failure_info, signals, |
| 21 force_try_job=force_try_job) | 77 heuristic_result, force_try_job) |
| 22 | 78 |
| 23 # Save reference to the try-jobs if any was scheduled. | 79 if not need_try_job: |
| 24 master_name = failure_info['master_name'] | 80 return |
| 25 builder_name = failure_info['builder_name'] | 81 |
| 26 build_number = failure_info['build_number'] | 82 try_job_type = failure_info['failure_type'] |
| 27 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 83 last_pass = _GetLastPass(build_number, failure_info, try_job_type) |
| 28 analysis.failure_result_map = failure_result_map | 84 if last_pass is None: # pragma: no cover |
| 29 analysis.put() | 85 logging.warning('Couldn"t start try job for build %s, %s, %d because' |
| 30 return True | 86 ' last_pass is not found.' % ( |
| 87 master_name, builder_name, build_number)) |
| 88 return |
| 89 |
| 90 blame_list = failure_info['builds'][str(build_number)]['blame_list'] |
| 91 good_revision = failure_info['builds'][str(last_pass)]['chromium_revision'] |
| 92 bad_revision = failure_info['builds'][str(build_number)][ |
| 93 'chromium_revision'] |
| 94 suspected_revisions = _GetSuspectsFromHeuristicResult(heuristic_result) |
| 95 |
| 96 if try_job_type == failure_type.COMPILE: |
| 97 compile_targets = try_job_util.GetFailedTargetsFromSignals( |
| 98 signals, master_name, builder_name) |
| 99 try_job_id = yield ScheduleCompileTryJobPipeline( |
| 100 master_name, builder_name, build_number, good_revision, bad_revision, |
| 101 try_job_type, compile_targets, suspected_revisions) |
| 102 try_job_result = yield MonitorTryJobPipeline( |
| 103 master_name, builder_name, build_number, try_job_type, try_job_id) |
| 104 yield IdentifyTryJobCulpritPipeline( |
| 105 master_name, builder_name, build_number, blame_list, try_job_type, |
| 106 try_job_id, try_job_result) |
| 107 else: |
| 108 # If try_job_type is other type, the pipeline has returned. |
| 109 # So here the try_job_type is failure_type.TEST. |
| 110 |
| 111 # Waits and gets the swarming tasks' results. |
| 112 reliable_tests = [] |
| 113 for step_name, step_failure in failure_info['failed_steps'].iteritems(): |
| 114 step_has_first_time_failure = _HasFirstTimeFailure( |
| 115 step_failure['tests'], build_number) |
| 116 if not step_has_first_time_failure: |
| 117 continue |
| 118 task_result = yield ProcessSwarmingTaskResultPipeline( |
| 119 master_name, builder_name, build_number, step_name) |
| 120 reliable_tests.append(task_result) |
| 121 |
| 122 try_job_id = yield ScheduleTestTryJobPipeline( |
| 123 master_name, builder_name, build_number, good_revision, bad_revision, |
| 124 try_job_type, suspected_revisions, *reliable_tests) |
| 125 try_job_result = yield MonitorTryJobPipeline( |
| 126 master_name, builder_name, build_number, try_job_type, try_job_id) |
| 127 yield IdentifyTryJobCulpritPipeline( |
| 128 master_name, builder_name, build_number, blame_list, try_job_type, |
| 129 try_job_id, try_job_result) |
| OLD | NEW |