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