def test_tree_changes_rename_detector(self): blob_a1 = make_object(Blob, data=b'a\nb\nc\nd\n') blob_a2 = make_object(Blob, data=b'a\nb\nc\ne\n') blob_b = make_object(Blob, data=b'b') tree1 = self.commit_tree([(b'a', blob_a1), (b'b', blob_b)]) tree2 = self.commit_tree([(b'c', blob_a2), (b'b', blob_b)]) detector = RenameDetector(self.store) self.assertChangesEqual( [TreeChange.delete((b'a', F, blob_a1.id)), TreeChange.add((b'c', F, blob_a2.id))], tree1, tree2) self.assertChangesEqual( [TreeChange.delete((b'a', F, blob_a1.id)), TreeChange(CHANGE_UNCHANGED, (b'b', F, blob_b.id), (b'b', F, blob_b.id)), TreeChange.add((b'c', F, blob_a2.id))], tree1, tree2, want_unchanged=True) self.assertChangesEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id), (b'c', F, blob_a2.id))], tree1, tree2, rename_detector=detector) self.assertChangesEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob_a1.id), (b'c', F, blob_a2.id)), TreeChange(CHANGE_UNCHANGED, (b'b', F, blob_b.id), (b'b', F, blob_b.id))], tree1, tree2, rename_detector=detector, want_unchanged=True)
def test_tree_changes_complex(self): blob_a_1 = make_object(Blob, data=b'a1_1') blob_bx1_1 = make_object(Blob, data=b'bx1_1') blob_bx2_1 = make_object(Blob, data=b'bx2_1') blob_by1_1 = make_object(Blob, data=b'by1_1') blob_by2_1 = make_object(Blob, data=b'by2_1') tree1 = self.commit_tree([ (b'a', blob_a_1), (b'b/x/1', blob_bx1_1), (b'b/x/2', blob_bx2_1), (b'b/y/1', blob_by1_1), (b'b/y/2', blob_by2_1), ]) blob_a_2 = make_object(Blob, data=b'a1_2') blob_bx1_2 = blob_bx1_1 blob_by_2 = make_object(Blob, data=b'by_2') blob_c_2 = make_object(Blob, data=b'c_2') tree2 = self.commit_tree([ (b'a', blob_a_2), (b'b/x/1', blob_bx1_2), (b'b/y', blob_by_2), (b'c', blob_c_2), ]) self.assertChangesEqual( [TreeChange(CHANGE_MODIFY, (b'a', F, blob_a_1.id), (b'a', F, blob_a_2.id)), TreeChange.delete((b'b/x/2', F, blob_bx2_1.id)), TreeChange.add((b'b/y', F, blob_by_2.id)), TreeChange.delete((b'b/y/1', F, blob_by1_1.id)), TreeChange.delete((b'b/y/2', F, blob_by2_1.id)), TreeChange.add((b'c', F, blob_c_2.id))], tree1, tree2)
def test_tree_changes_for_merge_delete_delete_conflict(self): blob1 = make_object(Blob, data=b'1') blob2 = make_object(Blob, data=b'2') parent1 = self.commit_tree([(b'a', blob1)]) parent2 = self.commit_tree([(b'a', blob2)]) merge = self.commit_tree([]) self.assertChangesForMergeEqual( [[TreeChange.delete((b'a', F, blob1.id)), TreeChange.delete((b'a', F, blob2.id))]], [parent1, parent2], merge)
def test_content_rename_gitlink(self): blob1 = make_object(Blob, data=b'blob1') blob2 = make_object(Blob, data=b'blob2') link1 = b'1' * 40 link2 = b'2' * 40 tree1 = self.commit_tree([(b'a', blob1), (b'b', link1, 0o160000)]) tree2 = self.commit_tree([(b'c', blob2), (b'd', link2, 0o160000)]) self.assertEqual( [TreeChange.delete((b'a', 0o100644, blob1.id)), TreeChange.delete((b'b', 0o160000, link1)), TreeChange.add((b'c', 0o100644, blob2.id)), TreeChange.add((b'd', 0o160000, link2))], self.detect_renames(tree1, tree2))
def test_tree_changes_add_delete(self): blob_a = make_object(Blob, data=b'a') blob_b = make_object(Blob, data=b'b') tree = self.commit_tree([(b'a', blob_a, 0o100644), (b'x/b', blob_b, 0o100755)]) self.assertChangesEqual( [TreeChange.add((b'a', 0o100644, blob_a.id)), TreeChange.add((b'x/b', 0o100755, blob_b.id))], self.empty_tree, tree) self.assertChangesEqual( [TreeChange.delete((b'a', 0o100644, blob_a.id)), TreeChange.delete((b'x/b', 0o100755, blob_b.id))], tree, self.empty_tree)
def test_tree_changes_name_order(self): blob = make_object(Blob, data=b'a') tree1 = self.commit_tree([(b'a', blob), (b'a.', blob), (b'a..', blob)]) # Tree order is the reverse of this, so if we used tree order, 'a..' # would not be merged. tree2 = self.commit_tree( [(b'a/x', blob), (b'a./x', blob), (b'a..', blob)]) self.assertChangesEqual( [TreeChange.delete((b'a', F, blob.id)), TreeChange.add((b'a/x', F, blob.id)), TreeChange.delete((b'a.', F, blob.id)), TreeChange.add((b'a./x', F, blob.id))], tree1, tree2)
def test_tree_changes_for_merge_octopus_delete(self): blob1 = make_object(Blob, data=b'1') blob2 = make_object(Blob, data=b'3') parent1 = self.commit_tree([(b'a', blob1)]) parent2 = self.commit_tree([(b'a', blob2)]) parent3 = merge = self.commit_tree([]) self.assertChangesForMergeEqual([], [parent1, parent1, parent1], merge) self.assertChangesForMergeEqual([], [parent1, parent1, parent3], merge) self.assertChangesForMergeEqual([], [parent1, parent3, parent3], merge) self.assertChangesForMergeEqual( [[TreeChange.delete((b'a', F, blob1.id)), TreeChange.delete((b'a', F, blob2.id)), None]], [parent1, parent2, parent3], merge)
def test_exact_rename_split_different_type(self): blob = make_object(Blob, data=b'/foo') tree1 = self.commit_tree([(b'a', blob, 0o100644)]) tree2 = self.commit_tree([(b'a', blob, 0o120000)]) self.assertEqual( [TreeChange.add((b'a', 0o120000, blob.id)), TreeChange.delete((b'a', 0o100644, blob.id))], self.detect_renames(tree1, tree2))
def test_exact_rename_many_to_one(self): blob = make_object(Blob, data=b'1') tree1 = self.commit_tree([(b'a', blob), (b'b', blob)]) tree2 = self.commit_tree([(b'c', blob)]) self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob.id), (b'c', F, blob.id)), TreeChange.delete((b'b', F, blob.id))], self.detect_renames(tree1, tree2))
def test_tree_changes_to_tree(self): blob_a = make_object(Blob, data=b'a') blob_x = make_object(Blob, data=b'x') tree1 = self.commit_tree([(b'a', blob_a)]) tree2 = self.commit_tree([(b'a/x', blob_x)]) self.assertChangesEqual( [TreeChange.delete((b'a', F, blob_a.id)), TreeChange.add((b'a/x', F, blob_x.id))], tree1, tree2)
def test_tree_changes_change_type(self): blob_a1 = make_object(Blob, data=b'a') blob_a2 = make_object(Blob, data=b'/foo/bar') tree1 = self.commit_tree([(b'a', blob_a1, 0o100644)]) tree2 = self.commit_tree([(b'a', blob_a2, 0o120000)]) self.assertChangesEqual( [TreeChange.delete((b'a', 0o100644, blob_a1.id)), TreeChange.add((b'a', 0o120000, blob_a2.id))], tree1, tree2)
def test_content_rename_max_files(self): blob1 = make_object(Blob, data=b'a\nb\nc\nd') blob4 = make_object(Blob, data=b'a\nb\nc\ne\n') blob2 = make_object(Blob, data=b'e\nf\ng\nh\n') blob3 = make_object(Blob, data=b'e\nf\ng\ni\n') tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3), (b'd', blob4)]) self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'd', F, blob4.id)), TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], self.detect_renames(tree1, tree2)) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), TreeChange.delete((b'b', F, blob2.id)), TreeChange.add((b'c', F, blob3.id)), TreeChange.add((b'd', F, blob4.id))], self.detect_renames(tree1, tree2, max_files=1))
def test_content_rename_many_to_one(self): blob1 = make_object(Blob, data=b'a\nb\nc\nd\n') blob2 = make_object(Blob, data=b'a\nb\nc\ne\n') blob3 = make_object(Blob, data=b'a\nb\nc\nf\n') tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3)]) self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), TreeChange.delete((b'b', F, blob2.id))], self.detect_renames(tree1, tree2))
def test_content_rename_one_to_one_ordering(self): blob1 = make_object(Blob, data=b'a\nb\nc\nd\ne\nf\n') blob2 = make_object(Blob, data=b'a\nb\nc\nd\ng\nh\n') # 6/10 match to blob1, 8/10 match to blob2 blob3 = make_object(Blob, data=b'a\nb\nc\nd\ng\ni\n') tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3)]) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), TreeChange(CHANGE_RENAME, (b'b', F, blob2.id), (b'c', F, blob3.id))], self.detect_renames(tree1, tree2)) tree3 = self.commit_tree([(b'a', blob2), (b'b', blob1)]) tree4 = self.commit_tree([(b'c', blob3)]) self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob2.id), (b'c', F, blob3.id)), TreeChange.delete((b'b', F, blob1.id))], self.detect_renames(tree3, tree4))
def test_content_rename_with_more_deletions(self): blob1 = make_object(Blob, data=b'') tree1 = self.commit_tree([(b'a', blob1), (b'b', blob1), (b'c', blob1), (b'd', blob1)]) tree2 = self.commit_tree([(b'e', blob1), (b'f', blob1), (b'g', blob1)]) self.maxDiff = None self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'e', F, blob1.id)), TreeChange(CHANGE_RENAME, (b'b', F, blob1.id), (b'f', F, blob1.id)), TreeChange(CHANGE_RENAME, (b'c', F, blob1.id), (b'g', F, blob1.id)), TreeChange.delete((b'd', F, blob1.id))], self.detect_renames(tree1, tree2))
def test_rename_threshold(self): blob1 = make_object(Blob, data=b'a\nb\nc\n') blob2 = make_object(Blob, data=b'a\nb\nd\n') tree1 = self.commit_tree([(b'a', blob1)]) tree2 = self.commit_tree([(b'b', blob2)]) self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'b', F, blob2.id))], self.detect_renames(tree1, tree2, rename_threshold=50)) self.assertEqual( [TreeChange.delete((b'a', F, blob1.id)), TreeChange.add((b'b', F, blob2.id))], self.detect_renames(tree1, tree2, rename_threshold=75))
def test_content_rename_many_to_many(self): blob1 = make_object(Blob, data=b'a\nb\nc\nd\n') blob2 = make_object(Blob, data=b'a\nb\nc\ne\n') blob3 = make_object(Blob, data=b'a\nb\nc\nf\n') blob4 = make_object(Blob, data=b'a\nb\nc\ng\n') tree1 = self.commit_tree([(b'a', blob1), (b'b', blob2)]) tree2 = self.commit_tree([(b'c', blob3), (b'd', blob4)]) # TODO(dborowitz): Distribute renames rather than greedily choosing # copies. self.assertEqual( [TreeChange(CHANGE_RENAME, (b'a', F, blob1.id), (b'c', F, blob3.id)), TreeChange(CHANGE_COPY, (b'a', F, blob1.id), (b'd', F, blob4.id)), TreeChange.delete((b'b', F, blob2.id))], self.detect_renames(tree1, tree2))
def test_tree_entry_sort(self): sha = 'abcd' * 10 expected_entries = [ TreeChange.add(TreeEntry(b'aaa', F, sha)), TreeChange(CHANGE_COPY, TreeEntry(b'bbb', F, sha), TreeEntry(b'aab', F, sha)), TreeChange(CHANGE_MODIFY, TreeEntry(b'bbb', F, sha), TreeEntry(b'bbb', F, b'dabc' * 10)), TreeChange(CHANGE_RENAME, TreeEntry(b'bbc', F, sha), TreeEntry(b'ddd', F, sha)), TreeChange.delete(TreeEntry(b'ccc', F, sha)), ] for perm in permutations(expected_entries): self.assertEqual(expected_entries, sorted(perm, key=_tree_change_key))
def test_changes_with_renames(self): blob = make_object(Blob, data=b'blob') c1, c2 = self.make_linear_commits(2, trees={ 1: [(b'a', blob)], 2: [(b'b', blob)] }) entry_a = (b'a', F, blob.id) entry_b = (b'b', F, blob.id) changes_without_renames = [ TreeChange.delete(entry_a), TreeChange.add(entry_b) ] changes_with_renames = [TreeChange(CHANGE_RENAME, entry_a, entry_b)] self.assertWalkYields([TestWalkEntry(c2, changes_without_renames)], [c2.id], max_entries=1) detector = RenameDetector(self.store) self.assertWalkYields([TestWalkEntry(c2, changes_with_renames)], [c2.id], max_entries=1, rename_detector=detector)