| 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 from collections import defaultdict |
| 6 |
| 7 from common.blame import Region, Blame |
| 8 from common.change_log import ChangeLog |
| 9 from common.dependency import Dependency, DependencyRoll |
| 10 from common.git_repository import GitRepository |
| 11 from crash import findit_for_crash |
| 12 from crash.callstack import StackFrame, CallStack |
| 13 from crash.results import MatchResult |
| 14 from crash.stacktrace import Stacktrace |
| 15 from crash.test.crash_test_suite import CrashTestSuite |
| 16 |
| 17 |
| 18 DUMMY_CHANGELOG1 = ChangeLog.FromDict({ |
| 19 'author_name': 'r@chromium.org', |
| 20 'message': 'dummy', |
| 21 'committer_email': 'r@chromium.org', |
| 22 'commit_position': 175900, |
| 23 'author_email': 'r@chromium.org', |
| 24 'touched_files': [ |
| 25 { |
| 26 'change_type': 'add', |
| 27 'new_path': 'a.cc', |
| 28 'old_path': None, |
| 29 }, |
| 30 ], |
| 31 'author_time': 'Thu Mar 31 21:24:43 2016', |
| 32 'committer_time': 'Thu Mar 31 21:28:39 2016', |
| 33 'commit_url': |
| 34 'https://repo.test/+/1', |
| 35 'code_review_url': 'https://codereview.chromium.org/3281', |
| 36 'committer_name': 'example@chromium.org', |
| 37 'revision': '1', |
| 38 'reverted_revision': None |
| 39 }) |
| 40 |
| 41 DUMMY_CHANGELOG2 = ChangeLog.FromDict({ |
| 42 'author_name': 'example@chromium.org', |
| 43 'message': 'dummy', |
| 44 'committer_email': 'example@chromium.org', |
| 45 'commit_position': 175976, |
| 46 'author_email': 'example@chromium.org', |
| 47 'touched_files': [ |
| 48 { |
| 49 'change_type': 'add', |
| 50 'new_path': 'f0.cc', |
| 51 'old_path': 'b/f0.cc' |
| 52 }, |
| 53 ], |
| 54 'author_time': 'Thu Mar 31 21:24:43 2016', |
| 55 'committer_time': 'Thu Mar 31 21:28:39 2016', |
| 56 'commit_url': |
| 57 'https://repo.test/+/2', |
| 58 'code_review_url': 'https://codereview.chromium.org/3281', |
| 59 'committer_name': 'example@chromium.org', |
| 60 'revision': '2', |
| 61 'reverted_revision': '1' |
| 62 }) |
| 63 |
| 64 DUMMY_CHANGELOG3 = ChangeLog.FromDict({ |
| 65 'author_name': 'e@chromium.org', |
| 66 'message': 'dummy', |
| 67 'committer_email': 'e@chromium.org', |
| 68 'commit_position': 176000, |
| 69 'author_email': 'e@chromium.org', |
| 70 'touched_files': [ |
| 71 { |
| 72 'change_type': 'modify', |
| 73 'new_path': 'f.cc', |
| 74 'old_path': 'f.cc' |
| 75 }, |
| 76 { |
| 77 'change_type': 'delete', |
| 78 'new_path': None, |
| 79 'old_path': 'f1.cc' |
| 80 }, |
| 81 ], |
| 82 'author_time': 'Thu Apr 1 21:24:43 2016', |
| 83 'committer_time': 'Thu Apr 1 21:28:39 2016', |
| 84 'commit_url': |
| 85 'https://repo.test/+/3', |
| 86 'code_review_url': 'https://codereview.chromium.org/3281', |
| 87 'committer_name': 'example@chromium.org', |
| 88 'revision': '3', |
| 89 'reverted_revision': None |
| 90 }) |
| 91 |
| 92 |
| 93 class FinditForCrashTest(CrashTestSuite): |
| 94 |
| 95 def testGetDepsInCrashStack(self): |
| 96 crash_stack = CallStack(0) |
| 97 crash_stack.extend([ |
| 98 StackFrame(0, 'src/', '', 'func0', 'f0.cc', [1]), |
| 99 StackFrame(1, 'src/', '', 'func1', 'f1.cc', [2, 3]), |
| 100 ]) |
| 101 crash_deps = {'src/': Dependency('src/', 'https://chromium_repo', '1'), |
| 102 'src/v8/': Dependency('src/v8/', 'https://v8_repo', '2')} |
| 103 |
| 104 expected_stack_deps = {'src/': crash_deps['src/']} |
| 105 |
| 106 self.assertEqual( |
| 107 findit_for_crash.GetDepsInCrashStack(crash_stack, crash_deps), |
| 108 expected_stack_deps) |
| 109 |
| 110 def testGetChangeLogsForFilesGroupedByDeps(self): |
| 111 regression_deps_rolls = { |
| 112 'src/dep1': { |
| 113 'path': 'src/dep1', |
| 114 'repo_url': 'https://url_dep1', |
| 115 'old_revision': '7', |
| 116 'new_revision': '9', |
| 117 }, |
| 118 'src/dep2': { |
| 119 'path': 'src/dep2', |
| 120 'repo_url': 'https://url_dep2', |
| 121 'old_revision': '3', |
| 122 'new_revision': None, |
| 123 }, |
| 124 'src/': { |
| 125 'path': 'src/', |
| 126 'repo_url': ('https://chromium.googlesource.com/chromium/' |
| 127 'src.git'), |
| 128 'old_revision': '4', |
| 129 'new_revision': '5', |
| 130 }, |
| 131 } |
| 132 |
| 133 stack_deps = { |
| 134 'src/': Dependency('src/', 'https://url_src', 'rev1', 'DEPS'), |
| 135 } |
| 136 |
| 137 def _MockGetChangeLogs(*_): |
| 138 return [DUMMY_CHANGELOG1, DUMMY_CHANGELOG2, DUMMY_CHANGELOG3] |
| 139 |
| 140 self.mock(GitRepository, 'GetChangeLogs', _MockGetChangeLogs) |
| 141 |
| 142 dep_file_to_changelogs, ignore_cls = ( |
| 143 findit_for_crash.GetChangeLogsForFilesGroupedByDeps( |
| 144 regression_deps_rolls, stack_deps)) |
| 145 dep_file_to_changelogs_json = defaultdict(lambda: defaultdict(list)) |
| 146 for dep, file_to_changelogs in dep_file_to_changelogs.iteritems(): |
| 147 for file_path, changelogs in file_to_changelogs.iteritems(): |
| 148 for changelog in changelogs: |
| 149 dep_file_to_changelogs_json[dep][file_path].append(changelog.ToDict()) |
| 150 |
| 151 expected_dep_file_to_changelogs_json = { |
| 152 'src/': { |
| 153 'a.cc': [DUMMY_CHANGELOG1.ToDict()], |
| 154 'f.cc': [DUMMY_CHANGELOG3.ToDict()] |
| 155 } |
| 156 } |
| 157 self.assertEqual(dep_file_to_changelogs_json, |
| 158 expected_dep_file_to_changelogs_json) |
| 159 self.assertEqual(ignore_cls, set(['1'])) |
| 160 |
| 161 def testGetStackInfosForFilesGroupedByDeps(self): |
| 162 |
| 163 main_stack = CallStack(0) |
| 164 main_stack.extend( |
| 165 [StackFrame(0, 'src/', '', 'c(p* &d)', 'a.cc', [177]), |
| 166 StackFrame(1, 'src/', '', 'd(a* c)', 'a.cc', [227, 228, 229]), |
| 167 StackFrame(2, 'src/v8/', '', 'e(int)', 'b.cc', [87, 88, 89, 90])]) |
| 168 |
| 169 low_priority_stack = CallStack(1) |
| 170 low_priority_stack.append( |
| 171 StackFrame(0, 'src/dummy/', '', 'c(p* &d)', 'd.cc', [17])) |
| 172 |
| 173 stacktrace = Stacktrace() |
| 174 stacktrace.extend([main_stack, low_priority_stack]) |
| 175 |
| 176 crashed_deps = {'src/': Dependency('src/', 'https//repo', '2'), |
| 177 'src/v8/': Dependency('src/v8', 'https//repo', '1')} |
| 178 |
| 179 expected_dep_file_to_stack_infos = { |
| 180 'src/': { |
| 181 'a.cc': [ |
| 182 (main_stack[0], 0), |
| 183 (main_stack[1], 0), |
| 184 ], |
| 185 }, |
| 186 'src/v8/': { |
| 187 'b.cc': [ |
| 188 (main_stack[2], 0), |
| 189 ] |
| 190 } |
| 191 } |
| 192 |
| 193 dep_file_to_stack_infos = ( |
| 194 findit_for_crash.GetStackInfosForFilesGroupedByDeps( |
| 195 stacktrace, crashed_deps)) |
| 196 |
| 197 self.assertEqual(len(dep_file_to_stack_infos), |
| 198 len(expected_dep_file_to_stack_infos)) |
| 199 |
| 200 for dep, file_to_stack_infos in dep_file_to_stack_infos.iteritems(): |
| 201 self.assertTrue(dep in expected_dep_file_to_stack_infos) |
| 202 expected_file_to_stack_infos = expected_dep_file_to_stack_infos[dep] |
| 203 |
| 204 for file_path, stack_infos in file_to_stack_infos.iteritems(): |
| 205 self.assertTrue(file_path in expected_file_to_stack_infos) |
| 206 expected_stack_infos = expected_file_to_stack_infos[file_path] |
| 207 |
| 208 self._VerifyTwoStackInfosEqual(stack_infos, expected_stack_infos) |
| 209 |
| 210 def testGetBlameForFilesGroupedByDeps(self): |
| 211 |
| 212 dummy_blame = Blame('1', 'a.cc') |
| 213 dummy_blame.AddRegion( |
| 214 Region(1, 5, '0', 'a', 'a@email.com', 'Thu Mar 25 21:24:43 2016')) |
| 215 dummy_blame.AddRegion( |
| 216 Region(6, 10, '1', 'b', 'b@email.com', 'Thu Mar 31 21:24:43 2016')) |
| 217 |
| 218 def _MockGetBlame(*_): |
| 219 return dummy_blame |
| 220 |
| 221 self.mock(GitRepository, 'GetBlame', _MockGetBlame) |
| 222 |
| 223 main_stack = CallStack(0) |
| 224 main_stack.extend( |
| 225 [StackFrame(0, 'src/', '', 'c(p* &d)', 'a.cc', [177]), |
| 226 StackFrame(1, 'src/', '', 'd(a* c)', 'a.cc', [227, 228, 229])]) |
| 227 |
| 228 low_priority_stack = CallStack(1) |
| 229 low_priority_stack.append( |
| 230 StackFrame(0, 'dummy_dep/', '', 'k(p* &d)', 'h.cc', [17])) |
| 231 |
| 232 stacktrace = Stacktrace() |
| 233 stacktrace.extend([main_stack, low_priority_stack]) |
| 234 |
| 235 crash_deps = {'src/': Dependency('src/', 'https://chromium_repo', '1')} |
| 236 |
| 237 dep_file_to_blame = findit_for_crash.GetBlameForFilesGroupedByDeps( |
| 238 stacktrace, |
| 239 findit_for_crash.GetDepsInCrashStack(main_stack, crash_deps)) |
| 240 |
| 241 expected_dep_file_to_blame = { |
| 242 'src/': { |
| 243 'a.cc': dummy_blame |
| 244 } |
| 245 } |
| 246 |
| 247 for dep, file_to_blame in dep_file_to_blame.iteritems(): |
| 248 |
| 249 self.assertTrue(dep in expected_dep_file_to_blame) |
| 250 |
| 251 expected_file_to_blame = expected_dep_file_to_blame[dep] |
| 252 for file_path, blame in file_to_blame.iteritems(): |
| 253 self.assertTrue(file_path in expected_file_to_blame) |
| 254 expected_blame = expected_file_to_blame[file_path] |
| 255 |
| 256 self._VerifyTwoBlamesEqual(blame, expected_blame) |
| 257 |
| 258 def testFindMatchResults(self): |
| 259 dep_file_to_changelogs = { |
| 260 'src/': { |
| 261 'a.cc': [ |
| 262 DUMMY_CHANGELOG1, |
| 263 ] |
| 264 } |
| 265 } |
| 266 |
| 267 dep_file_to_stack_infos = { |
| 268 'src/': { |
| 269 'a.cc': [ |
| 270 (StackFrame(0, 'src/', '', 'func', 'a.cc', [1]), 0), |
| 271 (StackFrame(1, 'src/', '', 'func', 'a.cc', [7]), 0), |
| 272 ], |
| 273 'b.cc': [ |
| 274 (StackFrame(2, 'src/', '', 'func', 'b.cc', [36]), 0), |
| 275 ] |
| 276 } |
| 277 } |
| 278 |
| 279 dummy_blame = Blame('9', 'a.cc') |
| 280 dummy_blame.AddRegion( |
| 281 Region(1, 5, '6', 'a', 'a@chromium.org', 'Thu Mar 31 21:24:43 2016')) |
| 282 dummy_blame.AddRegion( |
| 283 Region(6, 10, '1', 'b', 'b@chromium.org', 'Thu Jun 19 12:11:40 2015')) |
| 284 |
| 285 dep_file_to_blame = { |
| 286 'src/': { |
| 287 'a.cc': dummy_blame |
| 288 } |
| 289 } |
| 290 |
| 291 expected_match_results = [{ |
| 292 'url': 'https://repo.test/+/1', |
| 293 'revision': '1', |
| 294 'dep_path': 'src/', |
| 295 'component': '', |
| 296 'author': 'r@chromium.org', |
| 297 'time': 'Thu Mar 31 21:24:43 2016', |
| 298 'reason': None, |
| 299 'confidence': None, |
| 300 }] |
| 301 |
| 302 match_results = findit_for_crash.FindMatchResults(dep_file_to_changelogs, |
| 303 dep_file_to_stack_infos, |
| 304 dep_file_to_blame) |
| 305 self.assertEqual([result.ToDict() for result in match_results], |
| 306 expected_match_results) |
| 307 |
| 308 def testFindItForCrashNoRegressionRange(self): |
| 309 self.assertEqual( |
| 310 findit_for_crash.FindItForCrash(Stacktrace(), {}, {}), |
| 311 []) |
| 312 |
| 313 def testFindItForCrashNoMatchFound(self): |
| 314 |
| 315 def _MockFindMatchResults(*_): |
| 316 return [] |
| 317 |
| 318 self.mock(findit_for_crash, 'FindMatchResults', _MockFindMatchResults) |
| 319 |
| 320 regression_deps_rolls = {'src/': DependencyRoll('src/', 'https://repo', |
| 321 '1', '2')} |
| 322 self.assertEqual(findit_for_crash.FindItForCrash( |
| 323 Stacktrace(), regression_deps_rolls, {}), []) |
| 324 |
| 325 def testFindItForCrash(self): |
| 326 |
| 327 def _MockFindMatchResults(*_): |
| 328 match_result1 = MatchResult(DUMMY_CHANGELOG1, 'src/', '') |
| 329 match_result1.file_to_stack_infos = { |
| 330 'a.cc': [ |
| 331 (StackFrame(0, 'src/', '', 'func', 'a.cc', [1]), 0), |
| 332 (StackFrame(1, 'src/', '', 'func', 'a.cc', [7]), 0), |
| 333 ] |
| 334 } |
| 335 match_result1.min_distance = 0 |
| 336 |
| 337 match_result2 = MatchResult(DUMMY_CHANGELOG3, 'src/', '') |
| 338 match_result2.file_to_stack_infos = { |
| 339 'f.cc': [ |
| 340 (StackFrame(5, 'src/', '', 'func', 'f.cc', [1]), 0), |
| 341 ] |
| 342 } |
| 343 match_result2.min_distance = 20 |
| 344 |
| 345 return [match_result1, match_result2] |
| 346 |
| 347 self.mock(findit_for_crash, 'FindMatchResults', _MockFindMatchResults) |
| 348 |
| 349 expected_match_results = [ |
| 350 { |
| 351 'url': 'https://repo.test/+/1', |
| 352 'revision': '1', |
| 353 'dep_path': 'src/', |
| 354 'component': '', |
| 355 'author': 'r@chromium.org', |
| 356 'time': 'Thu Mar 31 21:24:43 2016', |
| 357 'reason': ('1. Top frame changed is frame #0 (score: 1)\n' |
| 358 '2. Minimum distance to crashed line is 0 (score: 1)\n' |
| 359 '\nChanged file a.cc which crashed in func (#0)' |
| 360 ', func (#1)'), |
| 361 'confidence': 1, |
| 362 }, |
| 363 { |
| 364 'url': 'https://repo.test/+/3', |
| 365 'revision': '3', |
| 366 'dep_path': 'src/', |
| 367 'component': '', |
| 368 'author': 'e@chromium.org', |
| 369 'time': 'Thu Apr 1 21:24:43 2016', |
| 370 'reason': ('1. Top frame changed is frame #5 (score: 0)\n' |
| 371 '2. Minimum distance to crashed line is 20 (score: 0)\n' |
| 372 '\nChanged file f.cc which crashed in func (#5)'), |
| 373 'confidence': 0.22857142857142856, |
| 374 }, |
| 375 ] |
| 376 |
| 377 regression_deps_rolls = {'src/': DependencyRoll('src/', 'https://repo', |
| 378 '1', '2')} |
| 379 |
| 380 self.assertEqual(findit_for_crash.FindItForCrash( |
| 381 Stacktrace(), regression_deps_rolls, {}), expected_match_results) |
| OLD | NEW |