| OLD | NEW |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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 import copy | 5 import copy |
| 6 import logging | 6 import logging |
| 7 import math | 7 import math |
| 8 import pprint | 8 import pprint |
| 9 | 9 |
| 10 from common.dependency import Dependency | 10 from common.dependency import Dependency |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 'code_review_url': 'https://codereview.chromium.org/3281', | 113 'code_review_url': 'https://codereview.chromium.org/3281', |
| 114 'revision': '3', | 114 'revision': '3', |
| 115 'reverted_revision': None | 115 'reverted_revision': None |
| 116 }) | 116 }) |
| 117 | 117 |
| 118 DUMMY_CALLSTACKS = [ | 118 DUMMY_CALLSTACKS = [ |
| 119 CallStack(0, [], CallStackFormatType.DEFAULT, LanguageType.CPP), | 119 CallStack(0, [], CallStackFormatType.DEFAULT, LanguageType.CPP), |
| 120 CallStack(1, [], CallStackFormatType.DEFAULT, LanguageType.CPP)] | 120 CallStack(1, [], CallStackFormatType.DEFAULT, LanguageType.CPP)] |
| 121 DUMMY_REPORT = CrashReport( | 121 DUMMY_REPORT = CrashReport( |
| 122 None, None, None, Stacktrace(DUMMY_CALLSTACKS, DUMMY_CALLSTACKS[0]), | 122 None, None, None, Stacktrace(DUMMY_CALLSTACKS, DUMMY_CALLSTACKS[0]), |
| 123 (None, None)) | 123 (None, None), None, None) |
| 124 | 124 |
| 125 | 125 |
| 126 class LogLinearChangelistClassifierTest(CrashTestSuite): | 126 class LogLinearChangelistClassifierTest(CrashTestSuite): |
| 127 | 127 |
| 128 def setUp(self): | 128 def setUp(self): |
| 129 super(LogLinearChangelistClassifierTest, self).setUp() | 129 super(LogLinearChangelistClassifierTest, self).setUp() |
| 130 meta_weight = MetaWeight({ | 130 meta_weight = MetaWeight({ |
| 131 'TouchCrashedFileMeta': MetaWeight({ | 131 'TouchCrashedFileMeta': MetaWeight({ |
| 132 'MinDistance': Weight(1.), | 132 'MinDistance': Weight(1.), |
| 133 'TopFrameIndex': Weight(1.), | 133 'TopFrameIndex': Weight(1.), |
| 134 'TouchCrashedFile': Weight(1.), | 134 'TouchCrashedFile': Weight(1.), |
| 135 }) | 135 }) |
| 136 }) | 136 }) |
| 137 get_repository = GitilesRepository.Factory(self.GetMockHttpClient()) | 137 get_repository = GitilesRepository.Factory(self.GetMockHttpClient()) |
| 138 meta_feature = WrapperMetaFeature( | 138 meta_feature = WrapperMetaFeature( |
| 139 [TouchCrashedFileMetaFeature(get_repository)]) | 139 [TouchCrashedFileMetaFeature(get_repository)]) |
| 140 | 140 |
| 141 self.changelist_classifier = LogLinearChangelistClassifier( | 141 self.changelist_classifier = LogLinearChangelistClassifier( |
| 142 get_repository, meta_feature, meta_weight) | 142 get_repository, meta_feature, meta_weight) |
| 143 | 143 |
| 144 # TODO(http://crbug.com/659346): why do these mocks give coverage | 144 # TODO(http://crbug.com/659346): why do these mocks give coverage |
| 145 # failures? That's almost surely hiding a bug in the tests themselves. | 145 # failures? That's almost surely hiding a bug in the tests themselves. |
| 146 def testFindItForCrashNoRegressionRange(self): # pragma: no cover | 146 def testFindItForCrashNoRegressionRange(self): # pragma: no cover |
| 147 self.mock(ChromeDependencyFetcher, 'GetDependencyRollsDict', lambda *_: {}) | |
| 148 self.mock(ChromeDependencyFetcher, 'GetDependency', lambda *_: {}) | |
| 149 # N.B., for this one test we really do want regression_range=None. | 147 # N.B., for this one test we really do want regression_range=None. |
| 150 report = CrashReport(None, None, None, Stacktrace(DUMMY_CALLSTACKS, | 148 report = CrashReport(None, None, None, Stacktrace(DUMMY_CALLSTACKS, |
| 151 DUMMY_CALLSTACKS[0]), | 149 DUMMY_CALLSTACKS[0]), |
| 152 None) | 150 None, {}, {}) |
| 153 self.assertListEqual(self.changelist_classifier(report), []) | 151 self.assertListEqual(self.changelist_classifier(report), []) |
| 154 | 152 |
| 155 def testFindItForCrashNoMatchFound(self): | 153 def testFindItForCrashNoMatchFound(self): |
| 156 self.mock(scorer_changelist_classifier, 'FindSuspects', lambda *_: []) | 154 self.mock(scorer_changelist_classifier, 'FindSuspects', lambda *_: []) |
| 157 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) | 155 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| 158 | 156 |
| 159 self.mock(scorer_changelist_classifier, 'FindSuspects', lambda *_: None) | 157 self.mock(scorer_changelist_classifier, 'FindSuspects', lambda *_: None) |
| 160 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) | 158 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| 161 | 159 |
| 162 def testFindItForCrash(self): | 160 def testFindItForCrash(self): |
| (...skipping 22 matching lines...) Expand all Loading... |
| 185 'GetChangeLogsForFilesGroupedByDeps', | 183 'GetChangeLogsForFilesGroupedByDeps', |
| 186 lambda *_: (None, None)) | 184 lambda *_: (None, None)) |
| 187 self.mock(scorer_changelist_classifier, 'FindSuspects', | 185 self.mock(scorer_changelist_classifier, 'FindSuspects', |
| 188 lambda *_: [suspect1, suspect2]) | 186 lambda *_: [suspect1, suspect2]) |
| 189 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 187 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 190 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 188 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 191 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | 189 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) |
| 192 frame4 = StackFrame(3, 'src/dep1', 'func', 'f.cc', 'src/dep1/f.cc', [1]) | 190 frame4 = StackFrame(3, 'src/dep1', 'func', 'f.cc', 'src/dep1/f.cc', [1]) |
| 193 stacks = [CallStack(0, frame_list=[frame1, frame2, frame3, frame4])] | 191 stacks = [CallStack(0, frame_list=[frame1, frame2, frame3, frame4])] |
| 194 stacktrace = Stacktrace(stacks, stacks[0]) | 192 stacktrace = Stacktrace(stacks, stacks[0]) |
| 195 report = CrashReport('6', 'sig', 'win', stacktrace, ('0', '4')) | 193 report = CrashReport( |
| 196 self.mock(ChromeDependencyFetcher, 'GetDependency', | 194 '6', 'sig', 'win', stacktrace, ('0', '4'), |
| 197 lambda *_: {'src/': Dependency('src/', 'https://repo', '6')}) | 195 {'src/': Dependency('src/', 'https://repo', '6')}, |
| 198 self.mock(ChromeDependencyFetcher, 'GetDependencyRollsDict', | 196 {'src/': DependencyRoll('src/', 'https://repo', '0', '4')} ) |
| 199 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', | |
| 200 '0', '4')}) | |
| 201 | 197 |
| 202 suspects = self.changelist_classifier(report) | 198 suspects = self.changelist_classifier(report) |
| 203 self.assertTrue(suspects, | 199 self.assertTrue(suspects, |
| 204 'Expected suspects, but the classifier didn\'t return any') | |
| 205 | |
| 206 expected_suspects = [ | |
| 207 { | |
| 208 'author': 'r@chromium.org', | |
| 209 'changed_files': [ | |
| 210 { | |
| 211 'blame_url': None, | |
| 212 'file': 'a.cc', | |
| 213 'info': ('Distance from touched lines and crashed lines is ' | |
| 214 '0, in frame #0') | |
| 215 } | |
| 216 ], | |
| 217 'confidence': 0., | |
| 218 'project_path': 'src/', | |
| 219 'reasons': ('MinDistance: 0.000000 -- Minimum distance is ' | |
| 220 '0\nTopFrameIndex: 0.000000 -- Top frame is #0\n' | |
| 221 'TouchCrashedFile: 0.000000 -- Touched files - a.cc'), | |
| 222 'review_url': 'https://codereview.chromium.org/3281', | |
| 223 'revision': '1', | |
| 224 'time': 'Thu Mar 31 21:24:43 2016', | |
| 225 'url': 'https://repo.test/+/1' | |
| 226 }, | |
| 227 ] | |
| 228 self.assertListEqual([suspect.ToDict() for suspect in suspects], | |
| 229 expected_suspects) | |
| 230 | |
| 231 def testFinditForCrashFilterZeroConfidenceSuspects(self): | |
| 232 suspect1 = Suspect(DUMMY_CHANGELOG1, 'src/') | |
| 233 suspect2 = Suspect(DUMMY_CHANGELOG3, 'src/') | |
| 234 | |
| 235 a_cc_blame = Blame('6', 'src/') | |
| 236 a_cc_blame.AddRegions([Region(0, 10, suspect1.changelog.revision, | |
| 237 suspect1.changelog.author.name, | |
| 238 suspect1.changelog.author.email, | |
| 239 suspect1.changelog.author.time)]) | |
| 240 f_cc_blame = Blame('6', 'src/') | |
| 241 f_cc_blame.AddRegions([Region(21, 10, suspect2.changelog.revision, | |
| 242 suspect2.changelog.author.name, | |
| 243 suspect2.changelog.author.email, | |
| 244 suspect2.changelog.author.time)]) | |
| 245 url_to_blame = {'6/a.cc': a_cc_blame, | |
| 246 '6/f.cc': f_cc_blame} | |
| 247 | |
| 248 def _MockGetBlame(_, path, revision): | |
| 249 revision_path = '%s/%s' % (revision, path) | |
| 250 return url_to_blame.get(revision_path) | |
| 251 | |
| 252 self.mock(GitilesRepository, 'GetBlame', _MockGetBlame) | |
| 253 self.mock(scorer_changelist_classifier, | |
| 254 'GetChangeLogsForFilesGroupedByDeps', | |
| 255 lambda *_: (None, None)) | |
| 256 self.mock(scorer_changelist_classifier, 'FindSuspects', | |
| 257 lambda *_: [suspect1, suspect2]) | |
| 258 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | |
| 259 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | |
| 260 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | |
| 261 stacks = [CallStack(0, frame_list=[frame1, frame2, frame3])] | |
| 262 stacktrace = Stacktrace(stacks, stacks[0]) | |
| 263 report = CrashReport('6', 'sig', 'win', stacktrace, ('0', '4')) | |
| 264 self.mock(ChromeDependencyFetcher, 'GetDependency', | |
| 265 lambda *_: {'src/': Dependency('src/', 'https://repo', '6')}) | |
| 266 self.mock(ChromeDependencyFetcher, 'GetDependencyRollsDict', | |
| 267 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', | |
| 268 '0', '4')}) | |
| 269 | |
| 270 suspects = self.changelist_classifier(report) | |
| 271 self.assertTrue(suspects, | |
| 272 'Expected suspects, but the classifier didn\'t return any') | 200 'Expected suspects, but the classifier didn\'t return any') |
| 273 | 201 |
| 274 expected_suspects = [ | 202 expected_suspects = [ |
| 275 { | 203 { |
| 276 'author': 'r@chromium.org', | 204 'author': 'r@chromium.org', |
| 277 'changed_files': [ | 205 'changed_files': [ |
| 278 { | 206 { |
| 279 'blame_url': None, | 207 'blame_url': None, |
| 280 'file': 'a.cc', | 208 'file': 'a.cc', |
| 281 'info': ('Distance from touched lines and crashed lines is ' | 209 'info': ('Distance from touched lines and crashed lines is ' |
| 282 '0, in frame #0') | 210 '0, in frame #0') |
| 283 } | 211 } |
| 284 ], | 212 ], |
| 285 'confidence': 0., | 213 'confidence': 0., |
| 286 'project_path': 'src/', | 214 'project_path': 'src/', |
| 287 'reasons': ('MinDistance: 0.000000 -- Minimum distance is ' | 215 'reasons': ('MinDistance: 0.000000 -- Minimum distance is ' |
| 288 '0\nTopFrameIndex: 0.000000 -- Top frame is #0\n' | 216 '0\nTopFrameIndex: 0.000000 -- Top frame is #0\n' |
| 289 'TouchCrashedFile: 0.000000 -- Touched files - a.cc'), | 217 'TouchCrashedFile: 0.000000 -- Touched files - a.cc'), |
| 290 'review_url': 'https://codereview.chromium.org/3281', | 218 'review_url': 'https://codereview.chromium.org/3281', |
| 291 'revision': '1', | 219 'revision': '1', |
| 292 'time': 'Thu Mar 31 21:24:43 2016', | 220 'time': 'Thu Mar 31 21:24:43 2016', |
| 293 'url': 'https://repo.test/+/1' | 221 'url': 'https://repo.test/+/1' |
| 294 }, | 222 }, |
| 295 ] | 223 ] |
| 296 self.assertListEqual([suspect.ToDict() for suspect in suspects], | 224 self.assertListEqual([suspect.ToDict() for suspect in suspects], |
| 297 expected_suspects) | 225 expected_suspects) |
| OLD | NEW |