~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_tree.py

Major code cleanup.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2009, 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for Tree and InterTree."""
 
18
 
 
19
from bzrlib import (
 
20
    errors,
 
21
    revision,
 
22
    tree as _mod_tree,
 
23
    )
 
24
from bzrlib.tests import TestCaseWithTransport
 
25
from bzrlib.tree import InterTree
 
26
 
 
27
 
 
28
class TestInterTree(TestCaseWithTransport):
 
29
 
 
30
    def test_revision_tree_revision_tree(self):
 
31
        # we should have an InterTree registered for RevisionTree to
 
32
        # RevisionTree.
 
33
        tree = self.make_branch_and_tree('.')
 
34
        rev_id = tree.commit('first post')
 
35
        rev_id2 = tree.commit('second post', allow_pointless=True)
 
36
        rev_tree = tree.branch.repository.revision_tree(rev_id)
 
37
        rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
 
38
        optimiser = InterTree.get(rev_tree, rev_tree2)
 
39
        self.assertIsInstance(optimiser, InterTree)
 
40
        optimiser = InterTree.get(rev_tree2, rev_tree)
 
41
        self.assertIsInstance(optimiser, InterTree)
 
42
 
 
43
    def test_working_tree_revision_tree(self):
 
44
        # we should have an InterTree available for WorkingTree to
 
45
        # RevisionTree.
 
46
        tree = self.make_branch_and_tree('.')
 
47
        rev_id = tree.commit('first post')
 
48
        rev_tree = tree.branch.repository.revision_tree(rev_id)
 
49
        optimiser = InterTree.get(rev_tree, tree)
 
50
        self.assertIsInstance(optimiser, InterTree)
 
51
        optimiser = InterTree.get(tree, rev_tree)
 
52
        self.assertIsInstance(optimiser, InterTree)
 
53
 
 
54
    def test_working_tree_working_tree(self):
 
55
        # we should have an InterTree available for WorkingTree to
 
56
        # WorkingTree.
 
57
        tree = self.make_branch_and_tree('1')
 
58
        tree2 = self.make_branch_and_tree('2')
 
59
        optimiser = InterTree.get(tree, tree2)
 
60
        self.assertIsInstance(optimiser, InterTree)
 
61
        optimiser = InterTree.get(tree2, tree)
 
62
        self.assertIsInstance(optimiser, InterTree)
 
63
 
 
64
 
 
65
class RecordingOptimiser(InterTree):
 
66
 
 
67
    calls = []
 
68
 
 
69
    def compare(self, want_unchanged=False, specific_files=None,
 
70
        extra_trees=None, require_versioned=False, include_root=False,
 
71
        want_unversioned=False):
 
72
        self.calls.append(
 
73
            ('compare', self.source, self.target, want_unchanged,
 
74
             specific_files, extra_trees, require_versioned,
 
75
             include_root, want_unversioned)
 
76
            )
 
77
 
 
78
    @classmethod
 
79
    def is_compatible(klass, source, target):
 
80
        return True
 
81
 
 
82
 
 
83
class TestTree(TestCaseWithTransport):
 
84
 
 
85
    def test_compare_calls_InterTree_compare(self):
 
86
        """This test tests the way Tree.compare() uses InterTree."""
 
87
        old_optimisers = InterTree._optimisers
 
88
        try:
 
89
            InterTree._optimisers = []
 
90
            RecordingOptimiser.calls = []
 
91
            InterTree.register_optimiser(RecordingOptimiser)
 
92
            tree = self.make_branch_and_tree('1')
 
93
            tree2 = self.make_branch_and_tree('2')
 
94
            # do a series of calls:
 
95
            # trivial usage
 
96
            tree.changes_from(tree2)
 
97
            # pass in all optional arguments by position
 
98
            tree.changes_from(tree2, 'unchanged', 'specific', 'extra',
 
99
                              'require', True)
 
100
            # pass in all optional arguments by keyword
 
101
            tree.changes_from(tree2,
 
102
                specific_files='specific',
 
103
                want_unchanged='unchanged',
 
104
                extra_trees='extra',
 
105
                require_versioned='require',
 
106
                include_root=True,
 
107
                want_unversioned=True,
 
108
                )
 
109
        finally:
 
110
            InterTree._optimisers = old_optimisers
 
111
        self.assertEqual(
 
112
            [
 
113
             ('compare', tree2, tree, False, None, None, False, False, False),
 
114
             ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
 
115
              'require', True, False),
 
116
             ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
 
117
              'require', True, True),
 
118
            ], RecordingOptimiser.calls)
 
119
 
 
120
    def test_changes_from_with_root(self):
 
121
        """Ensure the include_root option does what's expected."""
 
122
        wt = self.make_branch_and_tree('.')
 
123
        delta = wt.changes_from(wt.basis_tree())
 
124
        self.assertEqual(len(delta.added), 0)
 
125
        delta = wt.changes_from(wt.basis_tree(), include_root=True)
 
126
        self.assertEqual(len(delta.added), 1)
 
127
        self.assertEqual(delta.added[0][0], '')
 
128
 
 
129
    def test_changes_from_with_require_versioned(self):
 
130
        """Ensure the require_versioned option does what's expected."""
 
131
        wt = self.make_branch_and_tree('.')
 
132
        self.build_tree(['known_file', 'unknown_file'])
 
133
        wt.add('known_file')
 
134
 
 
135
        self.assertRaises(errors.PathsNotVersionedError,
 
136
            wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
 
137
            'unknown_file'], require_versioned=True)
 
138
 
 
139
        # we need to pass a known file with an unknown file to get this to
 
140
        # fail when expected.
 
141
        delta = wt.changes_from(wt.basis_tree(),
 
142
            specific_files=['known_file', 'unknown_file'] ,
 
143
            require_versioned=False)
 
144
        self.assertEqual(len(delta.added), 1)
 
145
 
 
146
 
 
147
class TestMultiWalker(TestCaseWithTransport):
 
148
 
 
149
    def assertStepOne(self, has_more, path, file_id, iterator):
 
150
        retval = _mod_tree.MultiWalker._step_one(iterator)
 
151
        if not has_more:
 
152
            self.assertIs(None, path)
 
153
            self.assertIs(None, file_id)
 
154
            self.assertEqual((False, None, None), retval)
 
155
        else:
 
156
            self.assertEqual((has_more, path, file_id),
 
157
                             (retval[0], retval[1], retval[2].file_id))
 
158
 
 
159
    def test__step_one_empty(self):
 
160
        tree = self.make_branch_and_tree('empty')
 
161
        repo = tree.branch.repository
 
162
        empty_tree = repo.revision_tree(revision.NULL_REVISION)
 
163
 
 
164
        iterator = empty_tree.iter_entries_by_dir()
 
165
        self.assertStepOne(False, None, None, iterator)
 
166
        self.assertStepOne(False, None, None, iterator)
 
167
 
 
168
    def test__step_one(self):
 
169
        tree = self.make_branch_and_tree('tree')
 
170
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
 
171
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
 
172
 
 
173
        iterator = tree.iter_entries_by_dir()
 
174
        tree.lock_read()
 
175
        self.addCleanup(tree.unlock)
 
176
 
 
177
        root_id = tree.path2id('')
 
178
        self.assertStepOne(True, '', root_id, iterator)
 
179
        self.assertStepOne(True, 'a', 'a-id', iterator)
 
180
        self.assertStepOne(True, 'b', 'b-id', iterator)
 
181
        self.assertStepOne(True, 'b/c', 'c-id', iterator)
 
182
        self.assertStepOne(False, None, None, iterator)
 
183
        self.assertStepOne(False, None, None, iterator)
 
184
 
 
185
    def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
 
186
                         exp_other_paths, iterator):
 
187
        """Check what happens when we step the iterator.
 
188
 
 
189
        :param path: The path for this entry
 
190
        :param file_id: The file_id for this entry
 
191
        :param master_has_node: Does the master tree have this entry?
 
192
        :param exp_other_paths: A list of other_path values.
 
193
        :param iterator: The iterator to step
 
194
        """
 
195
        path, file_id, master_ie, other_values = iterator.next()
 
196
        self.assertEqual((exp_path, exp_file_id), (path, file_id),
 
197
                         'Master entry did not match')
 
198
        if master_has_node:
 
199
            self.assertIsNot(None, master_ie, 'master should have an entry')
 
200
        else:
 
201
            self.assertIs(None, master_ie, 'master should not have an entry')
 
202
        self.assertEqual(len(exp_other_paths), len(other_values),
 
203
                            'Wrong number of other entries')
 
204
        other_paths = []
 
205
        other_file_ids = []
 
206
        for path, ie in other_values:
 
207
            other_paths.append(path)
 
208
            if ie is None:
 
209
                other_file_ids.append(None)
 
210
            else:
 
211
                other_file_ids.append(ie.file_id)
 
212
 
 
213
        exp_file_ids = []
 
214
        for path in exp_other_paths:
 
215
            if path is None:
 
216
                exp_file_ids.append(None)
 
217
            else:
 
218
                exp_file_ids.append(file_id)
 
219
        self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
 
220
        self.assertEqual(exp_file_ids, other_file_ids,
 
221
                         "Other file_ids incorrect")
 
222
 
 
223
    def lock_and_get_basis_and_root_id(self, tree):
 
224
        tree.lock_read()
 
225
        self.addCleanup(tree.unlock)
 
226
        basis_tree = tree.basis_tree()
 
227
        basis_tree.lock_read()
 
228
        self.addCleanup(basis_tree.unlock)
 
229
        root_id = tree.path2id('')
 
230
        return basis_tree, root_id
 
231
 
 
232
    def test_simple_stepping(self):
 
233
        tree = self.make_branch_and_tree('tree')
 
234
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
 
235
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
 
236
 
 
237
        tree.commit('first', rev_id='first-rev-id')
 
238
 
 
239
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
240
 
 
241
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
242
        iterator = walker.iter_all()
 
243
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
244
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
245
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
246
        self.assertWalkerNext(u'b/c', 'c-id', True, [u'b/c'], iterator)
 
247
        self.assertRaises(StopIteration, iterator.next)
 
248
 
 
249
    def test_master_has_extra(self):
 
250
        tree = self.make_branch_and_tree('tree')
 
251
        self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
 
252
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
253
 
 
254
        tree.commit('first', rev_id='first-rev-id')
 
255
 
 
256
        tree.add(['c'], ['c-id'])
 
257
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
258
 
 
259
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
260
        iterator = walker.iter_all()
 
261
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
262
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
263
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
264
        self.assertWalkerNext(u'c', 'c-id', True, [None], iterator)
 
265
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
266
        self.assertRaises(StopIteration, iterator.next)
 
267
 
 
268
    def test_master_renamed_to_earlier(self):
 
269
        """The record is still present, it just shows up early."""
 
270
        tree = self.make_branch_and_tree('tree')
 
271
        self.build_tree(['tree/a', 'tree/c', 'tree/d'])
 
272
        tree.add(['a', 'c', 'd'], ['a-id', 'c-id', 'd-id'])
 
273
        tree.commit('first', rev_id='first-rev-id')
 
274
        tree.rename_one('d', 'b')
 
275
 
 
276
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
277
 
 
278
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
279
        iterator = walker.iter_all()
 
280
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
281
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
282
        self.assertWalkerNext(u'b', 'd-id', True, [u'd'], iterator)
 
283
        self.assertWalkerNext(u'c', 'c-id', True, [u'c'], iterator)
 
284
        self.assertRaises(StopIteration, iterator.next)
 
285
 
 
286
    def test_master_renamed_to_later(self):
 
287
        tree = self.make_branch_and_tree('tree')
 
288
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
289
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
290
        tree.commit('first', rev_id='first-rev-id')
 
291
        tree.rename_one('b', 'e')
 
292
 
 
293
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
294
 
 
295
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
296
        iterator = walker.iter_all()
 
297
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
298
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
299
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
300
        self.assertWalkerNext(u'e', 'b-id', True, [u'b'], iterator)
 
301
        self.assertRaises(StopIteration, iterator.next)
 
302
 
 
303
    def test_other_extra_in_middle(self):
 
304
        tree = self.make_branch_and_tree('tree')
 
305
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
306
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
307
        tree.commit('first', rev_id='first-rev-id')
 
308
        tree.remove(['b'])
 
309
 
 
310
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
311
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
312
        iterator = walker.iter_all()
 
313
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
314
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
315
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
316
        self.assertWalkerNext(u'b', 'b-id', False, [u'b'], iterator)
 
317
        self.assertRaises(StopIteration, iterator.next)
 
318
 
 
319
    def test_other_extra_at_end(self):
 
320
        tree = self.make_branch_and_tree('tree')
 
321
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
322
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
323
        tree.commit('first', rev_id='first-rev-id')
 
324
        tree.remove(['d'])
 
325
 
 
326
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
327
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
328
        iterator = walker.iter_all()
 
329
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
330
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
331
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
332
        self.assertWalkerNext(u'd', 'd-id', False, [u'd'], iterator)
 
333
        self.assertRaises(StopIteration, iterator.next)
 
334
 
 
335
    def test_others_extra_at_end(self):
 
336
        tree = self.make_branch_and_tree('tree')
 
337
        self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
 
338
        tree.add(['a', 'b', 'c', 'd', 'e'],
 
339
                 ['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
 
340
        tree.commit('first', rev_id='first-rev-id')
 
341
        tree.remove(['e'])
 
342
        tree.commit('second', rev_id='second-rev-id')
 
343
        tree.remove(['d'])
 
344
        tree.commit('third', rev_id='third-rev-id')
 
345
        tree.remove(['c'])
 
346
 
 
347
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
348
        first_tree = tree.branch.repository.revision_tree('first-rev-id')
 
349
        second_tree = tree.branch.repository.revision_tree('second-rev-id')
 
350
        walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
 
351
                                              second_tree])
 
352
        iterator = walker.iter_all()
 
353
        self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
 
354
        self.assertWalkerNext(u'a', 'a-id', True, [u'a', u'a', u'a'], iterator)
 
355
        self.assertWalkerNext(u'b', 'b-id', True, [u'b', u'b', u'b'], iterator)
 
356
        self.assertWalkerNext(u'c', 'c-id', False, [u'c', u'c', u'c'], iterator)
 
357
        self.assertWalkerNext(u'd', 'd-id', False, [None, u'd', u'd'], iterator)
 
358
        self.assertWalkerNext(u'e', 'e-id', False, [None, u'e', None], iterator)
 
359
        self.assertRaises(StopIteration, iterator.next)
 
360
 
 
361
    def test_different_file_id_in_others(self):
 
362
        tree = self.make_branch_and_tree('tree')
 
363
        self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
 
364
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
 
365
        tree.commit('first', rev_id='first-rev-id')
 
366
 
 
367
        tree.rename_one('b', 'c/d')
 
368
        self.build_tree(['tree/b'])
 
369
        tree.add(['b'], ['b2-id'])
 
370
        tree.commit('second', rev_id='second-rev-id')
 
371
 
 
372
        tree.rename_one('a', 'c/e')
 
373
        self.build_tree(['tree/a'])
 
374
        tree.add(['a'], ['a2-id'])
 
375
 
 
376
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
377
        first_tree = tree.branch.repository.revision_tree('first-rev-id')
 
378
        walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
 
379
 
 
380
        iterator = walker.iter_all()
 
381
        self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
 
382
        self.assertWalkerNext(u'a', 'a2-id', True, [None, None], iterator)
 
383
        self.assertWalkerNext(u'b', 'b2-id', True, [u'b', None], iterator)
 
384
        self.assertWalkerNext(u'c', 'c-id', True, [u'c', u'c'], iterator)
 
385
        self.assertWalkerNext(u'c/d', 'b-id', True, [u'c/d', u'b'], iterator)
 
386
        self.assertWalkerNext(u'c/e', 'a-id', True, [u'a', u'a'], iterator)
 
387
        self.assertRaises(StopIteration, iterator.next)
 
388
 
 
389
    def assertCmpByDirblock(self, cmp_val, path1, path2):
 
390
        self.assertEqual(cmp_val,
 
391
            _mod_tree.MultiWalker._cmp_path_by_dirblock(path1, path2))
 
392
 
 
393
    def test__cmp_path_by_dirblock(self):
 
394
        # We only support Unicode strings at this point
 
395
        self.assertRaises(TypeError,
 
396
            _mod_tree.MultiWalker._cmp_path_by_dirblock, '', 'b')
 
397
        self.assertCmpByDirblock(0, u'', u'')
 
398
        self.assertCmpByDirblock(0, u'a', u'a')
 
399
        self.assertCmpByDirblock(0, u'a/b', u'a/b')
 
400
        self.assertCmpByDirblock(0, u'a/b/c', u'a/b/c')
 
401
        self.assertCmpByDirblock(1, u'a-a', u'a')
 
402
        self.assertCmpByDirblock(-1, u'a-a', u'a/a')
 
403
        self.assertCmpByDirblock(-1, u'a=a', u'a/a')
 
404
        self.assertCmpByDirblock(1, u'a-a/a', u'a/a')
 
405
        self.assertCmpByDirblock(1, u'a=a/a', u'a/a')
 
406
        self.assertCmpByDirblock(1, u'a-a/a', u'a/a/a')
 
407
        self.assertCmpByDirblock(1, u'a=a/a', u'a/a/a')
 
408
        self.assertCmpByDirblock(1, u'a-a/a/a', u'a/a/a')
 
409
        self.assertCmpByDirblock(1, u'a=a/a/a', u'a/a/a')
 
410
 
 
411
    def assertPathToKey(self, expected, path):
 
412
        self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
 
413
 
 
414
    def test__path_to_key(self):
 
415
        self.assertPathToKey(([u''], u''), u'')
 
416
        self.assertPathToKey(([u''], u'a'), u'a')
 
417
        self.assertPathToKey(([u'a'], u'b'), u'a/b')
 
418
        self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')