| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 #!/usr/bin/env python | 
|  | 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 | 
|  | 4 # found in the LICENSE file. | 
|  | 5 """Tests for git_dates.""" | 
|  | 6 | 
|  | 7 import datetime | 
|  | 8 import os | 
|  | 9 import StringIO | 
|  | 10 import sys | 
|  | 11 import unittest | 
|  | 12 | 
|  | 13 DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 
|  | 14 sys.path.insert(0, DEPOT_TOOLS_ROOT) | 
|  | 15 | 
|  | 16 from testing_support import coverage_utils | 
|  | 17 from testing_support import git_test_utils | 
|  | 18 | 
|  | 19 import git_common | 
|  | 20 | 
|  | 21 | 
|  | 22 class GitHyperBlameTestBase(git_test_utils.GitRepoReadOnlyTestBase): | 
|  | 23   @classmethod | 
|  | 24   def setUpClass(cls): | 
|  | 25     super(GitHyperBlameTestBase, cls).setUpClass() | 
|  | 26     import git_hyper_blame | 
|  | 27     cls.git_hyper_blame = git_hyper_blame | 
|  | 28 | 
|  | 29   def run_hyperblame(self, ignored, filename, revision): | 
|  | 30     buf = StringIO.StringIO() | 
|  | 31     ignored = [self.repo[c] for c in ignored] | 
|  | 32     retval = self.repo.run(self.git_hyper_blame.hyperblame, ignored, filename, | 
|  | 33                            revision=revision, out=buf) | 
|  | 34     return retval, buf.getvalue().rstrip().split('\n') | 
|  | 35 | 
|  | 36   def blame_line(self, commit_name, rest, filename=None): | 
|  | 37     """Generate a blame line from a commit. | 
|  | 38 | 
|  | 39     Args: | 
|  | 40       commit_name: The commit's schema name. | 
|  | 41       rest: The blame line after the timestamp. e.g., '2) file2 - merged'. | 
|  | 42     """ | 
|  | 43     short = self.repo[commit_name][:8] | 
|  | 44     start = '%s %s' % (short, filename) if filename else short | 
|  | 45     author = self.repo.show_commit(commit_name, format_string='%an %ai') | 
|  | 46     return '%s (%s %s' % (start, author, rest) | 
|  | 47 | 
|  | 48 class GitHyperBlameSimpleTest(GitHyperBlameTestBase): | 
|  | 49   REPO_SCHEMA = """ | 
|  | 50   A B D E F G H | 
|  | 51   A C D | 
|  | 52   """ | 
|  | 53 | 
|  | 54   COMMIT_A = { | 
|  | 55     'some/files/file1': {'data': 'file1'}, | 
|  | 56     'some/files/file2': {'data': 'file2'}, | 
|  | 57     'some/files/empty': {'data': ''}, | 
|  | 58     'some/other/file':  {'data': 'otherfile'}, | 
|  | 59   } | 
|  | 60 | 
|  | 61   COMMIT_B = { | 
|  | 62     'some/files/file2': { | 
|  | 63       'mode': 0755, | 
|  | 64       'data': 'file2 - vanilla\n'}, | 
|  | 65     'some/files/empty': {'data': 'not anymore'}, | 
|  | 66     'some/files/file3': {'data': 'file3'}, | 
|  | 67   } | 
|  | 68 | 
|  | 69   COMMIT_C = { | 
|  | 70     'some/files/file2': {'data': 'file2 - merged\n'}, | 
|  | 71   } | 
|  | 72 | 
|  | 73   COMMIT_D = { | 
|  | 74     'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged\n'}, | 
|  | 75   } | 
|  | 76 | 
|  | 77   COMMIT_E = { | 
|  | 78     'some/files/file2': {'data': 'file2 - vanilla\nfile_x - merged\n'}, | 
|  | 79   } | 
|  | 80 | 
|  | 81   COMMIT_F = { | 
|  | 82     'some/files/file2': {'data': 'file2 - vanilla\nfile_y - merged\n'}, | 
|  | 83   } | 
|  | 84 | 
|  | 85   # Move file2 from files to other. | 
|  | 86   COMMIT_G = { | 
|  | 87     'some/files/file2': {'data': None}, | 
|  | 88     'some/other/file2': {'data': 'file2 - vanilla\nfile_y - merged\n'}, | 
|  | 89   } | 
|  | 90 | 
|  | 91   COMMIT_H = { | 
|  | 92     'some/other/file2': {'data': 'file2 - vanilla\nfile_z - merged\n'}, | 
|  | 93   } | 
|  | 94 | 
|  | 95   def testBlameError(self): | 
|  | 96     """Tests a blame on a non-existent file.""" | 
|  | 97     expected_output = [''] | 
|  | 98     retval, output = self.run_hyperblame([], 'some/other/file2', 'tag_D') | 
|  | 99     self.assertNotEqual(0, retval) | 
|  | 100     self.assertEqual(expected_output, output) | 
|  | 101 | 
|  | 102   def testBlameEmpty(self): | 
|  | 103     """Tests a blame of an empty file with no ignores.""" | 
|  | 104     expected_output = [''] | 
|  | 105     retval, output = self.run_hyperblame([], 'some/files/empty', 'tag_A') | 
|  | 106     self.assertEqual(0, retval) | 
|  | 107     self.assertEqual(expected_output, output) | 
|  | 108 | 
|  | 109   def testBasicBlame(self): | 
|  | 110     """Tests a basic blame with no ignores.""" | 
|  | 111     # Expect to blame line 1 on B, line 2 on C. | 
|  | 112     expected_output = [self.blame_line('B', '1) file2 - vanilla'), | 
|  | 113                        self.blame_line('C', '2) file2 - merged')] | 
|  | 114     retval, output = self.run_hyperblame([], 'some/files/file2', 'tag_D') | 
|  | 115     self.assertEqual(0, retval) | 
|  | 116     self.assertEqual(expected_output, output) | 
|  | 117 | 
|  | 118   def testBlameRenamed(self): | 
|  | 119     """Tests a blame with no ignores on a renamed file.""" | 
|  | 120     # Expect to blame line 1 on B, line 2 on H. | 
|  | 121     # Because the file has a different name than it had when (some of) these | 
|  | 122     # lines were changed, expect the filenames to be displayed. | 
|  | 123     expected_output = [self.blame_line('B', '1) file2 - vanilla', | 
|  | 124                                        filename='some/files/file2'), | 
|  | 125                        self.blame_line('H', '2) file_z - merged', | 
|  | 126                                        filename='some/other/file2')] | 
|  | 127     retval, output = self.run_hyperblame([], 'some/other/file2', 'tag_H') | 
|  | 128     self.assertEqual(0, retval) | 
|  | 129     self.assertEqual(expected_output, output) | 
|  | 130 | 
|  | 131   def testIgnoreSimpleEdits(self): | 
|  | 132     """Tests a blame with simple (line-level changes) commits ignored.""" | 
|  | 133     # Expect to blame line 1 on B, line 2 on E. | 
|  | 134     expected_output = [self.blame_line('B', '1) file2 - vanilla'), | 
|  | 135                        self.blame_line('E', '2) file_x - merged')] | 
|  | 136     retval, output = self.run_hyperblame([], 'some/files/file2', 'tag_E') | 
|  | 137     self.assertEqual(0, retval) | 
|  | 138     self.assertEqual(expected_output, output) | 
|  | 139 | 
|  | 140     # Ignore E; blame line 1 on B, line 2 on C. | 
|  | 141     expected_output = [self.blame_line('B', ' 1) file2 - vanilla'), | 
|  | 142                        self.blame_line('C', '2*) file_x - merged')] | 
|  | 143     retval, output = self.run_hyperblame(['E'], 'some/files/file2', 'tag_E') | 
|  | 144     self.assertEqual(0, retval) | 
|  | 145     self.assertEqual(expected_output, output) | 
|  | 146 | 
|  | 147     # Ignore E and F; blame line 1 on B, line 2 on C. | 
|  | 148     expected_output = [self.blame_line('B', ' 1) file2 - vanilla'), | 
|  | 149                        self.blame_line('C', '2*) file_y - merged')] | 
|  | 150     retval, output = self.run_hyperblame(['E', 'F'], 'some/files/file2', | 
|  | 151                                          'tag_F') | 
|  | 152     self.assertEqual(0, retval) | 
|  | 153     self.assertEqual(expected_output, output) | 
|  | 154 | 
|  | 155   def testIgnoreInitialCommit(self): | 
|  | 156     """Tests a blame with the initial commit ignored.""" | 
|  | 157     # Ignore A. Expect A to get blamed anyway. | 
|  | 158     expected_output = [self.blame_line('A', '1) file1')] | 
|  | 159     retval, output = self.run_hyperblame(['A'], 'some/files/file1', 'tag_A') | 
|  | 160     self.assertEqual(0, retval) | 
|  | 161     self.assertEqual(expected_output, output) | 
|  | 162 | 
|  | 163   def testIgnoreFileAdd(self): | 
|  | 164     """Tests a blame ignoring the commit that added this file.""" | 
|  | 165     # Ignore A. Expect A to get blamed anyway. | 
|  | 166     expected_output = [self.blame_line('B', '1) file3')] | 
|  | 167     retval, output = self.run_hyperblame(['B'], 'some/files/file3', 'tag_B') | 
|  | 168     self.assertEqual(0, retval) | 
|  | 169     self.assertEqual(expected_output, output) | 
|  | 170 | 
|  | 171   def testIgnoreFilePopulate(self): | 
|  | 172     """Tests a blame ignoring the commit that added data to an empty file.""" | 
|  | 173     # Ignore A. Expect A to get blamed anyway. | 
|  | 174     expected_output = [self.blame_line('B', '1) not anymore')] | 
|  | 175     retval, output = self.run_hyperblame(['B'], 'some/files/empty', 'tag_B') | 
|  | 176     self.assertEqual(0, retval) | 
|  | 177     self.assertEqual(expected_output, output) | 
|  | 178 | 
|  | 179 class GitHyperBlameLineMotionTest(GitHyperBlameTestBase): | 
|  | 180   REPO_SCHEMA = """ | 
|  | 181   A B C D E | 
|  | 182   """ | 
|  | 183 | 
|  | 184   COMMIT_A = { | 
|  | 185     'file':  {'data': 'A\ngreen\nblue\n'}, | 
|  | 186   } | 
|  | 187 | 
|  | 188   # Change "green" to "yellow". | 
|  | 189   COMMIT_B = { | 
|  | 190     'file': {'data': 'A\nyellow\nblue\n'}, | 
|  | 191   } | 
|  | 192 | 
|  | 193   # Insert 2 lines at the top, | 
|  | 194   # Change "yellow" to "red". | 
|  | 195   COMMIT_C = { | 
|  | 196     'file': {'data': 'X\nY\nA\nred\nblue\n'}, | 
|  | 197   } | 
|  | 198 | 
|  | 199   # Insert 2 more lines at the top. | 
|  | 200   COMMIT_D = { | 
|  | 201     'file': {'data': 'earth\nfire\nX\nY\nA\nred\nblue\n'}, | 
|  | 202   } | 
|  | 203 | 
|  | 204   # Insert a line before "red", and indent "red" and "blue". | 
|  | 205   COMMIT_E = { | 
|  | 206     'file': {'data': 'earth\nfire\nX\nY\nA\ncolors:\n red\n blue\n'}, | 
|  | 207   } | 
|  | 208 | 
|  | 209   def testInterHunkLineMotion(self): | 
|  | 210     """Tests a blame with line motion in another hunk in the ignored commit.""" | 
|  | 211     # This test was mostly written as a demonstration of the limitations of the | 
|  | 212     # current algorithm (it exhibits non-ideal behaviour). | 
|  | 213 | 
|  | 214     # Blame from D, ignoring C. | 
|  | 215     # Lines 1, 2 were added by D. | 
|  | 216     # Lines 3, 4 were added by C (but ignored, so blame A, B, respectively). | 
|  | 217     # TODO(mgiuca): Ideally, this would blame both of these lines on A, because | 
|  | 218     # they add lines nowhere near the changes made by B. | 
|  | 219     # Line 5 was added by A. | 
|  | 220     # Line 6 was modified by C (but ignored, so blame A). | 
|  | 221     # TODO(mgiuca): Ideally, Line 6 would be blamed on B, because that was the | 
|  | 222     # last commit to touch that line (changing "green" to "yellow"), but the | 
|  | 223     # algorithm isn't yet able to figure out that Line 6 in D == Line 4 in C ~= | 
|  | 224     # Line 2 in B. | 
|  | 225     # Line 7 was added by A. | 
|  | 226     expected_output = [self.blame_line('D', ' 1) earth'), | 
|  | 227                        self.blame_line('D', ' 2) fire'), | 
|  | 228                        self.blame_line('A', '3*) X'), | 
|  | 229                        self.blame_line('B', '4*) Y'), | 
|  | 230                        self.blame_line('A', ' 5) A'), | 
|  | 231                        self.blame_line('A', '6*) red'), | 
|  | 232                        self.blame_line('A', ' 7) blue'), | 
|  | 233                        ] | 
|  | 234     retval, output = self.run_hyperblame(['C'], 'file', 'tag_D') | 
|  | 235     self.assertEqual(0, retval) | 
|  | 236     self.assertEqual(expected_output, output) | 
|  | 237 | 
|  | 238   def testIntraHunkLineMotion(self): | 
|  | 239     """Tests a blame with line motion in the same hunk in the ignored commit.""" | 
|  | 240     # This test was mostly written as a demonstration of the limitations of the | 
|  | 241     # current algorithm (it exhibits non-ideal behaviour). | 
|  | 242 | 
|  | 243     # Blame from E, ignoring E. | 
|  | 244     # Line 6 was added by E (but ignored, so blame C). | 
|  | 245     # Lines 7, 8 were modified by E (but ignored, so blame A). | 
|  | 246     # TODO(mgiuca): Ideally, this would blame Line 7 on C, because the line | 
|  | 247     # "red" was added by C, and this is just a small change to that line. But | 
|  | 248     # the current algorithm can't deal with line motion within a hunk, so it | 
|  | 249     # just assumes Line 7 in E ~= Line 7 in D == Line 3 in A (which was "blue"). | 
|  | 250     expected_output = [self.blame_line('D', ' 1) earth'), | 
|  | 251                        self.blame_line('D', ' 2) fire'), | 
|  | 252                        self.blame_line('C', ' 3) X'), | 
|  | 253                        self.blame_line('C', ' 4) Y'), | 
|  | 254                        self.blame_line('A', ' 5) A'), | 
|  | 255                        self.blame_line('C', '6*) colors:'), | 
|  | 256                        self.blame_line('A', '7*)  red'), | 
|  | 257                        self.blame_line('A', '8*)  blue'), | 
|  | 258                        ] | 
|  | 259     retval, output = self.run_hyperblame(['E'], 'file', 'tag_E') | 
|  | 260     self.assertEqual(0, retval) | 
|  | 261     self.assertEqual(expected_output, output) | 
|  | 262 | 
|  | 263 | 
|  | 264 if __name__ == '__main__': | 
|  | 265   sys.exit(coverage_utils.covered_main( | 
|  | 266     os.path.join(DEPOT_TOOLS_ROOT, 'git_hyper_blame.py'))) | 
| OLD | NEW | 
|---|