~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2008-10-16 21:27:10 UTC
  • mfrom: (0.15.26 unshelve)
  • mto: (0.16.74 shelf-ui)
  • mto: This revision was merged to the branch mainline in revision 3820.
  • Revision ID: aaron@aaronbentley.com-20081016212710-h9av3nhk76dvmsv5
Merge with unshelve

Show diffs side-by-side

added added

removed removed

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