| OLD | NEW |
| (Empty) | |
| 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 |
| 3 # found in the LICENSE file. |
| 4 |
| 5 from collections import namedtuple |
| 6 |
| 7 # TODO(http://crbug.com/659346): We do call this code from various |
| 8 # unittests, just not from culprit_test.py; so we need to add some extra |
| 9 # unittests there. |
| 10 class Culprit(namedtuple('Culprit', |
| 11 ['project', 'components', 'cls', 'regression_range', 'algorithm'] |
| 12 )): # pragma: no cover |
| 13 """The result of successfully identifying the culprit of a crash report. |
| 14 |
| 15 Args: |
| 16 project (str): the most-suspected project |
| 17 components (list of str): the suspected crbug components. |
| 18 cls (list of ??): the suspected CLs. |
| 19 regression_range (tuple): a pair of the last-good and first-bad versions. |
| 20 algorithm (str): What algorithm was used to produce this object. |
| 21 """ |
| 22 __slots__ = () |
| 23 |
| 24 @property |
| 25 def fields(self): |
| 26 return self._fields |
| 27 |
| 28 # TODO(http://crbug/644476): better name for this method. |
| 29 def ToDicts(self): |
| 30 """Convert this object to a pair of anonymous dicts for JSON. |
| 31 |
| 32 Returns: |
| 33 (analysis_result_dict, tag_dict) |
| 34 The analysis result is a dict like below: |
| 35 { |
| 36 # Indicate if Findit found any suspects_cls, project, |
| 37 # components or regression_range. |
| 38 "found": true, |
| 39 "suspected_project": "chromium-v8", # Which project is most suspected. |
| 40 "feedback_url": "https://.." |
| 41 "suspected_cls": [ |
| 42 { |
| 43 "revision": "commit-hash", |
| 44 "url": "https://chromium.googlesource.com/chromium/src/+/...", |
| 45 "review_url": "https://codereview.chromium.org/issue-number", |
| 46 "project_path": "third_party/pdfium", |
| 47 "author": "who@chromium.org", |
| 48 "time": "2015-08-17 03:38:16", |
| 49 "reason": "a plain string with '\n' as line break to expla..." |
| 50 "reason": [('MinDistance', 1, 'minimum distance is 0.'), |
| 51 ('TopFrame', 0.9, 'top frame is2nd frame.')], |
| 52 "changed_files": [ |
| 53 {"file": "file_name1.cc", |
| 54 "blame_url": "https://...", |
| 55 "info": "minimum distance (LOC) 0, frame #2"}, |
| 56 {"file": "file_name2.cc", |
| 57 "blame_url": "https://...", |
| 58 "info": "minimum distance (LOC) 20, frame #4"}, |
| 59 ... |
| 60 ], |
| 61 "confidence": 0.60 |
| 62 }, |
| 63 ..., |
| 64 ], |
| 65 "regression_range": [ # Detected regression range. |
| 66 "53.0.2765.0", |
| 67 "53.0.2766.0" |
| 68 ], |
| 69 "suspected_components": [ # A list of crbug components to file bugs. |
| 70 "Blink>JavaScript" |
| 71 ] |
| 72 } |
| 73 |
| 74 The code review url might not always be available, because not all |
| 75 commits go through code review. In that case, commit url should |
| 76 be used instead. |
| 77 |
| 78 The tag dict are allowed key/value pairs to tag the analysis result |
| 79 for query and monitoring purpose on Findit side. For allowed keys, |
| 80 please refer to crash_analysis.py and fracas_crash_analysis.py: |
| 81 For results with normal culprit-finding algorithm: { |
| 82 'found_suspects': True, |
| 83 'has_regression_range': True, |
| 84 'solution': 'core_algorithm', |
| 85 } |
| 86 For results using git blame without a regression range: { |
| 87 'found_suspects': True, |
| 88 'has_regression_range': False, |
| 89 'solution': 'blame', |
| 90 } |
| 91 If nothing is found: { |
| 92 'found_suspects': False, |
| 93 } |
| 94 """ |
| 95 # TODO(http://crbug.com/644411): reformulate the JSON stuff so we |
| 96 # can drop fields which are empty; so that, in turn, we can get rid |
| 97 # of the NullCulprit class. |
| 98 return ( |
| 99 { |
| 100 'found': (bool(self.project) or |
| 101 bool(self.components) or |
| 102 bool(self.cls) or |
| 103 bool(self.regression_range)), |
| 104 'regression_range': self.regression_range, |
| 105 'suspected_project': self.project, |
| 106 'suspected_components': self.components, |
| 107 'suspected_cls': [cl.ToDict() for cl in self.cls], |
| 108 }, |
| 109 { |
| 110 'found_suspects': bool(self.cls), |
| 111 'found_project': bool(self.project), |
| 112 'found_components': bool(self.components), |
| 113 'has_regression_range': bool(self.regression_range), |
| 114 'solution': self.algorithm, |
| 115 } |
| 116 ) |
| 117 |
| 118 |
| 119 # TODO(http://crbug.com/659346): We do call this code from various |
| 120 # unittests, just not from culprit_test.py; so we need to add some extra |
| 121 # unittests there. |
| 122 # TODO(http://crbug.com/659359): eliminate the need for this class. |
| 123 class NullCulprit(object): # pragma: no cover |
| 124 """The result of failing to identify the culprit of a crash report. |
| 125 |
| 126 This class serves as a helper so that we can avoid returning None. It |
| 127 has all the same properties and methods as the Culprit class, but |
| 128 returns the empty string, the empty list, or None, as appropriate. The |
| 129 main difference compared to using Culprit with all those falsy values |
| 130 is that the result of the ToDicts method is more minimalistic. |
| 131 |
| 132 Ideally we'd like to be able to refactor things to avoid the need |
| 133 for this class. Mostly that means (1) refactoring the unittests to |
| 134 allow |Findit.FindCulprit| to return None, and (2) reformulating |
| 135 |Culprit.ToDicts| to create minimal dicts and reformulating the JSON |
| 136 protocol to support that. |
| 137 """ |
| 138 __slots__ = () |
| 139 |
| 140 @property |
| 141 def fields(self): |
| 142 raise NotImplementedError() |
| 143 |
| 144 @property |
| 145 def project(self): |
| 146 return '' |
| 147 |
| 148 @property |
| 149 def components(self): |
| 150 return [] |
| 151 |
| 152 @property |
| 153 def cls(self): |
| 154 return [] |
| 155 |
| 156 @property |
| 157 def regression_range(self): |
| 158 # TODO(http://crbug.com/659359): or maybe return |(None,None)| instead... |
| 159 return None |
| 160 |
| 161 @property |
| 162 def algorithm(self): |
| 163 # TODO(http://crbug.com/659359): or maybe return the empty string instead... |
| 164 return None |
| 165 |
| 166 def ToDicts(self): |
| 167 return ( |
| 168 {'found': False}, |
| 169 {'found_suspects': False, |
| 170 'has_regression_range': False} |
| 171 ) |
| OLD | NEW |