Chromium Code Reviews| 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 | 7 |
| 7 from common.dependency import Dependency | 8 from common.dependency import Dependency |
| 8 from common.dependency import DependencyRoll | 9 from common.dependency import DependencyRoll |
| 9 from common.http_client_appengine import HttpClientAppengine | |
| 10 from common import chrome_dependency_fetcher | 10 from common import chrome_dependency_fetcher |
| 11 from crash import changelist_classifier | |
| 11 from crash.crash_report import CrashReport | 12 from crash.crash_report import CrashReport |
| 12 from crash import changelist_classifier | 13 from crash.results import AnalysisInfo |
| 13 from crash.results import MatchResult | 14 from crash.results import MatchResult |
| 14 from crash.stacktrace import CallStack | 15 from crash.stacktrace import CallStack |
| 15 from crash.stacktrace import StackFrame | 16 from crash.stacktrace import StackFrame |
| 16 from crash.stacktrace import Stacktrace | 17 from crash.stacktrace import Stacktrace |
| 17 from crash.test.crash_test_suite import CrashTestSuite | 18 from crash.test.crash_test_suite import CrashTestSuite |
| 18 from lib.gitiles.blame import Blame | 19 from lib.gitiles.blame import Blame |
| 19 from lib.gitiles.blame import Region | 20 from lib.gitiles.blame import Region |
| 20 from lib.gitiles.change_log import ChangeLog | 21 from lib.gitiles.change_log import ChangeLog |
| 21 from lib.gitiles.gitiles_repository import GitilesRepository | 22 from lib.gitiles.gitiles_repository import GitilesRepository |
| 22 | 23 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 102 # taking revision_range apart isn't actually required for the tests, | 103 # taking revision_range apart isn't actually required for the tests, |
| 103 # since we mock GetDEPSRollsDict. So, really what we ought to do in the | 104 # since we mock GetDEPSRollsDict. So, really what we ought to do in the |
| 104 # long run is redesign things so that GetDEPSRollsDict takes the | 105 # long run is redesign things so that GetDEPSRollsDict takes the |
| 105 # CrashReport directly and pulls out the revision_range and platform | 106 # CrashReport directly and pulls out the revision_range and platform |
| 106 # itself; that way ChangelistClassifier.__call__ needn't worry about it, | 107 # itself; that way ChangelistClassifier.__call__ needn't worry about it, |
| 107 # allowing us to clean up the tests here. | 108 # allowing us to clean up the tests here. |
| 108 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) | 109 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) |
| 109 | 110 |
| 110 class ChangelistClassifierTest(CrashTestSuite): | 111 class ChangelistClassifierTest(CrashTestSuite): |
| 111 | 112 |
| 113 def testSkipAddedAndDeletedRegressionRolls(self): | |
| 114 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | |
| 115 'GetDependency', lambda *_: {}) | |
| 116 dep_rolls = { | |
| 117 'src/dep': DependencyRoll('src/dep1', 'https://url_dep1', None, '9'), | |
| 118 'src/': DependencyRoll('src/', ('https://chromium.googlesource.com/' | |
| 119 'chromium/src.git'), '4', '5') | |
| 120 } | |
| 121 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | |
| 122 'GetDependencyRollsDict', lambda *_: dep_rolls) | |
| 123 | |
| 124 passed_in_regression_deps_rolls = [] | |
| 125 def _MockGetChangeLogsForFilesGroupedByDeps(regression_deps_rolls, *_): | |
| 126 passed_in_regression_deps_rolls.append(regression_deps_rolls) | |
| 127 return {}, None | |
| 128 | |
| 129 self.mock(changelist_classifier, 'GetChangeLogsForFilesGroupedByDeps', | |
| 130 _MockGetChangeLogsForFilesGroupedByDeps) | |
| 131 self.mock(changelist_classifier, 'GetStackInfosForFilesGroupedByDeps', | |
| 132 lambda *_: {}) | |
| 133 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: None) | |
| 134 | |
| 135 cl_classifier = changelist_classifier.ChangelistClassifier( | |
| 136 GitilesRepository(), 7) | |
| 137 cl_classifier(CrashReport(crashed_version = '5', | |
| 138 signature = 'sig', | |
| 139 platform = 'canary', | |
| 140 stacktrace = Stacktrace([CallStack(0)]), | |
| 141 regression_range = ['4', '5'])) | |
| 142 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) | |
| 143 del expected_regression_deps_rolls['src/dep'] | |
|
wrengr
2016/10/31 23:48:35
Could you add a comment about why this ``del`` is
Sharu Jiang
2016/11/01 23:54:09
Done.
| |
| 144 self.assertEqual(passed_in_regression_deps_rolls[0], | |
| 145 expected_regression_deps_rolls) | |
| 146 | |
| 112 def testGetDepsInCrashStack(self): | 147 def testGetDepsInCrashStack(self): |
| 113 crash_stack = CallStack(0) | 148 crash_stack = CallStack(0) |
| 114 crash_stack.extend([ | 149 crash_stack.extend([ |
| 115 StackFrame(0, 'src/', 'func0', 'f0.cc', 'src/f0.cc', [1]), | 150 StackFrame(0, 'src/', 'func0', 'f0.cc', 'src/f0.cc', [1]), |
| 116 StackFrame(1, 'src/', 'func1', 'f1.cc', 'src/f1.cc', [2, 3]), | 151 StackFrame(1, 'src/', 'func1', 'f1.cc', 'src/f1.cc', [2, 3]), |
| 117 StackFrame(1, '', 'func2', 'f2.cc', 'src/f2.cc', [2, 3]), | 152 StackFrame(1, '', 'func2', 'f2.cc', 'src/f2.cc', [2, 3]), |
| 118 ]) | 153 ]) |
| 119 crash_deps = {'src/': Dependency('src/', 'https://chromium_repo', '1'), | 154 crash_deps = {'src/': Dependency('src/', 'https://chromium_repo', '1'), |
| 120 'src/v8/': Dependency('src/v8/', 'https://v8_repo', '2')} | 155 'src/v8/': Dependency('src/v8/', 'https://v8_repo', '2')} |
| 121 | 156 |
| 122 expected_stack_deps = {'src/': crash_deps['src/']} | 157 expected_stack_deps = {'src/': crash_deps['src/']} |
| 123 | 158 |
| 124 self.assertDictEqual( | 159 self.assertDictEqual( |
| 125 changelist_classifier.GetDepsInCrashStack(crash_stack, crash_deps), | 160 changelist_classifier.GetDepsInCrashStack(crash_stack, crash_deps), |
| 126 expected_stack_deps) | 161 expected_stack_deps) |
| 127 | 162 |
| 128 def testGetChangeLogsForFilesGroupedByDeps(self): | 163 def testGetChangeLogsForFilesGroupedByDeps(self): |
| 129 regression_deps_rolls = { | 164 regression_deps_rolls = { |
| 130 'src/dep1': DependencyRoll('src/dep1', 'https://url_dep1', '7', '9'), | 165 'src/dep': DependencyRoll('src/dep1', 'https://url_dep1', '7', '9'), |
| 131 'src/dep2': DependencyRoll('src/dep2', 'repo_url', '3', None), | |
| 132 'src/': DependencyRoll('src/', ('https://chromium.googlesource.com/' | 166 'src/': DependencyRoll('src/', ('https://chromium.googlesource.com/' |
| 133 'chromium/src.git'), '4', '5') | 167 'chromium/src.git'), '4', '5') |
| 134 } | 168 } |
| 135 | 169 |
| 136 stack_deps = { | 170 stack_deps = { |
| 137 'src/': Dependency('src/', 'https://url_src', 'rev1', 'DEPS'), | 171 'src/': Dependency('src/', 'https://url_src', 'rev1', 'DEPS'), |
| 138 'src/new': Dependency('src/new', 'https://new', 'rev2', 'DEPS'), | 172 'src/new': Dependency('src/new', 'https://new', 'rev2', 'DEPS'), |
| 173 'src/dep': Dependency('src/dep', 'https://url_dep', 'rev', 'DEPS'), | |
| 139 } | 174 } |
| 140 | 175 |
| 141 def _MockGetChangeLogs(*_): | 176 def _MockGetChangeLogs(_, start_rev, end_rev): |
| 142 return [DUMMY_CHANGELOG1, DUMMY_CHANGELOG2, DUMMY_CHANGELOG3] | 177 if start_rev == '4' and end_rev == '5': |
| 178 return [DUMMY_CHANGELOG1, DUMMY_CHANGELOG2, DUMMY_CHANGELOG3] | |
| 179 | |
| 180 return [] | |
| 143 | 181 |
| 144 self.mock(GitilesRepository, 'GetChangeLogs', _MockGetChangeLogs) | 182 self.mock(GitilesRepository, 'GetChangeLogs', _MockGetChangeLogs) |
| 145 | 183 |
| 146 dep_file_to_changelogs, ignore_cls = ( | 184 dep_file_to_changelogs, ignore_cls = ( |
| 147 changelist_classifier.GetChangeLogsForFilesGroupedByDeps( | 185 changelist_classifier.GetChangeLogsForFilesGroupedByDeps( |
| 148 regression_deps_rolls, stack_deps, GitilesRepository())) | 186 regression_deps_rolls, stack_deps, GitilesRepository())) |
| 149 dep_file_to_changelogs_json = defaultdict(lambda: defaultdict(list)) | 187 dep_file_to_changelogs_json = defaultdict(lambda: defaultdict(list)) |
| 150 for dep, file_to_changelogs in dep_file_to_changelogs.iteritems(): | 188 for dep, file_to_changelogs in dep_file_to_changelogs.iteritems(): |
| 151 for file_path, changelogs in file_to_changelogs.iteritems(): | 189 for file_path, changelogs in file_to_changelogs.iteritems(): |
| 152 for changelog in changelogs: | 190 for changelog in changelogs: |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 def testFindItForCrash(self): | 326 def testFindItForCrash(self): |
| 289 | 327 |
| 290 def _MockFindMatchResults(*_): | 328 def _MockFindMatchResults(*_): |
| 291 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') | 329 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') |
| 292 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 330 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 293 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 331 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 294 match_result1.file_to_stack_infos = { | 332 match_result1.file_to_stack_infos = { |
| 295 'a.cc': [(frame1, 0), (frame2, 0)] | 333 'a.cc': [(frame1, 0), (frame2, 0)] |
| 296 } | 334 } |
| 297 match_result1.file_to_analysis_info = { | 335 match_result1.file_to_analysis_info = { |
| 298 'a.cc': {'min_distance': 0, 'min_distance_frame': frame1} | 336 'a.cc': AnalysisInfo(min_distance=0, min_distance_frame=frame1) |
| 299 } | 337 } |
| 300 | 338 |
| 301 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') | 339 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') |
| 302 frame3 = StackFrame(5, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | 340 frame3 = StackFrame(5, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) |
| 303 match_result2.file_to_stack_infos = { | 341 match_result2.file_to_stack_infos = { |
| 304 'f.cc': [(frame3, 0)] | 342 'f.cc': [(frame3, 0)] |
| 305 } | 343 } |
| 306 match_result2.file_to_analysis_info = { | 344 match_result2.file_to_analysis_info = { |
| 307 'a.cc': {'min_distance': 20, 'min_distance_frame': frame3} | 345 'a.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 308 } | 346 } |
| 309 | 347 |
| 310 return [match_result1, match_result2] | 348 return [match_result1, match_result2] |
| 311 | 349 |
| 312 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 350 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 313 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 351 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 314 'GetDependencyRollsDict', | 352 'GetDependencyRollsDict', |
| 315 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 353 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 316 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 354 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 317 'GetDependency', lambda *_: {}) | 355 'GetDependency', lambda *_: {}) |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 337 | 375 |
| 338 def testFinditForCrashFilterZeroConfidentResults(self): | 376 def testFinditForCrashFilterZeroConfidentResults(self): |
| 339 def _MockFindMatchResults(*_): | 377 def _MockFindMatchResults(*_): |
| 340 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') | 378 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') |
| 341 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 379 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 342 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 380 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 343 match_result1.file_to_stack_infos = { | 381 match_result1.file_to_stack_infos = { |
| 344 'a.cc': [(frame1, 0), (frame2, 0)] | 382 'a.cc': [(frame1, 0), (frame2, 0)] |
| 345 } | 383 } |
| 346 match_result1.file_to_analysis_info = { | 384 match_result1.file_to_analysis_info = { |
| 347 'a.cc': {'min_distance': 1, 'min_distance_frame': frame1} | 385 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) |
| 348 } | 386 } |
| 349 | 387 |
| 350 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') | 388 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') |
| 351 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) | 389 frame3 = StackFrame(15, 'src/', 'func', 'f.cc', 'src/f.cc', [1]) |
| 352 match_result2.file_to_stack_infos = { | 390 match_result2.file_to_stack_infos = { |
| 353 'f.cc': [(frame3, 0)] | 391 'f.cc': [(frame3, 0)] |
| 354 } | 392 } |
| 355 match_result2.file_to_analysis_info = { | 393 match_result2.file_to_analysis_info = { |
| 356 'f.cc': {'min_distance': 20, 'min_distance_frame': frame3} | 394 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 357 } | 395 } |
| 358 | 396 |
| 359 match_result3 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') | 397 match_result3 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') |
| 360 frame4 = StackFrame(3, 'src/', 'func', 'ff.cc', 'src/ff.cc', [1]) | 398 frame4 = StackFrame(3, 'src/', 'func', 'ff.cc', 'src/ff.cc', [1]) |
| 361 match_result3.file_to_stack_infos = { | 399 match_result3.file_to_stack_infos = { |
| 362 'f.cc': [(frame4, 0)] | 400 'f.cc': [(frame4, 0)] |
| 363 } | 401 } |
| 364 match_result3.file_to_analysis_info = { | 402 match_result3.file_to_analysis_info = { |
| 365 'f.cc': {'min_distance': 60, 'min_distance_frame': frame4} | 403 'f.cc': AnalysisInfo(min_distance=60, min_distance_frame=frame4) |
| 366 } | 404 } |
| 367 | 405 |
| 368 return [match_result1, match_result2, match_result3] | 406 return [match_result1, match_result2, match_result3] |
| 369 | 407 |
| 370 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 408 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 371 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 409 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 372 'GetDependencyRollsDict', | 410 'GetDependencyRollsDict', |
| 373 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 411 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 374 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 412 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 375 'GetDependency', lambda *_: {}) | 413 'GetDependency', lambda *_: {}) |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 404 | 442 |
| 405 def testFinditForCrashAllMatchResultsWithZeroConfidences(self): | 443 def testFinditForCrashAllMatchResultsWithZeroConfidences(self): |
| 406 def _MockFindMatchResults(*_): | 444 def _MockFindMatchResults(*_): |
| 407 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') | 445 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') |
| 408 frame1 = StackFrame(20, 'src/', '', 'func', 'a.cc', [1]) | 446 frame1 = StackFrame(20, 'src/', '', 'func', 'a.cc', [1]) |
| 409 frame2 = StackFrame(21, 'src/', '', 'func', 'a.cc', [7]) | 447 frame2 = StackFrame(21, 'src/', '', 'func', 'a.cc', [7]) |
| 410 match_result1.file_to_stack_infos = { | 448 match_result1.file_to_stack_infos = { |
| 411 'a.cc': [(frame1, 0), (frame2, 0)] | 449 'a.cc': [(frame1, 0), (frame2, 0)] |
| 412 } | 450 } |
| 413 match_result1.file_to_analysis_info = { | 451 match_result1.file_to_analysis_info = { |
| 414 'a.cc': {'min_distance': 1, 'min_distance_frame': frame1} | 452 'a.cc': AnalysisInfo(min_distance=1, min_distance_frame=frame1) |
| 415 } | 453 } |
| 416 | 454 |
| 417 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') | 455 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') |
| 418 frame3 = StackFrame(15, 'src/', '', 'func', 'f.cc', [1]) | 456 frame3 = StackFrame(15, 'src/', '', 'func', 'f.cc', [1]) |
| 419 match_result2.file_to_stack_infos = { | 457 match_result2.file_to_stack_infos = { |
| 420 'f.cc': [(frame3, 0)] | 458 'f.cc': [(frame3, 0)] |
| 421 } | 459 } |
| 422 match_result2.min_distance = 20 | 460 match_result2.min_distance = 20 |
| 423 match_result2.file_to_analysis_info = { | 461 match_result2.file_to_analysis_info = { |
| 424 'f.cc': {'min_distance': 20, 'min_distance_frame': frame3} | 462 'f.cc': AnalysisInfo(min_distance=20, min_distance_frame=frame3) |
| 425 } | 463 } |
| 426 | 464 |
| 427 return [match_result1, match_result2] | 465 return [match_result1, match_result2] |
| 428 | 466 |
| 429 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 467 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 430 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 468 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 431 'GetDependencyRollsDict', | 469 'GetDependencyRollsDict', |
| 432 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 470 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 433 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 471 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 434 'GetDependency', lambda *_: {}) | 472 'GetDependency', lambda *_: {}) |
| 435 | 473 |
| 436 cl_classifier = changelist_classifier.ChangelistClassifier(7, | 474 cl_classifier = changelist_classifier.ChangelistClassifier(7, |
| 437 GitilesRepository()) | 475 GitilesRepository()) |
| 438 self.assertListEqual(cl_classifier(DUMMY_REPORT), []) | 476 self.assertListEqual(cl_classifier(DUMMY_REPORT), []) |
| OLD | NEW |