| 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 base_handler import BaseHandler | 5 from base_handler import BaseHandler |
| 6 from base_handler import Permission | 6 from base_handler import Permission |
| 7 from model.wf_analysis import WfAnalysis | 7 from handlers import handlers_util |
| 8 from model import wf_analysis_status | |
| 9 from model.wf_swarming_task import WfSwarmingTask | |
| 10 from model.wf_try_job import WfTryJob | |
| 11 from waterfall import buildbot | 8 from waterfall import buildbot |
| 12 | 9 |
| 13 | 10 |
| 14 FLAKY = 'Flaky' | |
| 15 | |
| 16 | |
| 17 def _GetTryJobBuildNumber(url): | |
| 18 build_keys = buildbot.ParseBuildUrl(url) | |
| 19 return build_keys[2] | |
| 20 | |
| 21 | |
| 22 def _GetCulpritInfoForTryJobResult(try_job_key, culprits_info): | |
| 23 referred_build_keys = try_job_key.split('/') | |
| 24 try_job = WfTryJob.Get(*referred_build_keys) | |
| 25 if not try_job: | |
| 26 return | |
| 27 | |
| 28 if try_job.compile_results: | |
| 29 try_job_result = try_job.compile_results[-1] | |
| 30 elif try_job.test_results: | |
| 31 try_job_result = try_job.test_results[-1] | |
| 32 else: | |
| 33 try_job_result = None | |
| 34 | |
| 35 for culprit_info in culprits_info.values(): | |
| 36 if culprit_info['try_job_key'] != try_job_key: | |
| 37 continue | |
| 38 | |
| 39 # Only include try job result for reliable tests. | |
| 40 # Flaky tests have been marked as 'Flaky'. | |
| 41 culprit_info['status'] = ( | |
| 42 wf_analysis_status.TRY_JOB_STATUS_TO_DESCRIPTION[try_job.status] | |
| 43 if not culprit_info.get('status') else culprit_info['status']) | |
| 44 | |
| 45 if try_job_result and culprit_info['status'] != FLAKY: | |
| 46 if try_job_result.get('url'): | |
| 47 culprit_info['try_job_url'] = try_job_result['url'] | |
| 48 culprit_info['try_job_build_number'] = ( | |
| 49 _GetTryJobBuildNumber(try_job_result['url'])) | |
| 50 if try_job_result.get('culprit'): | |
| 51 try_job_culprits = try_job_result['culprit'] | |
| 52 step = culprit_info['step'] | |
| 53 test = culprit_info['test'] | |
| 54 if not try_job_culprits.get(step, {}).get('tests'): # Only step level | |
| 55 # For historical culprit found by try job for compile, | |
| 56 # step name is not recorded. | |
| 57 culprit = try_job_culprits.get(step) or try_job_culprits | |
| 58 elif test in try_job_culprits.get(step, {}).get('tests'): | |
| 59 culprit = try_job_culprits[step]['tests'][test] | |
| 60 else: # pragma: no cover | |
| 61 continue # No culprit for test found. | |
| 62 | |
| 63 culprit_info['revision'] = culprit.get('revision') | |
| 64 culprit_info['commit_position'] = culprit.get('commit_position') | |
| 65 culprit_info['review_url'] = culprit.get('review_url') | |
| 66 | |
| 67 | |
| 68 def _UpdateFlakiness(step_name, failure_key_set, culprits_info): | |
| 69 for failure_key in failure_key_set: | |
| 70 build_keys = failure_key.split('/') | |
| 71 task = WfSwarmingTask.Get(*build_keys, step_name=step_name) | |
| 72 classified_tests = task.classified_tests | |
| 73 for culprit_info in culprits_info.values(): | |
| 74 if (culprit_info['try_job_key'] == failure_key and | |
| 75 culprit_info['test'] in classified_tests.get('flaky_tests', [])): | |
| 76 culprit_info['status'] = 'Flaky' | |
| 77 | |
| 78 | |
| 79 def _GetAllTryJobResults(master_name, builder_name, build_number): | |
| 80 culprits_info = {} | |
| 81 try_job_keys = set() | |
| 82 | |
| 83 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | |
| 84 if not analysis: | |
| 85 return culprits_info | |
| 86 | |
| 87 failure_result_map = analysis.failure_result_map | |
| 88 if failure_result_map: | |
| 89 # failure_result_map uses step_names as keys and saves referred try_job_keys | |
| 90 # If non-swarming, step_name and referred_try_job_key match directly as: | |
| 91 # step_name: try_job_key | |
| 92 # If swarming, add one more layer of tests, so the format would be: | |
| 93 # step_name: { | |
| 94 # test_name1: try_job_key1, | |
| 95 # test_name2: try_job_key2, | |
| 96 # ... | |
| 97 # } | |
| 98 for step_name, step_failure_result_map in failure_result_map.iteritems(): | |
| 99 if isinstance(step_failure_result_map, dict): | |
| 100 step_refering_keys = set() | |
| 101 for failed_test, try_job_key in step_failure_result_map.iteritems(): | |
| 102 step_test_key = '%s-%s' % (step_name, failed_test) | |
| 103 culprits_info[step_test_key] = { | |
| 104 'step': step_name, | |
| 105 'test': failed_test, | |
| 106 'try_job_key': try_job_key | |
| 107 } | |
| 108 step_refering_keys.add(try_job_key) | |
| 109 | |
| 110 _UpdateFlakiness(step_name, step_refering_keys, culprits_info) | |
| 111 try_job_keys.update(step_refering_keys) | |
| 112 else: | |
| 113 culprits_info[step_name] = { | |
| 114 'step': step_name, | |
| 115 'test': 'N/A', | |
| 116 'try_job_key': step_failure_result_map | |
| 117 } | |
| 118 try_job_keys.add(step_failure_result_map) | |
| 119 | |
| 120 for try_job_key in try_job_keys: | |
| 121 _GetCulpritInfoForTryJobResult(try_job_key, culprits_info) | |
| 122 | |
| 123 return culprits_info | |
| 124 | |
| 125 | |
| 126 class TryJobResult(BaseHandler): | 11 class TryJobResult(BaseHandler): |
| 127 PERMISSION_LEVEL = Permission.ANYONE | 12 PERMISSION_LEVEL = Permission.ANYONE |
| 128 | 13 |
| 129 def HandleGet(self): | 14 def HandleGet(self): |
| 130 """Get the latest try job result if it's compile failure.""" | 15 """Get the latest try job result if it's compile failure.""" |
| 131 url = self.request.get('url').strip() | 16 url = self.request.get('url').strip() |
| 132 build_keys = buildbot.ParseBuildUrl(url) | 17 build_keys = buildbot.ParseBuildUrl(url) |
| 133 | 18 |
| 134 if not build_keys: # pragma: no cover | 19 if not build_keys: # pragma: no cover |
| 135 return {'data': {}} | 20 return {'data': {}} |
| 136 | 21 |
| 137 data = _GetAllTryJobResults(*build_keys) | 22 data = handlers_util.GetAllTryJobResults(*build_keys) |
| 138 | 23 |
| 139 return {'data': data} | 24 return {'data': data} |
| 140 | 25 |
| 141 def HandlePost(self): # pragma: no cover | 26 def HandlePost(self): # pragma: no cover |
| 142 return self.HandleGet() | 27 return self.HandleGet() |
| OLD | NEW |