def test_diff_with_rename(self): output = StringProcessAdapter(fixture('diff_rename')) diffs = Diff._index_from_patch_format(self.rorepo, output) self._assert_diff_format(diffs) self.assertEqual(1, len(diffs)) diff = diffs[0] self.assertTrue(diff.renamed_file) self.assertTrue(diff.renamed) self.assertEqual(diff.rename_from, 'Jérôme') self.assertEqual(diff.rename_to, 'müller') self.assertEqual(diff.raw_rename_from, b'J\xc3\xa9r\xc3\xb4me') self.assertEqual(diff.raw_rename_to, b'm\xc3\xbcller') assert isinstance(str(diff), str) output = StringProcessAdapter(to_raw(fixture('diff_rename_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] self.assertIsNotNone(diff.renamed_file) self.assertIsNotNone(diff.renamed) self.assertEqual(diff.rename_from, 'this') self.assertEqual(diff.rename_to, 'that') self.assertEqual(diff.change_type, 'R') self.assertEqual(diff.score, 100) self.assertEqual(len(list(diffs.iter_change_type('R'))), 1)
def test_should_display_blame_information(self, git): git.return_value = fixture('blame') b = self.rorepo.blame('master', 'lib/git.py') self.assertEqual(13, len(b)) self.assertEqual(2, len(b[0])) # self.assertEqual(25, reduce(lambda acc, x: acc + len(x[-1]), b)) self.assertEqual(hash(b[0][0]), hash(b[9][0])) c = b[0][0] self.assertTrue(git.called) self.assertEqual('634396b2f541a9f2d58b00be1a07f0c358b999b3', c.hexsha) self.assertEqual('Tom Preston-Werner', c.author.name) self.assertEqual('*****@*****.**', c.author.email) self.assertEqual(1191997100, c.authored_date) self.assertEqual('Tom Preston-Werner', c.committer.name) self.assertEqual('*****@*****.**', c.committer.email) self.assertEqual(1191997100, c.committed_date) self.assertRaisesRegex( ValueError, "634396b2f541a9f2d58b00be1a07f0c358b999b3 missing", lambda: c.message) # test the 'lines per commit' entries tlist = b[0][1] self.assertTrue(tlist) self.assertTrue(isinstance(tlist[0], str)) self.assertTrue( len(tlist) < sum(len(t) for t in tlist)) # test for single-char bug # BINARY BLAME git.return_value = fixture('blame_binary') blames = self.rorepo.blame('master', 'rps') self.assertEqual(len(blames), 2)
def test_diff_with_change_in_type(self): output = StringProcessAdapter(fixture('diff_change_in_type')) diffs = Diff._index_from_patch_format(self.rorepo, output) self._assert_diff_format(diffs) self.assertEqual(2, len(diffs)) diff = diffs[0] self.assertIsNotNone(diff.deleted_file) self.assertEqual(diff.a_path, 'this') self.assertEqual(diff.b_path, 'this') assert isinstance(str(diff), str) diff = diffs[1] self.assertEqual(diff.a_path, None) self.assertEqual(diff.b_path, 'this') self.assertIsNotNone(diff.new_file) assert isinstance(str(diff), str) output = StringProcessAdapter( to_raw(fixture('diff_change_in_type_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] self.assertEqual(diff.rename_from, None) self.assertEqual(diff.rename_to, None) self.assertEqual(diff.change_type, 'T') self.assertEqual(len(list(diffs.iter_change_type('T'))), 1)
def test_diff_unsafe_paths(self): output = StringProcessAdapter(fixture('diff_patch_unsafe_paths')) res = Diff._index_from_patch_format(None, output) # The "Additions" self.assertEqual(res[0].b_path, 'path/ starting with a space') self.assertEqual(res[1].b_path, 'path/"with-quotes"') self.assertEqual(res[2].b_path, "path/'with-single-quotes'") self.assertEqual(res[3].b_path, 'path/ending in a space ') self.assertEqual(res[4].b_path, 'path/with\ttab') self.assertEqual(res[5].b_path, 'path/with\nnewline') self.assertEqual(res[6].b_path, 'path/with spaces') self.assertEqual(res[7].b_path, 'path/with-question-mark?') self.assertEqual(res[8].b_path, 'path/¯\\_(ツ)_|¯') self.assertEqual(res[9].b_path, 'path/💩.txt') self.assertEqual(res[9].b_rawpath, b'path/\xf0\x9f\x92\xa9.txt') self.assertEqual(res[10].b_path, 'path/�-invalid-unicode-path.txt') self.assertEqual(res[10].b_rawpath, b'path/\x80-invalid-unicode-path.txt') # The "Moves" # NOTE: The path prefixes a/ and b/ here are legit! We're actually # verifying that it's not "a/a/" that shows up, see the fixture data. self.assertEqual(res[11].a_path, 'a/with spaces') # NOTE: path a/ here legit! self.assertEqual(res[11].b_path, 'b/with some spaces') # NOTE: path b/ here legit! self.assertEqual(res[12].a_path, 'a/ending in a space ') self.assertEqual(res[12].b_path, 'b/ending with space ') self.assertEqual(res[13].a_path, 'a/"with-quotes"') self.assertEqual(res[13].b_path, 'b/"with even more quotes"')
def test_uncommon_branch_names(self): stderr_lines = fixture('uncommon_branch_prefix_stderr').decode( 'ascii').splitlines() fetch_lines = fixture('uncommon_branch_prefix_FETCH_HEAD').decode( 'ascii').splitlines() # The contents of the files above must be fetched with a custom refspec: # +refs/pull/*:refs/heads/pull/* res = [ FetchInfo._from_line('ShouldntMatterRepo', stderr, fetch_line) for stderr, fetch_line in zip(stderr_lines, fetch_lines) ] self.assertGreater(len(res), 0) self.assertEqual(res[0].remote_ref_path, 'refs/pull/1/head') self.assertEqual(res[0].ref.path, 'refs/heads/pull/1/head') self.assertIsInstance(res[0].ref, Head)
def test_index_file_base(self): # read from file index = IndexFile(self.rorepo, fixture_path("index")) assert index.entries assert index.version > 0 # test entry entry = next(iter(index.entries.values())) for attr in ("path", "ctime", "mtime", "dev", "inode", "mode", "uid", "gid", "size", "binsha", "hexsha", "stage"): getattr(entry, attr) # END for each method # test update entries = index.entries assert isinstance(index.update(), IndexFile) assert entries is not index.entries # test stage index_merge = IndexFile(self.rorepo, fixture_path("index_merge")) self.assertEqual(len(index_merge.entries), 106) assert len([e for e in index_merge.entries.values() if e.stage != 0]) # write the data - it must match the original tmpfile = tempfile.mktemp() index_merge.write(tmpfile) with open(tmpfile, 'rb') as fp: self.assertEqual(fp.read(), fixture("index_merge")) os.remove(tmpfile)
def test_list_from_string_new_mode(self): output = StringProcessAdapter(fixture('diff_new_mode')) diffs = Diff._index_from_patch_format(self.rorepo, output) self._assert_diff_format(diffs) self.assertEqual(1, len(diffs)) self.assertEqual(8, len(diffs[0].diff.splitlines()))
def test_diff_patch_format(self): # test all of the 'old' format diffs for completness - it should at least # be able to deal with it fixtures = ("diff_2", "diff_2f", "diff_f", "diff_i", "diff_mode_only", "diff_new_mode", "diff_numstat", "diff_p", "diff_rename", "diff_tree_numstat_root", "diff_patch_unsafe_paths") for fixture_name in fixtures: diff_proc = StringProcessAdapter(fixture(fixture_name)) Diff._index_from_patch_format(self.rorepo, diff_proc)
def test_binary_diff(self, case): method, file_name = case res = method(None, StringProcessAdapter(fixture(file_name))) self.assertEqual(len(res), 1) self.assertEqual(len(list(res.iter_change_type('M'))), 1) if res[0].diff: self.assertEqual(res[0].diff, b"Binary files a/rps and b/rps differ\n", "in patch mode, we get a diff text") self.assertIsNotNone(str(res[0]), "This call should just work")
def test_diff_of_modified_files_not_added_to_the_index(self): output = StringProcessAdapter( to_raw(fixture('diff_abbrev-40_full-index_M_raw_no-color'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1, 'one modification') self.assertEqual(len(list(diffs.iter_change_type('M'))), 1, 'one modification') self.assertEqual(diffs[0].change_type, 'M') self.assertIsNone(diffs[0].b_blob, )
def test_diff_index(self): output = StringProcessAdapter(fixture('diff_index_patch')) res = Diff._index_from_patch_format(None, output) self.assertEqual(len(res), 6) for dr in res: self.assertTrue(dr.diff.startswith(b'@@'), dr) self.assertIsNotNone( str(dr), "Diff to string conversion should be possible") # end for each diff dr = res[3] assert dr.diff.endswith(b"+Binary files a/rps and b/rps differ\n")
def test_diff_with_copied_file(self): output = StringProcessAdapter(fixture('diff_copied_mode')) diffs = Diff._index_from_patch_format(self.rorepo, output) self._assert_diff_format(diffs) self.assertEqual(1, len(diffs)) diff = diffs[0] self.assertTrue(diff.copied_file) self.assertTrue(diff.a_path, 'test1.txt') self.assertTrue(diff.b_path, 'test2.txt') assert isinstance(str(diff), str) output = StringProcessAdapter(to_raw(fixture('diff_copied_mode_raw'))) diffs = Diff._index_from_raw_format(self.rorepo, output) self.assertEqual(len(diffs), 1) diff = diffs[0] self.assertEqual(diff.change_type, 'C') self.assertEqual(diff.score, 100) self.assertEqual(diff.a_path, 'test1.txt') self.assertEqual(diff.b_path, 'test2.txt') self.assertEqual(len(list(diffs.iter_change_type('C'))), 1)
def test_list_from_string(self): output = fixture('diff_numstat').decode(defenc) stats = Stats._list_from_string(self.rorepo, output) self.assertEqual(2, stats.total['files']) self.assertEqual(52, stats.total['lines']) self.assertEqual(29, stats.total['insertions']) self.assertEqual(23, stats.total['deletions']) self.assertEqual(29, stats.files["a.txt"]['insertions']) self.assertEqual(18, stats.files["a.txt"]['deletions']) self.assertEqual(0, stats.files["b.txt"]['insertions']) self.assertEqual(5, stats.files["b.txt"]['deletions'])
def test_diff_initial_commit(self): initial_commit = self.rorepo.commit( '33ebe7acec14b25c5f84f35a664803fcab2f7781') # Without creating a patch... diff_index = initial_commit.diff(NULL_TREE) self.assertEqual(diff_index[0].b_path, 'CHANGES') self.assertIsNotNone(diff_index[0].new_file) self.assertEqual(diff_index[0].diff, '') # ...and with creating a patch diff_index = initial_commit.diff(NULL_TREE, create_patch=True) self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path)) self.assertEqual(diff_index[0].b_path, 'CHANGES', repr(diff_index[0].b_path)) self.assertIsNotNone(diff_index[0].new_file) self.assertEqual(diff_index[0].diff, fixture('diff_initial'))
def test_git_file(self, rwrepo): # Move the .git directory to another location and create the .git file. real_path_abs = osp.abspath(join_path_native(rwrepo.working_tree_dir, '.real')) os.rename(rwrepo.git_dir, real_path_abs) git_file_path = join_path_native(rwrepo.working_tree_dir, '.git') with open(git_file_path, 'wb') as fp: fp.write(fixture('git_file')) # Create a repo and make sure it's pointing to the relocated .git directory. git_file_repo = Repo(rwrepo.working_tree_dir) self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs) # Test using an absolute gitdir path in the .git file. with open(git_file_path, 'wb') as fp: fp.write(('gitdir: %s\n' % real_path_abs).encode('ascii')) git_file_repo = Repo(rwrepo.working_tree_dir) self.assertEqual(osp.abspath(git_file_repo.git_dir), real_path_abs)
def test_blame_incremental(self, git): # loop over two fixtures, create a test fixture for 2.11.1+ syntax for git_fixture in ('blame_incremental', 'blame_incremental_2.11.1_plus'): git.return_value = fixture(git_fixture) blame_output = self.rorepo.blame_incremental( '9debf6b0aafb6f7781ea9d1383c86939a1aacde3', 'AUTHORS') blame_output = list(blame_output) self.assertEqual(len(blame_output), 5) # Check all outputted line numbers ranges = flatten([entry.linenos for entry in blame_output]) self.assertEqual( ranges, flatten([ range(2, 3), range(14, 15), range(1, 2), range(3, 14), range(15, 17) ])) commits = [entry.commit.hexsha[:7] for entry in blame_output] self.assertEqual( commits, ['82b8902', '82b8902', 'c76852d', 'c76852d', 'c76852d']) # Original filenames self.assertSequenceEqual( [entry.orig_path for entry in blame_output], ['AUTHORS'] * len(blame_output)) # Original line numbers orig_ranges = flatten( [entry.orig_linenos for entry in blame_output]) self.assertEqual(orig_ranges, flatten([ range(2, 3), range(14, 15), range(1, 2), range(2, 13), range(13, 15) ])) # noqa E501
def test_blame_complex_revision(self, git): git.return_value = fixture('blame_complex_revision') res = self.rorepo.blame("HEAD~10..HEAD", "README.md") self.assertEqual(len(res), 1) self.assertEqual(len(res[0][1]), 83, "Unexpected amount of parsed blame lines")
def test_diff_index_raw_format(self): output = StringProcessAdapter(fixture('diff_index_raw')) res = Diff._index_from_raw_format(None, output) self.assertIsNotNone(res[0].deleted_file) self.assertIsNone(res[0].b_path, )
def test_diff_with_spaces(self): data = StringProcessAdapter(fixture('diff_file_with_spaces')) diff_index = Diff._index_from_patch_format(self.rorepo, data) self.assertIsNone(diff_index[0].a_path, repr(diff_index[0].a_path)) self.assertEqual(diff_index[0].b_path, 'file with spaces', repr(diff_index[0].b_path))