OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 """Tests for git_dates.""" | 5 """Tests for git_dates.""" |
6 | 6 |
7 import datetime | 7 import datetime |
8 import os | 8 import os |
9 import shutil | 9 import shutil |
10 import StringIO | 10 import StringIO |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 def testIgnoreFilePopulate(self): | 272 def testIgnoreFilePopulate(self): |
273 """Tests a blame ignoring the commit that added data to an empty file.""" | 273 """Tests a blame ignoring the commit that added data to an empty file.""" |
274 # Ignore A. Expect A to get blamed anyway. | 274 # Ignore A. Expect A to get blamed anyway. |
275 expected_output = [self.blame_line('B', '1) not anymore')] | 275 expected_output = [self.blame_line('B', '1) not anymore')] |
276 retval, output = self.run_hyperblame(['B'], 'some/files/empty', 'tag_B') | 276 retval, output = self.run_hyperblame(['B'], 'some/files/empty', 'tag_B') |
277 self.assertEqual(0, retval) | 277 self.assertEqual(0, retval) |
278 self.assertEqual(expected_output, output) | 278 self.assertEqual(expected_output, output) |
279 | 279 |
280 class GitHyperBlameLineMotionTest(GitHyperBlameTestBase): | 280 class GitHyperBlameLineMotionTest(GitHyperBlameTestBase): |
281 REPO_SCHEMA = """ | 281 REPO_SCHEMA = """ |
282 A B C D E | 282 A B C D E F |
283 """ | 283 """ |
284 | 284 |
285 COMMIT_A = { | 285 COMMIT_A = { |
286 'file': {'data': 'A\ngreen\nblue\n'}, | 286 'file': {'data': 'A\ngreen\nblue\n'}, |
287 } | 287 } |
288 | 288 |
289 # Change "green" to "yellow". | 289 # Change "green" to "yellow". |
290 COMMIT_B = { | 290 COMMIT_B = { |
291 'file': {'data': 'A\nyellow\nblue\n'}, | 291 'file': {'data': 'A\nyellow\nblue\n'}, |
292 } | 292 } |
293 | 293 |
294 # Insert 2 lines at the top, | 294 # Insert 2 lines at the top, |
295 # Change "yellow" to "red". | 295 # Change "yellow" to "red". |
| 296 # Insert 1 line at the bottom. |
296 COMMIT_C = { | 297 COMMIT_C = { |
297 'file': {'data': 'X\nY\nA\nred\nblue\n'}, | 298 'file': {'data': 'X\nY\nA\nred\nblue\nZ\n'}, |
298 } | 299 } |
299 | 300 |
300 # Insert 2 more lines at the top. | 301 # Insert 2 more lines at the top. |
301 COMMIT_D = { | 302 COMMIT_D = { |
302 'file': {'data': 'earth\nfire\nX\nY\nA\nred\nblue\n'}, | 303 'file': {'data': 'earth\nfire\nX\nY\nA\nred\nblue\nZ\n'}, |
303 } | 304 } |
304 | 305 |
305 # Insert a line before "red", and indent "red" and "blue". | 306 # Insert a line before "red", and indent "red" and "blue". |
306 COMMIT_E = { | 307 COMMIT_E = { |
307 'file': {'data': 'earth\nfire\nX\nY\nA\ncolors:\n red\n blue\n'}, | 308 'file': {'data': 'earth\nfire\nX\nY\nA\ncolors:\n red\n blue\nZ\n'}, |
308 } | 309 } |
309 | 310 |
| 311 # Insert a line between "A" and "colors". |
| 312 COMMIT_F = { |
| 313 'file': {'data': 'earth\nfire\nX\nY\nA\nB\ncolors:\n red\n blue\nZ\n'}, |
| 314 } |
| 315 |
| 316 def testCacheDiffHunks(self): |
| 317 """Tests the cache_diff_hunks internal function.""" |
| 318 expected_hunks = [((0, 0), (1, 2)), |
| 319 ((2, 1), (4, 1)), |
| 320 ((3, 0), (6, 1)), |
| 321 ] |
| 322 hunks = self.repo.run(self.git_hyper_blame.cache_diff_hunks, 'tag_B', |
| 323 'tag_C') |
| 324 self.assertEqual(expected_hunks, hunks) |
| 325 |
| 326 def testApproxLinenoAcrossRevs(self): |
| 327 """Tests the approx_lineno_across_revs internal function.""" |
| 328 # Note: For all of these tests, the "old revision" and "new revision" are |
| 329 # reversed, which matches the usage by hyper_blame. |
| 330 |
| 331 # Test an unchanged line before any hunks in the diff. Should be unchanged. |
| 332 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 333 'file', 'file', 'tag_B', 'tag_A', 1) |
| 334 self.assertEqual(1, lineno) |
| 335 |
| 336 # Test an unchanged line after all hunks in the diff. Should be matched to |
| 337 # the line's previous position in the file. |
| 338 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 339 'file', 'file', 'tag_D', 'tag_C', 6) |
| 340 self.assertEqual(4, lineno) |
| 341 |
| 342 # Test a line added in a new hunk. Should be matched to the line *before* |
| 343 # where the hunk was inserted in the old version of the file. |
| 344 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 345 'file', 'file', 'tag_F', 'tag_E', 6) |
| 346 self.assertEqual(5, lineno) |
| 347 |
| 348 # Test lines added in a new hunk at the very start of the file. This tests |
| 349 # an edge case: normally it would be matched to the line *before* where the |
| 350 # hunk was inserted (Line 0), but since the hunk is at the start of the |
| 351 # file, we match to Line 1. |
| 352 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 353 'file', 'file', 'tag_C', 'tag_B', 1) |
| 354 self.assertEqual(1, lineno) |
| 355 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 356 'file', 'file', 'tag_C', 'tag_B', 2) |
| 357 self.assertEqual(1, lineno) |
| 358 |
| 359 # Test an unchanged line in between hunks in the diff. Should be matched to |
| 360 # the line's previous position in the file. |
| 361 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 362 'file', 'file', 'tag_C', 'tag_B', 3) |
| 363 self.assertEqual(1, lineno) |
| 364 |
| 365 # Test a changed line. Should be matched to the hunk's previous position in |
| 366 # the file. |
| 367 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 368 'file', 'file', 'tag_C', 'tag_B', 4) |
| 369 self.assertEqual(2, lineno) |
| 370 |
| 371 # Test a line added in a new hunk at the very end of the file. Should be |
| 372 # matched to the line *before* where the hunk was inserted (the last line of |
| 373 # the file). Technically same as the case above but good to boundary test. |
| 374 lineno = self.repo.run(self.git_hyper_blame.approx_lineno_across_revs, |
| 375 'file', 'file', 'tag_C', 'tag_B', 6) |
| 376 self.assertEqual(3, lineno) |
| 377 |
310 def testInterHunkLineMotion(self): | 378 def testInterHunkLineMotion(self): |
311 """Tests a blame with line motion in another hunk in the ignored commit.""" | 379 """Tests a blame with line motion in another hunk in the ignored commit.""" |
312 # This test was mostly written as a demonstration of the limitations of the | 380 # Blame from D, ignoring C. |
313 # current algorithm (it exhibits non-ideal behaviour). | |
314 | 381 |
315 # Blame from D, ignoring C. | |
316 # Lines 1, 2 were added by D. | 382 # Lines 1, 2 were added by D. |
317 # Lines 3, 4 were added by C (but ignored, so blame A, B, respectively). | 383 # Lines 3, 4 were added by C (but ignored, so blame A). |
318 # TODO(mgiuca): Ideally, this would blame both of these lines on A, because | |
319 # they add lines nowhere near the changes made by B. | |
320 # Line 5 was added by A. | 384 # Line 5 was added by A. |
321 # Line 6 was modified by C (but ignored, so blame A). | 385 # Line 6 was modified by C (but ignored, so blame B). (Note: This requires |
322 # TODO(mgiuca): Ideally, Line 6 would be blamed on B, because that was the | 386 # the algorithm to figure out that Line 6 in D == Line 4 in C ~= Line 2 in |
323 # last commit to touch that line (changing "green" to "yellow"), but the | 387 # B, so it blames B. Otherwise, it would blame A.) |
324 # algorithm isn't yet able to figure out that Line 6 in D == Line 4 in C ~= | |
325 # Line 2 in B. | |
326 # Line 7 was added by A. | 388 # Line 7 was added by A. |
| 389 # Line 8 was added by C (but ignored, so blame A). |
327 expected_output = [self.blame_line('D', ' 1) earth'), | 390 expected_output = [self.blame_line('D', ' 1) earth'), |
328 self.blame_line('D', ' 2) fire'), | 391 self.blame_line('D', ' 2) fire'), |
329 self.blame_line('A', '3*) X'), | 392 self.blame_line('A', '3*) X'), |
330 self.blame_line('B', '4*) Y'), | 393 self.blame_line('A', '4*) Y'), |
331 self.blame_line('A', ' 5) A'), | 394 self.blame_line('A', ' 5) A'), |
332 self.blame_line('A', '6*) red'), | 395 self.blame_line('B', '6*) red'), |
333 self.blame_line('A', ' 7) blue'), | 396 self.blame_line('A', ' 7) blue'), |
| 397 self.blame_line('A', '8*) Z'), |
334 ] | 398 ] |
335 retval, output = self.run_hyperblame(['C'], 'file', 'tag_D') | 399 retval, output = self.run_hyperblame(['C'], 'file', 'tag_D') |
336 self.assertEqual(0, retval) | 400 self.assertEqual(0, retval) |
337 self.assertEqual(expected_output, output) | 401 self.assertEqual(expected_output, output) |
338 | 402 |
339 def testIntraHunkLineMotion(self): | 403 def testIntraHunkLineMotion(self): |
340 """Tests a blame with line motion in the same hunk in the ignored commit.""" | 404 """Tests a blame with line motion in the same hunk in the ignored commit.""" |
341 # This test was mostly written as a demonstration of the limitations of the | 405 # This test was mostly written as a demonstration of the limitations of the |
342 # current algorithm (it exhibits non-ideal behaviour). | 406 # current algorithm (it exhibits non-ideal behaviour). |
343 | 407 |
344 # Blame from E, ignoring E. | 408 # Blame from E, ignoring E. |
345 # Line 6 was added by E (but ignored, so blame C). | 409 # Line 6 was added by E (but ignored, so blame C). |
346 # Lines 7, 8 were modified by E (but ignored, so blame A). | 410 # Lines 7, 8 were modified by E (but ignored, so blame A). |
347 # TODO(mgiuca): Ideally, this would blame Line 7 on C, because the line | 411 # TODO(mgiuca): Ideally, this would blame Line 7 on C, because the line |
348 # "red" was added by C, and this is just a small change to that line. But | 412 # "red" was added by C, and this is just a small change to that line. But |
349 # the current algorithm can't deal with line motion within a hunk, so it | 413 # the current algorithm can't deal with line motion within a hunk, so it |
350 # just assumes Line 7 in E ~= Line 7 in D == Line 3 in A (which was "blue"). | 414 # just assumes Line 7 in E ~= Line 7 in D == Line 3 in A (which was "blue"). |
351 expected_output = [self.blame_line('D', ' 1) earth'), | 415 expected_output = [self.blame_line('D', ' 1) earth'), |
352 self.blame_line('D', ' 2) fire'), | 416 self.blame_line('D', ' 2) fire'), |
353 self.blame_line('C', ' 3) X'), | 417 self.blame_line('C', ' 3) X'), |
354 self.blame_line('C', ' 4) Y'), | 418 self.blame_line('C', ' 4) Y'), |
355 self.blame_line('A', ' 5) A'), | 419 self.blame_line('A', ' 5) A'), |
356 self.blame_line('C', '6*) colors:'), | 420 self.blame_line('C', '6*) colors:'), |
357 self.blame_line('A', '7*) red'), | 421 self.blame_line('A', '7*) red'), |
358 self.blame_line('A', '8*) blue'), | 422 self.blame_line('A', '8*) blue'), |
| 423 self.blame_line('C', ' 9) Z'), |
359 ] | 424 ] |
360 retval, output = self.run_hyperblame(['E'], 'file', 'tag_E') | 425 retval, output = self.run_hyperblame(['E'], 'file', 'tag_E') |
361 self.assertEqual(0, retval) | 426 self.assertEqual(0, retval) |
362 self.assertEqual(expected_output, output) | 427 self.assertEqual(expected_output, output) |
363 | 428 |
364 | 429 |
365 if __name__ == '__main__': | 430 if __name__ == '__main__': |
366 sys.exit(coverage_utils.covered_main( | 431 sys.exit(coverage_utils.covered_main( |
367 os.path.join(DEPOT_TOOLS_ROOT, 'git_hyper_blame.py'))) | 432 os.path.join(DEPOT_TOOLS_ROOT, 'git_hyper_blame.py'))) |
OLD | NEW |