| 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 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 # taking revision_range apart isn't actually required for the tests, | 103 # taking revision_range apart isn't actually required for the tests, |
| 104 # 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 |
| 105 # long run is redesign things so that GetDEPSRollsDict takes the | 105 # long run is redesign things so that GetDEPSRollsDict takes the |
| 106 # CrashReport directly and pulls out the revision_range and platform | 106 # CrashReport directly and pulls out the revision_range and platform |
| 107 # itself; that way ChangelistClassifier.__call__ needn't worry about it, | 107 # itself; that way ChangelistClassifier.__call__ needn't worry about it, |
| 108 # allowing us to clean up the tests here. | 108 # allowing us to clean up the tests here. |
| 109 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) | 109 DUMMY_REPORT = CrashReport(None, None, None, Stacktrace(), (None, None)) |
| 110 | 110 |
| 111 class ChangelistClassifierTest(CrashTestSuite): | 111 class ChangelistClassifierTest(CrashTestSuite): |
| 112 | 112 |
| 113 def setUp(self): |
| 114 super(ChangelistClassifierTest, self).setUp() |
| 115 self.changelist_classifier = changelist_classifier.ChangelistClassifier( |
| 116 GitilesRepository(self.GetMockHttpClient()), 7) |
| 117 |
| 113 def testSkipAddedAndDeletedRegressionRolls(self): | 118 def testSkipAddedAndDeletedRegressionRolls(self): |
| 114 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 119 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 115 'GetDependency', lambda *_: {}) | 120 'GetDependency', lambda *_: {}) |
| 116 dep_rolls = { | 121 dep_rolls = { |
| 117 'src/dep': DependencyRoll('src/dep1', 'https://url_dep1', None, '9'), | 122 'src/dep': DependencyRoll('src/dep1', 'https://url_dep1', None, '9'), |
| 118 'src/': DependencyRoll('src/', ('https://chromium.googlesource.com/' | 123 'src/': DependencyRoll('src/', ('https://chromium.googlesource.com/' |
| 119 'chromium/src.git'), '4', '5') | 124 'chromium/src.git'), '4', '5') |
| 120 } | 125 } |
| 121 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 126 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 122 'GetDependencyRollsDict', lambda *_: dep_rolls) | 127 'GetDependencyRollsDict', lambda *_: dep_rolls) |
| 123 | 128 |
| 124 passed_in_regression_deps_rolls = [] | 129 passed_in_regression_deps_rolls = [] |
| 125 def _MockGetChangeLogsForFilesGroupedByDeps(regression_deps_rolls, *_): | 130 def _MockGetChangeLogsForFilesGroupedByDeps(regression_deps_rolls, *_): |
| 126 passed_in_regression_deps_rolls.append(regression_deps_rolls) | 131 passed_in_regression_deps_rolls.append(regression_deps_rolls) |
| 127 return {}, None | 132 return {}, None |
| 128 | 133 |
| 129 self.mock(changelist_classifier, 'GetChangeLogsForFilesGroupedByDeps', | 134 self.mock(changelist_classifier, 'GetChangeLogsForFilesGroupedByDeps', |
| 130 _MockGetChangeLogsForFilesGroupedByDeps) | 135 _MockGetChangeLogsForFilesGroupedByDeps) |
| 131 self.mock(changelist_classifier, 'GetStackInfosForFilesGroupedByDeps', | 136 self.mock(changelist_classifier, 'GetStackInfosForFilesGroupedByDeps', |
| 132 lambda *_: {}) | 137 lambda *_: {}) |
| 133 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: None) | 138 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: None) |
| 134 | 139 |
| 135 cl_classifier = changelist_classifier.ChangelistClassifier( | 140 self.changelist_classifier(CrashReport(crashed_version = '5', |
| 136 GitilesRepository(), 7) | 141 signature = 'sig', |
| 137 cl_classifier(CrashReport(crashed_version = '5', | 142 platform = 'canary', |
| 138 signature = 'sig', | 143 stacktrace = Stacktrace([CallStack(0)]), |
| 139 platform = 'canary', | 144 regression_range = ['4', '5'])) |
| 140 stacktrace = Stacktrace([CallStack(0)]), | |
| 141 regression_range = ['4', '5'])) | |
| 142 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) | 145 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) |
| 143 | 146 |
| 144 # Regression of a dep added/deleted (old_revision/new_revision is None) can | 147 # Regression of a dep added/deleted (old_revision/new_revision is None) can |
| 145 # not be known for sure and this case rarely happens, so just filter them | 148 # not be known for sure and this case rarely happens, so just filter them |
| 146 # out. | 149 # out. |
| 147 del expected_regression_deps_rolls['src/dep'] | 150 del expected_regression_deps_rolls['src/dep'] |
| 148 self.assertEqual(passed_in_regression_deps_rolls[0], | 151 self.assertEqual(passed_in_regression_deps_rolls[0], |
| 149 expected_regression_deps_rolls) | 152 expected_regression_deps_rolls) |
| 150 | 153 |
| 151 def testGetDepsInCrashStack(self): | 154 def testGetDepsInCrashStack(self): |
| (...skipping 28 matching lines...) Expand all Loading... |
| 180 def _MockGetChangeLogs(_, start_rev, end_rev): | 183 def _MockGetChangeLogs(_, start_rev, end_rev): |
| 181 if start_rev == '4' and end_rev == '5': | 184 if start_rev == '4' and end_rev == '5': |
| 182 return [DUMMY_CHANGELOG1, DUMMY_CHANGELOG2, DUMMY_CHANGELOG3] | 185 return [DUMMY_CHANGELOG1, DUMMY_CHANGELOG2, DUMMY_CHANGELOG3] |
| 183 | 186 |
| 184 return [] | 187 return [] |
| 185 | 188 |
| 186 self.mock(GitilesRepository, 'GetChangeLogs', _MockGetChangeLogs) | 189 self.mock(GitilesRepository, 'GetChangeLogs', _MockGetChangeLogs) |
| 187 | 190 |
| 188 dep_file_to_changelogs, ignore_cls = ( | 191 dep_file_to_changelogs, ignore_cls = ( |
| 189 changelist_classifier.GetChangeLogsForFilesGroupedByDeps( | 192 changelist_classifier.GetChangeLogsForFilesGroupedByDeps( |
| 190 regression_deps_rolls, stack_deps, GitilesRepository())) | 193 regression_deps_rolls, stack_deps, |
| 194 GitilesRepository(self.GetMockHttpClient()))) |
| 191 dep_file_to_changelogs_json = defaultdict(lambda: defaultdict(list)) | 195 dep_file_to_changelogs_json = defaultdict(lambda: defaultdict(list)) |
| 192 for dep, file_to_changelogs in dep_file_to_changelogs.iteritems(): | 196 for dep, file_to_changelogs in dep_file_to_changelogs.iteritems(): |
| 193 for file_path, changelogs in file_to_changelogs.iteritems(): | 197 for file_path, changelogs in file_to_changelogs.iteritems(): |
| 194 for changelog in changelogs: | 198 for changelog in changelogs: |
| 195 dep_file_to_changelogs_json[dep][file_path].append(changelog.ToDict()) | 199 dep_file_to_changelogs_json[dep][file_path].append(changelog.ToDict()) |
| 196 | 200 |
| 197 expected_dep_file_to_changelogs_json = { | 201 expected_dep_file_to_changelogs_json = { |
| 198 'src/': { | 202 'src/': { |
| 199 'a.cc': [DUMMY_CHANGELOG1.ToDict()], | 203 'a.cc': [DUMMY_CHANGELOG1.ToDict()], |
| 200 'f.cc': [DUMMY_CHANGELOG3.ToDict()] | 204 'f.cc': [DUMMY_CHANGELOG3.ToDict()] |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 'project_path': 'src/', | 296 'project_path': 'src/', |
| 293 'author': 'r@chromium.org', | 297 'author': 'r@chromium.org', |
| 294 'time': 'Thu Mar 31 21:24:43 2016', | 298 'time': 'Thu Mar 31 21:24:43 2016', |
| 295 'reasons': None, | 299 'reasons': None, |
| 296 'confidence': None, | 300 'confidence': None, |
| 297 'changed_files': None | 301 'changed_files': None |
| 298 }] | 302 }] |
| 299 | 303 |
| 300 match_results = changelist_classifier.FindMatchResults( | 304 match_results = changelist_classifier.FindMatchResults( |
| 301 dep_file_to_changelogs, dep_file_to_stack_infos, stack_deps, | 305 dep_file_to_changelogs, dep_file_to_stack_infos, stack_deps, |
| 302 GitilesRepository()) | 306 GitilesRepository(self.GetMockHttpClient())) |
| 303 self.assertListEqual([result.ToDict() for result in match_results], | 307 self.assertListEqual([result.ToDict() for result in match_results], |
| 304 expected_match_results) | 308 expected_match_results) |
| 305 | 309 |
| 306 # TODO(http://crbug.com/659346): why do these mocks give coverage | 310 # TODO(http://crbug.com/659346): why do these mocks give coverage |
| 307 # failures? That's almost surely hiding a bug in the tests themselves. | 311 # failures? That's almost surely hiding a bug in the tests themselves. |
| 308 def testFindItForCrashNoRegressionRange(self): # pragma: no cover | 312 def testFindItForCrashNoRegressionRange(self): # pragma: no cover |
| 309 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 313 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 310 'GetDependencyRollsDict', lambda *_: {}) | 314 'GetDependencyRollsDict', lambda *_: {}) |
| 311 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 315 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 312 'GetDependency', lambda *_: {}) | 316 'GetDependency', lambda *_: {}) |
| 313 cl_classifier = changelist_classifier.ChangelistClassifier(7, | |
| 314 GitilesRepository()) | |
| 315 # N.B., for this one test we really do want regression_range=None. | 317 # N.B., for this one test we really do want regression_range=None. |
| 316 report = DUMMY_REPORT._replace(regression_range=None) | 318 report = DUMMY_REPORT._replace(regression_range=None) |
| 317 self.assertListEqual(cl_classifier(report), []) | 319 self.assertListEqual(self.changelist_classifier(report), []) |
| 318 | 320 |
| 319 def testFindItForCrashNoMatchFound(self): | 321 def testFindItForCrashNoMatchFound(self): |
| 320 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: []) | 322 self.mock(changelist_classifier, 'FindMatchResults', lambda *_: []) |
| 321 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 323 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 322 'GetDependencyRollsDict', | 324 'GetDependencyRollsDict', |
| 323 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 325 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 324 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 326 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 325 'GetDependency', lambda *_: {}) | 327 'GetDependency', lambda *_: {}) |
| 326 cl_classifier = changelist_classifier.ChangelistClassifier(7, | 328 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| 327 GitilesRepository()) | |
| 328 self.assertListEqual(cl_classifier(DUMMY_REPORT), []) | |
| 329 | 329 |
| 330 def testFindItForCrash(self): | 330 def testFindItForCrash(self): |
| 331 | 331 |
| 332 def _MockFindMatchResults(*_): | 332 def _MockFindMatchResults(*_): |
| 333 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') | 333 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') |
| 334 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) | 334 frame1 = StackFrame(0, 'src/', 'func', 'a.cc', 'src/a.cc', [1]) |
| 335 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) | 335 frame2 = StackFrame(1, 'src/', 'func', 'a.cc', 'src/a.cc', [7]) |
| 336 match_result1.file_to_stack_infos = { | 336 match_result1.file_to_stack_infos = { |
| 337 'a.cc': [(frame1, 0), (frame2, 0)] | 337 'a.cc': [(frame1, 0), (frame2, 0)] |
| 338 } | 338 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 350 } | 350 } |
| 351 | 351 |
| 352 return [match_result1, match_result2] | 352 return [match_result1, match_result2] |
| 353 | 353 |
| 354 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 354 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 355 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 355 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 356 'GetDependencyRollsDict', | 356 'GetDependencyRollsDict', |
| 357 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 357 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 358 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 358 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 359 'GetDependency', lambda *_: {}) | 359 'GetDependency', lambda *_: {}) |
| 360 cl_classifier = changelist_classifier.ChangelistClassifier(7, | 360 results = self.changelist_classifier(DUMMY_REPORT) |
| 361 GitilesRepository()) | |
| 362 results = cl_classifier(DUMMY_REPORT) | |
| 363 expected_match_results = [ | 361 expected_match_results = [ |
| 364 { | 362 { |
| 365 'reasons': [('TopFrameIndex', 1.0, 'Top frame is #0'), | 363 'reasons': [('TopFrameIndex', 1.0, 'Top frame is #0'), |
| 366 ('MinDistance', 1, 'Minimum distance is 0')], | 364 ('MinDistance', 1, 'Minimum distance is 0')], |
| 367 'changed_files': [{'info': 'Minimum distance (LOC) 0, frame #0', | 365 'changed_files': [{'info': 'Minimum distance (LOC) 0, frame #0', |
| 368 'blame_url': None, 'file': 'a.cc'}], | 366 'blame_url': None, 'file': 'a.cc'}], |
| 369 'time': 'Thu Mar 31 21:24:43 2016', | 367 'time': 'Thu Mar 31 21:24:43 2016', |
| 370 'author': 'r@chromium.org', | 368 'author': 'r@chromium.org', |
| 371 'url': 'https://repo.test/+/1', | 369 'url': 'https://repo.test/+/1', |
| 372 'project_path': 'src/', | 370 'project_path': 'src/', |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 | 407 |
| 410 return [match_result1, match_result2, match_result3] | 408 return [match_result1, match_result2, match_result3] |
| 411 | 409 |
| 412 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 410 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 413 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 411 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 414 'GetDependencyRollsDict', | 412 'GetDependencyRollsDict', |
| 415 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 413 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 416 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 414 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 417 'GetDependency', lambda *_: {}) | 415 'GetDependency', lambda *_: {}) |
| 418 | 416 |
| 419 cl_classifier = changelist_classifier.ChangelistClassifier(7, | 417 results = self.changelist_classifier(DUMMY_REPORT) |
| 420 GitilesRepository()) | |
| 421 results = cl_classifier(DUMMY_REPORT) | |
| 422 expected_match_results = [ | 418 expected_match_results = [ |
| 423 { | 419 { |
| 424 'author': 'r@chromium.org', | 420 'author': 'r@chromium.org', |
| 425 'changed_files': [ | 421 'changed_files': [ |
| 426 { | 422 { |
| 427 'blame_url': None, | 423 'blame_url': None, |
| 428 'file': 'a.cc', | 424 'file': 'a.cc', |
| 429 'info': 'Minimum distance (LOC) 1, frame #0' | 425 'info': 'Minimum distance (LOC) 1, frame #0' |
| 430 } | 426 } |
| 431 ], | 427 ], |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 468 | 464 |
| 469 return [match_result1, match_result2] | 465 return [match_result1, match_result2] |
| 470 | 466 |
| 471 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) | 467 self.mock(changelist_classifier, 'FindMatchResults', _MockFindMatchResults) |
| 472 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 468 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 473 'GetDependencyRollsDict', | 469 'GetDependencyRollsDict', |
| 474 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) | 470 lambda *_: {'src/': DependencyRoll('src/', 'https://repo', '1', '2')}) |
| 475 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, | 471 self.mock(chrome_dependency_fetcher.ChromeDependencyFetcher, |
| 476 'GetDependency', lambda *_: {}) | 472 'GetDependency', lambda *_: {}) |
| 477 | 473 |
| 478 cl_classifier = changelist_classifier.ChangelistClassifier(7, | 474 self.assertListEqual(self.changelist_classifier(DUMMY_REPORT), []) |
| 479 GitilesRepository()) | |
| 480 self.assertListEqual(cl_classifier(DUMMY_REPORT), []) | |
| OLD | NEW |