| 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 import copy | |
| 6 | |
| 7 from common.chrome_dependency_fetcher import ChromeDependencyFetcher | |
| 8 from common.dependency import Dependency | |
| 9 from common.dependency import DependencyRoll | |
| 10 from crash.crash_report import CrashReport | |
| 11 from crash.crash_report_with_dependencies import _FrozenDict | |
| 12 from crash.crash_report_with_dependencies import CrashReportWithDependencies | |
| 13 from crash.stacktrace import CallStack | |
| 14 from crash.stacktrace import StackFrame | |
| 15 from crash.stacktrace import Stacktrace | |
| 16 from crash.test.crash_test_suite import CrashTestSuite | |
| 17 from crash.type_enums import CallStackFormatType | |
| 18 from crash.type_enums import LanguageType | |
| 19 from libs.gitiles.change_log import ChangeLog | |
| 20 from libs.gitiles.gitiles_repository import GitilesRepository | |
| 21 | |
| 22 DUMMY_CHANGELOG1 = ChangeLog.FromDict({ | |
| 23 'author': { | |
| 24 'name': 'r@chromium.org', | |
| 25 'email': 'r@chromium.org', | |
| 26 'time': 'Thu Mar 31 21:24:43 2016', | |
| 27 }, | |
| 28 'committer': { | |
| 29 'email': 'r@chromium.org', | |
| 30 'time': 'Thu Mar 31 21:28:39 2016', | |
| 31 'name': 'example@chromium.org', | |
| 32 }, | |
| 33 'message': 'dummy', | |
| 34 'commit_position': 175900, | |
| 35 'touched_files': [ | |
| 36 { | |
| 37 'change_type': 'add', | |
| 38 'new_path': 'a.cc', | |
| 39 'old_path': None, | |
| 40 }, | |
| 41 ], | |
| 42 'commit_url': 'https://repo.test/+/1', | |
| 43 'code_review_url': 'https://codereview.chromium.org/3281', | |
| 44 'revision': '1', | |
| 45 'reverted_revision': None | |
| 46 }) | |
| 47 | |
| 48 DUMMY_CHANGELOG2 = ChangeLog.FromDict({ | |
| 49 'author': { | |
| 50 'name': 'example@chromium.org', | |
| 51 'email': 'example@chromium.org', | |
| 52 'time': 'Thu Mar 31 21:24:43 2016', | |
| 53 }, | |
| 54 'committer': { | |
| 55 'name': 'example@chromium.org', | |
| 56 'email': 'example@chromium.org', | |
| 57 'time': 'Thu Mar 31 21:28:39 2016', | |
| 58 }, | |
| 59 'message': 'dummy', | |
| 60 'commit_position': 175976, | |
| 61 'touched_files': [ | |
| 62 { | |
| 63 'change_type': 'add', | |
| 64 'new_path': 'f0.cc', | |
| 65 'old_path': 'b/f0.cc' | |
| 66 }, | |
| 67 ], | |
| 68 'commit_url': 'https://repo.test/+/2', | |
| 69 'code_review_url': 'https://codereview.chromium.org/3281', | |
| 70 'revision': '2', | |
| 71 'reverted_revision': '1' | |
| 72 }) | |
| 73 | |
| 74 DUMMY_CHANGELOG3 = ChangeLog.FromDict({ | |
| 75 'author': { | |
| 76 'name': 'e@chromium.org', | |
| 77 'email': 'e@chromium.org', | |
| 78 'time': 'Thu Apr 1 21:24:43 2016', | |
| 79 }, | |
| 80 'committer': { | |
| 81 'name': 'example@chromium.org', | |
| 82 'email': 'e@chromium.org', | |
| 83 'time': 'Thu Apr 1 21:28:39 2016', | |
| 84 }, | |
| 85 'message': 'dummy', | |
| 86 'commit_position': 176000, | |
| 87 'touched_files': [ | |
| 88 { | |
| 89 'change_type': 'modify', | |
| 90 'new_path': 'f.cc', | |
| 91 'old_path': 'f.cc' | |
| 92 }, | |
| 93 { | |
| 94 'change_type': 'delete', | |
| 95 'new_path': None, | |
| 96 'old_path': 'f1.cc' | |
| 97 }, | |
| 98 ], | |
| 99 'commit_url': 'https://repo.test/+/3', | |
| 100 'code_review_url': 'https://codereview.chromium.org/3281', | |
| 101 'revision': '3', | |
| 102 'reverted_revision': None | |
| 103 }) | |
| 104 | |
| 105 DUMMY_CALLSTACKS = [ | |
| 106 CallStack(0, [], CallStackFormatType.DEFAULT, LanguageType.CPP), | |
| 107 CallStack(1, [], CallStackFormatType.DEFAULT, LanguageType.CPP)] | |
| 108 DUMMY_REPORT = CrashReport( | |
| 109 None, None, None, Stacktrace(DUMMY_CALLSTACKS, DUMMY_CALLSTACKS[0]), | |
| 110 (None, None)) | |
| 111 | |
| 112 | |
| 113 class FrozenDictTest(CrashTestSuite): | |
| 114 def testHash(self): | |
| 115 """Test that ``_FrozenDict`` does not throw an exception in ``hash``.""" | |
| 116 try: | |
| 117 _h = hash(_FrozenDict({'a': 1, 'b': 2, 'c': 3})) | |
| 118 except TypeError as e: # pragma: no cover | |
| 119 if 'unhashable type' in str(e): | |
| 120 self.fail('_FrozenDict.__hash__ does not work') | |
| 121 else: | |
| 122 raise e | |
| 123 | |
| 124 | |
| 125 class CrashReportWithDependenciesTest(CrashTestSuite): | |
| 126 | |
| 127 def setUp(self): | |
| 128 super(CrashReportWithDependenciesTest, self).setUp() | |
| 129 self.get_repository = GitilesRepository.Factory(self.GetMockHttpClient()) | |
| 130 self.dep_fetcher = ChromeDependencyFetcher(self.get_repository) | |
| 131 | |
| 132 # These mocks are shared by many (but not all) tests. | |
| 133 def empty_dict(*_): # pragma: no cover | |
| 134 """Constant function that returns the empty dict for all inputs. | |
| 135 | |
| 136 We break this out rather than using a lambda just so that we can | |
| 137 add the "no cover" pragma. We really don't care if some particular | |
| 138 test doesn't call these mocks. | |
| 139 """ | |
| 140 return {} | |
| 141 self.mock(ChromeDependencyFetcher, 'GetDependencyRollsDict', empty_dict) | |
| 142 self.mock(ChromeDependencyFetcher, 'GetDependency', empty_dict) | |
| 143 | |
| 144 def testMissingRegressionRange(self): | |
| 145 """Test that ``__new__`` fails when the regression range is missing.""" | |
| 146 report = DUMMY_REPORT._replace(regression_range=None) | |
| 147 self.assertIsNone(CrashReportWithDependencies(report, self.dep_fetcher)) | |
| 148 | |
| 149 def testMissingLastGoodRevision(self): | |
| 150 """Test that ``__new__`` ?? when the last-good revision is missing.""" | |
| 151 report = DUMMY_REPORT._replace(regression_range=(None, '5')) | |
| 152 # TODO(wrengr): find something worthwhile to test here. | |
| 153 self.assertIsNotNone(CrashReportWithDependencies(report, self.dep_fetcher)) | |
| 154 | |
| 155 def testMissingFirstBadRevision(self): | |
| 156 """Test that ``__new__`` ?? when the first-bad revision is missing.""" | |
| 157 report = DUMMY_REPORT._replace(regression_range=('4', None)) | |
| 158 # TODO(wrengr): find something worthwhile to test here. | |
| 159 self.assertIsNotNone(CrashReportWithDependencies(report, self.dep_fetcher)) | |
| 160 | |
| 161 def testRegressionRangeProperty(self): | |
| 162 # Define some non-trivial crash report (i.e., not DUMMY_REPORT) | |
| 163 callstack = CallStack(0) | |
| 164 report = CrashReport( | |
| 165 crashed_version = '5', | |
| 166 signature = 'sig', | |
| 167 platform = 'canary', | |
| 168 stacktrace = Stacktrace([callstack], callstack), | |
| 169 regression_range = ('4', '5'), | |
| 170 ) | |
| 171 self.assertTupleEqual( | |
| 172 report.regression_range, | |
| 173 CrashReportWithDependencies(report, self.dep_fetcher).regression_range) | |
| 174 | |
| 175 def testSkipAddedAndDeletedRegressionRolls(self): | |
| 176 # Zipping these up to reduce repetition and chance for typos. | |
| 177 zipped_deps = [ | |
| 178 ('src/', 'https://chromium.googlesource.com/chromium/src.git', | |
| 179 '4', '5', '6'), | |
| 180 ('src/dep1', 'https://url_dep1', None, '9', '10'), | |
| 181 ] | |
| 182 | |
| 183 deps = { | |
| 184 dep_path: Dependency(dep_path, repo_url, current_revision) | |
| 185 for dep_path, repo_url, _0, _1, current_revision | |
| 186 in zipped_deps | |
| 187 } | |
| 188 self.mock(ChromeDependencyFetcher, 'GetDependency', lambda *_: deps) | |
| 189 | |
| 190 dep_rolls = { | |
| 191 dep_path: DependencyRoll(dep_path, url, old_revision, new_revision) | |
| 192 for dep_path, url, old_revision, new_revision, _ in zipped_deps | |
| 193 } | |
| 194 self.mock(ChromeDependencyFetcher, 'GetDependencyRollsDict', | |
| 195 lambda *_: dep_rolls) | |
| 196 | |
| 197 # N.B., the crash_stack must be non-empty in order to get any deps/rolls. | |
| 198 frames = [ | |
| 199 StackFrame( | |
| 200 index, dep_path, 'func', 'a.cc', '%s/a.cc' % dep_path, [42], url) | |
| 201 for index, (dep_path, url, _0, _1, _2) in enumerate(zipped_deps) | |
| 202 ] | |
| 203 callstack = CallStack(0, frames) | |
| 204 report = CrashReportWithDependencies( | |
| 205 CrashReport( | |
| 206 crashed_version = '5', | |
| 207 signature = 'sig', | |
| 208 platform = 'canary', | |
| 209 stacktrace = Stacktrace([callstack], callstack), | |
| 210 regression_range = ('4', '5')), | |
| 211 self.dep_fetcher) | |
| 212 | |
| 213 # Regression of a dep added/deleted (old_revision/new_revision is None) can | |
| 214 # not be known for sure and this case rarely happens, so just filter them | |
| 215 # out. | |
| 216 expected_regression_deps_rolls = copy.deepcopy(dep_rolls) | |
| 217 del expected_regression_deps_rolls['src/dep1'] | |
| 218 self.assertEqual(report.dependency_rolls, expected_regression_deps_rolls) | |
| OLD | NEW |