/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2009-12-14 06:06:59 UTC
  • mfrom: (4889 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4891.
  • Revision ID: mbp@sourcefrog.net-20091214060659-1ucv8hpnky0cbnaj
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for the InterTree.compare() function."""
18
18
 
19
19
import os
20
20
import shutil
21
21
 
22
 
from bzrlib import errors, tests, workingtree_4
 
22
from bzrlib import (
 
23
    errors,
 
24
    mutabletree,
 
25
    tests,
 
26
    workingtree_4,
 
27
    )
23
28
from bzrlib.osutils import file_kind, has_symlinks
24
 
from bzrlib.tests.intertree_implementations import TestCaseWithTwoTrees
 
29
from bzrlib.tests import TestNotApplicable
 
30
from bzrlib.tests.per_intertree import TestCaseWithTwoTrees
25
31
 
26
32
# TODO: test the include_root option.
27
33
# TODO: test that renaming a directory x->y does not emit a rename for the
31
37
#        -> that is, when the renamed parent is not processed by the function.
32
38
# TODO: test items are only emitted once when a specific_files list names a dir
33
39
#       whose parent is now a child.
34
 
# TODO: test specific_files when the target tree has a file and the source a
35
 
#       dir with children, same id and same path. 
36
40
# TODO: test comparisons between trees with different root ids. mbp 20070301
37
41
#
38
42
# TODO: More comparisons between trees with subtrees in different states.
49
53
        tree2.set_root_id(tree1.get_root_id())
50
54
        tree1 = self.get_tree_no_parents_no_content(tree1)
51
55
        tree2 = self.get_tree_no_parents_no_content(tree2)
52
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
56
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
53
57
        d = self.intertree_class(tree1, tree2).compare()
54
58
        self.assertEqual([], d.added)
55
59
        self.assertEqual([], d.modified)
63
67
        tree2.set_root_id(tree1.get_root_id())
64
68
        tree1 = self.get_tree_no_parents_no_content(tree1)
65
69
        tree2 = self.get_tree_no_parents_abc_content(tree2)
66
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
70
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
67
71
        d = self.intertree_class(tree1, tree2).compare()
68
72
        self.assertEqual([('a', 'a-id', 'file'),
69
73
                          ('b', 'b-id', 'directory'),
93
97
        # default intertree, but may perform a commit for other tree types,
94
98
        # which may reduce the validity of the test. XXX: Think about how to
95
99
        # address this.
96
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
100
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
97
101
        d = self.intertree_class(tree1, tree2).compare()
98
102
        self.assertEqual([], d.added)
99
103
        self.assertEqual([], d.modified)
107
111
        tree2.set_root_id(tree1.get_root_id())
108
112
        tree1 = self.get_tree_no_parents_abc_content(tree1)
109
113
        tree2 = self.get_tree_no_parents_no_content(tree2)
110
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
114
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
111
115
        d = self.intertree_class(tree1, tree2).compare()
112
116
        self.assertEqual([], d.added)
113
117
        self.assertEqual([], d.modified)
124
128
        tree2.set_root_id(tree1.get_root_id())
125
129
        tree1 = self.get_tree_no_parents_abc_content(tree1)
126
130
        tree2 = self.get_tree_no_parents_abc_content_2(tree2)
127
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
131
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
128
132
        d = self.intertree_class(tree1, tree2).compare()
129
133
        self.assertEqual([], d.added)
130
134
        self.assertEqual([('a', 'a-id', 'file', True, False)], d.modified)
131
135
        self.assertEqual([], d.removed)
132
136
        self.assertEqual([], d.renamed)
133
137
        self.assertEqual([], d.unchanged)
134
 
        
 
138
 
135
139
    def test_meta_modification(self):
136
140
        tree1 = self.make_branch_and_tree('1')
137
141
        tree2 = self.make_to_branch_and_tree('2')
138
142
        tree2.set_root_id(tree1.get_root_id())
139
143
        tree1 = self.get_tree_no_parents_abc_content(tree1)
140
144
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
141
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
145
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
142
146
        d = self.intertree_class(tree1, tree2).compare()
143
147
        self.assertEqual([], d.added)
144
148
        self.assertEqual([('b/c', 'c-id', 'file', False, True)], d.modified)
152
156
        tree2.set_root_id(tree1.get_root_id())
153
157
        tree1 = self.get_tree_no_parents_abc_content(tree1)
154
158
        tree2 = self.get_tree_no_parents_abc_content_4(tree2)
155
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
159
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
156
160
        d = self.intertree_class(tree1, tree2).compare()
157
161
        self.assertEqual([], d.added)
158
162
        self.assertEqual([], d.modified)
166
170
        tree2.set_root_id(tree1.get_root_id())
167
171
        tree1 = self.get_tree_no_parents_abc_content(tree1)
168
172
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
169
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
173
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
170
174
        d = self.intertree_class(tree1, tree2).compare()
171
175
        self.assertEqual([], d.added)
172
176
        self.assertEqual([], d.modified)
180
184
        tree2.set_root_id(tree1.get_root_id())
181
185
        tree1 = self.get_tree_no_parents_abc_content(tree1)
182
186
        tree2 = self.get_tree_no_parents_abc_content_6(tree2)
183
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
187
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
184
188
        d = self.intertree_class(tree1, tree2).compare()
185
189
        self.assertEqual([], d.added)
186
190
        self.assertEqual([], d.modified)
194
198
        tree2.set_root_id(tree1.get_root_id())
195
199
        tree1 = self.get_tree_no_parents_no_content(tree1)
196
200
        tree2 = self.get_tree_no_parents_abc_content(tree2)
197
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
201
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
198
202
        d = self.intertree_class(tree1, tree2).compare(specific_files=['a'])
199
203
        self.assertEqual([('a', 'a-id', 'file')], d.added)
200
204
        self.assertEqual([], d.modified)
207
211
        tree2 = self.make_to_branch_and_tree('2')
208
212
        tree1 = self.get_tree_no_parents_no_content(tree1)
209
213
        tree2 = self.get_tree_no_parents_abc_content(tree2)
210
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
214
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
211
215
        d = self.intertree_class(tree1, tree2).compare(
212
216
            specific_files=['a', 'b/c'])
213
217
        self.assertEqual(
214
 
            [('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')],
215
220
            d.added)
216
221
        self.assertEqual([], d.modified)
217
222
        self.assertEqual([], d.removed)
218
223
        self.assertEqual([], d.renamed)
219
224
        self.assertEqual([], d.unchanged)
220
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
 
221
241
    def test_empty_to_abc_content_b_only(self):
222
242
        """Restricting to a dir matches the children of the dir."""
223
243
        tree1 = self.make_branch_and_tree('1')
224
244
        tree2 = self.make_to_branch_and_tree('2')
225
245
        tree1 = self.get_tree_no_parents_no_content(tree1)
226
246
        tree2 = self.get_tree_no_parents_abc_content(tree2)
227
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
247
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
228
248
        d = self.intertree_class(tree1, tree2).compare(specific_files=['b'])
229
249
        self.assertEqual(
230
 
            [('b', 'b-id', 'directory'),('b/c', 'c-id', 'file')],
 
250
            [('b', 'b-id', 'directory'), ('b/c', 'c-id', 'file')],
231
251
            d.added)
232
252
        self.assertEqual([], d.modified)
233
253
        self.assertEqual([], d.removed)
240
260
        tree2 = self.make_to_branch_and_tree('2')
241
261
        tree1 = self.get_tree_no_parents_abc_content(tree1)
242
262
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
243
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
263
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
244
264
        d = self.intertree_class(tree1, tree2).compare(want_unchanged=True)
245
265
        self.assertEqual([], d.added)
246
266
        self.assertEqual([], d.modified)
256
276
        tree2 = self.make_to_branch_and_tree('2')
257
277
        tree1 = self.get_tree_no_parents_abc_content(tree1)
258
278
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
259
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
279
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
260
280
        d = self.intertree_class(tree1, tree2).compare(specific_files=['b'])
261
281
        # the type of tree-3 does not matter - it is used as a lookup, not
262
282
        # a dispatch. XXX: For dirstate it does speak to the optimisability of
285
305
    def test_require_versioned(self):
286
306
        # this does not quite robustly test, as it is passing in missing paths
287
307
        # rather than present-but-not-versioned paths. At the moment there is
288
 
        # no mechanism for managing the test trees (which are readonly) to 
 
308
        # no mechanism for managing the test trees (which are readonly) to
289
309
        # get present-but-not-versioned files for trees that can do that.
290
310
        tree1 = self.make_branch_and_tree('1')
291
311
        tree2 = self.make_to_branch_and_tree('2')
292
312
        tree1 = self.get_tree_no_parents_no_content(tree1)
293
313
        tree2 = self.get_tree_no_parents_abc_content(tree2)
294
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
295
 
        self.assertRaises(errors.PathsNotVersionedError, 
 
314
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
315
        self.assertRaises(errors.PathsNotVersionedError,
296
316
            self.intertree_class(tree1, tree2).compare,
297
317
            specific_files=['d'],
298
318
            require_versioned=True)
306
326
        tree1.add(['a', 'c'], ['a-id', 'c-id'])
307
327
        tree2.add(['a', 'c'], ['a-id', 'c-id'])
308
328
 
309
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
329
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
310
330
        d = self.intertree_class(tree1, tree2).compare()
311
331
        self.assertEqual([], d.added)
312
332
        self.assertEqual([(u'a', 'a-id', 'file', True, False),
326
346
            links_supported = True
327
347
        else:
328
348
            links_supported = False
329
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
349
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
350
        self.not_applicable_if_cannot_represent_unversioned(tree2)
330
351
        d = self.intertree_class(tree1, tree2).compare(want_unversioned=True)
331
352
        self.assertEqual([], d.added)
332
353
        self.assertEqual([], d.modified)
343
364
class TestIterChanges(TestCaseWithTwoTrees):
344
365
    """Test the comparison iterator"""
345
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
 
346
399
    def do_iter_changes(self, tree1, tree2, **extra_args):
347
400
        """Helper to run iter_changes from tree1 to tree2.
348
 
        
 
401
 
349
402
        :param tree1, tree2:  The source and target trees. These will be locked
350
403
            automatically.
351
404
        :param **extra_args: Extra args to pass to iter_changes. This is not
361
414
            tree1.unlock()
362
415
            tree2.unlock()
363
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
 
364
437
    def mutable_trees_to_locked_test_trees(self, tree1, tree2):
365
438
        """Convert the working trees into test trees.
366
439
 
367
440
        Read lock them, and add the unlock to the cleanup.
368
441
        """
369
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
442
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
370
443
        tree1.lock_read()
371
444
        self.addCleanup(tree1.unlock)
372
445
        tree2.lock_read()
428
501
        tree2 = self.make_to_branch_and_tree('2')
429
502
        tree1 = self.get_tree_no_parents_no_content(tree1)
430
503
        tree2 = self.get_tree_no_parents_no_content(tree2)
431
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
504
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
432
505
        self.assertEqual([], self.do_iter_changes(tree1, tree2))
 
506
        self.check_has_changes(False, tree1, tree2)
433
507
 
434
508
    def added(self, tree, file_id):
435
 
        entry = tree.inventory[file_id]
436
 
        path = tree.id2path(file_id)
 
509
        path, entry = self.get_path_entry(tree, file_id)
437
510
        return (file_id, (None, path), True, (False, True), (None, entry.parent_id),
438
511
                (None, entry.name), (None, entry.kind),
439
512
                (None, entry.executable))
440
513
 
 
514
    @staticmethod
 
515
    def get_path_entry(tree, file_id):
 
516
        iterator = tree.iter_entries_by_dir(specific_file_ids=[file_id])
 
517
        return iterator.next()
 
518
 
441
519
    def content_changed(self, tree, file_id):
442
 
        entry = tree.inventory[file_id]
443
 
        path = tree.id2path(file_id)
444
 
        return (file_id, (path, path), True, (True, True), (entry.parent_id, entry.parent_id),
 
520
        path, entry = self.get_path_entry(tree, file_id)
 
521
        return (file_id, (path, path), True, (True, True),
 
522
                (entry.parent_id, entry.parent_id),
445
523
                (entry.name, entry.name), (entry.kind, entry.kind),
446
524
                (entry.executable, entry.executable))
447
525
 
448
526
    def kind_changed(self, from_tree, to_tree, file_id):
449
 
        old_entry = from_tree.inventory[file_id]
450
 
        new_entry = to_tree.inventory[file_id]
451
 
        path = to_tree.id2path(file_id)
452
 
        from_path = from_tree.id2path(file_id)
453
 
        return (file_id, (from_path, path), True, (True, True), (old_entry.parent_id, new_entry.parent_id),
454
 
                (old_entry.name, new_entry.name), (old_entry.kind, new_entry.kind),
 
527
        from_path, old_entry = self.get_path_entry(from_tree, file_id)
 
528
        path, new_entry = self.get_path_entry(to_tree, file_id)
 
529
        return (file_id, (from_path, path), True, (True, True),
 
530
                (old_entry.parent_id, new_entry.parent_id),
 
531
                (old_entry.name, new_entry.name),
 
532
                (old_entry.kind, new_entry.kind),
455
533
                (old_entry.executable, new_entry.executable))
456
534
 
457
535
    def missing(self, file_id, from_path, to_path, parent_id, kind):
470
548
                (entry.executable, None))
471
549
 
472
550
    def renamed(self, from_tree, to_tree, file_id, content_changed):
473
 
        from_entry = from_tree.inventory[file_id]
474
 
        to_entry = to_tree.inventory[file_id]
475
 
        from_path = from_tree.id2path(file_id)
476
 
        to_path = to_tree.id2path(file_id)
 
551
        from_path, from_entry = self.get_path_entry(from_tree, file_id)
 
552
        to_path, to_entry = self.get_path_entry(to_tree, file_id)
477
553
        return (file_id, (from_path, to_path), content_changed, (True, True),
478
554
            (from_entry.parent_id, to_entry.parent_id),
479
555
            (from_entry.name, to_entry.name),
481
557
            (from_entry.executable, to_entry.executable))
482
558
 
483
559
    def unchanged(self, tree, file_id):
484
 
        entry = tree.inventory[file_id]
 
560
        path, entry = self.get_path_entry(tree, file_id)
485
561
        parent = entry.parent_id
486
562
        name = entry.name
487
563
        kind = entry.kind
488
564
        executable = entry.executable
489
 
        path = tree.id2path(file_id)
490
565
        return (file_id, (path, path), False, (True, True),
491
566
               (parent, parent), (name, name), (kind, kind),
492
567
               (executable, executable))
494
569
    def unversioned(self, tree, path):
495
570
        """Create an unversioned result."""
496
571
        _, basename = os.path.split(path)
497
 
        kind = file_kind(tree.abspath(path))
 
572
        kind = tree._comparison_data(None, path)[0]
498
573
        return (None, (None, path), True, (False, False), (None, None),
499
574
                (None, basename), (None, kind),
500
575
                (None, False))
512
587
            self.added(tree2, 'c-id'),
513
588
            self.deleted(tree1, 'empty-root-id')])
514
589
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
 
590
        self.check_has_changes(True, tree1, tree2)
515
591
 
516
592
    def test_empty_specific_files(self):
517
593
        tree1 = self.make_branch_and_tree('1')
535
611
            self.added(tree2, 'c-id'),
536
612
            self.deleted(tree1, 'empty-root-id')])
537
613
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
 
614
        self.check_has_changes(True, tree1, tree2)
538
615
 
539
616
    def test_empty_to_abc_content_a_only(self):
540
617
        tree1 = self.make_branch_and_tree('1')
543
620
        tree2 = self.get_tree_no_parents_abc_content(tree2)
544
621
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
545
622
        self.assertEqual(
546
 
            [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')]),
547
626
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
548
627
 
549
 
    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.
550
630
        tree1 = self.make_branch_and_tree('1')
551
631
        tree2 = self.make_to_branch_and_tree('2')
552
632
        tree1 = self.get_tree_no_parents_abc_content(tree1)
556
636
            [self.deleted(tree1, 'a-id')],
557
637
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
558
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
 
559
650
    def test_empty_to_abc_content_a_and_c_only(self):
560
651
        tree1 = self.make_branch_and_tree('1')
561
652
        tree2 = self.make_to_branch_and_tree('2')
562
653
        tree1 = self.get_tree_no_parents_no_content(tree1)
563
654
        tree2 = self.get_tree_no_parents_abc_content(tree2)
564
655
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
565
 
        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')])
566
659
        self.assertEqual(expected_result,
567
660
            self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
568
661
 
572
665
        tree1 = self.get_tree_no_parents_abc_content(tree1)
573
666
        tree2 = self.get_tree_no_parents_no_content(tree2)
574
667
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
575
 
        def deleted(file_id):
576
 
            entry = tree1.inventory[file_id]
577
 
            path = tree1.id2path(file_id)
578
 
            return (file_id, (path, None), True, (True, False),
579
 
                    (entry.parent_id, None),
580
 
                    (entry.name, None), (entry.kind, None),
581
 
                    (entry.executable, None))
582
668
        expected_results = sorted([
583
669
            self.added(tree2, 'empty-root-id'),
584
 
            deleted('root-id'), deleted('a-id'),
585
 
            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')])
586
672
        self.assertEqual(
587
673
            expected_results,
588
674
            self.do_iter_changes(tree1, tree2))
 
675
        self.check_has_changes(True, tree1, tree2)
589
676
 
590
677
    def test_content_modification(self):
591
678
        tree1 = self.make_branch_and_tree('1')
592
679
        tree2 = self.make_to_branch_and_tree('2')
593
680
        tree1 = self.get_tree_no_parents_abc_content(tree1)
594
681
        tree2 = self.get_tree_no_parents_abc_content_2(tree2)
595
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
682
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
596
683
        root_id = tree1.path2id('')
597
684
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
598
685
                           (root_id, root_id), ('a', 'a'),
599
686
                           ('file', 'file'), (False, False))],
600
687
                         self.do_iter_changes(tree1, tree2))
 
688
        self.check_has_changes(True, tree1, tree2)
601
689
 
602
690
    def test_meta_modification(self):
603
691
        tree1 = self.make_branch_and_tree('1')
604
692
        tree2 = self.make_to_branch_and_tree('2')
605
693
        tree1 = self.get_tree_no_parents_abc_content(tree1)
606
694
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
607
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
695
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
608
696
        self.assertEqual([('c-id', ('b/c', 'b/c'), False, (True, True),
609
697
                           ('b-id', 'b-id'), ('c', 'c'), ('file', 'file'),
610
698
                          (False, True))],
620
708
        self.build_tree(['1/a-empty/', '2/a-empty/'])
621
709
        tree1.add(['a-empty'], ['a-empty'])
622
710
        tree2.add(['a-empty'], ['a-empty'])
623
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
711
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
624
712
        expected = []
625
713
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
626
714
 
629
717
        tree2 = self.make_to_branch_and_tree('2')
630
718
        tree1 = self.get_tree_no_parents_abc_content(tree1)
631
719
        tree2 = self.get_tree_no_parents_abc_content_4(tree2)
632
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
720
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
633
721
        root_id = tree1.path2id('')
634
722
        self.assertEqual([('a-id', ('a', 'd'), False, (True, True),
635
723
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
641
729
        tree2 = self.make_to_branch_and_tree('2')
642
730
        tree1 = self.get_tree_no_parents_abc_content(tree1)
643
731
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
644
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
732
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
645
733
        root_id = tree1.path2id('')
646
734
        self.assertEqual([('a-id', ('a', 'd'), True, (True, True),
647
735
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
648
736
                           (False, False))],
649
737
                         self.do_iter_changes(tree1, tree2))
650
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
 
651
939
    def test_file_rename_and_meta_modification(self):
652
940
        tree1 = self.make_branch_and_tree('1')
653
941
        tree2 = self.make_to_branch_and_tree('2')
654
942
        tree1 = self.get_tree_no_parents_abc_content(tree1)
655
943
        tree2 = self.get_tree_no_parents_abc_content_6(tree2)
656
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
944
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
657
945
        root_id = tree1.path2id('')
658
946
        self.assertEqual([('c-id', ('b/c', 'e'), False, (True, True),
659
947
                           ('b-id', root_id), ('c', 'e'), ('file', 'file'),
660
948
                           (False, True))],
661
949
                         self.do_iter_changes(tree1, tree2))
662
950
 
 
951
    def test_file_becomes_unversionable_bug_438569(self):
 
952
        # This isn't strictly a intertree problem, but its the intertree code
 
953
        # path that triggers all stat cache updates on both xml and dirstate
 
954
        # trees.
 
955
        # In bug 438569, a file becoming a fifo causes an assert. Fifo's are
 
956
        # not versionable or diffable. For now, we simply stop cold when they
 
957
        # are detected (because we don't know how far through the code the 
 
958
        # assumption 'fifo's do not exist' goes). In future we could report 
 
959
        # the kind change and have commit refuse to go futher, or something
 
960
        # similar. One particular reason for choosing this approach is that
 
961
        # there is no minikind for 'fifo' in dirstate today, so we can't 
 
962
        # actually update records that way.
 
963
        # To add confusion, the totally generic code path works - but it
 
964
        # doesn't update persistent metadata. So this test permits InterTrees
 
965
        # to either work, or fail with BadFileKindError.
 
966
        self.requireFeature(tests.OsFifoFeature)
 
967
        tree1 = self.make_branch_and_tree('1')
 
968
        self.build_tree(['1/a'])
 
969
        tree1.set_root_id('root-id')
 
970
        tree1.add(['a'], ['a-id'])
 
971
        tree2 = self.make_branch_and_tree('2')
 
972
        os.mkfifo('2/a')
 
973
        tree2.add(['a'], ['a-id'], ['file'])
 
974
        try:
 
975
            tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
976
        except (KeyError,):
 
977
            raise tests.TestNotApplicable(
 
978
                "Cannot represent a FIFO in this case %s" % self.id())
 
979
        try:
 
980
            self.do_iter_changes(tree1, tree2)
 
981
        except errors.BadFileKindError:
 
982
            pass
 
983
 
663
984
    def test_missing_in_target(self):
664
985
        """Test with the target files versioned but absent from disk."""
665
986
        tree1 = self.make_branch_and_tree('1')
669
990
        os.unlink('2/a')
670
991
        shutil.rmtree('2/b')
671
992
        # TODO ? have a symlink here?
672
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
993
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
994
        self.not_applicable_if_missing_in('a', tree2)
 
995
        self.not_applicable_if_missing_in('b', tree2)
673
996
        root_id = tree1.path2id('')
674
997
        expected = sorted([
675
998
            self.missing('a-id', 'a', 'a', root_id, 'file'),
688
1011
        tree2.add(['directory'], ['file-id'])
689
1012
        os.rmdir('tree2/directory')
690
1013
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1014
        self.not_applicable_if_missing_in('directory', tree2)
691
1015
 
692
1016
        root_id = tree1.path2id('')
693
1017
        expected = sorted([
695
1019
            ])
696
1020
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
697
1021
 
 
1022
    def test_only_in_source_and_missing(self):
 
1023
        tree1 = self.make_branch_and_tree('tree1')
 
1024
        tree2 = self.make_to_branch_and_tree('tree2')
 
1025
        tree2.set_root_id(tree1.get_root_id())
 
1026
        self.build_tree(['tree1/file'])
 
1027
        tree1.add(['file'], ['file-id'])
 
1028
        os.unlink('tree1/file')
 
1029
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1030
        self.not_applicable_if_missing_in('file', tree1)
 
1031
        root_id = tree1.path2id('')
 
1032
        expected = [('file-id', ('file', None), False, (True, False),
 
1033
            (root_id, None), ('file', None), (None, None), (False, None))]
 
1034
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1035
 
 
1036
    def test_only_in_target_and_missing(self):
 
1037
        tree1 = self.make_branch_and_tree('tree1')
 
1038
        tree2 = self.make_to_branch_and_tree('tree2')
 
1039
        tree2.set_root_id(tree1.get_root_id())
 
1040
        self.build_tree(['tree2/file'])
 
1041
        tree2.add(['file'], ['file-id'])
 
1042
        os.unlink('tree2/file')
 
1043
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1044
        self.not_applicable_if_missing_in('file', tree2)
 
1045
        root_id = tree1.path2id('')
 
1046
        expected = [('file-id', (None, 'file'), False, (False, True),
 
1047
            (None, root_id), (None, 'file'), (None, None), (None, False))]
 
1048
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1049
 
 
1050
    def test_only_in_target_missing_subtree_specific_bug_367632(self):
 
1051
        tree1 = self.make_branch_and_tree('tree1')
 
1052
        tree2 = self.make_to_branch_and_tree('tree2')
 
1053
        tree2.set_root_id(tree1.get_root_id())
 
1054
        self.build_tree(['tree2/a-dir/', 'tree2/a-dir/a-file'])
 
1055
        tree2.add(['a-dir', 'a-dir/a-file'], ['dir-id', 'file-id'])
 
1056
        os.unlink('tree2/a-dir/a-file')
 
1057
        os.rmdir('tree2/a-dir')
 
1058
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1059
        self.not_applicable_if_missing_in('a-dir', tree2)
 
1060
        root_id = tree1.path2id('')
 
1061
        expected = [
 
1062
            ('dir-id', (None, 'a-dir'), False, (False, True),
 
1063
            (None, root_id), (None, 'a-dir'), (None, None), (None, False)),
 
1064
            ('file-id', (None, 'a-dir/a-file'), False, (False, True),
 
1065
            (None, 'dir-id'), (None, 'a-file'), (None, None), (None, False))
 
1066
            ]
 
1067
        # bug 367632 showed that specifying the root broke some code paths,
 
1068
        # so we check this contract with and without it.
 
1069
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1070
        self.assertEqual(expected,
 
1071
            self.do_iter_changes(tree1, tree2, specific_files=['']))
 
1072
 
698
1073
    def test_unchanged_with_renames_and_modifications(self):
699
1074
        """want_unchanged should generate a list of unchanged entries."""
700
1075
        tree1 = self.make_branch_and_tree('1')
703
1078
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
704
1079
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
705
1080
        root_id = tree1.path2id('')
706
 
 
707
1081
        self.assertEqual(sorted([self.unchanged(tree1, root_id),
708
1082
            self.unchanged(tree1, 'b-id'),
709
1083
            ('a-id', ('a', 'd'), True, (True, True),
753
1127
 
754
1128
    def test_disk_in_subtrees_skipped(self):
755
1129
        """subtrees are considered not-in-the-current-tree.
756
 
        
 
1130
 
757
1131
        This test tests the trivial case, where the basis has no paths in the
758
1132
        current trees subtree.
759
1133
        """
765
1139
        tree2.set_root_id('root-id')
766
1140
        subtree2 = self.make_to_branch_and_tree('2/sub')
767
1141
        subtree2.set_root_id('subtree-id')
768
 
        tree2.add(['sub'], ['subtree-id'])
 
1142
        tree2.add_reference(subtree2)
769
1143
        self.build_tree(['2/sub/file'])
770
1144
        subtree2.add(['file'])
771
1145
 
796
1170
            self.content_changed(tree2, 'c-id'),
797
1171
            ])
798
1172
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1173
        self.check_has_changes(True, tree1, tree2)
799
1174
 
800
1175
    def test_unversioned_paths_in_tree(self):
801
1176
        tree1 = self.make_branch_and_tree('tree1')
808
1183
        else:
809
1184
            links_supported = False
810
1185
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1186
        self.not_applicable_if_cannot_represent_unversioned(tree2)
811
1187
        expected = [
812
1188
            self.unversioned(tree2, 'file'),
813
1189
            self.unversioned(tree2, 'dir'),
828
1204
        else:
829
1205
            links_supported = False
830
1206
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1207
        self.not_applicable_if_cannot_represent_unversioned(tree2)
831
1208
        expected = [
832
1209
            self.unversioned(tree2, 'file'),
833
1210
            self.unversioned(tree2, 'dir'),
843
1220
 
844
1221
    def test_unversioned_paths_in_target_matching_source_old_names(self):
845
1222
        # its likely that naive implementations of unversioned file support
846
 
        # will fail if the path was versioned, but is not any more, 
 
1223
        # will fail if the path was versioned, but is not any more,
847
1224
        # due to a rename, not due to unversioning it.
848
1225
        # That is, if the old tree has a versioned file 'foo', and
849
1226
        # the new tree has the same file but versioned as 'bar', and also
868
1245
            tree1.add(['link'], ['link-id'])
869
1246
            tree2.add(['movedlink'], ['link-id'])
870
1247
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1248
        self.not_applicable_if_cannot_represent_unversioned(tree2)
871
1249
        root_id = tree1.path2id('')
872
1250
        expected = [
873
1251
            self.renamed(tree1, tree2, 'dir-id', False),
917
1295
                  ['a-id', 'b-id', 'c-id', 'd-id', 'a-c-id', 'e-id'])
918
1296
 
919
1297
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1298
        self.not_applicable_if_cannot_represent_unversioned(tree2)
920
1299
 
921
1300
        self.assertEqual([], self.do_iter_changes(tree1, tree2,
922
1301
                                                  want_unversioned=True))
940
1319
        tree2 = self.make_to_branch_and_tree('tree2')
941
1320
        tree2.set_root_id(tree1.get_root_id())
942
1321
        self.build_tree(['tree2/dir/', 'tree2/dir/file'])
943
 
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
1322
        tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
 
1323
        self.not_applicable_if_cannot_represent_unversioned(tree2)
944
1324
        expected = [
945
1325
            self.unversioned(tree2, 'dir'),
946
1326
            ]
990
1370
    def test_versioned_symlinks(self):
991
1371
        self.requireFeature(tests.SymlinkFeature)
992
1372
        tree1, tree2 = self.make_trees_with_symlinks()
 
1373
        self.not_applicable_if_cannot_represent_unversioned(tree2)
993
1374
        root_id = tree1.path2id('')
994
1375
        expected = [
995
1376
            self.unchanged(tree1, tree1.path2id('')),
1007
1388
        self.assertEqual(expected,
1008
1389
            self.do_iter_changes(tree1, tree2, include_unchanged=True,
1009
1390
                want_unversioned=True))
 
1391
        self.check_has_changes(True, tree1, tree2)
1010
1392
 
1011
1393
    def test_versioned_symlinks_specific_files(self):
1012
1394
        self.requireFeature(tests.SymlinkFeature)
1028
1410
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1029
1411
            specific_files=['added', 'changed', 'fromdir', 'fromfile',
1030
1412
            'removed', 'unchanged', 'todir', 'tofile']))
 
1413
        self.check_has_changes(True, tree1, tree2)
1031
1414
 
1032
1415
    def test_tree_with_special_names(self):
1033
1416
        tree1, tree2, paths, path_ids = self.make_tree_with_special_names()
1034
1417
        expected = sorted(self.added(tree2, f_id) for f_id in path_ids)
1035
1418
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1419
        self.check_has_changes(True, tree1, tree2)
1036
1420
 
1037
1421
    def test_trees_with_special_names(self):
1038
1422
        tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
1039
1423
        expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids
1040
1424
                          if f_id.endswith('_f-id'))
1041
1425
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1426
        self.check_has_changes(True, tree1, tree2)
1042
1427
 
1043
1428
    def test_trees_with_deleted_dir(self):
1044
1429
        tree1 = self.make_branch_and_tree('tree1')
1053
1438
 
1054
1439
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1055
1440
        # We should notice that 'b' and all its children are deleted
1056
 
        expected = sorted([
 
1441
        expected = [
1057
1442
            self.content_changed(tree2, 'a-id'),
1058
1443
            self.content_changed(tree2, 'g-id'),
1059
1444
            self.deleted(tree1, 'b-id'),
1060
1445
            self.deleted(tree1, 'c-id'),
1061
1446
            self.deleted(tree1, 'd-id'),
1062
1447
            self.deleted(tree1, 'e-id'),
1063
 
            ])
1064
 
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1448
            ]
 
1449
        self.assertEqualIterChanges(expected,
 
1450
            self.do_iter_changes(tree1, tree2))
 
1451
        self.check_has_changes(True, tree1, tree2)
1065
1452
 
1066
1453
    def test_added_unicode(self):
1067
1454
        tree1 = self.make_branch_and_tree('tree1')
1090
1477
        self.assertEqual([self.added(tree2, added_id)],
1091
1478
                         self.do_iter_changes(tree1, tree2,
1092
1479
                                              specific_files=[u'\u03b1']))
 
1480
        self.check_has_changes(True, tree1, tree2)
1093
1481
 
1094
1482
    def test_deleted_unicode(self):
1095
1483
        tree1 = self.make_branch_and_tree('tree1')
1118
1506
        self.assertEqual([self.deleted(tree1, deleted_id)],
1119
1507
                         self.do_iter_changes(tree1, tree2,
1120
1508
                                              specific_files=[u'\u03b1']))
 
1509
        self.check_has_changes(True, tree1, tree2)
1121
1510
 
1122
1511
    def test_modified_unicode(self):
1123
1512
        tree1 = self.make_branch_and_tree('tree1')
1147
1536
        self.assertEqual([self.content_changed(tree1, mod_id)],
1148
1537
                         self.do_iter_changes(tree1, tree2,
1149
1538
                                              specific_files=[u'\u03b1']))
 
1539
        self.check_has_changes(True, tree1, tree2)
1150
1540
 
1151
1541
    def test_renamed_unicode(self):
1152
1542
        tree1 = self.make_branch_and_tree('tree1')
1174
1564
 
1175
1565
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1176
1566
                         self.do_iter_changes(tree1, tree2))
1177
 
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1178
 
                         self.do_iter_changes(tree1, tree2,
1179
 
                                              specific_files=[u'\u03b1']))
 
1567
        self.assertEqualIterChanges(
 
1568
            [self.renamed(tree1, tree2, rename_id, False)],
 
1569
            self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1']))
 
1570
        self.check_has_changes(True, tree1, tree2)
1180
1571
 
1181
1572
    def test_unchanged_unicode(self):
1182
1573
        tree1 = self.make_branch_and_tree('tree1')
1223
1614
            self.unchanged(tree1, subfile_id),
1224
1615
            ])
1225
1616
        self.assertEqual(expected,
1226
 
                         self.do_iter_changes(tree1, tree2,
1227
 
                                              specific_files=[u'\u03b1'],
1228
 
                                              include_unchanged=True))
 
1617
            self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1'],
 
1618
                include_unchanged=True))
1229
1619
 
1230
1620
    def test_unknown_unicode(self):
1231
1621
        tree1 = self.make_branch_and_tree('tree1')
1249
1639
        tree2.add([u'\u03b1'], [a_id])
1250
1640
 
1251
1641
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1642
        self.not_applicable_if_cannot_represent_unversioned(tree2)
1252
1643
 
1253
1644
        expected = sorted([
1254
1645
            self.unversioned(tree2, u'\u03b1/unknown_dir'),
1264
1655
                                              want_unversioned=True))
1265
1656
        self.assertEqual([], # Without want_unversioned we should get nothing
1266
1657
                         self.do_iter_changes(tree1, tree2))
 
1658
        self.check_has_changes(False, tree1, tree2)
1267
1659
 
1268
1660
        # We should also be able to select just a subset
1269
1661
        expected = sorted([
1299
1691
        self.build_tree(['tree2/a/file', 'tree2/a/dir/', 'tree2/a/dir/subfile'])
1300
1692
 
1301
1693
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1694
        self.not_applicable_if_cannot_represent_unversioned(tree2)
1302
1695
 
1303
1696
        expected = sorted([
1304
1697
            self.unversioned(tree2, u'a/file'),
1343
1736
            ])
1344
1737
        self.assertEqual(expected,
1345
1738
                         self.do_iter_changes(tree1, tree2))
 
1739
        self.check_has_changes(True, tree1, tree2)
1346
1740
 
1347
1741
    def test_deleted_and_unknown(self):
1348
1742
        """Test a file marked removed, but still present on disk."""
1366
1760
        tree2.add(['a', 'c'], ['a-id', 'c-id'])
1367
1761
 
1368
1762
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1763
        self.not_applicable_if_cannot_represent_unversioned(tree2)
1369
1764
 
1370
1765
        expected = sorted([
1371
1766
            self.deleted(tree1, 'b-id'),
1439
1834
        os.rename('tree2/a', 'tree2/a2')
1440
1835
 
1441
1836
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1837
        self.not_applicable_if_missing_in('a', tree2)
1442
1838
 
1443
1839
        expected = sorted([
1444
1840
            self.missing('a-id', 'a', 'a', tree2.get_root_id(), 'file'),