1
# Copyright (C) 2006-2009, 2011 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."""
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tree import InterTree
28
class TestInterTree(TestCaseWithTransport):
30
def test_revision_tree_revision_tree(self):
31
# we should have an InterTree registered for RevisionTree to
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)
43
def test_working_tree_revision_tree(self):
44
# we should have an InterTree available for WorkingTree to
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)
54
def test_working_tree_working_tree(self):
55
# we should have an InterTree available for WorkingTree to
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)
65
class RecordingOptimiser(InterTree):
69
def compare(self, want_unchanged=False, specific_files=None,
70
extra_trees=None, require_versioned=False, include_root=False,
71
want_unversioned=False):
73
('compare', self.source, self.target, want_unchanged,
74
specific_files, extra_trees, require_versioned,
75
include_root, want_unversioned)
79
def is_compatible(klass, source, target):
83
class TestTree(TestCaseWithTransport):
85
def test_compare_calls_InterTree_compare(self):
86
"""This test tests the way Tree.compare() uses InterTree."""
87
old_optimisers = InterTree._optimisers
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:
96
tree.changes_from(tree2)
97
# pass in all optional arguments by position
98
tree.changes_from(tree2, 'unchanged', 'specific', 'extra',
100
# pass in all optional arguments by keyword
101
tree.changes_from(tree2,
102
specific_files='specific',
103
want_unchanged='unchanged',
105
require_versioned='require',
107
want_unversioned=True,
110
InterTree._optimisers = old_optimisers
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)
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], '')
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'])
135
self.assertRaises(errors.PathsNotVersionedError,
136
wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
137
'unknown_file'], require_versioned=True)
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)
147
class TestMultiWalker(TestCaseWithTransport):
149
def assertStepOne(self, has_more, path, file_id, iterator):
150
retval = _mod_tree.MultiWalker._step_one(iterator)
152
self.assertIs(None, path)
153
self.assertIs(None, file_id)
154
self.assertEqual((False, None, None), retval)
156
self.assertEqual((has_more, path, file_id),
157
(retval[0], retval[1], retval[2].file_id))
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)
164
iterator = empty_tree.iter_entries_by_dir()
165
self.assertStepOne(False, None, None, iterator)
166
self.assertStepOne(False, None, None, iterator)
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'])
173
iterator = tree.iter_entries_by_dir()
175
self.addCleanup(tree.unlock)
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)
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.
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
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')
199
self.assertIsNot(None, master_ie, 'master should have an entry')
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')
206
for path, ie in other_values:
207
other_paths.append(path)
209
other_file_ids.append(None)
211
other_file_ids.append(ie.file_id)
214
for path in exp_other_paths:
216
exp_file_ids.append(None)
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")
223
def lock_and_get_basis_and_root_id(self, tree):
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
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'])
237
tree.commit('first', rev_id='first-rev-id')
239
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
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)
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'])
254
tree.commit('first', rev_id='first-rev-id')
256
tree.add(['c'], ['c-id'])
257
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
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)
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')
276
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
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)
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')
293
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
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)
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')
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)
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')
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)
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')
342
tree.commit('second', rev_id='second-rev-id')
344
tree.commit('third', rev_id='third-rev-id')
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,
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)
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')
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')
372
tree.rename_one('a', 'c/e')
373
self.build_tree(['tree/a'])
374
tree.add(['a'], ['a2-id'])
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])
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)
389
def assertCmpByDirblock(self, cmp_val, path1, path2):
390
self.assertEqual(cmp_val,
391
_mod_tree.MultiWalker._cmp_path_by_dirblock(path1, path2))
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')
411
def assertPathToKey(self, expected, path):
412
self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
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')