Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(167)

Side by Side Diff: appengine/findit/waterfall/identify_try_job_culprit_pipeline.py

Issue 2230103002: [Findit] Pipeline change to save suspected cls to data store. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@0808-resubmit-suspected_cl_model
Patch Set: . Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 collections import defaultdict 5 from collections import defaultdict
6 import logging 6 import logging
7 7
8 from google.appengine.ext import ndb 8 from google.appengine.ext import ndb
9 9
10 from common.git_repository import GitRepository 10 from common.git_repository import GitRepository
11 from common.http_client_appengine import HttpClientAppengine as HttpClient 11 from common.http_client_appengine import HttpClientAppengine as HttpClient
12 from common.pipeline_wrapper import BasePipeline 12 from common.pipeline_wrapper import BasePipeline
13 from common.waterfall import failure_type 13 from common.waterfall import failure_type
14 from model import analysis_approach_type
14 from model import analysis_status 15 from model import analysis_status
15 from model import result_status 16 from model import result_status
16 from model.wf_analysis import WfAnalysis 17 from model.wf_analysis import WfAnalysis
17 from model.wf_try_job import WfTryJob 18 from model.wf_try_job import WfTryJob
18 from model.wf_try_job_data import WfTryJobData 19 from model.wf_try_job_data import WfTryJobData
19 from waterfall.send_notification_for_culprit_pipeline import ( 20 from waterfall.send_notification_for_culprit_pipeline import (
20 SendNotificationForCulpritPipeline) 21 SendNotificationForCulpritPipeline)
22 from waterfall import suspected_cl_util
21 23
22 24
23 GIT_REPO = GitRepository( 25 GIT_REPO = GitRepository(
24 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) 26 'https://chromium.googlesource.com/chromium/src.git', HttpClient())
25 27
26 28
27 def _GetResultAnalysisStatus(analysis, result): 29 def _GetResultAnalysisStatus(analysis, result):
28 """Returns the analysis status based on existing status and try job result. 30 """Returns the analysis status based on existing status and try job result.
29 31
30 Args: 32 Args:
(...skipping 13 matching lines...) Expand all
44 if (try_job_found_culprit and 46 if (try_job_found_culprit and
45 (old_result_status is None or 47 (old_result_status is None or
46 old_result_status == result_status.NOT_FOUND_UNTRIAGED or 48 old_result_status == result_status.NOT_FOUND_UNTRIAGED or
47 old_result_status == result_status.NOT_FOUND_INCORRECT or 49 old_result_status == result_status.NOT_FOUND_INCORRECT or
48 old_result_status == result_status.NOT_FOUND_CORRECT)): 50 old_result_status == result_status.NOT_FOUND_CORRECT)):
49 return result_status.FOUND_UNTRIAGED 51 return result_status.FOUND_UNTRIAGED
50 52
51 return old_result_status 53 return old_result_status
52 54
53 55
54 def _GetSuspectedCLs(analysis, result): 56 def _GetTestTryJobSuspectedCLs(culprit):
57 # Suspected CLs are from test failures.
58 suspected_cl_revisions = []
59 suspected_cls = []
60 for results in culprit.itervalues():
61 for test_cl_info in results['tests'].itervalues():
62 revision = test_cl_info['revision']
63 if revision not in suspected_cl_revisions:
64 suspected_cl_revisions.append(revision)
65 suspected_cls.append(test_cl_info)
66
67 return suspected_cls
68
69
70 def _GetTryJobSuspectedCLs(result):
71 """Returns all suspected CLs found by the try job.
72
73 Args:
74 result: A result dict containing the culprit from the results of
75 this try job.
76
77 Returns:
78 A list of suspected CLs found by this try job.
79 """
80 if not result or not result.get('culprit'):
81 return []
82
83 culprit = result.get('culprit')
84 compile_cl_info = culprit.get('compile')
85
86 if compile_cl_info:
87 # Suspected CL is from compile failure.
88 return [compile_cl_info]
89
90 return _GetTestTryJobSuspectedCLs(culprit)
91
92
93 def _GetSuspectedCLs(analysis, try_job_suspected_cls):
55 """Returns a list of suspected CLs. 94 """Returns a list of suspected CLs.
56 95
57 Args: 96 Args:
58 analysis: The WfAnalysis entity corresponding to this try job. 97 analysis: The WfAnalysis entity corresponding to this try job.
59 result: A result dict containing the culprit from the results of 98 is_compile_try_job: True if the try job is for compile failure,
lijeffrey 2016/08/17 19:31:11 where is this arg?
chanli 2016/09/12 20:56:15 Done.
60 this try job. 99 otherwise False.
100 try_job_suspected_cls: A list of suspected CLs found by the try job.
61 101
62 Returns: 102 Returns:
63 A combined list of suspected CLs from those already in analysis and those 103 A combined list of suspected CLs from those already in analysis and those
64 found by this try job. 104 found by this try job.
65 """ 105 """
66 suspected_cls = analysis.suspected_cls[:] if analysis.suspected_cls else [] 106 suspected_cls = analysis.suspected_cls[:] if analysis.suspected_cls else []
67 suspected_cl_revisions = [cl['revision'] for cl in suspected_cls] 107 suspected_cl_revisions = [cl['revision'] for cl in suspected_cls]
68 culprit = result.get('culprit')
69 compile_cl_info = culprit.get('compile')
70 108
71 if compile_cl_info: 109 for try_job_suspected_cl in try_job_suspected_cls:
72 # Suspected CL is from compile failure. 110 if try_job_suspected_cl['revision'] not in suspected_cl_revisions:
73 revision = compile_cl_info.get('revision') 111 suspected_cl_revisions.append(try_job_suspected_cl['revision'])
74 if revision not in suspected_cl_revisions: 112 suspected_cls.append(try_job_suspected_cl)
75 suspected_cl_revisions.append(revision)
76 suspected_cls.append(compile_cl_info)
77 return suspected_cls
78
79 # Suspected CLs are from test failures.
80 for results in culprit.itervalues():
81 if results.get('revision'):
82 # Non swarming test failures, only have step level failure info.
83 revision = results.get('revision')
84 cl_info = {
85 'url': results.get('url'),
86 'repo_name': results.get('repo_name'),
87 'revision': results.get('revision'),
88 'commit_position': results.get('commit_position')
89 }
90 if revision not in suspected_cl_revisions:
91 suspected_cl_revisions.append(revision)
92 suspected_cls.append(cl_info)
93 else:
94 for test_cl_info in results['tests'].values():
95 revision = test_cl_info.get('revision')
96 if revision not in suspected_cl_revisions:
97 suspected_cl_revisions.append(revision)
98 suspected_cls.append(test_cl_info)
99 113
100 return suspected_cls 114 return suspected_cls
101 115
102 116
103 def _GetFailedRevisionFromResultsDict(results_dict): 117 def _GetFailedRevisionFromResultsDict(results_dict):
104 """Finds the failed revision from the given dict of revisions. 118 """Finds the failed revision from the given dict of revisions.
105 119
106 Args: 120 Args:
107 results_dict: (dict) A dict that maps revisions to their results. For 121 results_dict: (dict) A dict that maps revisions to their results. For
108 example: 122 example:
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 if (not test_result['valid'] or 175 if (not test_result['valid'] or
162 test_result['status'] != 'failed'): # pragma: no cover 176 test_result['status'] != 'failed'): # pragma: no cover
163 continue 177 continue
164 178
165 failed_revisions.add(revision) 179 failed_revisions.add(revision)
166 180
167 if step not in culprit_map: 181 if step not in culprit_map:
168 culprit_map[step] = { 182 culprit_map[step] = {
169 'tests': {} 183 'tests': {}
170 } 184 }
171 if (not test_result['failures'] and
172 not culprit_map[step].get('revision')):
173 # Non swarming test failures, only have step level failure info.
174 culprit_map[step]['revision'] = revision
175 for failed_test in test_result['failures']: 185 for failed_test in test_result['failures']:
176 # Swarming tests, gets first failed revision for each test. 186 # Swarming tests, gets first failed revision for each test.
177 if failed_test not in culprit_map[step]['tests']: 187 if failed_test not in culprit_map[step]['tests']:
178 culprit_map[step]['tests'][failed_test] = { 188 culprit_map[step]['tests'][failed_test] = {
179 'revision': revision 189 'revision': revision
180 } 190 }
181 191
182 return culprit_map, list(failed_revisions) 192 return culprit_map, list(failed_revisions)
183 193
184 194
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 } 251 }
242 failed_revisions.add(revision) 252 failed_revisions.add(revision)
243 return culprit_map, list(failed_revisions) 253 return culprit_map, list(failed_revisions)
244 254
245 return _GetCulpritsForTestsFromResultsDict( 255 return _GetCulpritsForTestsFromResultsDict(
246 blame_list, result['report'].get('result')) 256 blame_list, result['report'].get('result'))
247 257
248 def _UpdateCulpritMapWithCulpritInfo(self, culprit_map, culprits): 258 def _UpdateCulpritMapWithCulpritInfo(self, culprit_map, culprits):
249 """Fills in commit_position and review url for each failed rev in map.""" 259 """Fills in commit_position and review url for each failed rev in map."""
250 for step_culprit in culprit_map.values(): 260 for step_culprit in culprit_map.values():
251 if step_culprit.get('revision'):
252 culprit = culprits[step_culprit['revision']]
253 step_culprit['commit_position'] = culprit['commit_position']
254 step_culprit['url'] = culprit['url']
255 step_culprit['repo_name'] = culprit['repo_name']
256 for test_culprit in step_culprit.get('tests', {}).values(): 261 for test_culprit in step_culprit.get('tests', {}).values():
257 test_revision = test_culprit['revision'] 262 test_revision = test_culprit['revision']
258 test_culprit.update(culprits[test_revision]) 263 test_culprit.update(culprits[test_revision])
259 264
260 def _GetCulpritDataForTest(self, culprit_map): 265 def _GetCulpritDataForTest(self, culprit_map):
261 """Gets culprit revision for each failure for try job metadata.""" 266 """Gets culprit revision for each failure for try job metadata."""
262 culprit_data = {} 267 culprit_data = {}
263 for step, step_culprit in culprit_map.iteritems(): 268 for step, step_culprit in culprit_map.iteritems():
264 if step_culprit['tests']: 269 culprit_data[step] = {}
265 culprit_data[step] = {} 270 for test, test_culprit in step_culprit['tests'].iteritems():
266 for test, test_culprit in step_culprit['tests'].iteritems(): 271 culprit_data[step][test] = test_culprit['revision']
267 culprit_data[step][test] = test_culprit['revision']
268 else:
269 culprit_data[step] = step_culprit['revision']
270 return culprit_data 272 return culprit_data
271 273
272 # Arguments number differs from overridden method - pylint: disable=W0221 274 # Arguments number differs from overridden method - pylint: disable=W0221
273 def run( 275 def run(
274 self, master_name, builder_name, build_number, blame_list, try_job_type, 276 self, master_name, builder_name, build_number, blame_list, try_job_type,
275 try_job_id, result): 277 try_job_id, result):
276 """Identifies the information for failed revisions. 278 """Identifies the information for failed revisions.
277 279
278 Please refer to try_job_result_format.md for format check. 280 Please refer to try_job_result_format.md for format check.
279 """ 281 """
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 try_job_result.test_results) 313 try_job_result.test_results)
312 if (result_to_update and 314 if (result_to_update and
313 result_to_update[-1]['try_job_id'] == try_job_id): 315 result_to_update[-1]['try_job_id'] == try_job_id):
314 result_to_update[-1].update(result) 316 result_to_update[-1].update(result)
315 else: # pragma: no cover 317 else: # pragma: no cover
316 result_to_update.append(result) 318 result_to_update.append(result)
317 try_job_result.status = analysis_status.COMPLETED 319 try_job_result.status = analysis_status.COMPLETED
318 try_job_result.put() 320 try_job_result.put()
319 321
320 @ndb.transactional 322 @ndb.transactional
321 def UpdateWfAnalysisWithTryJobResult(): 323 def UpdateWfAnalysisWithTryJobResult(try_job_suspected_cls):
322 if not culprits: 324 if not culprits:
323 return 325 return
324
325 # Update analysis result and suspected CLs with results of this try job if 326 # Update analysis result and suspected CLs with results of this try job if
326 # culprits were found. 327 # culprits were found.
327 analysis = WfAnalysis.Get(master_name, builder_name, build_number) 328 analysis = WfAnalysis.Get(master_name, builder_name, build_number)
328 updated_result_status = _GetResultAnalysisStatus(analysis, result) 329 updated_result_status = _GetResultAnalysisStatus(analysis, result)
329 updated_suspected_cls = _GetSuspectedCLs(analysis, result) 330 updated_suspected_cls = _GetSuspectedCLs(analysis, try_job_suspected_cls)
330
331 if (analysis.result_status != updated_result_status or 331 if (analysis.result_status != updated_result_status or
332 analysis.suspected_cls != updated_suspected_cls): 332 analysis.suspected_cls != updated_suspected_cls):
333 analysis.result_status = updated_result_status 333 analysis.result_status = updated_result_status
334 analysis.suspected_cls = updated_suspected_cls 334 analysis.suspected_cls = updated_suspected_cls
335 analysis.put() 335 analysis.put()
336 336
337 def UpdateSuspectedCLs(try_job_suspected_cls):
338 if not culprits:
339 return
340
341 # Creates or updates each suspected_cl.
342 for culprit in try_job_suspected_cls:
343 suspected_cl_util.UpdateSuspectedCL(
344 analysis_approach_type.TRY_JOB, master_name, builder_name,
345 build_number, try_job_type,
346 culprit['repo_name'], culprit['revision'],
347 culprit.get('commit_position'))
348
349 try_job_suspected_cls = _GetTryJobSuspectedCLs(result)
337 # Store try-job results. 350 # Store try-job results.
338 UpdateTryJobResult() 351 UpdateTryJobResult()
339 # Add try-job results to WfAnalysis. 352 # Add try-job results to WfAnalysis.
340 UpdateWfAnalysisWithTryJobResult() 353 UpdateWfAnalysisWithTryJobResult(try_job_suspected_cls)
354 # Updates suspected_cl
lijeffrey 2016/08/17 19:31:11 nit: comment ends with .
chanli 2016/09/12 20:56:15 Done.
355 UpdateSuspectedCLs(try_job_suspected_cls)
341 356
342 _NotifyCulprits(master_name, builder_name, build_number, culprits) 357 _NotifyCulprits(master_name, builder_name, build_number, culprits)
343 return result.get('culprit') if result else None 358 return result.get('culprit') if result else None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698