Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 from common.git_repository import GitRepository | 5 from common.git_repository import GitRepository |
| 6 from common.http_client_appengine import HttpClientAppengine as HttpClient | 6 from common.http_client_appengine import HttpClientAppengine as HttpClient |
| 7 from model import wf_analysis_status | 7 from model import wf_analysis_status |
| 8 from model.wf_try_job import WfTryJob | 8 from model.wf_try_job import WfTryJob |
| 9 from pipeline_wrapper import BasePipeline | 9 from pipeline_wrapper import BasePipeline |
| 10 | 10 |
| 11 | 11 |
| 12 GIT_REPO = GitRepository( | |
| 13 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | |
| 14 | |
| 15 | |
| 12 class IdentifyTryJobCulpritPipeline(BasePipeline): | 16 class IdentifyTryJobCulpritPipeline(BasePipeline): |
| 13 """A pipeline to identify culprit CL info based on try job compile results.""" | 17 """A pipeline to identify culprit CL info based on try job compile results.""" |
| 14 | 18 |
| 19 def _GetCulpritInfo(self, failed_revisions): | |
| 20 """Gets commit_positions and review_urls for revisions.""" | |
| 21 culprits = {} | |
| 22 for failed_revision in failed_revisions: | |
| 23 change_log = GIT_REPO.GetChangeLog(failed_revision) | |
| 24 if change_log: | |
| 25 culprits[failed_revision] = { | |
| 26 'revision': failed_revision, | |
| 27 'commit_position': change_log.commit_position, | |
| 28 'review_url': change_log.code_review_url | |
| 29 } | |
| 30 return culprits | |
| 31 | |
| 15 # Arguments number differs from overridden method - pylint: disable=W0221 | 32 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 16 def run( | 33 def run( |
| 17 self, master_name, builder_name, build_number, try_job_id, | 34 self, master_name, builder_name, build_number, blame_list, try_job_type, |
| 18 compile_result): | 35 try_job_id, result): |
| 19 culprit = None | 36 """Identify the information for failed revisions. |
| 20 | 37 |
| 21 if compile_result and len(compile_result.get('result', [])) > 0: | 38 The format for final try-job result for compile failures is: |
| 22 # For compile failures, the try job will stop if one revision fails, so | 39 { |
| 23 # the culprit will be the last revision in the result. | 40 'result': [ |
| 24 result_for_last_checked_revision = compile_result['result'][-1] | 41 ['rev1', 'passed'], |
| 25 failed_revision = ( | 42 ['rev2', 'failed'] |
| 26 result_for_last_checked_revision[0] if | 43 ], |
| 27 result_for_last_checked_revision[1].lower() == 'failed' else None) | 44 'url': 'url', |
| 45 'try_job_id': '1', | |
| 46 'culprit': { | |
| 47 'revision': 'rev2', | |
| 48 'commit_position': '2', | |
| 49 'review_url': 'url_2' | |
| 50 } | |
| 51 } | |
| 28 | 52 |
| 29 if failed_revision: | 53 The format for final try-job result for test failures is: |
| 30 git_repo = GitRepository( | 54 { |
| 31 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | 55 'result': { |
| 32 change_log = git_repo.GetChangeLog(failed_revision) | 56 'rev1': { |
| 33 if change_log: | 57 'a_test': { |
| 34 culprit = { | 58 'status': 'failed', |
| 35 'revision': failed_revision, | 59 'valid': True, |
| 36 'commit_position': change_log.commit_position, | 60 'failures': ['a_test1'] |
| 37 'review_url': change_log.code_review_url | 61 }, |
| 38 } | 62 'b_test': { |
| 39 compile_result['culprit'] = culprit | 63 'status': 'failed', |
| 64 'valid': True, | |
| 65 'failures': ['b_test1'] | |
| 66 } | |
| 67 }, | |
| 68 'rev2': { | |
| 69 'a_test': { | |
| 70 'status': 'failed', | |
| 71 'valid': True, | |
| 72 'failures': ['a_test2'] | |
| 73 }, | |
| 74 'b_test': { | |
| 75 'status': 'passed', | |
| 76 'valid': True | |
| 77 } | |
| 78 } | |
| 79 }, | |
| 80 'url': 'url', | |
| 81 'try_job_id': '1', | |
| 82 'culprit': { | |
| 83 'a_test': { | |
| 84 'revision': 'rev1', | |
| 85 'commit_position': '1', | |
| 86 'review_url': 'url_1', | |
| 87 'tests': { | |
| 88 'a_test1': { | |
| 89 'revision': 'rev1', | |
| 90 'commit_position': '1', | |
| 91 'review_url': 'url_1' | |
| 92 }, | |
| 93 'a_test2': { | |
| 94 'revision': 'rev2', | |
| 95 'commit_position': '2', | |
| 96 'review_url': 'url_2' | |
| 97 } | |
| 98 } | |
| 99 }, | |
| 100 'b_test': { | |
| 101 'revision': 'rev1', | |
| 102 'commit_position': '1', | |
| 103 'review_url': 'url_1', | |
| 104 'tests': { | |
| 105 'b_test1': { | |
| 106 'revision': 'rev1', | |
| 107 'commit_position': '1', | |
| 108 'review_url': 'url_1' | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 } | |
| 113 } | |
| 114 """ | |
| 115 culprits = None | |
| 116 | |
| 117 if try_job_type == 'compile': | |
| 118 if result and len(result.get('result', [])) > 0: | |
|
lijeffrey
2016/01/16 00:27:12
I think this can be reduced to just result.get('re
chanli
2016/01/20 18:28:04
Done.
| |
| 119 # For compile failures, the try job will stop if one revision fails, so | |
| 120 # the culprit will be the last revision in the result. | |
| 121 result_for_last_checked_revision = result['result'][-1] | |
| 122 failed_revision = ( | |
| 123 result_for_last_checked_revision[0] if | |
| 124 result_for_last_checked_revision[1].lower() == 'failed' else None) | |
| 125 | |
| 126 if failed_revision: | |
| 127 culprits = self._GetCulpritInfo([failed_revision]) | |
| 128 if culprits: | |
| 129 result['culprit'] = culprits[failed_revision] | |
| 130 else: | |
| 131 if result and result.get('result'): | |
|
lijeffrey
2016/01/16 00:27:12
it looks like this if statement can be moved outsi
chanli
2016/01/20 18:28:04
Done.
| |
| 132 # For test failures, the try job will run against every revision, | |
| 133 # so we need to traverse the result dict in order to identify the | |
| 134 # culprits to each failed step or test. | |
| 135 culprit_map = {} | |
| 136 failed_revisions = [] | |
| 137 for revision in blame_list: | |
| 138 for step, step_result in result['result'][revision].iteritems(): | |
| 139 if step_result['valid'] and step_result['status'] == 'failed': | |
| 140 if revision not in failed_revisions: | |
| 141 failed_revisions.append(revision) | |
| 142 | |
| 143 if step not in culprit_map: | |
| 144 culprit_map[step] = {} | |
| 145 culprit_map[step]['revision'] = revision | |
| 146 culprit_map[step]['tests'] = {} | |
| 147 for failed_test in step_result['failures']: | |
| 148 if failed_test not in culprit_map[step]['tests']: | |
| 149 culprit_map[step]['tests'][failed_test] = {} | |
| 150 culprit_map[step]['tests'][failed_test]['revision'] = ( | |
| 151 revision) | |
| 152 | |
| 153 if failed_revisions: | |
|
lijeffrey
2016/01/16 00:27:12
nit: maybe add if failed_revisions to _GetCulpritI
chanli
2016/01/20 18:28:04
Done.
| |
| 154 culprits = self._GetCulpritInfo(failed_revisions) | |
| 155 if culprits: | |
| 156 for step_culprit in culprit_map.values(): | |
| 157 step_revision = step_culprit['revision'] | |
| 158 step_culprit['commit_position'] = ( | |
| 159 culprits[step_revision]['commit_position']) | |
| 160 step_culprit['review_url'] = culprits[step_revision]['review_url'] | |
| 161 for test_culprit in step_culprit.get('tests', {}).values(): | |
| 162 test_revision = test_culprit['revision'] | |
| 163 test_culprit.update(culprits[test_revision]) | |
| 164 result['culprit'] = culprit_map | |
| 40 | 165 |
| 41 # Store try job results. | 166 # Store try job results. |
| 42 try_job_result = WfTryJob.Get(master_name, builder_name, build_number) | 167 try_job_result = WfTryJob.Get(master_name, builder_name, build_number) |
| 43 if culprit: | 168 if culprits: |
| 44 if (try_job_result.compile_results and | 169 result_needs_update = ( |
| 45 try_job_result.compile_results[-1]['try_job_id'] == try_job_id): | 170 try_job_result.compile_results if |
| 46 try_job_result.compile_results[-1].update(compile_result) | 171 try_job_type == 'compile' else try_job_result.test_results) |
| 172 if (result_needs_update and | |
| 173 result_needs_update[-1]['try_job_id'] == try_job_id): | |
| 174 result_needs_update[-1].update(result) | |
| 47 else: # pragma: no cover | 175 else: # pragma: no cover |
| 48 try_job_result.compile_results.append(compile_result) | 176 result_needs_update.append(result) |
| 49 | 177 |
| 50 try_job_result.status = wf_analysis_status.ANALYZED | 178 try_job_result.status = wf_analysis_status.ANALYZED |
| 51 try_job_result.put() | 179 try_job_result.put() |
| 52 | 180 |
| 53 return culprit | 181 return result.get('culprit', None) if result else None |
| OLD | NEW |