~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Andrew Bennetts
  • Date: 2007-03-28 07:08:42 UTC
  • mfrom: (2380 +trunk)
  • mto: (2018.5.146 hpss)
  • mto: This revision was merged to the branch mainline in revision 2414.
  • Revision ID: andrew.bennetts@canonical.com-20070328070842-r843houy668oxb9o
Merge from 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
        # try:
 
325
        os.symlink('target', 'tree2/link')
 
326
        links_supported = True
 
327
        # except ???:
 
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
        self.assertEqual([(u'dir', None, 'directory'), (u'file', None, 'file'),
 
337
            (u'link', None, 'symlink')], d.unversioned)
 
338
 
242
339
 
243
340
class TestIterChanges(TestCaseWithTwoTrees):
244
341
    """Test the comparison iterator"""
245
342
 
 
343
    def do_iter_changes(self, tree1, tree2, **extra_args):
 
344
        """Helper to run _iter_changes from tree1 to tree2.
 
345
        
 
346
        :param tree1, tree2:  The source and target trees. These will be locked
 
347
            automatically.
 
348
        :param **extra_args: Extra args to pass to _iter_changes. This is not
 
349
            inspected by this test helper.
 
350
        """
 
351
        tree1.lock_read()
 
352
        tree2.lock_read()
 
353
        try:
 
354
            # sort order of output is not strictly defined
 
355
            return sorted(self.intertree_class(tree1, tree2)
 
356
                ._iter_changes(**extra_args))
 
357
        finally:
 
358
            tree1.unlock()
 
359
            tree2.unlock()
 
360
 
 
361
    def mutable_trees_to_locked_test_trees(self, tree1, tree2):
 
362
        """Convert the working trees into test trees.
 
363
 
 
364
        Read lock them, and add the unlock to the cleanup.
 
365
        """
 
366
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
367
        tree1.lock_read()
 
368
        self.addCleanup(tree1.unlock)
 
369
        tree2.lock_read()
 
370
        self.addCleanup(tree2.unlock)
 
371
        return tree1, tree2
 
372
 
 
373
    def make_tree_with_special_names(self):
 
374
        """Create a tree with filenames chosen to exercise the walk order."""
 
375
        tree1 = self.make_branch_and_tree('tree1')
 
376
        tree2 = self.make_to_branch_and_tree('tree2')
 
377
        tree2.set_root_id(tree1.get_root_id())
 
378
        paths, path_ids = self._create_special_names(tree2, 'tree2')
 
379
        tree2.commit('initial', rev_id='rev-1')
 
380
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
381
        return (tree1, tree2, paths, path_ids)
 
382
 
 
383
    def make_trees_with_special_names(self):
 
384
        """Both trees will use the special names.
 
385
 
 
386
        But the contents will differ for each file.
 
387
        """
 
388
        tree1 = self.make_branch_and_tree('tree1')
 
389
        tree2 = self.make_to_branch_and_tree('tree2')
 
390
        tree2.set_root_id(tree1.get_root_id())
 
391
        paths, path_ids = self._create_special_names(tree1, 'tree1')
 
392
        paths, path_ids = self._create_special_names(tree2, 'tree2')
 
393
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
394
        return (tree1, tree2, paths, path_ids)
 
395
 
 
396
    def _create_special_names(self, tree, base_path):
 
397
        """Create a tree with paths that expose differences in sort orders."""
 
398
        # Each directory will have a single file named 'f' inside
 
399
        dirs = ['a',
 
400
                'a-a',
 
401
                'a/a',
 
402
                'a/a-a',
 
403
                'a/a/a',
 
404
                'a/a/a-a',
 
405
                'a/a/a/a',
 
406
                'a/a/a/a-a',
 
407
                'a/a/a/a/a',
 
408
               ]
 
409
        with_slashes = []
 
410
        paths = []
 
411
        path_ids = []
 
412
        for d in dirs:
 
413
            with_slashes.append(base_path + '/' + d + '/')
 
414
            with_slashes.append(base_path + '/' + d + '/f')
 
415
            paths.append(d)
 
416
            paths.append(d+'/f')
 
417
            path_ids.append(d.replace('/', '_') + '-id')
 
418
            path_ids.append(d.replace('/', '_') + '_f-id')
 
419
        self.build_tree(with_slashes)
 
420
        tree.add(paths, path_ids)
 
421
        return paths, path_ids
 
422
 
246
423
    def test_compare_empty_trees(self):
247
424
        tree1 = self.make_branch_and_tree('1')
248
425
        tree2 = self.make_to_branch_and_tree('2')
249
426
        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)))
 
427
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
428
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
429
        self.assertEqual([], self.do_iter_changes(tree1, tree2))
252
430
 
253
431
    def added(self, tree, file_id):
254
432
        entry = tree.inventory[file_id]
255
433
        path = tree.id2path(file_id)
256
 
        return (file_id, path, True, (False, True), (None, entry.parent_id),
257
 
                (None, entry.name), (None, entry.kind), 
 
434
        return (file_id, (None, path), True, (False, True), (None, entry.parent_id),
 
435
                (None, entry.name), (None, entry.kind),
258
436
                (None, entry.executable))
259
437
 
 
438
    def content_changed(self, tree, file_id):
 
439
        entry = tree.inventory[file_id]
 
440
        path = tree.id2path(file_id)
 
441
        return (file_id, (path, path), True, (True, True), (entry.parent_id, entry.parent_id),
 
442
                (entry.name, entry.name), (entry.kind, entry.kind),
 
443
                (entry.executable, entry.executable))
 
444
 
 
445
    def kind_changed(self, from_tree, to_tree, file_id):
 
446
        old_entry = from_tree.inventory[file_id]
 
447
        new_entry = to_tree.inventory[file_id]
 
448
        path = to_tree.id2path(file_id)
 
449
        from_path = from_tree.id2path(file_id)
 
450
        return (file_id, (from_path, path), True, (True, True), (old_entry.parent_id, new_entry.parent_id),
 
451
                (old_entry.name, new_entry.name), (old_entry.kind, new_entry.kind),
 
452
                (old_entry.executable, new_entry.executable))
 
453
 
 
454
    def missing(self, file_id, from_path, to_path, parent_id, kind):
 
455
        _, from_basename = os.path.split(from_path)
 
456
        _, to_basename = os.path.split(to_path)
 
457
        # missing files have both paths, but no kind.
 
458
        return (file_id, (from_path, to_path), True, (True, True),
 
459
            (parent_id, parent_id),
 
460
            (from_basename, to_basename), (kind, None), (False, False))
 
461
 
260
462
    def deleted(self, tree, file_id):
261
463
        entry = tree.inventory[file_id]
262
464
        path = tree.id2path(file_id)
263
 
        return (file_id, path, True, (True, False), (entry.parent_id, None),
264
 
                (entry.name, None), (entry.kind, None), 
 
465
        return (file_id, (path, None), True, (True, False), (entry.parent_id, None),
 
466
                (entry.name, None), (entry.kind, None),
265
467
                (entry.executable, None))
266
468
 
 
469
    def renamed(self, from_tree, to_tree, file_id, content_changed):
 
470
        from_entry = from_tree.inventory[file_id]
 
471
        to_entry = to_tree.inventory[file_id]
 
472
        from_path = from_tree.id2path(file_id)
 
473
        to_path = to_tree.id2path(file_id)
 
474
        return (file_id, (from_path, to_path), content_changed, (True, True),
 
475
            (from_entry.parent_id, to_entry.parent_id),
 
476
            (from_entry.name, to_entry.name),
 
477
            (from_entry.kind, to_entry.kind),
 
478
            (from_entry.executable, to_entry.executable))
 
479
 
 
480
    def unchanged(self, tree, file_id):
 
481
        entry = tree.inventory[file_id]
 
482
        parent = entry.parent_id
 
483
        name = entry.name
 
484
        kind = entry.kind
 
485
        executable = entry.executable
 
486
        path = tree.id2path(file_id)
 
487
        return (file_id, (path, path), False, (True, True),
 
488
               (parent, parent), (name, name), (kind, kind),
 
489
               (executable, executable))
 
490
 
 
491
    def unversioned(self, tree, path):
 
492
        """Create an unversioned result."""
 
493
        _, basename = os.path.split(path)
 
494
        kind = file_kind(tree.abspath(path))
 
495
        return (None, (None, path), True, (False, False), (None, None),
 
496
                (None, basename), (None, kind),
 
497
                (None, False))
 
498
 
267
499
    def test_empty_to_abc_content(self):
268
500
        tree1 = self.make_branch_and_tree('1')
269
501
        tree2 = self.make_to_branch_and_tree('2')
270
502
        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)))
 
503
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
504
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
505
        expected_results = sorted([
 
506
            self.added(tree2, 'root-id'),
 
507
            self.added(tree2, 'a-id'),
 
508
            self.added(tree2, 'b-id'),
 
509
            self.added(tree2, 'c-id'),
 
510
            self.deleted(tree1, 'empty-root-id')])
 
511
        self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
279
512
 
280
513
    def test_empty_to_abc_content_a_only(self):
281
514
        tree1 = self.make_branch_and_tree('1')
282
515
        tree2 = self.make_to_branch_and_tree('2')
283
516
        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'])))
 
517
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
518
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
519
        self.assertEqual(
 
520
            [self.added(tree2, 'a-id')],
 
521
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
 
522
 
 
523
    def test_abc_content_to_empty_to_abc_content_a_only(self):
 
524
        tree1 = self.make_branch_and_tree('1')
 
525
        tree2 = self.make_to_branch_and_tree('2')
 
526
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
527
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
528
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
529
        self.assertEqual(
 
530
            [self.deleted(tree1, 'a-id')],
 
531
            self.do_iter_changes(tree1, tree2, specific_files=['a']))
291
532
 
292
533
    def test_empty_to_abc_content_a_and_c_only(self):
293
534
        tree1 = self.make_branch_and_tree('1')
294
535
        tree2 = self.make_to_branch_and_tree('2')
295
536
        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'])
 
537
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
538
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
539
        expected_result = [self.added(tree2, 'a-id'), self.added(tree2, 'c-id')]
 
540
        self.assertEqual(expected_result,
 
541
            self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
304
542
 
305
 
    def test_abc_content(self):
 
543
    def test_abc_content_to_empty(self):
306
544
        tree1 = self.make_branch_and_tree('1')
307
545
        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)
 
546
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
547
        tree2 = self.get_tree_no_parents_no_content(tree2)
 
548
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
310
549
        def deleted(file_id):
311
 
            entry = tree2.inventory[file_id]
312
 
            path = tree2.id2path(file_id)
313
 
            return (file_id, path, True, (True, False), 
 
550
            entry = tree1.inventory[file_id]
 
551
            path = tree1.id2path(file_id)
 
552
            return (file_id, (path, None), True, (True, False),
314
553
                    (entry.parent_id, None),
315
 
                    (entry.name, None), (entry.kind, None), 
 
554
                    (entry.name, None), (entry.kind, None),
316
555
                    (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)))
 
556
        expected_results = sorted([
 
557
            self.added(tree2, 'empty-root-id'),
 
558
            deleted('root-id'), deleted('a-id'),
 
559
            deleted('b-id'), deleted('c-id')])
 
560
        self.assertEqual(
 
561
            expected_results,
 
562
            self.do_iter_changes(tree1, tree2))
321
563
 
322
564
    def test_content_modification(self):
323
565
        tree1 = self.make_branch_and_tree('1')
324
566
        tree2 = self.make_to_branch_and_tree('2')
325
567
        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)))
 
568
        tree2 = self.get_tree_no_parents_abc_content_2(tree2)
 
569
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
570
        root_id = tree1.path2id('')
 
571
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
572
                           (root_id, root_id), ('a', 'a'),
 
573
                           ('file', 'file'), (False, False))],
 
574
                         self.do_iter_changes(tree1, tree2))
332
575
 
333
576
    def test_meta_modification(self):
334
577
        tree1 = self.make_branch_and_tree('1')
335
578
        tree2 = self.make_to_branch_and_tree('2')
336
579
        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)))
 
580
        tree2 = self.get_tree_no_parents_abc_content_3(tree2)
 
581
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
582
        self.assertEqual([('c-id', ('b/c', 'b/c'), False, (True, True),
 
583
                           ('b-id', 'b-id'), ('c', 'c'), ('file', 'file'),
 
584
                          (False, True))],
 
585
                         self.do_iter_changes(tree1, tree2))
 
586
 
 
587
    def test_empty_dir(self):
 
588
        """an empty dir should not cause glitches to surrounding files."""
 
589
        tree1 = self.make_branch_and_tree('1')
 
590
        tree2 = self.make_to_branch_and_tree('2')
 
591
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
592
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
593
        # the pathname is chosen to fall between 'a' and 'b'.
 
594
        self.build_tree(['1/a-empty/', '2/a-empty/'])
 
595
        tree1.add(['a-empty'], ['a-empty'])
 
596
        tree2.add(['a-empty'], ['a-empty'])
 
597
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
598
        expected = []
 
599
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
341
600
 
342
601
    def test_file_rename(self):
343
602
        tree1 = self.make_branch_and_tree('1')
344
603
        tree2 = self.make_to_branch_and_tree('2')
345
604
        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)))
 
605
        tree2 = self.get_tree_no_parents_abc_content_4(tree2)
 
606
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
607
        root_id = tree1.path2id('')
 
608
        self.assertEqual([('a-id', ('a', 'd'), False, (True, True),
 
609
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
610
                           (False, False))],
 
611
                         self.do_iter_changes(tree1, tree2))
351
612
 
352
613
    def test_file_rename_and_modification(self):
353
614
        tree1 = self.make_branch_and_tree('1')
354
615
        tree2 = self.make_to_branch_and_tree('2')
355
616
        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)))
 
617
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
618
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
619
        root_id = tree1.path2id('')
 
620
        self.assertEqual([('a-id', ('a', 'd'), True, (True, True),
 
621
                           (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
622
                           (False, False))],
 
623
                         self.do_iter_changes(tree1, tree2))
361
624
 
362
625
    def test_file_rename_and_meta_modification(self):
363
626
        tree1 = self.make_branch_and_tree('1')
364
627
        tree2 = self.make_to_branch_and_tree('2')
365
628
        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)))
 
629
        tree2 = self.get_tree_no_parents_abc_content_6(tree2)
 
630
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
631
        root_id = tree1.path2id('')
 
632
        self.assertEqual([('c-id', ('b/c', 'e'), False, (True, True),
 
633
                           ('b-id', root_id), ('c', 'e'), ('file', 'file'),
 
634
                           (False, True))],
 
635
                         self.do_iter_changes(tree1, tree2))
 
636
 
 
637
    def test_missing_in_target(self):
 
638
        """Test with the target files versioned but absent from disk."""
 
639
        tree1 = self.make_branch_and_tree('1')
 
640
        tree2 = self.make_to_branch_and_tree('2')
 
641
        tree1 = self.get_tree_no_parents_abc_content(tree1)
 
642
        tree2 = self.get_tree_no_parents_abc_content(tree2)
 
643
        os.unlink('2/a')
 
644
        shutil.rmtree('2/b')
 
645
        # TODO ? have a symlink here?
 
646
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
647
        root_id = tree1.path2id('')
 
648
        expected = sorted([
 
649
            self.missing('a-id', 'a', 'a', root_id, 'file'),
 
650
            self.missing('b-id', 'b', 'b', root_id, 'directory'),
 
651
            self.missing('c-id', 'b/c', 'b/c', 'b-id', 'file'),
 
652
            ])
 
653
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
654
 
 
655
    def test_missing_and_renamed(self):
 
656
        tree1 = self.make_branch_and_tree('tree1')
 
657
        tree2 = self.make_to_branch_and_tree('tree2')
 
658
        tree2.set_root_id(tree1.get_root_id())
 
659
        self.build_tree(['tree1/file'])
 
660
        tree1.add(['file'], ['file-id'])
 
661
        self.build_tree(['tree2/directory/'])
 
662
        tree2.add(['directory'], ['file-id'])
 
663
        os.rmdir('tree2/directory')
 
664
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
665
 
 
666
        root_id = tree1.path2id('')
 
667
        expected = sorted([
 
668
            self.missing('file-id', 'file', 'directory', root_id, 'file'),
 
669
            ])
 
670
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
371
671
 
372
672
    def test_unchanged_with_renames_and_modifications(self):
373
673
        """want_unchanged should generate a list of unchanged entries."""
374
674
        tree1 = self.make_branch_and_tree('1')
375
675
        tree2 = self.make_to_branch_and_tree('2')
376
676
        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)))
 
677
        tree2 = self.get_tree_no_parents_abc_content_5(tree2)
 
678
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
679
        root_id = tree1.path2id('')
 
680
 
 
681
        self.assertEqual(sorted([self.unchanged(tree1, root_id),
 
682
            self.unchanged(tree1, 'b-id'),
 
683
            ('a-id', ('a', 'd'), True, (True, True),
 
684
             (root_id, root_id), ('a', 'd'), ('file', 'file'),
 
685
            (False, False)), self.unchanged(tree1, 'c-id')]),
 
686
            self.do_iter_changes(tree1, tree2, include_unchanged=True))
 
687
 
 
688
    def test_compare_subtrees(self):
 
689
        tree1 = self.make_branch_and_tree('1')
 
690
        if not tree1.supports_tree_reference():
 
691
            return
 
692
        tree1.set_root_id('root-id')
 
693
        subtree1 = self.make_branch_and_tree('1/sub')
 
694
        subtree1.set_root_id('subtree-id')
 
695
        tree1.add_reference(subtree1)
 
696
 
 
697
        tree2 = self.make_to_branch_and_tree('2')
 
698
        if not tree2.supports_tree_reference():
 
699
            return
 
700
        tree2.set_root_id('root-id')
 
701
        subtree2 = self.make_to_branch_and_tree('2/sub')
 
702
        subtree2.set_root_id('subtree-id')
 
703
        tree2.add_reference(subtree2)
 
704
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
705
 
 
706
        self.assertEqual([], list(tree2._iter_changes(tree1)))
 
707
        subtree1.commit('commit', rev_id='commit-a')
 
708
        self.assertEqual([
 
709
            ('root-id',
 
710
             (u'', u''),
 
711
             False,
 
712
             (True, True),
 
713
             (None, None),
 
714
             (u'', u''),
 
715
             ('directory', 'directory'),
 
716
             (False, False)),
 
717
            ('subtree-id',
 
718
             ('sub', 'sub',),
 
719
             False,
 
720
             (True, True),
 
721
             ('root-id', 'root-id'),
 
722
             ('sub', 'sub'),
 
723
             ('tree-reference', 'tree-reference'),
 
724
             (False, False))],
 
725
                         list(tree2._iter_changes(tree1,
 
726
                             include_unchanged=True)))
 
727
 
 
728
    def test_disk_in_subtrees_skipped(self):
 
729
        """subtrees are considered not-in-the-current-tree.
 
730
        
 
731
        This test tests the trivial case, where the basis has no paths in the
 
732
        current trees subtree.
 
733
        """
 
734
        tree1 = self.make_branch_and_tree('1')
 
735
        tree1.set_root_id('root-id')
 
736
        tree2 = self.make_to_branch_and_tree('2')
 
737
        if not tree2.supports_tree_reference():
 
738
            return
 
739
        tree2.set_root_id('root-id')
 
740
        subtree2 = self.make_to_branch_and_tree('2/sub')
 
741
        subtree2.set_root_id('subtree-id')
 
742
        tree2.add(['sub'], ['subtree-id'])
 
743
        self.build_tree(['2/sub/file'])
 
744
        subtree2.add(['file'])
 
745
 
 
746
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
747
        # this should filter correctly from above
 
748
        self.assertEqual([self.added(tree2, 'subtree-id')],
 
749
            self.do_iter_changes(tree1, tree2, want_unversioned=True))
 
750
        # and when the path is named
 
751
        self.assertEqual([self.added(tree2, 'subtree-id')],
 
752
            self.do_iter_changes(tree1, tree2, specific_files=['sub'],
 
753
                want_unversioned=True))
 
754
 
 
755
    def test_default_ignores_unversioned_files(self):
 
756
        tree1 = self.make_branch_and_tree('tree1')
 
757
        tree2 = self.make_to_branch_and_tree('tree2')
 
758
        tree2.set_root_id(tree1.get_root_id())
 
759
        self.build_tree(['tree1/a', 'tree1/c',
 
760
                         'tree2/a', 'tree2/b', 'tree2/c'])
 
761
        tree1.add(['a', 'c'], ['a-id', 'c-id'])
 
762
        tree2.add(['a', 'c'], ['a-id', 'c-id'])
 
763
 
 
764
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
765
 
 
766
        # We should ignore the fact that 'b' exists in tree-2
 
767
        # because the want_unversioned parameter was not given.
 
768
        expected = sorted([
 
769
            self.content_changed(tree2, 'a-id'),
 
770
            self.content_changed(tree2, 'c-id'),
 
771
            ])
 
772
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
773
 
 
774
    def test_unversioned_paths_in_tree(self):
 
775
        tree1 = self.make_branch_and_tree('tree1')
 
776
        tree2 = self.make_to_branch_and_tree('tree2')
 
777
        tree2.set_root_id(tree1.get_root_id())
 
778
        self.build_tree(['tree2/file', 'tree2/dir/'])
 
779
        # try:
 
780
        os.symlink('target', 'tree2/link')
 
781
        links_supported = True
 
782
        # except ???:
 
783
        #   links_supported = False
 
784
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
785
        expected = [
 
786
            self.unversioned(tree2, 'file'),
 
787
            self.unversioned(tree2, 'dir'),
 
788
            ]
 
789
        if links_supported:
 
790
            expected.append(self.unversioned(tree2, 'link'))
 
791
        expected = sorted(expected)
 
792
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
793
            want_unversioned=True))
 
794
 
 
795
    def test_unversioned_paths_in_tree_specific_files(self):
 
796
        tree1 = self.make_branch_and_tree('tree1')
 
797
        tree2 = self.make_to_branch_and_tree('tree2')
 
798
        self.build_tree(['tree2/file', 'tree2/dir/'])
 
799
        # try:
 
800
        os.symlink('target', 'tree2/link')
 
801
        links_supported = True
 
802
        # except ???:
 
803
        #   links_supported = False
 
804
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
805
        expected = [
 
806
            self.unversioned(tree2, 'file'),
 
807
            self.unversioned(tree2, 'dir'),
 
808
            ]
 
809
        specific_files=['file', 'dir']
 
810
        if links_supported:
 
811
            expected.append(self.unversioned(tree2, 'link'))
 
812
            specific_files.append('link')
 
813
        expected = sorted(expected)
 
814
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
815
            specific_files=specific_files, require_versioned=False,
 
816
            want_unversioned=True))
 
817
 
 
818
    def test_unversioned_paths_in_target_matching_source_old_names(self):
 
819
        # its likely that naive implementations of unversioned file support
 
820
        # will fail if the path was versioned, but is not any more, 
 
821
        # due to a rename, not due to unversioning it.
 
822
        # That is, if the old tree has a versioned file 'foo', and
 
823
        # the new tree has the same file but versioned as 'bar', and also
 
824
        # has an unknown file 'foo', we should get back output for
 
825
        # both foo and bar.
 
826
        tree1 = self.make_branch_and_tree('tree1')
 
827
        tree2 = self.make_to_branch_and_tree('tree2')
 
828
        tree2.set_root_id(tree1.get_root_id())
 
829
        self.build_tree(['tree2/file', 'tree2/dir/',
 
830
            'tree1/file', 'tree2/movedfile',
 
831
            'tree1/dir/', 'tree2/moveddir/'])
 
832
        # try:
 
833
        os.symlink('target', 'tree1/link')
 
834
        os.symlink('target', 'tree2/link')
 
835
        os.symlink('target', 'tree2/movedlink')
 
836
        links_supported = True
 
837
        # except ???:
 
838
        #   links_supported = False
 
839
        tree1.add(['file', 'dir', 'link'], ['file-id', 'dir-id', 'link-id'])
 
840
        tree2.add(['movedfile', 'moveddir', 'movedlink'],
 
841
            ['file-id', 'dir-id', 'link-id'])
 
842
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
843
        root_id = tree1.path2id('')
 
844
        expected = [
 
845
            self.renamed(tree1, tree2, 'dir-id', False),
 
846
            self.renamed(tree1, tree2, 'file-id', True),
 
847
            self.unversioned(tree2, 'file'),
 
848
            self.unversioned(tree2, 'dir'),
 
849
            ]
 
850
        specific_files=['file', 'dir']
 
851
        if links_supported:
 
852
            expected.append(self.renamed(tree1, tree2, 'link-id', False))
 
853
            expected.append(self.unversioned(tree2, 'link'))
 
854
            specific_files.append('link')
 
855
        expected = sorted(expected)
 
856
        # run once with, and once without specific files, to catch
 
857
        # potentially different code paths.
 
858
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
859
            require_versioned=False,
 
860
            want_unversioned=True))
 
861
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
862
            specific_files=specific_files, require_versioned=False,
 
863
            want_unversioned=True))
 
864
 
 
865
    def test_unversioned_subtree_only_emits_root(self):
 
866
        tree1 = self.make_branch_and_tree('tree1')
 
867
        tree2 = self.make_to_branch_and_tree('tree2')
 
868
        tree2.set_root_id(tree1.get_root_id())
 
869
        self.build_tree(['tree2/dir/', 'tree2/dir/file'])
 
870
        tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
 
871
        expected = [
 
872
            self.unversioned(tree2, 'dir'),
 
873
            ]
 
874
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
875
            want_unversioned=True))
 
876
 
 
877
    def make_trees_with_symlinks(self):
 
878
        tree1 = self.make_branch_and_tree('tree1')
 
879
        tree2 = self.make_to_branch_and_tree('tree2')
 
880
        tree2.set_root_id(tree1.get_root_id())
 
881
        self.build_tree(['tree1/fromfile', 'tree1/fromdir/'])
 
882
        self.build_tree(['tree2/tofile', 'tree2/todir/', 'tree2/unknown'])
 
883
        # try:
 
884
        os.symlink('original', 'tree1/changed')
 
885
        os.symlink('original', 'tree1/removed')
 
886
        os.symlink('original', 'tree1/tofile')
 
887
        os.symlink('original', 'tree1/todir')
 
888
        # we make the unchanged link point at unknown to catch incorrect
 
889
        # symlink-following code in the specified_files test.
 
890
        os.symlink('unknown', 'tree1/unchanged')
 
891
        os.symlink('new',      'tree2/added')
 
892
        os.symlink('new',      'tree2/changed')
 
893
        os.symlink('new',      'tree2/fromfile')
 
894
        os.symlink('new',      'tree2/fromdir')
 
895
        os.symlink('unknown', 'tree2/unchanged')
 
896
        from_paths_and_ids = [
 
897
            'fromdir',
 
898
            'fromfile',
 
899
            'changed',
 
900
            'removed',
 
901
            'todir',
 
902
            'tofile',
 
903
            'unchanged',
 
904
            ]
 
905
        to_paths_and_ids = [
 
906
            'added',
 
907
            'fromdir',
 
908
            'fromfile',
 
909
            'changed',
 
910
            'todir',
 
911
            'tofile',
 
912
            'unchanged',
 
913
            ]
 
914
        tree1.add(from_paths_and_ids, from_paths_and_ids)
 
915
        tree2.add(to_paths_and_ids, to_paths_and_ids)
 
916
        # except ???:
 
917
        #   raise TestSkipped('OS does not support symlinks')
 
918
        #   links_supported = False
 
919
        return self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
920
 
 
921
    def test_versioned_symlinks(self):
 
922
        if not has_symlinks():
 
923
            raise tests.TestSkipped("No symlink support")
 
924
        tree1, tree2 = self.make_trees_with_symlinks()
 
925
        root_id = tree1.path2id('')
 
926
        expected = [
 
927
            self.unchanged(tree1, tree1.path2id('')),
 
928
            self.added(tree2, 'added'),
 
929
            self.content_changed(tree2, 'changed'),
 
930
            self.kind_changed(tree1, tree2, 'fromdir'),
 
931
            self.kind_changed(tree1, tree2, 'fromfile'),
 
932
            self.deleted(tree1, 'removed'),
 
933
            self.unchanged(tree2, 'unchanged'),
 
934
            self.unversioned(tree2, 'unknown'),
 
935
            self.kind_changed(tree1, tree2, 'todir'),
 
936
            self.kind_changed(tree1, tree2, 'tofile'),
 
937
            ]
 
938
        expected = sorted(expected)
 
939
        self.assertEqual(expected,
 
940
            self.do_iter_changes(tree1, tree2, include_unchanged=True,
 
941
                want_unversioned=True))
 
942
 
 
943
    def test_versioned_symlinks_specific_files(self):
 
944
        if not has_symlinks():
 
945
            raise tests.TestSkipped("No symlink support")
 
946
        tree1, tree2 = self.make_trees_with_symlinks()
 
947
        root_id = tree1.path2id('')
 
948
        expected = [
 
949
            self.added(tree2, 'added'),
 
950
            self.content_changed(tree2, 'changed'),
 
951
            self.kind_changed(tree1, tree2, 'fromdir'),
 
952
            self.kind_changed(tree1, tree2, 'fromfile'),
 
953
            self.deleted(tree1, 'removed'),
 
954
            self.kind_changed(tree1, tree2, 'todir'),
 
955
            self.kind_changed(tree1, tree2, 'tofile'),
 
956
            ]
 
957
        expected = sorted(expected)
 
958
        # we should get back just the changed links. We pass in 'unchanged' to
 
959
        # make sure that it is correctly not returned - and neither is the
 
960
        # unknown path 'unknown' which it points at.
 
961
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
 
962
            specific_files=['added', 'changed', 'fromdir', 'fromfile',
 
963
            'removed', 'unchanged', 'todir', 'tofile']))
 
964
 
 
965
    def test_tree_with_special_names(self):
 
966
        tree1, tree2, paths, path_ids = self.make_tree_with_special_names()
 
967
        expected = sorted(self.added(tree2, f_id) for f_id in path_ids)
 
968
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
969
 
 
970
    def test_trees_with_special_names(self):
 
971
        tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
 
972
        expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids
 
973
                          if f_id.endswith('_f-id'))
 
974
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
975
 
 
976
    def test_trees_with_deleted_dir(self):
 
977
        tree1 = self.make_branch_and_tree('tree1')
 
978
        tree2 = self.make_to_branch_and_tree('tree2')
 
979
        tree2.set_root_id(tree1.get_root_id())
 
980
        self.build_tree(['tree1/a', 'tree1/b/', 'tree1/b/c',
 
981
                         'tree1/b/d/', 'tree1/b/d/e', 'tree1/f/', 'tree1/f/g',
 
982
                         'tree2/a', 'tree2/f/', 'tree2/f/g'])
 
983
        tree1.add(['a', 'b', 'b/c', 'b/d/', 'b/d/e', 'f', 'f/g'],
 
984
                  ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id', 'g-id'])
 
985
        tree2.add(['a', 'f', 'f/g'], ['a-id', 'f-id', 'g-id'])
 
986
 
 
987
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
988
        # We should notice that 'b' and all its children are deleted
 
989
        expected = sorted([
 
990
            self.content_changed(tree2, 'a-id'),
 
991
            self.content_changed(tree2, 'g-id'),
 
992
            self.deleted(tree1, 'b-id'),
 
993
            self.deleted(tree1, 'c-id'),
 
994
            self.deleted(tree1, 'd-id'),
 
995
            self.deleted(tree1, 'e-id'),
 
996
            ])
 
997
        self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
 
998
 
 
999
    def test_added_unicode(self):
 
1000
        tree1 = self.make_branch_and_tree('tree1')
 
1001
        tree2 = self.make_to_branch_and_tree('tree2')
 
1002
        root_id = tree1.get_root_id()
 
1003
        tree2.set_root_id(root_id)
 
1004
 
 
1005
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1006
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1007
        a_id = u'\u03b1-id'.encode('utf8')
 
1008
        added_id = u'\u03c9_added_id'.encode('utf8')
 
1009
        try:
 
1010
            self.build_tree([u'tree1/\u03b1/',
 
1011
                             u'tree2/\u03b1/',
 
1012
                             u'tree2/\u03b1/\u03c9-added',
 
1013
                            ])
 
1014
        except UnicodeError:
 
1015
            raise tests.TestSkipped("Could not create Unicode files.")
 
1016
        tree1.add([u'\u03b1'], [a_id])
 
1017
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-added'], [a_id, added_id])
 
1018
 
 
1019
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1020
 
 
1021
        self.assertEqual([self.added(tree2, added_id)],
 
1022
                         self.do_iter_changes(tree1, tree2))
 
1023
        self.assertEqual([self.added(tree2, added_id)],
 
1024
                         self.do_iter_changes(tree1, tree2,
 
1025
                                              specific_files=[u'\u03b1']))
 
1026
 
 
1027
    def test_deleted_unicode(self):
 
1028
        tree1 = self.make_branch_and_tree('tree1')
 
1029
        tree2 = self.make_to_branch_and_tree('tree2')
 
1030
        root_id = tree1.get_root_id()
 
1031
        tree2.set_root_id(root_id)
 
1032
 
 
1033
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1034
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1035
        a_id = u'\u03b1-id'.encode('utf8')
 
1036
        deleted_id = u'\u03c9_deleted_id'.encode('utf8')
 
1037
        try:
 
1038
            self.build_tree([u'tree1/\u03b1/',
 
1039
                             u'tree1/\u03b1/\u03c9-deleted',
 
1040
                             u'tree2/\u03b1/',
 
1041
                            ])
 
1042
        except UnicodeError:
 
1043
            raise tests.TestSkipped("Could not create Unicode files.")
 
1044
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-deleted'], [a_id, deleted_id])
 
1045
        tree2.add([u'\u03b1'], [a_id])
 
1046
 
 
1047
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1048
 
 
1049
        self.assertEqual([self.deleted(tree1, deleted_id)],
 
1050
                         self.do_iter_changes(tree1, tree2))
 
1051
        self.assertEqual([self.deleted(tree1, deleted_id)],
 
1052
                         self.do_iter_changes(tree1, tree2,
 
1053
                                              specific_files=[u'\u03b1']))
 
1054
 
 
1055
    def test_modified_unicode(self):
 
1056
        tree1 = self.make_branch_and_tree('tree1')
 
1057
        tree2 = self.make_to_branch_and_tree('tree2')
 
1058
        root_id = tree1.get_root_id()
 
1059
        tree2.set_root_id(root_id)
 
1060
 
 
1061
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1062
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1063
        a_id = u'\u03b1-id'.encode('utf8')
 
1064
        mod_id = u'\u03c9_mod_id'.encode('utf8')
 
1065
        try:
 
1066
            self.build_tree([u'tree1/\u03b1/',
 
1067
                             u'tree1/\u03b1/\u03c9-modified',
 
1068
                             u'tree2/\u03b1/',
 
1069
                             u'tree2/\u03b1/\u03c9-modified',
 
1070
                            ])
 
1071
        except UnicodeError:
 
1072
            raise tests.TestSkipped("Could not create Unicode files.")
 
1073
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
 
1074
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
 
1075
 
 
1076
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1077
 
 
1078
        self.assertEqual([self.content_changed(tree1, mod_id)],
 
1079
                         self.do_iter_changes(tree1, tree2))
 
1080
        self.assertEqual([self.content_changed(tree1, mod_id)],
 
1081
                         self.do_iter_changes(tree1, tree2,
 
1082
                                              specific_files=[u'\u03b1']))
 
1083
 
 
1084
    def test_renamed_unicode(self):
 
1085
        tree1 = self.make_branch_and_tree('tree1')
 
1086
        tree2 = self.make_to_branch_and_tree('tree2')
 
1087
        root_id = tree1.get_root_id()
 
1088
        tree2.set_root_id(root_id)
 
1089
 
 
1090
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1091
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1092
        a_id = u'\u03b1-id'.encode('utf8')
 
1093
        rename_id = u'\u03c9_rename_id'.encode('utf8')
 
1094
        try:
 
1095
            self.build_tree([u'tree1/\u03b1/',
 
1096
                             u'tree2/\u03b1/',
 
1097
                            ])
 
1098
        except UnicodeError:
 
1099
            raise tests.TestSkipped("Could not create Unicode files.")
 
1100
        self.build_tree_contents([(u'tree1/\u03c9-source', 'contents\n'),
 
1101
                                  (u'tree2/\u03b1/\u03c9-target', 'contents\n'),
 
1102
                                 ])
 
1103
        tree1.add([u'\u03b1', u'\u03c9-source'], [a_id, rename_id])
 
1104
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-target'], [a_id, rename_id])
 
1105
 
 
1106
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1107
 
 
1108
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
 
1109
                         self.do_iter_changes(tree1, tree2))
 
1110
        self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
 
1111
                         self.do_iter_changes(tree1, tree2,
 
1112
                                              specific_files=[u'\u03b1']))
 
1113
 
 
1114
    def test_unchanged_unicode(self):
 
1115
        tree1 = self.make_branch_and_tree('tree1')
 
1116
        tree2 = self.make_to_branch_and_tree('tree2')
 
1117
        root_id = tree1.get_root_id()
 
1118
        tree2.set_root_id(root_id)
 
1119
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1120
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1121
        a_id = u'\u03b1-id'.encode('utf8')
 
1122
        subfile_id = u'\u03c9-subfile-id'.encode('utf8')
 
1123
        rootfile_id = u'\u03c9-root-id'.encode('utf8')
 
1124
        try:
 
1125
            self.build_tree([u'tree1/\u03b1/',
 
1126
                             u'tree2/\u03b1/',
 
1127
                            ])
 
1128
        except UnicodeError:
 
1129
            raise tests.TestSkipped("Could not create Unicode files.")
 
1130
        self.build_tree_contents([
 
1131
            (u'tree1/\u03b1/\u03c9-subfile', 'sub contents\n'),
 
1132
            (u'tree2/\u03b1/\u03c9-subfile', 'sub contents\n'),
 
1133
            (u'tree1/\u03c9-rootfile', 'root contents\n'),
 
1134
            (u'tree2/\u03c9-rootfile', 'root contents\n'),
 
1135
            ])
 
1136
        tree1.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
 
1137
                  [a_id, subfile_id, rootfile_id])
 
1138
        tree2.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
 
1139
                  [a_id, subfile_id, rootfile_id])
 
1140
 
 
1141
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1142
 
 
1143
        expected = sorted([
 
1144
            self.unchanged(tree1, root_id),
 
1145
            self.unchanged(tree1, a_id),
 
1146
            self.unchanged(tree1, subfile_id),
 
1147
            self.unchanged(tree1, rootfile_id),
 
1148
            ])
 
1149
        self.assertEqual(expected,
 
1150
                         self.do_iter_changes(tree1, tree2,
 
1151
                                              include_unchanged=True))
 
1152
 
 
1153
        # We should also be able to select just a subset
 
1154
        expected = sorted([
 
1155
            self.unchanged(tree1, a_id),
 
1156
            self.unchanged(tree1, subfile_id),
 
1157
            ])
 
1158
        self.assertEqual(expected,
 
1159
                         self.do_iter_changes(tree1, tree2,
 
1160
                                              specific_files=[u'\u03b1'],
 
1161
                                              include_unchanged=True))
 
1162
 
 
1163
    def test_unknown_unicode(self):
 
1164
        tree1 = self.make_branch_and_tree('tree1')
 
1165
        tree2 = self.make_to_branch_and_tree('tree2')
 
1166
        root_id = tree1.get_root_id()
 
1167
        tree2.set_root_id(root_id)
 
1168
        # u'\u03b1' == GREEK SMALL LETTER ALPHA
 
1169
        # u'\u03c9' == GREEK SMALL LETTER OMEGA
 
1170
        a_id = u'\u03b1-id'.encode('utf8')
 
1171
        try:
 
1172
            self.build_tree([u'tree1/\u03b1/',
 
1173
                             u'tree2/\u03b1/',
 
1174
                             u'tree2/\u03b1/unknown_dir/',
 
1175
                             u'tree2/\u03b1/unknown_file',
 
1176
                             u'tree2/\u03b1/unknown_dir/file',
 
1177
                             u'tree2/\u03c9-unknown_root_file',
 
1178
                            ])
 
1179
        except UnicodeError:
 
1180
            raise tests.TestSkipped("Could not create Unicode files.")
 
1181
        tree1.add([u'\u03b1'], [a_id])
 
1182
        tree2.add([u'\u03b1'], [a_id])
 
1183
 
 
1184
        tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
 
1185
 
 
1186
        expected = sorted([
 
1187
            self.unversioned(tree2, u'\u03b1/unknown_dir'),
 
1188
            self.unversioned(tree2, u'\u03b1/unknown_file'),
 
1189
            self.unversioned(tree2, u'\u03c9-unknown_root_file'),
 
1190
            # a/unknown_dir/file should not be included because we should not
 
1191
            # recurse into unknown_dir
 
1192
            # self.unversioned(tree2, 'a/unknown_dir/file'),
 
1193
            ])
 
1194
        self.assertEqual(expected,
 
1195
                         self.do_iter_changes(tree1, tree2,
 
1196
                                              require_versioned=False,
 
1197
                                              want_unversioned=True))
 
1198
        self.assertEqual([], # Without want_unversioned we should get nothing
 
1199
                         self.do_iter_changes(tree1, tree2))
 
1200
 
 
1201
        # We should also be able to select just a subset
 
1202
        expected = sorted([
 
1203
            self.unversioned(tree2, u'\u03b1/unknown_dir'),
 
1204
            self.unversioned(tree2, u'\u03b1/unknown_file'),
 
1205
            ])
 
1206
        self.assertEqual(expected,
 
1207
                         self.do_iter_changes(tree1, tree2,
 
1208
                                              specific_files=[u'\u03b1'],
 
1209
                                              require_versioned=False,
 
1210
                                              want_unversioned=True))
 
1211
        self.assertEqual([], # Without want_unversioned we should get nothing
 
1212
                         self.do_iter_changes(tree1, tree2,
 
1213
                                              specific_files=[u'\u03b1']))