~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_intertree/test_compare.py

merge 2.0 branch rev 4647

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from bzrlib import (
23
23
    errors,
 
24
    mutabletree,
24
25
    tests,
25
26
    workingtree_4,
26
27
    )
27
28
from bzrlib.osutils import file_kind, has_symlinks
28
29
from bzrlib.tests import TestNotApplicable
29
 
from bzrlib.tests.intertree_implementations import TestCaseWithTwoTrees
 
30
from bzrlib.tests.per_intertree import TestCaseWithTwoTrees
30
31
 
31
32
# TODO: test the include_root option.
32
33
# TODO: test that renaming a directory x->y does not emit a rename for the
36
37
#        -> that is, when the renamed parent is not processed by the function.
37
38
# TODO: test items are only emitted once when a specific_files list names a dir
38
39
#       whose parent is now a child.
39
 
# TODO: test specific_files when the target tree has a file and the source a
40
 
#       dir with children, same id and same path.
41
40
# TODO: test comparisons between trees with different root ids. mbp 20070301
42
41
#
43
42
# TODO: More comparisons between trees with subtrees in different states.
216
215
        d = self.intertree_class(tree1, tree2).compare(
217
216
            specific_files=['a', 'b/c'])
218
217
        self.assertEqual(
219
 
            [('a', 'a-id', 'file'), ('b/c', 'c-id', 'file')],
 
218
            [('a', 'a-id', 'file'),  (u'b', 'b-id', 'directory'),
 
219
             ('b/c', 'c-id', 'file')],
220
220
            d.added)
221
221
        self.assertEqual([], d.modified)
222
222
        self.assertEqual([], d.removed)
223
223
        self.assertEqual([], d.renamed)
224
224
        self.assertEqual([], d.unchanged)
225
225
 
 
226
    def test_empty_to_abc_content_c_only(self):
 
227
        tree1 = self.make_branch_and_tree('1')
 
228
        tree2 = self.make_to_branch_and_tree('2')
 
229
        tree1 = self.get_tree_no_parents_no_content(tree1)
 
230
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
231
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
232
        d = self.intertree_class(tree1, tree2).compare(
 
233
            specific_files=['b/c'])
 
234
        self.assertEqual(
 
235
            [(u'b', 'b-id', 'directory'), ('b/c', 'c-id', 'file')], d.added)
 
236
        self.assertEqual([], d.modified)
 
237
        self.assertEqual([], d.removed)
 
238
        self.assertEqual([], d.renamed)
 
239
        self.assertEqual([], d.unchanged)
 
240
 
226
241
    def test_empty_to_abc_content_b_only(self):
227
242
        """Restricting to a dir matches the children of the dir."""
228
243
        tree1 = self.make_branch_and_tree('1')
232
247
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
233
248
        d = self.intertree_class(tree1, tree2).compare(specific_files=['b'])
234
249
        self.assertEqual(
235
 
            [('b', 'b-id', 'directory'),('b/c', 'c-id', 'file')],
 
250
            [('b', 'b-id', 'directory'), ('b/c', 'c-id', 'file')],
236
251
            d.added)
237
252
        self.assertEqual([], d.modified)
238
253
        self.assertEqual([], d.removed)
349
364
class TestIterChanges(TestCaseWithTwoTrees):
350
365
    """Test the comparison iterator"""
351
366
 
 
367
    def assertEqualIterChanges(self, left_changes, right_changes):
 
368
        """Assert that left_changes == right_changes.
 
369
 
 
370
        :param left_changes: A list of the output from iter_changes.
 
371
        :param right_changes: A list of the output from iter_changes.
 
372
        """
 
373
        left_changes = sorted(left_changes)
 
374
        right_changes = sorted(right_changes)
 
375
        if left_changes == right_changes:
 
376
            return
 
377
        # setify to get item by item differences, but we can only do this
 
378
        # when all the ids are unique on both sides.
 
379
        left_dict = dict((item[0], item) for item in left_changes)
 
380
        right_dict = dict((item[0], item) for item in right_changes)
 
381
        if (len(left_dict) != len(left_changes) or
 
382
            len(right_dict) != len(right_changes)):
 
383
            # Can't do a direct comparison. We could do a sequence diff, but
 
384
            # for now just do a regular assertEqual for now.
 
385
            self.assertEqual(left_changes, right_changes)
 
386
        keys = set(left_dict).union(set(right_dict))
 
387
        different = []
 
388
        same = []
 
389
        for key in keys:
 
390
            left_item = left_dict.get(key)
 
391
            right_item = right_dict.get(key)
 
392
            if left_item == right_item:
 
393
                same.append(str(left_item))
 
394
            else:
 
395
                different.append(" %s\n %s" % (left_item, right_item))
 
396
        self.fail("iter_changes output different. Unchanged items:\n" +
 
397
            "\n".join(same) + "\nChanged items:\n" + "\n".join(different))
 
398
 
352
399
    def do_iter_changes(self, tree1, tree2, **extra_args):
353
400
        """Helper to run iter_changes from tree1 to tree2.
354
401
 
367
414
            tree1.unlock()
368
415
            tree2.unlock()
369
416
 
 
417
    def check_has_changes(self, expected, tree1, tree2):
 
418
        # has_changes is defined for mutable trees only
 
419
        if not isinstance(tree2, mutabletree.MutableTree):
 
420
            if isinstance(tree1, mutabletree.MutableTree):
 
421
                # Let's switch the trees since has_changes() is commutative
 
422
                # (where we can apply it)
 
423
                tree2, tree1 = tree1, tree2
 
424
            else:
 
425
                # Neither tree can be used
 
426
                return
 
427
        tree1.lock_read()
 
428
        try:
 
429
            tree2.lock_read()
 
430
            try:
 
431
                return tree2.has_changes(tree1)
 
432
            finally:
 
433
                tree2.unlock()
 
434
        finally:
 
435
            tree1.unlock()
 
436
 
370
437
    def mutable_trees_to_locked_test_trees(self, tree1, tree2):
371
438
        """Convert the working trees into test trees.
372
439
 
436
503
        tree2 = self.get_tree_no_parents_no_content(tree2)
437
504
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
438
505
        self.assertEqual([], self.do_iter_changes(tree1, tree2))
 
506
        self.check_has_changes(False, tree1, tree2)
439
507
 
440
508
    def added(self, tree, file_id):
441
509
        path, entry = self.get_path_entry(tree, file_id)
519
587
            self.added(tree2, 'c-id'),
520
588
            self.deleted(tree1, 'empty-root-id')])
521
589
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
 
590
        self.check_has_changes(True, tree1, tree2)
522
591
 
523
592
    def test_empty_specific_files(self):
524
593
        tree1 = self.make_branch_and_tree('1')
542
611
            self.added(tree2, 'c-id'),
543
612
            self.deleted(tree1, 'empty-root-id')])
544
613
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
 
614
        self.check_has_changes(True, tree1, tree2)
545
615
 
546
616
    def test_empty_to_abc_content_a_only(self):
547
617
        tree1 = self.make_branch_and_tree('1')
550
620
        tree2 = self.get_tree_no_parents_abc_content(tree2)
551
621
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
552
622
        self.assertEqual(
553
 
            [self.added(tree2, 'a-id')],
 
623
            sorted([self.added(tree2, 'root-id'),
 
624
             self.added(tree2, 'a-id'),
 
625
             self.deleted(tree1, 'empty-root-id')]),
554
626
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
555
627
 
556
 
    def test_abc_content_to_empty_to_abc_content_a_only(self):
 
628
    def test_abc_content_to_empty_a_only(self):
 
629
        # For deletes we don't need to pickup parents.
557
630
        tree1 = self.make_branch_and_tree('1')
558
631
        tree2 = self.make_to_branch_and_tree('2')
559
632
        tree1 = self.get_tree_no_parents_abc_content(tree1)
563
636
            [self.deleted(tree1, 'a-id')],
564
637
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
565
638
 
 
639
    def test_abc_content_to_empty_b_only(self):
 
640
        # When b stops being a directory we have to pick up b/c as well.
 
641
        tree1 = self.make_branch_and_tree('1')
 
642
        tree2 = self.make_to_branch_and_tree('2')
 
643
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
644
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
645
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
646
        self.assertEqual(
 
647
            [self.deleted(tree1, 'b-id'), self.deleted(tree1, 'c-id')],
 
648
            self.do_iter_changes(tree1, tree2, specific_files=['b']))
 
649
 
566
650
    def test_empty_to_abc_content_a_and_c_only(self):
567
651
        tree1 = self.make_branch_and_tree('1')
568
652
        tree2 = self.make_to_branch_and_tree('2')
569
653
        tree1 = self.get_tree_no_parents_no_content(tree1)
570
654
        tree2 = self.get_tree_no_parents_abc_content(tree2)
571
655
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
572
 
        expected_result = [self.added(tree2, 'a-id'), self.added(tree2, 'c-id')]
 
656
        expected_result = sorted([self.added(tree2, 'root-id'),
 
657
            self.added(tree2, 'a-id'), self.added(tree2, 'b-id'),
 
658
            self.added(tree2, 'c-id'), self.deleted(tree1, 'empty-root-id')])
573
659
        self.assertEqual(expected_result,
574
660
            self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
575
661
 
579
665
        tree1 = self.get_tree_no_parents_abc_content(tree1)
580
666
        tree2 = self.get_tree_no_parents_no_content(tree2)
581
667
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
582
 
        def deleted(file_id):
583
 
            entry = tree1.inventory[file_id]
584
 
            path = tree1.id2path(file_id)
585
 
            return (file_id, (path, None), True, (True, False),
586
 
                    (entry.parent_id, None),
587
 
                    (entry.name, None), (entry.kind, None),
588
 
                    (entry.executable, None))
589
668
        expected_results = sorted([
590
669
            self.added(tree2, 'empty-root-id'),
591
 
            deleted('root-id'), deleted('a-id'),
592
 
            deleted('b-id'), deleted('c-id')])
 
670
            self.deleted(tree1, 'root-id'), self.deleted(tree1, 'a-id'),
 
671
            self.deleted(tree1, 'b-id'), self.deleted(tree1, 'c-id')])
593
672
        self.assertEqual(
594
673
            expected_results,
595
674
            self.do_iter_changes(tree1, tree2))
 
675
        self.check_has_changes(True, tree1, tree2)
596
676
 
597
677
    def test_content_modification(self):
598
678
        tree1 = self.make_branch_and_tree('1')
605
685
                           (root_id, root_id), ('a', 'a'),
606
686
                           ('file', 'file'), (False, False))],
607
687
                         self.do_iter_changes(tree1, tree2))
 
688
        self.check_has_changes(True, tree1, tree2)
608
689
 
609
690
    def test_meta_modification(self):
610
691
        tree1 = self.make_branch_and_tree('1')
655
736
                           (False, False))],
656
737
                         self.do_iter_changes(tree1, tree2))
657
738
 
 
739
    def test_specific_content_modification_grabs_parents(self):
 
740
        # WHen the only direct change to a specified file is a content change,
 
741
        # and its in a reparented subtree, the parents are grabbed.
 
742
        tree1 = self.make_branch_and_tree('1')
 
743
        tree1.mkdir('changing', 'parent-id')
 
744
        tree1.mkdir('changing/unchanging', 'mid-id')
 
745
        tree1.add(['changing/unchanging/file'], ['file-id'], ['file'])
 
746
        tree1.put_file_bytes_non_atomic('file-id', 'a file')
 
747
        tree2 = self.make_to_branch_and_tree('2')
 
748
        tree2.set_root_id(tree1.get_root_id())
 
749
        tree2.mkdir('changed', 'parent-id')
 
750
        tree2.mkdir('changed/unchanging', 'mid-id')
 
751
        tree2.add(['changed/unchanging/file'], ['file-id'], ['file'])
 
752
        tree2.put_file_bytes_non_atomic('file-id', 'changed content')
 
753
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
754
        # parent-id has changed, as has file-id
 
755
        root_id = tree1.path2id('')
 
756
        self.assertEqualIterChanges(
 
757
            [self.renamed(tree1, tree2, 'parent-id', False),
 
758
             self.renamed(tree1, tree2, 'file-id', True)],
 
759
             self.do_iter_changes(tree1, tree2,
 
760
             specific_files=['changed/unchanging/file']))
 
761
 
 
762
    def test_specific_content_modification_grabs_parents_root_changes(self):
 
763
        # WHen the only direct change to a specified file is a content change,
 
764
        # and its in a reparented subtree, the parents are grabbed, even if
 
765
        # that includes the root.
 
766
        tree1 = self.make_branch_and_tree('1')
 
767
        tree1.set_root_id('old')
 
768
        tree1.mkdir('changed', 'parent-id')
 
769
        tree1.mkdir('changed/unchanging', 'mid-id')
 
770
        tree1.add(['changed/unchanging/file'], ['file-id'], ['file'])
 
771
        tree1.put_file_bytes_non_atomic('file-id', 'a file')
 
772
        tree2 = self.make_to_branch_and_tree('2')
 
773
        tree2.set_root_id('new')
 
774
        tree2.mkdir('changed', 'parent-id')
 
775
        tree2.mkdir('changed/unchanging', 'mid-id')
 
776
        tree2.add(['changed/unchanging/file'], ['file-id'], ['file'])
 
777
        tree2.put_file_bytes_non_atomic('file-id', 'changed content')
 
778
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
779
        # old is gone, new is added, parent-id has changed(reparented), as has
 
780
        # file-id(content)
 
781
        root_id = tree1.path2id('')
 
782
        self.assertEqualIterChanges(
 
783
            [self.renamed(tree1, tree2, 'parent-id', False),
 
784
             self.added(tree2, 'new'),
 
785
             self.deleted(tree1, 'old'),
 
786
             self.renamed(tree1, tree2, 'file-id', True)],
 
787
             self.do_iter_changes(tree1, tree2,
 
788
             specific_files=['changed/unchanging/file']))
 
789
 
 
790
    def test_specific_with_rename_under_new_dir_reports_new_dir(self):
 
791
        tree1 = self.make_branch_and_tree('1')
 
792
        tree2 = self.make_to_branch_and_tree('2')
 
793
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
794
        tree2 = self.get_tree_no_parents_abc_content_7(tree2)
 
795
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
796
        # d(d-id) is new, e is b-id renamed. 
 
797
        root_id = tree1.path2id('')
 
798
        self.assertEqualIterChanges(
 
799
            [self.renamed(tree1, tree2, 'b-id', False),
 
800
             self.added(tree2, 'd-id')],
 
801
             self.do_iter_changes(tree1, tree2, specific_files=['d/e']))
 
802
 
 
803
    def test_specific_with_rename_under_dir_under_new_dir_reports_new_dir(self):
 
804
        tree1 = self.make_branch_and_tree('1')
 
805
        tree2 = self.make_to_branch_and_tree('2')
 
806
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
807
        tree2 = self.get_tree_no_parents_abc_content_7(tree2)
 
808
        tree2.rename_one('a', 'd/e/a')
 
809
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
810
        # d is new, d/e is b-id renamed, d/e/a is a-id renamed 
 
811
        root_id = tree1.path2id('')
 
812
        self.assertEqualIterChanges(
 
813
            [self.renamed(tree1, tree2, 'b-id', False),
 
814
             self.added(tree2, 'd-id'),
 
815
             self.renamed(tree1, tree2, 'a-id', False)],
 
816
             self.do_iter_changes(tree1, tree2, specific_files=['d/e/a']))
 
817
 
 
818
    def test_specific_old_parent_same_path_new_parent(self):
 
819
        # when a parent is new at its path, if the path was used in the source
 
820
        # it must be emitted as a change.
 
821
        tree1 = self.make_branch_and_tree('1')
 
822
        tree1.add(['a'], ['a-id'], ['file'])
 
823
        tree1.put_file_bytes_non_atomic('a-id', 'a file')
 
824
        tree2 = self.make_to_branch_and_tree('2')
 
825
        tree2.set_root_id(tree1.get_root_id())
 
826
        tree2.mkdir('a', 'b-id')
 
827
        tree2.add(['a/c'], ['c-id'], ['file'])
 
828
        tree2.put_file_bytes_non_atomic('c-id', 'another file')
 
829
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
830
        # a-id is gone, b-id and c-id are added.
 
831
        self.assertEqualIterChanges(
 
832
            [self.deleted(tree1, 'a-id'),
 
833
             self.added(tree2, 'b-id'),
 
834
             self.added(tree2, 'c-id')],
 
835
             self.do_iter_changes(tree1, tree2, specific_files=['a/c']))
 
836
 
 
837
    def test_specific_old_parent_becomes_file(self):
 
838
        # When an old parent included because of a path conflict becomes a
 
839
        # non-directory, its children have to be all included in the delta.
 
840
        tree1 = self.make_branch_and_tree('1')
 
841
        tree1.mkdir('a', 'a-old-id')
 
842
        tree1.mkdir('a/reparented', 'reparented-id')
 
843
        tree1.mkdir('a/deleted', 'deleted-id')
 
844
        tree2 = self.make_to_branch_and_tree('2')
 
845
        tree2.set_root_id(tree1.get_root_id())
 
846
        tree2.mkdir('a', 'a-new-id')
 
847
        tree2.mkdir('a/reparented', 'reparented-id')
 
848
        tree2.add(['b'], ['a-old-id'], ['file'])
 
849
        tree2.put_file_bytes_non_atomic('a-old-id', '')
 
850
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
851
        # a-old-id is kind-changed, a-new-id is added, reparented-id is renamed,
 
852
        # deleted-id is gone
 
853
        self.assertEqualIterChanges(
 
854
            [self.kind_changed(tree1, tree2, 'a-old-id'),
 
855
             self.added(tree2, 'a-new-id'),
 
856
             self.renamed(tree1, tree2, 'reparented-id', False),
 
857
             self.deleted(tree1, 'deleted-id')],
 
858
             self.do_iter_changes(tree1, tree2,
 
859
                specific_files=['a/reparented']))
 
860
 
 
861
    def test_specific_old_parent_is_deleted(self):
 
862
        # When an old parent included because of a path conflict is removed,
 
863
        # its children have to be all included in the delta.
 
864
        tree1 = self.make_branch_and_tree('1')
 
865
        tree1.mkdir('a', 'a-old-id')
 
866
        tree1.mkdir('a/reparented', 'reparented-id')
 
867
        tree1.mkdir('a/deleted', 'deleted-id')
 
868
        tree2 = self.make_to_branch_and_tree('2')
 
869
        tree2.set_root_id(tree1.get_root_id())
 
870
        tree2.mkdir('a', 'a-new-id')
 
871
        tree2.mkdir('a/reparented', 'reparented-id')
 
872
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
873
        # a-old-id is gone, a-new-id is added, reparented-id is renamed,
 
874
        # deleted-id is gone
 
875
        self.assertEqualIterChanges(
 
876
            [self.deleted(tree1, 'a-old-id'),
 
877
             self.added(tree2, 'a-new-id'),
 
878
             self.renamed(tree1, tree2, 'reparented-id', False),
 
879
             self.deleted(tree1, 'deleted-id')],
 
880
             self.do_iter_changes(tree1, tree2,
 
881
                specific_files=['a/reparented']))
 
882
 
 
883
    def test_specific_old_parent_child_collides_with_unselected_new(self):
 
884
        # When the child of an old parent because of a path conflict becomes a
 
885
        # path conflict with some unselected item in the source, that item also
 
886
        # needs to be included (because otherwise the output of applying the
 
887
        # delta to the source would have two items at that path).
 
888
        tree1 = self.make_branch_and_tree('1')
 
889
        tree1.mkdir('a', 'a-old-id')
 
890
        tree1.mkdir('a/reparented', 'reparented-id')
 
891
        tree1.mkdir('collides', 'collides-id')
 
892
        tree2 = self.make_to_branch_and_tree('2')
 
893
        tree2.set_root_id(tree1.get_root_id())
 
894
        tree2.mkdir('a', 'a-new-id')
 
895
        tree2.mkdir('a/selected', 'selected-id')
 
896
        tree2.mkdir('collides', 'reparented-id')
 
897
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
898
        # a-old-id is one, a-new-id is added, reparented-id is renamed,
 
899
        # collides-id is gone, selected-id is new.
 
900
        self.assertEqualIterChanges(
 
901
            [self.deleted(tree1, 'a-old-id'),
 
902
             self.added(tree2, 'a-new-id'),
 
903
             self.renamed(tree1, tree2, 'reparented-id', False),
 
904
             self.deleted(tree1, 'collides-id'),
 
905
             self.added(tree2, 'selected-id')],
 
906
             self.do_iter_changes(tree1, tree2,
 
907
                specific_files=['a/selected']))
 
908
 
 
909
    def test_specific_old_parent_child_dir_stops_being_dir(self):
 
910
        # When the child of an old parent also stops being a directory, its
 
911
        # children must also be included. This test checks that downward
 
912
        # recursion is done appropriately by starting at a child of the root of
 
913
        # a deleted subtree (a/reparented), and checking that a sibling
 
914
        # directory (a/deleted) has its children included in the delta.
 
915
        tree1 = self.make_branch_and_tree('1')
 
916
        tree1.mkdir('a', 'a-old-id')
 
917
        tree1.mkdir('a/reparented', 'reparented-id-1')
 
918
        tree1.mkdir('a/deleted', 'deleted-id-1')
 
919
        tree1.mkdir('a/deleted/reparented', 'reparented-id-2')
 
920
        tree1.mkdir('a/deleted/deleted', 'deleted-id-2')
 
921
        tree2 = self.make_to_branch_and_tree('2')
 
922
        tree2.set_root_id(tree1.get_root_id())
 
923
        tree2.mkdir('a', 'a-new-id')
 
924
        tree2.mkdir('a/reparented', 'reparented-id-1')
 
925
        tree2.mkdir('reparented', 'reparented-id-2')
 
926
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
927
        # a-old-id is gone, a-new-id is added, reparented-id-1, -2 are renamed,
 
928
        # deleted-id-1 and -2 are gone.
 
929
        self.assertEqualIterChanges(
 
930
            [self.deleted(tree1, 'a-old-id'),
 
931
             self.added(tree2, 'a-new-id'),
 
932
             self.renamed(tree1, tree2, 'reparented-id-1', False),
 
933
             self.renamed(tree1, tree2, 'reparented-id-2', False),
 
934
             self.deleted(tree1, 'deleted-id-1'),
 
935
             self.deleted(tree1, 'deleted-id-2')],
 
936
             self.do_iter_changes(tree1, tree2,
 
937
                specific_files=['a/reparented']))
 
938
 
658
939
    def test_file_rename_and_meta_modification(self):
659
940
        tree1 = self.make_branch_and_tree('1')
660
941
        tree2 = self.make_to_branch_and_tree('2')
733
1014
            (None, root_id), (None, 'file'), (None, None), (None, False))]
734
1015
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
735
1016
 
 
1017
    def test_only_in_target_missing_subtree_specific_bug_367632(self):
 
1018
        tree1 = self.make_branch_and_tree('tree1')
 
1019
        tree2 = self.make_to_branch_and_tree('tree2')
 
1020
        tree2.set_root_id(tree1.get_root_id())
 
1021
        self.build_tree(['tree2/a-dir/', 'tree2/a-dir/a-file'])
 
1022
        tree2.add(['a-dir', 'a-dir/a-file'], ['dir-id', 'file-id'])
 
1023
        os.unlink('tree2/a-dir/a-file')
 
1024
        os.rmdir('tree2/a-dir')
 
1025
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1026
        self.not_applicable_if_missing_in('a-dir', tree2)
 
1027
        root_id = tree1.path2id('')
 
1028
        expected = [
 
1029
            ('dir-id', (None, 'a-dir'), False, (False, True),
 
1030
            (None, root_id), (None, 'a-dir'), (None, None), (None, False)),
 
1031
            ('file-id', (None, 'a-dir/a-file'), False, (False, True),
 
1032
            (None, 'dir-id'), (None, 'a-file'), (None, None), (None, False))
 
1033
            ]
 
1034
        # bug 367632 showed that specifying the root broke some code paths,
 
1035
        # so we check this contract with and without it.
 
1036
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1037
        self.assertEqual(expected,
 
1038
            self.do_iter_changes(tree1, tree2, specific_files=['']))
 
1039
 
736
1040
    def test_unchanged_with_renames_and_modifications(self):
737
1041
        """want_unchanged should generate a list of unchanged entries."""
738
1042
        tree1 = self.make_branch_and_tree('1')
741
1045
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
742
1046
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
743
1047
        root_id = tree1.path2id('')
744
 
 
745
1048
        self.assertEqual(sorted([self.unchanged(tree1, root_id),
746
1049
            self.unchanged(tree1, 'b-id'),
747
1050
            ('a-id', ('a', 'd'), True, (True, True),
834
1137
            self.content_changed(tree2, 'c-id'),
835
1138
            ])
836
1139
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1140
        self.check_has_changes(True, tree1, tree2)
837
1141
 
838
1142
    def test_unversioned_paths_in_tree(self):
839
1143
        tree1 = self.make_branch_and_tree('tree1')
1051
1355
        self.assertEqual(expected,
1052
1356
            self.do_iter_changes(tree1, tree2, include_unchanged=True,
1053
1357
                want_unversioned=True))
 
1358
        self.check_has_changes(True, tree1, tree2)
1054
1359
 
1055
1360
    def test_versioned_symlinks_specific_files(self):
1056
1361
        self.requireFeature(tests.SymlinkFeature)
1072
1377
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1073
1378
            specific_files=['added', 'changed', 'fromdir', 'fromfile',
1074
1379
            'removed', 'unchanged', 'todir', 'tofile']))
 
1380
        self.check_has_changes(True, tree1, tree2)
1075
1381
 
1076
1382
    def test_tree_with_special_names(self):
1077
1383
        tree1, tree2, paths, path_ids = self.make_tree_with_special_names()
1078
1384
        expected = sorted(self.added(tree2, f_id) for f_id in path_ids)
1079
1385
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1386
        self.check_has_changes(True, tree1, tree2)
1080
1387
 
1081
1388
    def test_trees_with_special_names(self):
1082
1389
        tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
1083
1390
        expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids
1084
1391
                          if f_id.endswith('_f-id'))
1085
1392
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1393
        self.check_has_changes(True, tree1, tree2)
1086
1394
 
1087
1395
    def test_trees_with_deleted_dir(self):
1088
1396
        tree1 = self.make_branch_and_tree('tree1')
1097
1405
 
1098
1406
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1099
1407
        # We should notice that 'b' and all its children are deleted
1100
 
        expected = sorted([
 
1408
        expected = [
1101
1409
            self.content_changed(tree2, 'a-id'),
1102
1410
            self.content_changed(tree2, 'g-id'),
1103
1411
            self.deleted(tree1, 'b-id'),
1104
1412
            self.deleted(tree1, 'c-id'),
1105
1413
            self.deleted(tree1, 'd-id'),
1106
1414
            self.deleted(tree1, 'e-id'),
1107
 
            ])
1108
 
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1415
            ]
 
1416
        self.assertEqualIterChanges(expected,
 
1417
            self.do_iter_changes(tree1, tree2))
 
1418
        self.check_has_changes(True, tree1, tree2)
1109
1419
 
1110
1420
    def test_added_unicode(self):
1111
1421
        tree1 = self.make_branch_and_tree('tree1')
1134
1444
        self.assertEqual([self.added(tree2, added_id)],
1135
1445
                         self.do_iter_changes(tree1, tree2,
1136
1446
                                              specific_files=[u'\u03b1']))
 
1447
        self.check_has_changes(True, tree1, tree2)
1137
1448
 
1138
1449
    def test_deleted_unicode(self):
1139
1450
        tree1 = self.make_branch_and_tree('tree1')
1162
1473
        self.assertEqual([self.deleted(tree1, deleted_id)],
1163
1474
                         self.do_iter_changes(tree1, tree2,
1164
1475
                                              specific_files=[u'\u03b1']))
 
1476
        self.check_has_changes(True, tree1, tree2)
1165
1477
 
1166
1478
    def test_modified_unicode(self):
1167
1479
        tree1 = self.make_branch_and_tree('tree1')
1191
1503
        self.assertEqual([self.content_changed(tree1, mod_id)],
1192
1504
                         self.do_iter_changes(tree1, tree2,
1193
1505
                                              specific_files=[u'\u03b1']))
 
1506
        self.check_has_changes(True, tree1, tree2)
1194
1507
 
1195
1508
    def test_renamed_unicode(self):
1196
1509
        tree1 = self.make_branch_and_tree('tree1')
1218
1531
 
1219
1532
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1220
1533
                         self.do_iter_changes(tree1, tree2))
1221
 
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1222
 
                         self.do_iter_changes(tree1, tree2,
1223
 
                                              specific_files=[u'\u03b1']))
 
1534
        self.assertEqualIterChanges(
 
1535
            [self.renamed(tree1, tree2, rename_id, False)],
 
1536
            self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1']))
 
1537
        self.check_has_changes(True, tree1, tree2)
1224
1538
 
1225
1539
    def test_unchanged_unicode(self):
1226
1540
        tree1 = self.make_branch_and_tree('tree1')
1267
1581
            self.unchanged(tree1, subfile_id),
1268
1582
            ])
1269
1583
        self.assertEqual(expected,
1270
 
                         self.do_iter_changes(tree1, tree2,
1271
 
                                              specific_files=[u'\u03b1'],
1272
 
                                              include_unchanged=True))
 
1584
            self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1'],
 
1585
                include_unchanged=True))
1273
1586
 
1274
1587
    def test_unknown_unicode(self):
1275
1588
        tree1 = self.make_branch_and_tree('tree1')
1309
1622
                                              want_unversioned=True))
1310
1623
        self.assertEqual([], # Without want_unversioned we should get nothing
1311
1624
                         self.do_iter_changes(tree1, tree2))
 
1625
        self.check_has_changes(False, tree1, tree2)
1312
1626
 
1313
1627
        # We should also be able to select just a subset
1314
1628
        expected = sorted([
1389
1703
            ])
1390
1704
        self.assertEqual(expected,
1391
1705
                         self.do_iter_changes(tree1, tree2))
 
1706
        self.check_has_changes(True, tree1, tree2)
1392
1707
 
1393
1708
    def test_deleted_and_unknown(self):
1394
1709
        """Test a file marked removed, but still present on disk."""