~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
"""Tests for the InterTree.compare() function."""
18
18
 
19
19
import os
 
20
import shutil
20
21
 
21
 
from bzrlib import errors
 
22
from bzrlib import errors, tests, workingtree_4
 
23
from bzrlib.osutils import file_kind, has_symlinks
22
24
from bzrlib.tests.intertree_implementations import TestCaseWithTwoTrees
23
25
 
 
26
# TODO: test the include_root option.
 
27
# TODO: test that renaming a directory x->y does not emit a rename for the
 
28
#       child x/a->y/a.
 
29
# TODO: test that renaming a directory x-> does not emit a rename for the child
 
30
#        x/a -> y/a when a supplied_files argument gives either 'x/' or 'y/a'
 
31
#        -> that is, when the renamed parent is not processed by the function.
 
32
# TODO: test items are only emitted once when a specific_files list names a dir
 
33
#       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
# TODO: test comparisons between trees with different root ids. mbp 20070301
 
37
#
 
38
# TODO: More comparisons between trees with subtrees in different states.
 
39
#
 
40
# TODO: Many tests start out by setting the tree roots ids the same, maybe
 
41
#       that should just be the default for these tests, by changing
 
42
#       make_branch_and_tree.  mbp 20070307
24
43
 
25
44
class TestCompare(TestCaseWithTwoTrees):
26
45
 
27
46
    def test_compare_empty_trees(self):
28
47
        tree1 = self.make_branch_and_tree('1')
29
48
        tree2 = self.make_to_branch_and_tree('2')
 
49
        tree2.set_root_id(tree1.get_root_id())
30
50
        tree1 = self.get_tree_no_parents_no_content(tree1)
31
 
        tree2 = self.get_to_tree_no_parents_no_content(tree2)
 
51
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
52
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
32
53
        d = self.intertree_class(tree1, tree2).compare()
33
54
        self.assertEqual([], d.added)
34
55
        self.assertEqual([], d.modified)
39
60
    def test_empty_to_abc_content(self):
40
61
        tree1 = self.make_branch_and_tree('1')
41
62
        tree2 = self.make_to_branch_and_tree('2')
 
63
        tree2.set_root_id(tree1.get_root_id())
42
64
        tree1 = self.get_tree_no_parents_no_content(tree1)
43
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
65
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
66
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
44
67
        d = self.intertree_class(tree1, tree2).compare()
45
68
        self.assertEqual([('a', 'a-id', 'file'),
46
69
                          ('b', 'b-id', 'directory'),
52
75
        self.assertEqual([], d.unchanged)
53
76
 
54
77
    def test_dangling(self):
 
78
        # This test depends on the ability for some trees to have a difference
 
79
        # between a 'versioned present' and 'versioned not present' (aka
 
80
        # dangling) file. In this test there are two trees each with a separate
 
81
        # dangling file, and the dangling files should be considered absent for
 
82
        # the test.
55
83
        tree1 = self.make_branch_and_tree('1')
56
 
        tree2 = self.make_branch_and_tree('2')
 
84
        tree2 = self.make_to_branch_and_tree('2')
 
85
        tree2.set_root_id(tree1.get_root_id())
57
86
        self.build_tree(['2/a'])
58
87
        tree2.add('a')
59
88
        os.unlink('2/a')
60
89
        self.build_tree(['1/b'])
61
90
        tree1.add('b')
62
91
        os.unlink('1/b')
 
92
        # the conversion to test trees here will leave the trees intact for the
 
93
        # default intertree, but may perform a commit for other tree types,
 
94
        # which may reduce the validity of the test. XXX: Think about how to
 
95
        # address this.
 
96
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
63
97
        d = self.intertree_class(tree1, tree2).compare()
64
98
        self.assertEqual([], d.added)
65
99
        self.assertEqual([], d.modified)
70
104
    def test_abc_content_to_empty(self):
71
105
        tree1 = self.make_branch_and_tree('1')
72
106
        tree2 = self.make_to_branch_and_tree('2')
 
107
        tree2.set_root_id(tree1.get_root_id())
73
108
        tree1 = self.get_tree_no_parents_abc_content(tree1)
74
 
        tree2 = self.get_to_tree_no_parents_no_content(tree2)
 
109
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
110
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
75
111
        d = self.intertree_class(tree1, tree2).compare()
76
112
        self.assertEqual([], d.added)
77
113
        self.assertEqual([], d.modified)
85
121
    def test_content_modification(self):
86
122
        tree1 = self.make_branch_and_tree('1')
87
123
        tree2 = self.make_to_branch_and_tree('2')
 
124
        tree2.set_root_id(tree1.get_root_id())
88
125
        tree1 = self.get_tree_no_parents_abc_content(tree1)
89
 
        tree2 = self.get_to_tree_no_parents_abc_content_2(tree2)
 
126
        tree2 = self.get_tree_no_parents_abc_content_2(tree2)
 
127
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
90
128
        d = self.intertree_class(tree1, tree2).compare()
91
129
        self.assertEqual([], d.added)
92
130
        self.assertEqual([('a', 'a-id', 'file', True, False)], d.modified)
97
135
    def test_meta_modification(self):
98
136
        tree1 = self.make_branch_and_tree('1')
99
137
        tree2 = self.make_to_branch_and_tree('2')
 
138
        tree2.set_root_id(tree1.get_root_id())
100
139
        tree1 = self.get_tree_no_parents_abc_content(tree1)
101
 
        tree2 = self.get_to_tree_no_parents_abc_content_3(tree2)
 
140
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
 
141
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
102
142
        d = self.intertree_class(tree1, tree2).compare()
103
143
        self.assertEqual([], d.added)
104
144
        self.assertEqual([('b/c', 'c-id', 'file', False, True)], d.modified)
109
149
    def test_file_rename(self):
110
150
        tree1 = self.make_branch_and_tree('1')
111
151
        tree2 = self.make_to_branch_and_tree('2')
 
152
        tree2.set_root_id(tree1.get_root_id())
112
153
        tree1 = self.get_tree_no_parents_abc_content(tree1)
113
 
        tree2 = self.get_to_tree_no_parents_abc_content_4(tree2)
 
154
        tree2 = self.get_tree_no_parents_abc_content_4(tree2)
 
155
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
114
156
        d = self.intertree_class(tree1, tree2).compare()
115
157
        self.assertEqual([], d.added)
116
158
        self.assertEqual([], d.modified)
121
163
    def test_file_rename_and_modification(self):
122
164
        tree1 = self.make_branch_and_tree('1')
123
165
        tree2 = self.make_to_branch_and_tree('2')
 
166
        tree2.set_root_id(tree1.get_root_id())
124
167
        tree1 = self.get_tree_no_parents_abc_content(tree1)
125
 
        tree2 = self.get_to_tree_no_parents_abc_content_5(tree2)
 
168
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
169
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
126
170
        d = self.intertree_class(tree1, tree2).compare()
127
171
        self.assertEqual([], d.added)
128
172
        self.assertEqual([], d.modified)
133
177
    def test_file_rename_and_meta_modification(self):
134
178
        tree1 = self.make_branch_and_tree('1')
135
179
        tree2 = self.make_to_branch_and_tree('2')
 
180
        tree2.set_root_id(tree1.get_root_id())
136
181
        tree1 = self.get_tree_no_parents_abc_content(tree1)
137
 
        tree2 = self.get_to_tree_no_parents_abc_content_6(tree2)
 
182
        tree2 = self.get_tree_no_parents_abc_content_6(tree2)
 
183
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
138
184
        d = self.intertree_class(tree1, tree2).compare()
139
185
        self.assertEqual([], d.added)
140
186
        self.assertEqual([], d.modified)
145
191
    def test_empty_to_abc_content_a_only(self):
146
192
        tree1 = self.make_branch_and_tree('1')
147
193
        tree2 = self.make_to_branch_and_tree('2')
 
194
        tree2.set_root_id(tree1.get_root_id())
148
195
        tree1 = self.get_tree_no_parents_no_content(tree1)
149
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
196
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
197
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
150
198
        d = self.intertree_class(tree1, tree2).compare(specific_files=['a'])
151
199
        self.assertEqual([('a', 'a-id', 'file')], d.added)
152
200
        self.assertEqual([], d.modified)
158
206
        tree1 = self.make_branch_and_tree('1')
159
207
        tree2 = self.make_to_branch_and_tree('2')
160
208
        tree1 = self.get_tree_no_parents_no_content(tree1)
161
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
209
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
210
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
162
211
        d = self.intertree_class(tree1, tree2).compare(
163
212
            specific_files=['a', 'b/c'])
164
213
        self.assertEqual(
174
223
        tree1 = self.make_branch_and_tree('1')
175
224
        tree2 = self.make_to_branch_and_tree('2')
176
225
        tree1 = self.get_tree_no_parents_no_content(tree1)
177
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
226
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
227
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
178
228
        d = self.intertree_class(tree1, tree2).compare(specific_files=['b'])
179
229
        self.assertEqual(
180
230
            [('b', 'b-id', 'directory'),('b/c', 'c-id', 'file')],
189
239
        tree1 = self.make_branch_and_tree('1')
190
240
        tree2 = self.make_to_branch_and_tree('2')
191
241
        tree1 = self.get_tree_no_parents_abc_content(tree1)
192
 
        tree2 = self.get_to_tree_no_parents_abc_content_5(tree2)
 
242
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
243
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
193
244
        d = self.intertree_class(tree1, tree2).compare(want_unchanged=True)
194
245
        self.assertEqual([], d.added)
195
246
        self.assertEqual([], d.modified)
204
255
        tree1 = self.make_branch_and_tree('1')
205
256
        tree2 = self.make_to_branch_and_tree('2')
206
257
        tree1 = self.get_tree_no_parents_abc_content(tree1)
207
 
        tree2 = self.get_to_tree_no_parents_abc_content_3(tree2)
 
258
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
 
259
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
260
        d = self.intertree_class(tree1, tree2).compare(specific_files=['b'])
208
261
        # the type of tree-3 does not matter - it is used as a lookup, not
209
 
        # a dispatch
 
262
        # a dispatch. XXX: For dirstate it does speak to the optimisability of
 
263
        # the lookup, in merged trees it can be fast-pathed. We probably want
 
264
        # two tests: one as is, and one with it as a pending merge.
210
265
        tree3 = self.make_branch_and_tree('3')
211
266
        tree3 = self.get_tree_no_parents_abc_content_6(tree3)
 
267
        tree3.lock_read()
 
268
        self.addCleanup(tree3.unlock)
212
269
        # tree 3 has 'e' which is 'c-id'. Tree 1 has c-id at b/c, and Tree 2
213
270
        # has c-id at b/c with its exec flag toggled.
214
271
        # without extra_trees, we should get no modifications from this
233
290
        tree1 = self.make_branch_and_tree('1')
234
291
        tree2 = self.make_to_branch_and_tree('2')
235
292
        tree1 = self.get_tree_no_parents_no_content(tree1)
236
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
293
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
294
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
237
295
        self.assertRaises(errors.PathsNotVersionedError, 
238
296
            self.intertree_class(tree1, tree2).compare,
239
297
            specific_files=['d'],
240
298
            require_versioned=True)
241
299
 
 
300
    def test_default_ignores_unversioned_files(self):
 
301
        tree1 = self.make_branch_and_tree('tree1')
 
302
        tree2 = self.make_to_branch_and_tree('tree2')
 
303
        tree2.set_root_id(tree1.get_root_id())
 
304
        self.build_tree(['tree1/a', 'tree1/c',
 
305
                         'tree2/a', 'tree2/b', 'tree2/c'])
 
306
        tree1.add(['a', 'c'], ['a-id', 'c-id'])
 
307
        tree2.add(['a', 'c'], ['a-id', 'c-id'])
 
308
 
 
309
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
310
        d = self.intertree_class(tree1, tree2).compare()
 
311
        self.assertEqual([], d.added)
 
312
        self.assertEqual([(u'a', 'a-id', 'file', True, False),
 
313
            (u'c', 'c-id', 'file', True, False)], d.modified)
 
314
        self.assertEqual([], d.removed)
 
315
        self.assertEqual([], d.renamed)
 
316
        self.assertEqual([], d.unchanged)
 
317
        self.assertEqual([], d.unversioned)
 
318
 
 
319
    def test_unversioned_paths_in_tree(self):
 
320
        tree1 = self.make_branch_and_tree('tree1')
 
321
        tree2 = self.make_to_branch_and_tree('tree2')
 
322
        tree2.set_root_id(tree1.get_root_id())
 
323
        self.build_tree(['tree2/file', 'tree2/dir/'])
 
324
        if has_symlinks():
 
325
            os.symlink('target', 'tree2/link')
 
326
            links_supported = True
 
327
        else:
 
328
            links_supported = False
 
329
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
330
        d = self.intertree_class(tree1, tree2).compare(want_unversioned=True)
 
331
        self.assertEqual([], d.added)
 
332
        self.assertEqual([], d.modified)
 
333
        self.assertEqual([], d.removed)
 
334
        self.assertEqual([], d.renamed)
 
335
        self.assertEqual([], d.unchanged)
 
336
        expected_unversioned = [(u'dir', None, 'directory'),
 
337
                                (u'file', None, 'file')]
 
338
        if links_supported:
 
339
            expected_unversioned.append((u'link', None, 'symlink'))
 
340
        self.assertEqual(expected_unversioned, d.unversioned)
 
341
 
242
342
 
243
343
class TestIterChanges(TestCaseWithTwoTrees):
244
344
    """Test the comparison iterator"""
245
345
 
 
346
    def do_iter_changes(self, tree1, tree2, **extra_args):
 
347
        """Helper to run _iter_changes from tree1 to tree2.
 
348
        
 
349
        :param tree1, tree2:  The source and target trees. These will be locked
 
350
            automatically.
 
351
        :param **extra_args: Extra args to pass to _iter_changes. This is not
 
352
            inspected by this test helper.
 
353
        """
 
354
        tree1.lock_read()
 
355
        tree2.lock_read()
 
356
        try:
 
357
            # sort order of output is not strictly defined
 
358
            return sorted(self.intertree_class(tree1, tree2)
 
359
                ._iter_changes(**extra_args))
 
360
        finally:
 
361
            tree1.unlock()
 
362
            tree2.unlock()
 
363
 
 
364
    def mutable_trees_to_locked_test_trees(self, tree1, tree2):
 
365
        """Convert the working trees into test trees.
 
366
 
 
367
        Read lock them, and add the unlock to the cleanup.
 
368
        """
 
369
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
370
        tree1.lock_read()
 
371
        self.addCleanup(tree1.unlock)
 
372
        tree2.lock_read()
 
373
        self.addCleanup(tree2.unlock)
 
374
        return tree1, tree2
 
375
 
 
376
    def make_tree_with_special_names(self):
 
377
        """Create a tree with filenames chosen to exercise the walk order."""
 
378
        tree1 = self.make_branch_and_tree('tree1')
 
379
        tree2 = self.make_to_branch_and_tree('tree2')
 
380
        tree2.set_root_id(tree1.get_root_id())
 
381
        paths, path_ids = self._create_special_names(tree2, 'tree2')
 
382
        tree2.commit('initial', rev_id='rev-1')
 
383
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
384
        return (tree1, tree2, paths, path_ids)
 
385
 
 
386
    def make_trees_with_special_names(self):
 
387
        """Both trees will use the special names.
 
388
 
 
389
        But the contents will differ for each file.
 
390
        """
 
391
        tree1 = self.make_branch_and_tree('tree1')
 
392
        tree2 = self.make_to_branch_and_tree('tree2')
 
393
        tree2.set_root_id(tree1.get_root_id())
 
394
        paths, path_ids = self._create_special_names(tree1, 'tree1')
 
395
        paths, path_ids = self._create_special_names(tree2, 'tree2')
 
396
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
397
        return (tree1, tree2, paths, path_ids)
 
398
 
 
399
    def _create_special_names(self, tree, base_path):
 
400
        """Create a tree with paths that expose differences in sort orders."""
 
401
        # Each directory will have a single file named 'f' inside
 
402
        dirs = ['a',
 
403
                'a-a',
 
404
                'a/a',
 
405
                'a/a-a',
 
406
                'a/a/a',
 
407
                'a/a/a-a',
 
408
                'a/a/a/a',
 
409
                'a/a/a/a-a',
 
410
                'a/a/a/a/a',
 
411
               ]
 
412
        with_slashes = []
 
413
        paths = []
 
414
        path_ids = []
 
415
        for d in dirs:
 
416
            with_slashes.append(base_path + '/' + d + '/')
 
417
            with_slashes.append(base_path + '/' + d + '/f')
 
418
            paths.append(d)
 
419
            paths.append(d+'/f')
 
420
            path_ids.append(d.replace('/', '_') + '-id')
 
421
            path_ids.append(d.replace('/', '_') + '_f-id')
 
422
        self.build_tree(with_slashes)
 
423
        tree.add(paths, path_ids)
 
424
        return paths, path_ids
 
425
 
246
426
    def test_compare_empty_trees(self):
247
427
        tree1 = self.make_branch_and_tree('1')
248
428
        tree2 = self.make_to_branch_and_tree('2')
249
429
        tree1 = self.get_tree_no_parents_no_content(tree1)
250
 
        tree2 = self.get_to_tree_no_parents_no_content(tree2)
251
 
        self.assertEqual([], list(tree2._iter_changes(tree1)))
 
430
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
431
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
432
        self.assertEqual([], self.do_iter_changes(tree1, tree2))
252
433
 
253
434
    def added(self, tree, file_id):
254
435
        entry = tree.inventory[file_id]
255
436
        path = tree.id2path(file_id)
256
 
        return (file_id, path, True, (False, True), (None, entry.parent_id),
257
 
                (None, entry.name), (None, entry.kind), 
 
437
        return (file_id, (None, path), True, (False, True), (None, entry.parent_id),
 
438
                (None, entry.name), (None, entry.kind),
258
439
                (None, entry.executable))
259
440
 
 
441
    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),
 
445
                (entry.name, entry.name), (entry.kind, entry.kind),
 
446
                (entry.executable, entry.executable))
 
447
 
 
448
    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),
 
455
                (old_entry.executable, new_entry.executable))
 
456
 
 
457
    def missing(self, file_id, from_path, to_path, parent_id, kind):
 
458
        _, from_basename = os.path.split(from_path)
 
459
        _, to_basename = os.path.split(to_path)
 
460
        # missing files have both paths, but no kind.
 
461
        return (file_id, (from_path, to_path), True, (True, True),
 
462
            (parent_id, parent_id),
 
463
            (from_basename, to_basename), (kind, None), (False, False))
 
464
 
260
465
    def deleted(self, tree, file_id):
261
466
        entry = tree.inventory[file_id]
262
467
        path = tree.id2path(file_id)
263
 
        return (file_id, path, True, (True, False), (entry.parent_id, None),
264
 
                (entry.name, None), (entry.kind, None), 
 
468
        return (file_id, (path, None), True, (True, False), (entry.parent_id, None),
 
469
                (entry.name, None), (entry.kind, None),
265
470
                (entry.executable, None))
266
471
 
 
472
    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)
 
477
        return (file_id, (from_path, to_path), content_changed, (True, True),
 
478
            (from_entry.parent_id, to_entry.parent_id),
 
479
            (from_entry.name, to_entry.name),
 
480
            (from_entry.kind, to_entry.kind),
 
481
            (from_entry.executable, to_entry.executable))
 
482
 
 
483
    def unchanged(self, tree, file_id):
 
484
        entry = tree.inventory[file_id]
 
485
        parent = entry.parent_id
 
486
        name = entry.name
 
487
        kind = entry.kind
 
488
        executable = entry.executable
 
489
        path = tree.id2path(file_id)
 
490
        return (file_id, (path, path), False, (True, True),
 
491
               (parent, parent), (name, name), (kind, kind),
 
492
               (executable, executable))
 
493
 
 
494
    def unversioned(self, tree, path):
 
495
        """Create an unversioned result."""
 
496
        _, basename = os.path.split(path)
 
497
        kind = file_kind(tree.abspath(path))
 
498
        return (None, (None, path), True, (False, False), (None, None),
 
499
                (None, basename), (None, kind),
 
500
                (None, False))
 
501
 
267
502
    def test_empty_to_abc_content(self):
268
503
        tree1 = self.make_branch_and_tree('1')
269
504
        tree2 = self.make_to_branch_and_tree('2')
270
505
        tree1 = self.get_tree_no_parents_no_content(tree1)
271
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
272
 
            
273
 
        self.assertEqual([self.added(tree2, 'root-id'),
274
 
                          self.added(tree2, 'a-id'), 
275
 
                          self.added(tree2, 'b-id'), 
276
 
                          self.added(tree2, 'c-id'),
277
 
                          self.deleted(tree1, 'empty-root-id')],
278
 
                         list(tree2._iter_changes(tree1)))
 
506
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
507
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
508
        expected_results = sorted([
 
509
            self.added(tree2, 'root-id'),
 
510
            self.added(tree2, 'a-id'),
 
511
            self.added(tree2, 'b-id'),
 
512
            self.added(tree2, 'c-id'),
 
513
            self.deleted(tree1, 'empty-root-id')])
 
514
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
279
515
 
280
516
    def test_empty_to_abc_content_a_only(self):
281
517
        tree1 = self.make_branch_and_tree('1')
282
518
        tree2 = self.make_to_branch_and_tree('2')
283
519
        tree1 = self.get_tree_no_parents_no_content(tree1)
284
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
285
 
        self.assertEqual([self.added(tree2, 'a-id')],
286
 
                         list(tree2._iter_changes(tree1, 
287
 
                                                 specific_file_ids=['a-id'])))
288
 
        self.assertEqual([self.deleted(tree2, 'a-id')],
289
 
                         list(tree1._iter_changes(tree2, 
290
 
                                                 specific_file_ids=['a-id'])))
 
520
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
521
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
522
        self.assertEqual(
 
523
            [self.added(tree2, 'a-id')],
 
524
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
 
525
 
 
526
    def test_abc_content_to_empty_to_abc_content_a_only(self):
 
527
        tree1 = self.make_branch_and_tree('1')
 
528
        tree2 = self.make_to_branch_and_tree('2')
 
529
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
530
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
531
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
532
        self.assertEqual(
 
533
            [self.deleted(tree1, 'a-id')],
 
534
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
291
535
 
292
536
    def test_empty_to_abc_content_a_and_c_only(self):
293
537
        tree1 = self.make_branch_and_tree('1')
294
538
        tree2 = self.make_to_branch_and_tree('2')
295
539
        tree1 = self.get_tree_no_parents_no_content(tree1)
296
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
297
 
        self.assertEqual([self.added(tree2, 'a-id'),
298
 
                          self.added(tree2, 'c-id')],
299
 
                         list(tree2._iter_changes(tree1, 
300
 
                                                 specific_file_ids=['a-id', 
301
 
                                                                    'c-id'])))
302
 
        d = self.intertree_class(tree1, tree2).compare(
303
 
            specific_files=['a', 'b/c'])
 
540
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
541
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
542
        expected_result = [self.added(tree2, 'a-id'), self.added(tree2, 'c-id')]
 
543
        self.assertEqual(expected_result,
 
544
            self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
304
545
 
305
 
    def test_abc_content(self):
 
546
    def test_abc_content_to_empty(self):
306
547
        tree1 = self.make_branch_and_tree('1')
307
548
        tree2 = self.make_to_branch_and_tree('2')
308
 
        tree1 = self.get_tree_no_parents_no_content(tree1)
309
 
        tree2 = self.get_to_tree_no_parents_abc_content(tree2)
 
549
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
550
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
551
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
310
552
        def deleted(file_id):
311
 
            entry = tree2.inventory[file_id]
312
 
            path = tree2.id2path(file_id)
313
 
            return (file_id, path, True, (True, False), 
 
553
            entry = tree1.inventory[file_id]
 
554
            path = tree1.id2path(file_id)
 
555
            return (file_id, (path, None), True, (True, False),
314
556
                    (entry.parent_id, None),
315
 
                    (entry.name, None), (entry.kind, None), 
 
557
                    (entry.name, None), (entry.kind, None),
316
558
                    (entry.executable, None))
317
 
        self.assertEqual([self.added(tree1, 'empty-root-id'), 
318
 
                          deleted('root-id'), deleted('a-id'), 
319
 
                          deleted('b-id'), deleted('c-id')],
320
 
                          list(tree1._iter_changes(tree2)))
 
559
        expected_results = sorted([
 
560
            self.added(tree2, 'empty-root-id'),
 
561
            deleted('root-id'), deleted('a-id'),
 
562
            deleted('b-id'), deleted('c-id')])
 
563
        self.assertEqual(
 
564
            expected_results,
 
565
            self.do_iter_changes(tree1, tree2))
321
566
 
322
567
    def test_content_modification(self):
323
568
        tree1 = self.make_branch_and_tree('1')
324
569
        tree2 = self.make_to_branch_and_tree('2')
325
570
        tree1 = self.get_tree_no_parents_abc_content(tree1)
326
 
        tree2 = self.get_to_tree_no_parents_abc_content_2(tree2)
327
 
        root_id = tree1.inventory.root.file_id
328
 
        self.assertEqual([('a-id', 'a', True, (True, True), 
329
 
                          (root_id, root_id), ('a', 'a'), 
330
 
                          ('file', 'file'), (False, False))], 
331
 
                         list(tree2._iter_changes(tree1)))
 
571
        tree2 = self.get_tree_no_parents_abc_content_2(tree2)
 
572
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
573
        root_id = tree1.path2id('')
 
574
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
575
                           (root_id, root_id), ('a', 'a'),
 
576
                           ('file', 'file'), (False, False))],
 
577
                         self.do_iter_changes(tree1, tree2))
332
578
 
333
579
    def test_meta_modification(self):
334
580
        tree1 = self.make_branch_and_tree('1')
335
581
        tree2 = self.make_to_branch_and_tree('2')
336
582
        tree1 = self.get_tree_no_parents_abc_content(tree1)
337
 
        tree2 = self.get_to_tree_no_parents_abc_content_3(tree2)
338
 
        self.assertEqual([('c-id', 'b/c', False, (True, True), 
339
 
                          ('b-id', 'b-id'), ('c', 'c'), ('file', 'file'), 
340
 
                          (False, True))], list(tree2._iter_changes(tree1)))
 
583
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
 
584
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
585
        self.assertEqual([('c-id', ('b/c', 'b/c'), False, (True, True),
 
586
                           ('b-id', 'b-id'), ('c', 'c'), ('file', 'file'),
 
587
                          (False, True))],
 
588
                         self.do_iter_changes(tree1, tree2))
 
589
 
 
590
    def test_empty_dir(self):
 
591
        """an empty dir should not cause glitches to surrounding files."""
 
592
        tree1 = self.make_branch_and_tree('1')
 
593
        tree2 = self.make_to_branch_and_tree('2')
 
594
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
595
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
596
        # the pathname is chosen to fall between 'a' and 'b'.
 
597
        self.build_tree(['1/a-empty/', '2/a-empty/'])
 
598
        tree1.add(['a-empty'], ['a-empty'])
 
599
        tree2.add(['a-empty'], ['a-empty'])
 
600
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
601
        expected = []
 
602
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
341
603
 
342
604
    def test_file_rename(self):
343
605
        tree1 = self.make_branch_and_tree('1')
344
606
        tree2 = self.make_to_branch_and_tree('2')
345
607
        tree1 = self.get_tree_no_parents_abc_content(tree1)
346
 
        tree2 = self.get_to_tree_no_parents_abc_content_4(tree2)
347
 
        root_id = tree1.inventory.root.file_id
348
 
        self.assertEqual([('a-id', 'd', False, (True, True), 
349
 
                          (root_id, root_id), ('a', 'd'), ('file', 'file'),
350
 
                          (False, False))], list(tree2._iter_changes(tree1)))
 
608
        tree2 = self.get_tree_no_parents_abc_content_4(tree2)
 
609
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
610
        root_id = tree1.path2id('')
 
611
        self.assertEqual([('a-id', ('a', 'd'), False, (True, True),
 
612
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
613
                           (False, False))],
 
614
                         self.do_iter_changes(tree1, tree2))
351
615
 
352
616
    def test_file_rename_and_modification(self):
353
617
        tree1 = self.make_branch_and_tree('1')
354
618
        tree2 = self.make_to_branch_and_tree('2')
355
619
        tree1 = self.get_tree_no_parents_abc_content(tree1)
356
 
        tree2 = self.get_to_tree_no_parents_abc_content_5(tree2)
357
 
        root_id = tree1.inventory.root.file_id
358
 
        self.assertEqual([('a-id', 'd', True, (True, True), 
359
 
                          (root_id, root_id), ('a', 'd'), ('file', 'file'),
360
 
                           (False, False))], list(tree2._iter_changes(tree1)))
 
620
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
621
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
622
        root_id = tree1.path2id('')
 
623
        self.assertEqual([('a-id', ('a', 'd'), True, (True, True),
 
624
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
625
                           (False, False))],
 
626
                         self.do_iter_changes(tree1, tree2))
361
627
 
362
628
    def test_file_rename_and_meta_modification(self):
363
629
        tree1 = self.make_branch_and_tree('1')
364
630
        tree2 = self.make_to_branch_and_tree('2')
365
631
        tree1 = self.get_tree_no_parents_abc_content(tree1)
366
 
        tree2 = self.get_to_tree_no_parents_abc_content_6(tree2)
367
 
        root_id = tree1.inventory.root.file_id
368
 
        self.assertEqual([('c-id', 'e', False, (True, True), 
369
 
                          ('b-id', root_id), ('c', 'e'), ('file', 'file'), 
370
 
                          (False, True))], list(tree2._iter_changes(tree1)))
 
632
        tree2 = self.get_tree_no_parents_abc_content_6(tree2)
 
633
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
634
        root_id = tree1.path2id('')
 
635
        self.assertEqual([('c-id', ('b/c', 'e'), False, (True, True),
 
636
                           ('b-id', root_id), ('c', 'e'), ('file', 'file'),
 
637
                           (False, True))],
 
638
                         self.do_iter_changes(tree1, tree2))
 
639
 
 
640
    def test_missing_in_target(self):
 
641
        """Test with the target files versioned but absent from disk."""
 
642
        tree1 = self.make_branch_and_tree('1')
 
643
        tree2 = self.make_to_branch_and_tree('2')
 
644
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
645
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
646
        os.unlink('2/a')
 
647
        shutil.rmtree('2/b')
 
648
        # TODO ? have a symlink here?
 
649
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
650
        root_id = tree1.path2id('')
 
651
        expected = sorted([
 
652
            self.missing('a-id', 'a', 'a', root_id, 'file'),
 
653
            self.missing('b-id', 'b', 'b', root_id, 'directory'),
 
654
            self.missing('c-id', 'b/c', 'b/c', 'b-id', 'file'),
 
655
            ])
 
656
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
657
 
 
658
    def test_missing_and_renamed(self):
 
659
        tree1 = self.make_branch_and_tree('tree1')
 
660
        tree2 = self.make_to_branch_and_tree('tree2')
 
661
        tree2.set_root_id(tree1.get_root_id())
 
662
        self.build_tree(['tree1/file'])
 
663
        tree1.add(['file'], ['file-id'])
 
664
        self.build_tree(['tree2/directory/'])
 
665
        tree2.add(['directory'], ['file-id'])
 
666
        os.rmdir('tree2/directory')
 
667
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
668
 
 
669
        root_id = tree1.path2id('')
 
670
        expected = sorted([
 
671
            self.missing('file-id', 'file', 'directory', root_id, 'file'),
 
672
            ])
 
673
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
371
674
 
372
675
    def test_unchanged_with_renames_and_modifications(self):
373
676
        """want_unchanged should generate a list of unchanged entries."""
374
677
        tree1 = self.make_branch_and_tree('1')
375
678
        tree2 = self.make_to_branch_and_tree('2')
376
679
        tree1 = self.get_tree_no_parents_abc_content(tree1)
377
 
        tree2 = self.get_to_tree_no_parents_abc_content_5(tree2)
378
 
        root_id = tree1.inventory.root.file_id
379
 
        def unchanged(file_id):
380
 
            entry = tree1.inventory[file_id]
381
 
            parent = entry.parent_id
382
 
            name = entry.name
383
 
            kind = entry.kind
384
 
            executable = entry.executable
385
 
            return (file_id, tree1.id2path(file_id), False, (True, True), 
386
 
                   (parent, parent), (name, name), (kind, kind), 
387
 
                   (executable, executable))
388
 
        self.assertEqual([unchanged(root_id), unchanged('b-id'),
389
 
                          ('a-id', 'd', True, (True, True), 
390
 
                          (root_id, root_id), ('a', 'd'), ('file', 'file'),
391
 
                          (False, False)), unchanged('c-id')],
392
 
                         list(tree2._iter_changes(tree1, 
393
 
                                                 include_unchanged=True)))
 
680
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
681
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
682
        root_id = tree1.path2id('')
 
683
 
 
684
        self.assertEqual(sorted([self.unchanged(tree1, root_id),
 
685
            self.unchanged(tree1, 'b-id'),
 
686
            ('a-id', ('a', 'd'), True, (True, True),
 
687
             (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
688
            (False, False)), self.unchanged(tree1, 'c-id')]),
 
689
            self.do_iter_changes(tree1, tree2, include_unchanged=True))
 
690
 
 
691
    def test_compare_subtrees(self):
 
692
        tree1 = self.make_branch_and_tree('1')
 
693
        if not tree1.supports_tree_reference():
 
694
            return
 
695
        tree1.set_root_id('root-id')
 
696
        subtree1 = self.make_branch_and_tree('1/sub')
 
697
        subtree1.set_root_id('subtree-id')
 
698
        tree1.add_reference(subtree1)
 
699
 
 
700
        tree2 = self.make_to_branch_and_tree('2')
 
701
        if not tree2.supports_tree_reference():
 
702
            return
 
703
        tree2.set_root_id('root-id')
 
704
        subtree2 = self.make_to_branch_and_tree('2/sub')
 
705
        subtree2.set_root_id('subtree-id')
 
706
        tree2.add_reference(subtree2)
 
707
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
708
 
 
709
        self.assertEqual([], list(tree2._iter_changes(tree1)))
 
710
        subtree1.commit('commit', rev_id='commit-a')
 
711
        self.assertEqual([
 
712
            ('root-id',
 
713
             (u'', u''),
 
714
             False,
 
715
             (True, True),
 
716
             (None, None),
 
717
             (u'', u''),
 
718
             ('directory', 'directory'),
 
719
             (False, False)),
 
720
            ('subtree-id',
 
721
             ('sub', 'sub',),
 
722
             False,
 
723
             (True, True),
 
724
             ('root-id', 'root-id'),
 
725
             ('sub', 'sub'),
 
726
             ('tree-reference', 'tree-reference'),
 
727
             (False, False))],
 
728
                         list(tree2._iter_changes(tree1,
 
729
                             include_unchanged=True)))
 
730
 
 
731
    def test_disk_in_subtrees_skipped(self):
 
732
        """subtrees are considered not-in-the-current-tree.
 
733
        
 
734
        This test tests the trivial case, where the basis has no paths in the
 
735
        current trees subtree.
 
736
        """
 
737
        tree1 = self.make_branch_and_tree('1')
 
738
        tree1.set_root_id('root-id')
 
739
        tree2 = self.make_to_branch_and_tree('2')
 
740
        if not tree2.supports_tree_reference():
 
741
            return
 
742
        tree2.set_root_id('root-id')
 
743
        subtree2 = self.make_to_branch_and_tree('2/sub')
 
744
        subtree2.set_root_id('subtree-id')
 
745
        tree2.add(['sub'], ['subtree-id'])
 
746
        self.build_tree(['2/sub/file'])
 
747
        subtree2.add(['file'])
 
748
 
 
749
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
750
        # this should filter correctly from above
 
751
        self.assertEqual([self.added(tree2, 'subtree-id')],
 
752
            self.do_iter_changes(tree1, tree2, want_unversioned=True))
 
753
        # and when the path is named
 
754
        self.assertEqual([self.added(tree2, 'subtree-id')],
 
755
            self.do_iter_changes(tree1, tree2, specific_files=['sub'],
 
756
                want_unversioned=True))
 
757
 
 
758
    def test_default_ignores_unversioned_files(self):
 
759
        tree1 = self.make_branch_and_tree('tree1')
 
760
        tree2 = self.make_to_branch_and_tree('tree2')
 
761
        tree2.set_root_id(tree1.get_root_id())
 
762
        self.build_tree(['tree1/a', 'tree1/c',
 
763
                         'tree2/a', 'tree2/b', 'tree2/c'])
 
764
        tree1.add(['a', 'c'], ['a-id', 'c-id'])
 
765
        tree2.add(['a', 'c'], ['a-id', 'c-id'])
 
766
 
 
767
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
768
 
 
769
        # We should ignore the fact that 'b' exists in tree-2
 
770
        # because the want_unversioned parameter was not given.
 
771
        expected = sorted([
 
772
            self.content_changed(tree2, 'a-id'),
 
773
            self.content_changed(tree2, 'c-id'),
 
774
            ])
 
775
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
776
 
 
777
    def test_unversioned_paths_in_tree(self):
 
778
        tree1 = self.make_branch_and_tree('tree1')
 
779
        tree2 = self.make_to_branch_and_tree('tree2')
 
780
        tree2.set_root_id(tree1.get_root_id())
 
781
        self.build_tree(['tree2/file', 'tree2/dir/'])
 
782
        if has_symlinks():
 
783
            os.symlink('target', 'tree2/link')
 
784
            links_supported = True
 
785
        else:
 
786
            links_supported = False
 
787
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
788
        expected = [
 
789
            self.unversioned(tree2, 'file'),
 
790
            self.unversioned(tree2, 'dir'),
 
791
            ]
 
792
        if links_supported:
 
793
            expected.append(self.unversioned(tree2, 'link'))
 
794
        expected = sorted(expected)
 
795
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
796
            want_unversioned=True))
 
797
 
 
798
    def test_unversioned_paths_in_tree_specific_files(self):
 
799
        tree1 = self.make_branch_and_tree('tree1')
 
800
        tree2 = self.make_to_branch_and_tree('tree2')
 
801
        self.build_tree(['tree2/file', 'tree2/dir/'])
 
802
        if has_symlinks():
 
803
            os.symlink('target', 'tree2/link')
 
804
            links_supported = True
 
805
        else:
 
806
            links_supported = False
 
807
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
808
        expected = [
 
809
            self.unversioned(tree2, 'file'),
 
810
            self.unversioned(tree2, 'dir'),
 
811
            ]
 
812
        specific_files=['file', 'dir']
 
813
        if links_supported:
 
814
            expected.append(self.unversioned(tree2, 'link'))
 
815
            specific_files.append('link')
 
816
        expected = sorted(expected)
 
817
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
818
            specific_files=specific_files, require_versioned=False,
 
819
            want_unversioned=True))
 
820
 
 
821
    def test_unversioned_paths_in_target_matching_source_old_names(self):
 
822
        # its likely that naive implementations of unversioned file support
 
823
        # will fail if the path was versioned, but is not any more, 
 
824
        # due to a rename, not due to unversioning it.
 
825
        # That is, if the old tree has a versioned file 'foo', and
 
826
        # the new tree has the same file but versioned as 'bar', and also
 
827
        # has an unknown file 'foo', we should get back output for
 
828
        # both foo and bar.
 
829
        tree1 = self.make_branch_and_tree('tree1')
 
830
        tree2 = self.make_to_branch_and_tree('tree2')
 
831
        tree2.set_root_id(tree1.get_root_id())
 
832
        self.build_tree(['tree2/file', 'tree2/dir/',
 
833
            'tree1/file', 'tree2/movedfile',
 
834
            'tree1/dir/', 'tree2/moveddir/'])
 
835
        if has_symlinks():
 
836
            os.symlink('target', 'tree1/link')
 
837
            os.symlink('target', 'tree2/link')
 
838
            os.symlink('target', 'tree2/movedlink')
 
839
            links_supported = True
 
840
        else:
 
841
            links_supported = False
 
842
        tree1.add(['file', 'dir'], ['file-id', 'dir-id'])
 
843
        tree2.add(['movedfile', 'moveddir'], ['file-id', 'dir-id'])
 
844
        if links_supported:
 
845
            tree1.add(['link'], ['link-id'])
 
846
            tree2.add(['movedlink'], ['link-id'])
 
847
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
848
        root_id = tree1.path2id('')
 
849
        expected = [
 
850
            self.renamed(tree1, tree2, 'dir-id', False),
 
851
            self.renamed(tree1, tree2, 'file-id', True),
 
852
            self.unversioned(tree2, 'file'),
 
853
            self.unversioned(tree2, 'dir'),
 
854
            ]
 
855
        specific_files=['file', 'dir']
 
856
        if links_supported:
 
857
            expected.append(self.renamed(tree1, tree2, 'link-id', False))
 
858
            expected.append(self.unversioned(tree2, 'link'))
 
859
            specific_files.append('link')
 
860
        expected = sorted(expected)
 
861
        # run once with, and once without specific files, to catch
 
862
        # potentially different code paths.
 
863
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
864
            require_versioned=False,
 
865
            want_unversioned=True))
 
866
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
867
            specific_files=specific_files, require_versioned=False,
 
868
            want_unversioned=True))
 
869
 
 
870
    def test_unversioned_subtree_only_emits_root(self):
 
871
        tree1 = self.make_branch_and_tree('tree1')
 
872
        tree2 = self.make_to_branch_and_tree('tree2')
 
873
        tree2.set_root_id(tree1.get_root_id())
 
874
        self.build_tree(['tree2/dir/', 'tree2/dir/file'])
 
875
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
876
        expected = [
 
877
            self.unversioned(tree2, 'dir'),
 
878
            ]
 
879
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
880
            want_unversioned=True))
 
881
 
 
882
    def make_trees_with_symlinks(self):
 
883
        tree1 = self.make_branch_and_tree('tree1')
 
884
        tree2 = self.make_to_branch_and_tree('tree2')
 
885
        tree2.set_root_id(tree1.get_root_id())
 
886
        self.build_tree(['tree1/fromfile', 'tree1/fromdir/'])
 
887
        self.build_tree(['tree2/tofile', 'tree2/todir/', 'tree2/unknown'])
 
888
        # try:
 
889
        os.symlink('original', 'tree1/changed')
 
890
        os.symlink('original', 'tree1/removed')
 
891
        os.symlink('original', 'tree1/tofile')
 
892
        os.symlink('original', 'tree1/todir')
 
893
        # we make the unchanged link point at unknown to catch incorrect
 
894
        # symlink-following code in the specified_files test.
 
895
        os.symlink('unknown', 'tree1/unchanged')
 
896
        os.symlink('new',      'tree2/added')
 
897
        os.symlink('new',      'tree2/changed')
 
898
        os.symlink('new',      'tree2/fromfile')
 
899
        os.symlink('new',      'tree2/fromdir')
 
900
        os.symlink('unknown', 'tree2/unchanged')
 
901
        from_paths_and_ids = [
 
902
            'fromdir',
 
903
            'fromfile',
 
904
            'changed',
 
905
            'removed',
 
906
            'todir',
 
907
            'tofile',
 
908
            'unchanged',
 
909
            ]
 
910
        to_paths_and_ids = [
 
911
            'added',
 
912
            'fromdir',
 
913
            'fromfile',
 
914
            'changed',
 
915
            'todir',
 
916
            'tofile',
 
917
            'unchanged',
 
918
            ]
 
919
        tree1.add(from_paths_and_ids, from_paths_and_ids)
 
920
        tree2.add(to_paths_and_ids, to_paths_and_ids)
 
921
        # except ???:
 
922
        #   raise TestSkipped('OS does not support symlinks')
 
923
        #   links_supported = False
 
924
        return self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
925
 
 
926
    def test_versioned_symlinks(self):
 
927
        if not has_symlinks():
 
928
            raise tests.TestSkipped("No symlink support")
 
929
        tree1, tree2 = self.make_trees_with_symlinks()
 
930
        root_id = tree1.path2id('')
 
931
        expected = [
 
932
            self.unchanged(tree1, tree1.path2id('')),
 
933
            self.added(tree2, 'added'),
 
934
            self.content_changed(tree2, 'changed'),
 
935
            self.kind_changed(tree1, tree2, 'fromdir'),
 
936
            self.kind_changed(tree1, tree2, 'fromfile'),
 
937
            self.deleted(tree1, 'removed'),
 
938
            self.unchanged(tree2, 'unchanged'),
 
939
            self.unversioned(tree2, 'unknown'),
 
940
            self.kind_changed(tree1, tree2, 'todir'),
 
941
            self.kind_changed(tree1, tree2, 'tofile'),
 
942
            ]
 
943
        expected = sorted(expected)
 
944
        self.assertEqual(expected,
 
945
            self.do_iter_changes(tree1, tree2, include_unchanged=True,
 
946
                want_unversioned=True))
 
947
 
 
948
    def test_versioned_symlinks_specific_files(self):
 
949
        if not has_symlinks():
 
950
            raise tests.TestSkipped("No symlink support")
 
951
        tree1, tree2 = self.make_trees_with_symlinks()
 
952
        root_id = tree1.path2id('')
 
953
        expected = [
 
954
            self.added(tree2, 'added'),
 
955
            self.content_changed(tree2, 'changed'),
 
956
            self.kind_changed(tree1, tree2, 'fromdir'),
 
957
            self.kind_changed(tree1, tree2, 'fromfile'),
 
958
            self.deleted(tree1, 'removed'),
 
959
            self.kind_changed(tree1, tree2, 'todir'),
 
960
            self.kind_changed(tree1, tree2, 'tofile'),
 
961
            ]
 
962
        expected = sorted(expected)
 
963
        # we should get back just the changed links. We pass in 'unchanged' to
 
964
        # make sure that it is correctly not returned - and neither is the
 
965
        # unknown path 'unknown' which it points at.
 
966
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
967
            specific_files=['added', 'changed', 'fromdir', 'fromfile',
 
968
            'removed', 'unchanged', 'todir', 'tofile']))
 
969
 
 
970
    def test_tree_with_special_names(self):
 
971
        tree1, tree2, paths, path_ids = self.make_tree_with_special_names()
 
972
        expected = sorted(self.added(tree2, f_id) for f_id in path_ids)
 
973
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
974
 
 
975
    def test_trees_with_special_names(self):
 
976
        tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
 
977
        expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids
 
978
                          if f_id.endswith('_f-id'))
 
979
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
980
 
 
981
    def test_trees_with_deleted_dir(self):
 
982
        tree1 = self.make_branch_and_tree('tree1')
 
983
        tree2 = self.make_to_branch_and_tree('tree2')
 
984
        tree2.set_root_id(tree1.get_root_id())
 
985
        self.build_tree(['tree1/a', 'tree1/b/', 'tree1/b/c',
 
986
                         'tree1/b/d/', 'tree1/b/d/e', 'tree1/f/', 'tree1/f/g',
 
987
                         'tree2/a', 'tree2/f/', 'tree2/f/g'])
 
988
        tree1.add(['a', 'b', 'b/c', 'b/d/', 'b/d/e', 'f', 'f/g'],
 
989
                  ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id', 'g-id'])
 
990
        tree2.add(['a', 'f', 'f/g'], ['a-id', 'f-id', 'g-id'])
 
991
 
 
992
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
993
        # We should notice that 'b' and all its children are deleted
 
994
        expected = sorted([
 
995
            self.content_changed(tree2, 'a-id'),
 
996
            self.content_changed(tree2, 'g-id'),
 
997
            self.deleted(tree1, 'b-id'),
 
998
            self.deleted(tree1, 'c-id'),
 
999
            self.deleted(tree1, 'd-id'),
 
1000
            self.deleted(tree1, 'e-id'),
 
1001
            ])
 
1002
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
1003
 
 
1004
    def test_added_unicode(self):
 
1005
        tree1 = self.make_branch_and_tree('tree1')
 
1006
        tree2 = self.make_to_branch_and_tree('tree2')
 
1007
        root_id = tree1.get_root_id()
 
1008
        tree2.set_root_id(root_id)
 
1009
 
 
1010
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1011
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1012
        a_id = u'\u03b1-id'.encode('utf8')
 
1013
        added_id = u'\u03c9_added_id'.encode('utf8')
 
1014
        try:
 
1015
            self.build_tree([u'tree1/\u03b1/',
 
1016
                             u'tree2/\u03b1/',
 
1017
                             u'tree2/\u03b1/\u03c9-added',
 
1018
                            ])
 
1019
        except UnicodeError:
 
1020
            raise tests.TestSkipped("Could not create Unicode files.")
 
1021
        tree1.add([u'\u03b1'], [a_id])
 
1022
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-added'], [a_id, added_id])
 
1023
 
 
1024
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1025
 
 
1026
        self.assertEqual([self.added(tree2, added_id)],
 
1027
                         self.do_iter_changes(tree1, tree2))
 
1028
        self.assertEqual([self.added(tree2, added_id)],
 
1029
                         self.do_iter_changes(tree1, tree2,
 
1030
                                              specific_files=[u'\u03b1']))
 
1031
 
 
1032
    def test_deleted_unicode(self):
 
1033
        tree1 = self.make_branch_and_tree('tree1')
 
1034
        tree2 = self.make_to_branch_and_tree('tree2')
 
1035
        root_id = tree1.get_root_id()
 
1036
        tree2.set_root_id(root_id)
 
1037
 
 
1038
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1039
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1040
        a_id = u'\u03b1-id'.encode('utf8')
 
1041
        deleted_id = u'\u03c9_deleted_id'.encode('utf8')
 
1042
        try:
 
1043
            self.build_tree([u'tree1/\u03b1/',
 
1044
                             u'tree1/\u03b1/\u03c9-deleted',
 
1045
                             u'tree2/\u03b1/',
 
1046
                            ])
 
1047
        except UnicodeError:
 
1048
            raise tests.TestSkipped("Could not create Unicode files.")
 
1049
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-deleted'], [a_id, deleted_id])
 
1050
        tree2.add([u'\u03b1'], [a_id])
 
1051
 
 
1052
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1053
 
 
1054
        self.assertEqual([self.deleted(tree1, deleted_id)],
 
1055
                         self.do_iter_changes(tree1, tree2))
 
1056
        self.assertEqual([self.deleted(tree1, deleted_id)],
 
1057
                         self.do_iter_changes(tree1, tree2,
 
1058
                                              specific_files=[u'\u03b1']))
 
1059
 
 
1060
    def test_modified_unicode(self):
 
1061
        tree1 = self.make_branch_and_tree('tree1')
 
1062
        tree2 = self.make_to_branch_and_tree('tree2')
 
1063
        root_id = tree1.get_root_id()
 
1064
        tree2.set_root_id(root_id)
 
1065
 
 
1066
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1067
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1068
        a_id = u'\u03b1-id'.encode('utf8')
 
1069
        mod_id = u'\u03c9_mod_id'.encode('utf8')
 
1070
        try:
 
1071
            self.build_tree([u'tree1/\u03b1/',
 
1072
                             u'tree1/\u03b1/\u03c9-modified',
 
1073
                             u'tree2/\u03b1/',
 
1074
                             u'tree2/\u03b1/\u03c9-modified',
 
1075
                            ])
 
1076
        except UnicodeError:
 
1077
            raise tests.TestSkipped("Could not create Unicode files.")
 
1078
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
 
1079
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
 
1080
 
 
1081
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1082
 
 
1083
        self.assertEqual([self.content_changed(tree1, mod_id)],
 
1084
                         self.do_iter_changes(tree1, tree2))
 
1085
        self.assertEqual([self.content_changed(tree1, mod_id)],
 
1086
                         self.do_iter_changes(tree1, tree2,
 
1087
                                              specific_files=[u'\u03b1']))
 
1088
 
 
1089
    def test_renamed_unicode(self):
 
1090
        tree1 = self.make_branch_and_tree('tree1')
 
1091
        tree2 = self.make_to_branch_and_tree('tree2')
 
1092
        root_id = tree1.get_root_id()
 
1093
        tree2.set_root_id(root_id)
 
1094
 
 
1095
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1096
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1097
        a_id = u'\u03b1-id'.encode('utf8')
 
1098
        rename_id = u'\u03c9_rename_id'.encode('utf8')
 
1099
        try:
 
1100
            self.build_tree([u'tree1/\u03b1/',
 
1101
                             u'tree2/\u03b1/',
 
1102
                            ])
 
1103
        except UnicodeError:
 
1104
            raise tests.TestSkipped("Could not create Unicode files.")
 
1105
        self.build_tree_contents([(u'tree1/\u03c9-source', 'contents\n'),
 
1106
                                  (u'tree2/\u03b1/\u03c9-target', 'contents\n'),
 
1107
                                 ])
 
1108
        tree1.add([u'\u03b1', u'\u03c9-source'], [a_id, rename_id])
 
1109
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-target'], [a_id, rename_id])
 
1110
 
 
1111
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1112
 
 
1113
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
 
1114
                         self.do_iter_changes(tree1, tree2))
 
1115
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
 
1116
                         self.do_iter_changes(tree1, tree2,
 
1117
                                              specific_files=[u'\u03b1']))
 
1118
 
 
1119
    def test_unchanged_unicode(self):
 
1120
        tree1 = self.make_branch_and_tree('tree1')
 
1121
        tree2 = self.make_to_branch_and_tree('tree2')
 
1122
        root_id = tree1.get_root_id()
 
1123
        tree2.set_root_id(root_id)
 
1124
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1125
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1126
        a_id = u'\u03b1-id'.encode('utf8')
 
1127
        subfile_id = u'\u03c9-subfile-id'.encode('utf8')
 
1128
        rootfile_id = u'\u03c9-root-id'.encode('utf8')
 
1129
        try:
 
1130
            self.build_tree([u'tree1/\u03b1/',
 
1131
                             u'tree2/\u03b1/',
 
1132
                            ])
 
1133
        except UnicodeError:
 
1134
            raise tests.TestSkipped("Could not create Unicode files.")
 
1135
        self.build_tree_contents([
 
1136
            (u'tree1/\u03b1/\u03c9-subfile', 'sub contents\n'),
 
1137
            (u'tree2/\u03b1/\u03c9-subfile', 'sub contents\n'),
 
1138
            (u'tree1/\u03c9-rootfile', 'root contents\n'),
 
1139
            (u'tree2/\u03c9-rootfile', 'root contents\n'),
 
1140
            ])
 
1141
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
 
1142
                  [a_id, subfile_id, rootfile_id])
 
1143
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
 
1144
                  [a_id, subfile_id, rootfile_id])
 
1145
 
 
1146
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1147
 
 
1148
        expected = sorted([
 
1149
            self.unchanged(tree1, root_id),
 
1150
            self.unchanged(tree1, a_id),
 
1151
            self.unchanged(tree1, subfile_id),
 
1152
            self.unchanged(tree1, rootfile_id),
 
1153
            ])
 
1154
        self.assertEqual(expected,
 
1155
                         self.do_iter_changes(tree1, tree2,
 
1156
                                              include_unchanged=True))
 
1157
 
 
1158
        # We should also be able to select just a subset
 
1159
        expected = sorted([
 
1160
            self.unchanged(tree1, a_id),
 
1161
            self.unchanged(tree1, subfile_id),
 
1162
            ])
 
1163
        self.assertEqual(expected,
 
1164
                         self.do_iter_changes(tree1, tree2,
 
1165
                                              specific_files=[u'\u03b1'],
 
1166
                                              include_unchanged=True))
 
1167
 
 
1168
    def test_unknown_unicode(self):
 
1169
        tree1 = self.make_branch_and_tree('tree1')
 
1170
        tree2 = self.make_to_branch_and_tree('tree2')
 
1171
        root_id = tree1.get_root_id()
 
1172
        tree2.set_root_id(root_id)
 
1173
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1174
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1175
        a_id = u'\u03b1-id'.encode('utf8')
 
1176
        try:
 
1177
            self.build_tree([u'tree1/\u03b1/',
 
1178
                             u'tree2/\u03b1/',
 
1179
                             u'tree2/\u03b1/unknown_dir/',
 
1180
                             u'tree2/\u03b1/unknown_file',
 
1181
                             u'tree2/\u03b1/unknown_dir/file',
 
1182
                             u'tree2/\u03c9-unknown_root_file',
 
1183
                            ])
 
1184
        except UnicodeError:
 
1185
            raise tests.TestSkipped("Could not create Unicode files.")
 
1186
        tree1.add([u'\u03b1'], [a_id])
 
1187
        tree2.add([u'\u03b1'], [a_id])
 
1188
 
 
1189
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1190
 
 
1191
        expected = sorted([
 
1192
            self.unversioned(tree2, u'\u03b1/unknown_dir'),
 
1193
            self.unversioned(tree2, u'\u03b1/unknown_file'),
 
1194
            self.unversioned(tree2, u'\u03c9-unknown_root_file'),
 
1195
            # a/unknown_dir/file should not be included because we should not
 
1196
            # recurse into unknown_dir
 
1197
            # self.unversioned(tree2, 'a/unknown_dir/file'),
 
1198
            ])
 
1199
        self.assertEqual(expected,
 
1200
                         self.do_iter_changes(tree1, tree2,
 
1201
                                              require_versioned=False,
 
1202
                                              want_unversioned=True))
 
1203
        self.assertEqual([], # Without want_unversioned we should get nothing
 
1204
                         self.do_iter_changes(tree1, tree2))
 
1205
 
 
1206
        # We should also be able to select just a subset
 
1207
        expected = sorted([
 
1208
            self.unversioned(tree2, u'\u03b1/unknown_dir'),
 
1209
            self.unversioned(tree2, u'\u03b1/unknown_file'),
 
1210
            ])
 
1211
        self.assertEqual(expected,
 
1212
                         self.do_iter_changes(tree1, tree2,
 
1213
                                              specific_files=[u'\u03b1'],
 
1214
                                              require_versioned=False,
 
1215
                                              want_unversioned=True))
 
1216
        self.assertEqual([], # Without want_unversioned we should get nothing
 
1217
                         self.do_iter_changes(tree1, tree2,
 
1218
                                              specific_files=[u'\u03b1']))
 
1219
 
 
1220
    def test_unknown_empty_dir(self):
 
1221
        tree1 = self.make_branch_and_tree('tree1')
 
1222
        tree2 = self.make_to_branch_and_tree('tree2')
 
1223
        root_id = tree1.get_root_id()
 
1224
        tree2.set_root_id(root_id)
 
1225
 
 
1226
        # Start with 2 identical trees
 
1227
        self.build_tree(['tree1/a/', 'tree1/b/',
 
1228
                         'tree2/a/', 'tree2/b/'])
 
1229
        self.build_tree_contents([('tree1/b/file', 'contents\n'),
 
1230
                                  ('tree2/b/file', 'contents\n')])
 
1231
        tree1.add(['a', 'b', 'b/file'], ['a-id', 'b-id', 'b-file-id'])
 
1232
        tree2.add(['a', 'b', 'b/file'], ['a-id', 'b-id', 'b-file-id'])
 
1233
 
 
1234
        # Now create some unknowns in tree2
 
1235
        # We should find both a/file and a/dir as unknown, but we shouldn't
 
1236
        # recurse into a/dir to find that a/dir/subfile is also unknown.
 
1237
        self.build_tree(['tree2/a/file', 'tree2/a/dir/', 'tree2/a/dir/subfile'])
 
1238
 
 
1239
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1240
 
 
1241
        expected = sorted([
 
1242
            self.unversioned(tree2, u'a/file'),
 
1243
            self.unversioned(tree2, u'a/dir'),
 
1244
            ])
 
1245
        self.assertEqual(expected,
 
1246
                         self.do_iter_changes(tree1, tree2,
 
1247
                                              require_versioned=False,
 
1248
                                              want_unversioned=True))