| 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 from collections import defaultdict | 5 from collections import defaultdict |
| 6 import copy | 6 import copy |
| 7 | 7 |
| 8 from common.dependency import Dependency | 8 from common.dependency import Dependency |
| 9 from common.dependency import DependencyRoll | 9 from common.dependency import DependencyRoll |
| 10 from common import chrome_dependency_fetcher | 10 from common import chrome_dependency_fetcher |
| 11 from crash import changelist_classifier | 11 from crash import changelist_classifier |
| 12 from crash.crash_report import CrashReport | 12 from crash.crash_report import CrashReport |
| 13 from crash.results import AnalysisInfo | 13 from crash.suspect import AnalysisInfo |
| 14 from crash.results import StackInfo | 14 from crash.suspect import StackInfo |
| 15 from crash.results import MatchResult | 15 from crash.suspect import Suspect |
| 16 from crash.stacktrace import CallStack | 16 from crash.stacktrace import CallStack |
| 17 from crash.stacktrace import StackFrame | 17 from crash.stacktrace import StackFrame |
| 18 from crash.stacktrace import Stacktrace | 18 from crash.stacktrace import Stacktrace |
| 19 from crash.test.crash_test_suite import CrashTestSuite | 19 from crash.test.crash_test_suite import CrashTestSuite |
| 20 from libs.gitiles.blame import Blame | 20 from libs.gitiles.blame import Blame |
| 21 from libs.gitiles.blame import Region | 21 from libs.gitiles.blame import Region |
| 22 from libs.gitiles.change_log import ChangeLog | 22 from libs.gitiles.change_log import ChangeLog |
| 23 from libs.gitiles.gitiles_repository import GitilesRepository | 23 from libs.gitiles.gitiles_repository import GitilesRepository |
| 24 | 24 |
| 25 DUMMY_CHANGELOG1 = ChangeLog.FromDict({ | 25 DUMMY_CHANGELOG1 = ChangeLog.FromDict({ |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 'code_review_url': 'https://codereview.chromium.org/3281', | 93 'code_review_url': 'https://codereview.chromium.org/3281', |
| 94 'committer_name': 'example@chromium.org', | 94 'committer_name': 'example@chromium.org', |
| 95 'revision': '3', | 95 'revision': '3', |
| 96 'reverted_revision': None | 96 'reverted_revision': None |
| 97 }) | 97 }) |
| 98 | 98 |
| 99 # TODO(wrengr): re crrev.com/2414523002: we need to have a specified | 99 # TODO(wrengr): re crrev.com/2414523002: we need to have a specified |
| 100 # revision_range (even if the versions therein are None), because | 100 # revision_range (even if the versions therein are None), because |
| 101 # ChangelistClassifier.__call__ will take it apart in order to call | 101 # ChangelistClassifier.__call__ will take it apart in order to call |
| 102 # GetDEPSRollsDict; if it can't then it will immediately return the | 102 # GetDEPSRollsDict; if it can't then it will immediately return the |
| 103 # empty list of results, breaking many of the tests here. Of course, | 103 # empty list of suspects, breaking many of the tests here. Of course, |
| 104 # taking revision_range apart isn't actually required for the tests, | 104 # taking revision_range apart isn't actually required for the tests, |
| 105 # since we mock GetDEPSRollsDict. So, really what we ought to do in the | 105 # since we mock GetDEPSRollsDict. So, really what we ought to do in the |
| 106 # long run is redesign things so that GetDEPSRollsDict takes the | 106 # long run is redesign things so that GetDEPSRollsDict takes the |
| 107 # CrashReport directly and pulls out the revision_range and platform | 107 # CrashReport directly and pulls out the revision_range and platform |
| 108 # itself; that way ChangelistClassifier.__call__ needn't worry about it, | 108 # itself; that way ChangelistClassifier.__call__ needn't worry about it, |
| 109 # allowing us to clean up the tests here. | 109 # allowing us to clean up the tests here. |
| 110 # TODO(wrengr): this empty stacktrace causes the unittests to emit | 110 # TODO(wrengr): this empty stacktrace causes the unittests to emit |
| 111 # logging warnings about the fact that we can't get the crash_stack. Would | 111 # logging warnings about the fact that we can't get the crash_stack. Would |
| 112 # be nice to avoid that. | 112 # be nice to avoid that. |
| 113 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) | 113 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 132 | 132 |
| 133 passed_in_regression_deps_rolls = [] | 133 passed_in_regression_deps_rolls = [] |
| 134 def _MockGetChangeLogsForFilesGroupedByDeps(regression_deps_rolls, *_): | 134 def _MockGetChangeLogsForFilesGroupedByDeps(regression_deps_rolls, *_): |
| 135 passed_in_regression_deps_rolls.append(regression_deps_rolls) | 135 passed_in_regression_deps_rolls.append(regression_deps_rolls) |
| 136 return {}, None | 136 return {}, None |
| 137 | 137 |
| 138 self.mock(changelist_classifier, 'GetChangeLogsForFilesGroupedByDeps', | 138 self.mock(changelist_classifier, 'GetChangeLogsForFilesGroupedByDeps', |
| 139 _MockGetChangeLogsForFilesGroupedByDeps) | 139 _MockGetChangeLogsForFilesGroupedByDeps) |
| 140 self.mock(changelist_classifier, 'GetStackInfosForFilesGroupedByDeps', | 140 self.mock(changelist_classifier, 'GetStackInfosForFilesGroupedByDeps', |
| 141 lambda *_: {}) | 141 lambda *_: {}) |
| 142 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: None) | 142 self.mock(changelist_classifier, 'FindSuspects', lambda *_: None) |
| 143 | 143 |
| 144 self.changelist_classifier(CrashReport(crashed_version = '5', | 144 self.changelist_classifier(CrashReport(crashed_version = '5', |
| 145 signature = 'sig', | 145 signature = 'sig', |
| 146 platform = 'canary', | 146 platform = 'canary', |
| 147 stacktrace = Stacktrace([CallStack(0)]), | 147 stacktrace = Stacktrace([CallStack(0)]), |
| 148 regression_range = ['4', '5'])) | 148 regression_range = ['4', '5'])) |
| 149 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) | 149 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) |
| 150 | 150 |
| 151 # Regression of a dep added/deleted (old_revision/new_revision is None) can | 151 # Regression of a dep added/deleted (old_revision/new_revision is None) can |
| 152 # not be known for sure and this case rarely happens, so just filter them | 152 # not be known for sure and this case rarely happens, so just filter them |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 for dep, file_to_stack_infos in dep_file_to_stack_infos.iteritems(): | 249 for dep, file_to_stack_infos in dep_file_to_stack_infos.iteritems(): |
| 250 self.assertTrue(dep in expected_dep_file_to_stack_infos) | 250 self.assertTrue(dep in expected_dep_file_to_stack_infos) |
| 251 expected_file_to_stack_infos = expected_dep_file_to_stack_infos[dep] | 251 expected_file_to_stack_infos = expected_dep_file_to_stack_infos[dep] |
| 252 | 252 |
| 253 for file_path, stack_infos in file_to_stack_infos.iteritems(): | 253 for file_path, stack_infos in file_to_stack_infos.iteritems(): |
| 254 self.assertTrue(file_path in expected_file_to_stack_infos) | 254 self.assertTrue(file_path in expected_file_to_stack_infos) |
| 255 expected_stack_infos = expected_file_to_stack_infos[file_path] | 255 expected_stack_infos = expected_file_to_stack_infos[file_path] |
| 256 | 256 |
| 257 self._VerifyTwoStackInfosEqual(stack_infos, expected_stack_infos) | 257 self._VerifyTwoStackInfosEqual(stack_infos, expected_stack_infos) |
| 258 | 258 |
| 259 def testFindMatchResults(self): | 259 def testFindSuspects(self): |
| 260 dep_file_to_changelogs = { | 260 dep_file_to_changelogs = { |
| 261 'src/': { | 261 'src/': { |
| 262 'a.cc': [ | 262 'a.cc': [ |
| 263 DUMMY_CHANGELOG1, | 263 DUMMY_CHANGELOG1, |
| 264 ] | 264 ] |
| 265 } | 265 } |
| 266 } | 266 } |
| 267 | 267 |
| 268 dep_file_to_stack_infos = { | 268 dep_file_to_stack_infos = { |
| 269 'src/': { | 269 'src/': { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 285 Region(1, 5, '6', 'a', 'a@chromium.org', 'Thu Mar 31 21:24:43 2016')) | 285 Region(1, 5, '6', 'a', 'a@chromium.org', 'Thu Mar 31 21:24:43 2016')) |
| 286 dummy_blame.AddRegion( | 286 dummy_blame.AddRegion( |
| 287 Region(6, 10, '1', 'b', 'b@chromium.org', 'Thu Jun 19 12:11:40 2015')) | 287 Region(6, 10, '1', 'b', 'b@chromium.org', 'Thu Jun 19 12:11:40 2015')) |
| 288 | 288 |
| 289 self.mock(GitilesRepository, 'GetBlame', lambda *_: dummy_blame) | 289 self.mock(GitilesRepository, 'GetBlame', lambda *_: dummy_blame) |
| 290 | 290 |
| 291 stack_deps = { | 291 stack_deps = { |
| 292 'src/': Dependency('src/', 'https://url_src', 'rev1', 'DEPS'), | 292 'src/': Dependency('src/', 'https://url_src', 'rev1', 'DEPS'), |
| 293 } | 293 } |
| 294 | 294 |
| 295 expected_match_results = [{ | 295 expected_suspects = [{ |
| 296 'url': 'https://repo.test/+/1', | 296 'url': 'https://repo.test/+/1', |
| 297 'review_url': 'https://codereview.chromium.org/3281', | 297 'review_url': 'https://codereview.chromium.org/3281', |
| 298 'revision': '1', | 298 'revision': '1', |
| 299 'project_path': 'src/', | 299 'project_path': 'src/', |
| 300 'author': 'r@chromium.org', | 300 'author': 'r@chromium.org', |
| 301 'time': 'Thu Mar 31 21:24:43 2016', | 301 'time': 'Thu Mar 31 21:24:43 2016', |
| 302 'reasons': None, | 302 'reasons': None, |
| 303 'confidence': None, | 303 'confidence': None, |
| 304 'changed_files': None | 304 'changed_files': None |
| 305 }] | 305 }] |
| 306 | 306 |
| 307 match_results = changelist_classifier.FindMatchResults( | 307 suspects = changelist_classifier.FindSuspects( |
| 308 dep_file_to_changelogs, dep_file_to_stack_infos, stack_deps, | 308 dep_file_to_changelogs, dep_file_to_stack_infos, stack_deps, |
| 309 GitilesRepository(self.GetMockHttpClient())) | 309 GitilesRepository(self.GetMockHttpClient())) |
| 310 self.assertListEqual([result.ToDict() for result in match_results], | 310 self.assertListEqual([suspect.ToDict() for suspect in suspects], |
| 311 expected_match_results) | 311 expected_suspects) |
| 312 | 312 |
| 313 # TODO(http://crbug.com/659346): why do these mocks give coverage | 313 # TODO(http://crbug.com/659346): why do these mocks give coverage |
| 314 # failures? That's almost surely hiding a bug in the tests themselves. | 314 # failures? That's almost surely hiding a bug in the tests themselves. |
| 315 def testFindItForCrashNoRegressionRange(self): # pragma: no cover | 315 def testFindItForCrashNoRegressionRange(self): # pragma: no cover |
| 316 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 316 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 317 'GetDependencyRollsDict', lambda *_: {}) | 317 'GetDependencyRollsDict', lambda *_: {}) |
| 318 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 318 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 319 'GetDependency', lambda *_: {}) | 319 'GetDependency', lambda *_: {}) |
| 320 # N.B., for this one test we really do want regression_range=None. | 320 # N.B., for this one test we really do want regression_range=None. |
| 321 report = DUMMY_REPORT._replace(regression_range=None) | 321 report = DUMMY_REPORT._replace(regression_range=None) |
| 322 self.assertListEqual(self.changelist_classifier(report), []) | 322 self.assertListEqual(self.changelist_classifier(report), []) |
| 323 | 323 |
| 324 def testFindItForCrashNoMatchFound(self): | 324 def testFindItForCrashNoMatchFound(self): |
| 325 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: []) | 325 self.mock(changelist_classifier, 'FindSuspects', lambda *_: []) |
| 326 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 326 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 327 'GetDependencyRollsDict', | 327 'GetDependencyRollsDict', |
| 328 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 328 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 329 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 329 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 330 'GetDependency', lambda *_: {}) | 330 'GetDependency', lambda *_: {}) |
| 331 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) | 331 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| 332 | 332 |
| 333 def testFindItForCrash(self): | 333 def testFindItForCrash(self): |
| 334 | 334 |
| 335 def _MockFindMatchResults(*_): | 335 def _MockFindSuspects(*_): |
| 336 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', confidence=0.) | 336 suspect1 = Suspect(DUMMY_CHANGELOG1, 'src/', confidence=0.) |
| 337 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 337 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 338 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 338 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 339 match_result1.file_to_stack_infos = { | 339 suspect1.file_to_stack_infos = { |
| 340 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] | 340 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] |
| 341 } | 341 } |
| 342 match_result1.file_to_analysis_info = { | 342 suspect1.file_to_analysis_info = { |
| 343 'a.cc': AnalysisInfo(min_distance=0, min_distance_frame=frame1) | 343 'a.cc': AnalysisInfo(min_distance=0, min_distance_frame=frame1) |
| 344 } | 344 } |
| 345 | 345 |
| 346 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', confidence=0.) | 346 suspect2 = Suspect(DUMMY_CHANGELOG3, 'src/', confidence=0.) |
| 347 frame3 = StackFrame(5, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | 347 frame3 = StackFrame(5, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) |
| 348 match_result2.file_to_stack_infos = { | 348 suspect2.file_to_stack_infos = { |
| 349 'f.cc': [StackInfo(frame3, 0)] | 349 'f.cc': [StackInfo(frame3, 0)] |
| 350 } | 350 } |
| 351 match_result2.file_to_analysis_info = { | 351 suspect2.file_to_analysis_info = { |
| 352 'a.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) | 352 'a.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 353 } | 353 } |
| 354 | 354 |
| 355 return [match_result1, match_result2] | 355 return [suspect1, suspect2] |
| 356 | 356 |
| 357 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 357 self.mock(changelist_classifier, 'FindSuspects', _MockFindSuspects) |
| 358 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 358 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 359 'GetDependencyRollsDict', | 359 'GetDependencyRollsDict', |
| 360 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 360 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 361 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 361 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 362 'GetDependency', lambda *_: {}) | 362 'GetDependency', lambda *_: {}) |
| 363 results = self.changelist_classifier(DUMMY_REPORT) | 363 suspects = self.changelist_classifier(DUMMY_REPORT) |
| 364 expected_match_results = [ | 364 expected_suspects = [ |
| 365 { | 365 { |
| 366 'reasons': [('TopFrameIndex', 1.0, 'Top frame is #0'), | 366 'reasons': [('TopFrameIndex', 1.0, 'Top frame is #0'), |
| 367 ('MinDistance', 1, 'Minimum distance is 0')], | 367 ('MinDistance', 1, 'Minimum distance is 0')], |
| 368 'changed_files': [{'info': 'Minimum distance (LOC) 0, frame #0', | 368 'changed_files': [{'info': 'Minimum distance (LOC) 0, frame #0', |
| 369 'blame_url': None, 'file': 'a.cc'}], | 369 'blame_url': None, 'file': 'a.cc'}], |
| 370 'time': 'Thu Mar 31 21:24:43 2016', | 370 'time': 'Thu Mar 31 21:24:43 2016', |
| 371 'author': 'r@chromium.org', | 371 'author': 'r@chromium.org', |
| 372 'url': 'https://repo.test/+/1', | 372 'url': 'https://repo.test/+/1', |
| 373 'project_path': 'src/', | 373 'project_path': 'src/', |
| 374 'review_url': 'https://codereview.chromium.org/3281', | 374 'review_url': 'https://codereview.chromium.org/3281', |
| 375 'confidence': 1.0, 'revision': '1' | 375 'confidence': 1.0, 'revision': '1' |
| 376 }, | 376 }, |
| 377 ] | 377 ] |
| 378 self.assertListEqual([result.ToDict() for result in results], | 378 self.assertListEqual([suspect.ToDict() for suspect in suspects], |
| 379 expected_match_results) | 379 expected_suspects) |
| 380 | 380 |
| 381 def testFinditForCrashFilterZeroConfidentResults(self): | 381 def testFinditForCrashFilterZeroConfidentResults(self): |
| 382 def _MockFindMatchResults(*_): | 382 def _MockFindSuspects(*_): |
| 383 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', confidence=0.) | 383 suspect1 = Suspect(DUMMY_CHANGELOG1, 'src/', confidence=0.) |
| 384 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 384 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 385 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 385 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 386 match_result1.file_to_stack_infos = { | 386 suspect1.file_to_stack_infos = { |
| 387 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] | 387 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] |
| 388 } | 388 } |
| 389 match_result1.file_to_analysis_info = { | 389 suspect1.file_to_analysis_info = { |
| 390 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) | 390 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) |
| 391 } | 391 } |
| 392 | 392 |
| 393 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', confidence=0.) | 393 suspect2 = Suspect(DUMMY_CHANGELOG3, 'src/', confidence=0.) |
| 394 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | 394 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) |
| 395 match_result2.file_to_stack_infos = { | 395 suspect2.file_to_stack_infos = { |
| 396 'f.cc': [StackInfo(frame3, 0)] | 396 'f.cc': [StackInfo(frame3, 0)] |
| 397 } | 397 } |
| 398 match_result2.file_to_analysis_info = { | 398 suspect2.file_to_analysis_info = { |
| 399 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) | 399 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 400 } | 400 } |
| 401 | 401 |
| 402 match_result3 = MatchResult(DUMMY_CHANGELOG3, 'src/', confidence=0.) | 402 suspect3 = Suspect(DUMMY_CHANGELOG3, 'src/', confidence=0.) |
| 403 frame4 = StackFrame(3, 'src/', 'func', 'ff.cc', 'src/ff.cc', [1]) | 403 frame4 = StackFrame(3, 'src/', 'func', 'ff.cc', 'src/ff.cc', [1]) |
| 404 match_result3.file_to_stack_infos = { | 404 suspect3.file_to_stack_infos = { |
| 405 'f.cc': [StackInfo(frame4, 0)] | 405 'f.cc': [StackInfo(frame4, 0)] |
| 406 } | 406 } |
| 407 match_result3.file_to_analysis_info = { | 407 suspect3.file_to_analysis_info = { |
| 408 'f.cc': AnalysisInfo(min_distance=60, min_distance_frame=frame4) | 408 'f.cc': AnalysisInfo(min_distance=60, min_distance_frame=frame4) |
| 409 } | 409 } |
| 410 | 410 |
| 411 return [match_result1, match_result2, match_result3] | 411 return [suspect1, suspect2, suspect3] |
| 412 | 412 |
| 413 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 413 self.mock(changelist_classifier, 'FindSuspects', _MockFindSuspects) |
| 414 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 414 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 415 'GetDependencyRollsDict', | 415 'GetDependencyRollsDict', |
| 416 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 416 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 417 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 417 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 418 'GetDependency', lambda *_: {}) | 418 'GetDependency', lambda *_: {}) |
| 419 | 419 |
| 420 results = self.changelist_classifier(DUMMY_REPORT) | 420 suspects = self.changelist_classifier(DUMMY_REPORT) |
| 421 expected_match_results = [ | 421 expected_suspects = [ |
| 422 { | 422 { |
| 423 'author': 'r@chromium.org', | 423 'author': 'r@chromium.org', |
| 424 'changed_files': [ | 424 'changed_files': [ |
| 425 { | 425 { |
| 426 'blame_url': None, | 426 'blame_url': None, |
| 427 'file': 'a.cc', | 427 'file': 'a.cc', |
| 428 'info': 'Minimum distance (LOC) 1, frame #0' | 428 'info': 'Minimum distance (LOC) 1, frame #0' |
| 429 } | 429 } |
| 430 ], | 430 ], |
| 431 'confidence': 0.8, | 431 'confidence': 0.8, |
| 432 'project_path': 'src/', | 432 'project_path': 'src/', |
| 433 'reasons': [ | 433 'reasons': [ |
| 434 ('TopFrameIndex', 1.0, 'Top frame is #0'), | 434 ('TopFrameIndex', 1.0, 'Top frame is #0'), |
| 435 ('MinDistance', 0.8, 'Minimum distance is 1') | 435 ('MinDistance', 0.8, 'Minimum distance is 1') |
| 436 ], | 436 ], |
| 437 'review_url': 'https://codereview.chromium.org/3281', | 437 'review_url': 'https://codereview.chromium.org/3281', |
| 438 'revision': '1', | 438 'revision': '1', |
| 439 'time': 'Thu Mar 31 21:24:43 2016', | 439 'time': 'Thu Mar 31 21:24:43 2016', |
| 440 'url': 'https://repo.test/+/1' | 440 'url': 'https://repo.test/+/1' |
| 441 } | 441 } |
| 442 ] | 442 ] |
| 443 self.assertListEqual([result.ToDict() for result in results], | 443 self.assertListEqual([suspect.ToDict() for suspect in suspects], |
| 444 expected_match_results) | 444 expected_suspects) |
| 445 | 445 |
| 446 def testFinditForCrashAllMatchResultsWithZeroConfidences(self): | 446 def testFinditForCrashAllSuspectsWithZeroConfidences(self): |
| 447 """Test that we filter out results with too-large frame indices. | 447 """Test that we filter out suspects with too-large frame indices. |
| 448 | 448 |
| 449 In the mock results below we return frames with indices | 449 In the mock suspects below we return frames with indices |
| 450 15, 20, 21 which are all larger than the ``max_top_n`` of | 450 15, 20, 21 which are all larger than the ``max_top_n`` of |
| 451 ``TopFrameIndexScorer``. Therefore we should get a score of zero | 451 ``TopFrameIndexScorer``. Therefore we should get a score of zero |
| 452 for that feature, which causes the total score to be zero, and so | 452 for that feature, which causes the total score to be zero, and so |
| 453 we should not return these results. | 453 we should not return these suspects. |
| 454 """ | 454 """ |
| 455 def _MockFindMatchResults(*_): | 455 def _MockFindSuspects(*_): |
| 456 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', confidence=0.) | 456 suspect1 = Suspect(DUMMY_CHANGELOG1, 'src/', confidence=0.) |
| 457 frame1 = StackFrame(20, 'src/', '', 'func', 'a.cc', [1]) | 457 frame1 = StackFrame(20, 'src/', '', 'func', 'a.cc', [1]) |
| 458 frame2 = StackFrame(21, 'src/', '', 'func', 'a.cc', [7]) | 458 frame2 = StackFrame(21, 'src/', '', 'func', 'a.cc', [7]) |
| 459 match_result1.file_to_stack_infos = { | 459 suspect1.file_to_stack_infos = { |
| 460 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] | 460 'a.cc': [StackInfo(frame1, 0), StackInfo(frame2, 0)] |
| 461 } | 461 } |
| 462 match_result1.file_to_analysis_info = { | 462 suspect1.file_to_analysis_info = { |
| 463 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) | 463 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) |
| 464 } | 464 } |
| 465 | 465 |
| 466 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', confidence=0.) | 466 suspect2 = Suspect(DUMMY_CHANGELOG3, 'src/', confidence=0.) |
| 467 frame3 = StackFrame(15, 'src/', '', 'func', 'f.cc', [1]) | 467 frame3 = StackFrame(15, 'src/', '', 'func', 'f.cc', [1]) |
| 468 match_result2.file_to_stack_infos = { | 468 suspect2.file_to_stack_infos = { |
| 469 'f.cc': [StackInfo(frame3, 0)] | 469 'f.cc': [StackInfo(frame3, 0)] |
| 470 } | 470 } |
| 471 match_result2.min_distance = 20 | 471 suspect2.min_distance = 20 |
| 472 match_result2.file_to_analysis_info = { | 472 suspect2.file_to_analysis_info = { |
| 473 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) | 473 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 474 } | 474 } |
| 475 | 475 |
| 476 return [match_result1, match_result2] | 476 return [suspect1, suspect2] |
| 477 | 477 |
| 478 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 478 self.mock(changelist_classifier, 'FindSuspects', _MockFindSuspects) |
| 479 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 479 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 480 'GetDependencyRollsDict', | 480 'GetDependencyRollsDict', |
| 481 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 481 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 482 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 482 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 483 'GetDependency', lambda *_: {}) | 483 'GetDependency', lambda *_: {}) |
| 484 | 484 |
| 485 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) | 485 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| OLD | NEW |