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 from waterfall.try_job_enums import TryJobType | |
| 11 | |
| 12 | |
| 13 GIT_REPO = GitRepository( | |
| 14 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | |
| 10 | 15 |
| 11 | 16 |
| 12 class IdentifyTryJobCulpritPipeline(BasePipeline): | 17 class IdentifyTryJobCulpritPipeline(BasePipeline): |
| 13 """A pipeline to identify culprit CL info based on try job compile results.""" | 18 """A pipeline to identify culprit CL info based on try job compile results.""" |
| 14 | 19 |
| 20 def _GetCulpritInfo(self, failed_revisions): | |
| 21 """Gets commit_positions and review_urls for revisions.""" | |
| 22 culprits = {} | |
| 23 for failed_revision in failed_revisions: | |
| 24 change_log = GIT_REPO.GetChangeLog(failed_revision) | |
| 25 if change_log: | |
|
stgao
2016/01/29 18:55:01
If the request to Gitiles fails for some reason, d
chanli
2016/01/30 02:22:37
Yes i think we should return the hash anyway.
| |
| 26 culprits[failed_revision] = { | |
| 27 'revision': failed_revision, | |
| 28 'commit_position': change_log.commit_position, | |
| 29 'review_url': change_log.code_review_url | |
| 30 } | |
| 31 return culprits | |
| 32 | |
| 33 def _FindCulpritForEachTestFailure(self, blame_list, result): | |
| 34 # For test failures, the try job will run against every revision, | |
| 35 # so we need to traverse the result dict in chronological order to identify | |
| 36 # the culprits for each failed step or test. | |
| 37 culprit_map = {} | |
| 38 failed_revisions = [] | |
| 39 for revision in blame_list: | |
| 40 for step, step_result in result['result'][revision].iteritems(): | |
| 41 if step_result['valid'] and step_result['status'] == 'failed': | |
| 42 if revision not in failed_revisions: | |
| 43 failed_revisions.append(revision) | |
| 44 | |
| 45 if step not in culprit_map: | |
| 46 culprit_map[step] = { | |
| 47 'tests': {} | |
| 48 } | |
| 49 | |
| 50 if (not step_result['failures'] and | |
| 51 not culprit_map[step].get('revision')): | |
| 52 # Non swarming test failures, only have step level failure info. | |
| 53 culprit_map[step]['revision'] = revision | |
| 54 | |
| 55 for failed_test in step_result['failures']: | |
| 56 # Swarming tests, gets first failed revision for each test. | |
| 57 if failed_test not in culprit_map[step]['tests']: | |
| 58 culprit_map[step]['tests'][failed_test] = { | |
| 59 'revision': revision | |
| 60 } | |
| 61 return culprit_map, failed_revisions | |
| 62 | |
| 63 def _UpdateCulpritMapWithCulpritInfo(self, culprit_map, culprits): | |
| 64 """Fills in commit_position and review_url for each failed rev in map.""" | |
| 65 for step_culprit in culprit_map.values(): | |
| 66 if step_culprit.get('revision'): | |
| 67 culprit = culprits[step_culprit['revision']] | |
| 68 step_culprit['commit_position'] = culprit['commit_position'] | |
| 69 step_culprit['review_url'] = culprit['review_url'] | |
| 70 for test_culprit in step_culprit.get('tests', {}).values(): | |
| 71 test_revision = test_culprit['revision'] | |
| 72 test_culprit.update(culprits[test_revision]) | |
|
stgao
2016/01/29 18:55:02
If line #25 is false, this will cause an exception
chanli
2016/01/30 02:22:37
Made the change to include revision, this is fine
| |
| 73 | |
| 15 # Arguments number differs from overridden method - pylint: disable=W0221 | 74 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 16 def run( | 75 def run( |
|
lijeffrey
2016/01/29 19:49:46
you may want to do a rebase, a lot of code has cha
chanli
2016/01/30 02:22:37
Done.
| |
| 17 self, master_name, builder_name, build_number, try_job_id, | 76 self, master_name, builder_name, build_number, blame_list, try_job_type, |
| 18 compile_result): | 77 try_job_id, result): |
| 19 culprit = None | 78 """Identifies the information for failed revisions. |
| 20 | 79 |
| 21 if compile_result and len(compile_result.get('result', [])) > 0: | 80 Please refer to try_job_result_format.md for format check. |
| 22 # For compile failures, the try job will stop if one revision fails, so | 81 """ |
| 23 # the culprit will be the last revision in the result. | 82 culprits = None |
| 24 result_for_last_checked_revision = compile_result['result'][-1] | |
| 25 failed_revision = ( | |
| 26 result_for_last_checked_revision[0] if | |
| 27 result_for_last_checked_revision[1].lower() == 'failed' else None) | |
| 28 | 83 |
| 29 if failed_revision: | 84 if result and result.get('result'): |
| 30 git_repo = GitRepository( | 85 if try_job_type == TryJobType.COMPILE: |
| 31 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | 86 # For compile failures, the try job will stop if one revision fails, so |
| 32 change_log = git_repo.GetChangeLog(failed_revision) | 87 # the culprit will be the last revision in the result. |
| 33 if change_log: | 88 result_for_last_checked_revision = result['result'][-1] |
| 34 culprit = { | 89 failed_revisions = ( |
| 35 'revision': failed_revision, | 90 [result_for_last_checked_revision[0]] if |
| 36 'commit_position': change_log.commit_position, | 91 result_for_last_checked_revision[1].lower() == 'failed' else []) |
| 37 'review_url': change_log.code_review_url | 92 |
| 38 } | 93 culprits = self._GetCulpritInfo(failed_revisions) |
| 39 compile_result['culprit'] = culprit | 94 if culprits: |
| 95 result['culprit'] = culprits[failed_revisions[0]] | |
| 96 else: # try_job_type is 'test'. | |
| 97 culprit_map, failed_revisions = self._FindCulpritForEachTestFailure( | |
| 98 blame_list, result) | |
| 99 culprits = self._GetCulpritInfo(failed_revisions) | |
| 100 if culprits: | |
| 101 self._UpdateCulpritMapWithCulpritInfo(culprit_map, culprits) | |
| 102 result['culprit'] = culprit_map | |
| 40 | 103 |
| 41 # Store try job results. | 104 # Store try job results. |
| 42 try_job_result = WfTryJob.Get(master_name, builder_name, build_number) | 105 try_job_result = WfTryJob.Get(master_name, builder_name, build_number) |
| 43 if culprit: | 106 if culprits: |
| 44 if (try_job_result.compile_results and | 107 result_to_update = ( |
| 45 try_job_result.compile_results[-1]['try_job_id'] == try_job_id): | 108 try_job_result.compile_results if |
| 46 try_job_result.compile_results[-1].update(compile_result) | 109 try_job_type == TryJobType.COMPILE else |
| 110 try_job_result.test_results) | |
| 111 if (result_to_update and | |
| 112 result_to_update[-1]['try_job_id'] == try_job_id): | |
| 113 result_to_update[-1].update(result) | |
| 47 else: # pragma: no cover | 114 else: # pragma: no cover |
| 48 try_job_result.compile_results.append(compile_result) | 115 result_to_update.append(result) |
| 49 | 116 |
| 50 try_job_result.status = wf_analysis_status.ANALYZED | 117 try_job_result.status = wf_analysis_status.ANALYZED |
| 51 try_job_result.put() | 118 try_job_result.put() |
| 52 | 119 |
| 53 return culprit | 120 return result.get('culprit', None) if result else None |
| OLD | NEW |