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 breezy.tests import (
26
TestCaseWithTransport,
28
from breezy.tree import (
29
FileTimestampUnavailable,
34
class TestErrors(TestCase):
36
def test_file_timestamp_unavailable(self):
37
e = FileTimestampUnavailable("/path/foo")
38
self.assertEqual("The filestamp for /path/foo is not available.",
42
class TestInterTree(TestCaseWithTransport):
44
def test_revision_tree_revision_tree(self):
45
# we should have an InterTree registered for RevisionTree to
47
tree = self.make_branch_and_tree('.')
48
rev_id = tree.commit('first post')
49
rev_id2 = tree.commit('second post', allow_pointless=True)
50
rev_tree = tree.branch.repository.revision_tree(rev_id)
51
rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
52
optimiser = InterTree.get(rev_tree, rev_tree2)
53
self.assertIsInstance(optimiser, InterTree)
54
optimiser = InterTree.get(rev_tree2, rev_tree)
55
self.assertIsInstance(optimiser, InterTree)
57
def test_working_tree_revision_tree(self):
58
# we should have an InterTree available for WorkingTree to
60
tree = self.make_branch_and_tree('.')
61
rev_id = tree.commit('first post')
62
rev_tree = tree.branch.repository.revision_tree(rev_id)
63
optimiser = InterTree.get(rev_tree, tree)
64
self.assertIsInstance(optimiser, InterTree)
65
optimiser = InterTree.get(tree, rev_tree)
66
self.assertIsInstance(optimiser, InterTree)
68
def test_working_tree_working_tree(self):
69
# we should have an InterTree available for WorkingTree to
71
tree = self.make_branch_and_tree('1')
72
tree2 = self.make_branch_and_tree('2')
73
optimiser = InterTree.get(tree, tree2)
74
self.assertIsInstance(optimiser, InterTree)
75
optimiser = InterTree.get(tree2, tree)
76
self.assertIsInstance(optimiser, InterTree)
79
class RecordingOptimiser(InterTree):
83
def compare(self, want_unchanged=False, specific_files=None,
84
extra_trees=None, require_versioned=False, include_root=False,
85
want_unversioned=False):
87
('compare', self.source, self.target, want_unchanged,
88
specific_files, extra_trees, require_versioned,
89
include_root, want_unversioned)
93
def is_compatible(klass, source, target):
97
class TestTree(TestCaseWithTransport):
99
def test_compare_calls_InterTree_compare(self):
100
"""This test tests the way Tree.compare() uses InterTree."""
101
old_optimisers = InterTree._optimisers
103
InterTree._optimisers = []
104
RecordingOptimiser.calls = []
105
InterTree.register_optimiser(RecordingOptimiser)
106
tree = self.make_branch_and_tree('1')
107
tree2 = self.make_branch_and_tree('2')
108
# do a series of calls:
110
tree.changes_from(tree2)
111
# pass in all optional arguments by position
112
tree.changes_from(tree2, 'unchanged', 'specific', 'extra',
114
# pass in all optional arguments by keyword
115
tree.changes_from(tree2,
116
specific_files='specific',
117
want_unchanged='unchanged',
119
require_versioned='require',
121
want_unversioned=True,
124
InterTree._optimisers = old_optimisers
127
('compare', tree2, tree, False, None, None, False, False, False),
128
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
129
'require', True, False),
130
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
131
'require', True, True),
132
], RecordingOptimiser.calls)
134
def test_changes_from_with_root(self):
135
"""Ensure the include_root option does what's expected."""
136
wt = self.make_branch_and_tree('.')
137
delta = wt.changes_from(wt.basis_tree())
138
self.assertEqual(len(delta.added), 0)
139
delta = wt.changes_from(wt.basis_tree(), include_root=True)
140
self.assertEqual(len(delta.added), 1)
141
self.assertEqual(delta.added[0][0], '')
143
def test_changes_from_with_require_versioned(self):
144
"""Ensure the require_versioned option does what's expected."""
145
wt = self.make_branch_and_tree('.')
146
self.build_tree(['known_file', 'unknown_file'])
149
self.assertRaises(errors.PathsNotVersionedError,
150
wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
151
'unknown_file'], require_versioned=True)
153
# we need to pass a known file with an unknown file to get this to
154
# fail when expected.
155
delta = wt.changes_from(wt.basis_tree(),
156
specific_files=['known_file', 'unknown_file'] ,
157
require_versioned=False)
158
self.assertEqual(len(delta.added), 1)
161
class TestMultiWalker(TestCaseWithTransport):
163
def assertStepOne(self, has_more, path, file_id, iterator):
164
retval = _mod_tree.MultiWalker._step_one(iterator)
166
self.assertIs(None, path)
167
self.assertIs(None, file_id)
168
self.assertEqual((False, None, None), retval)
170
self.assertEqual((has_more, path, file_id),
171
(retval[0], retval[1], retval[2].file_id))
173
def test__step_one_empty(self):
174
tree = self.make_branch_and_tree('empty')
175
repo = tree.branch.repository
176
empty_tree = repo.revision_tree(revision.NULL_REVISION)
178
iterator = empty_tree.iter_entries_by_dir()
179
self.assertStepOne(False, None, None, iterator)
180
self.assertStepOne(False, None, None, iterator)
182
def test__step_one(self):
183
tree = self.make_branch_and_tree('tree')
184
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
185
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
187
iterator = tree.iter_entries_by_dir()
189
self.addCleanup(tree.unlock)
191
root_id = tree.path2id('')
192
self.assertStepOne(True, '', root_id, iterator)
193
self.assertStepOne(True, 'a', 'a-id', iterator)
194
self.assertStepOne(True, 'b', 'b-id', iterator)
195
self.assertStepOne(True, 'b/c', 'c-id', iterator)
196
self.assertStepOne(False, None, None, iterator)
197
self.assertStepOne(False, None, None, iterator)
199
def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
200
exp_other_paths, iterator):
201
"""Check what happens when we step the iterator.
203
:param path: The path for this entry
204
:param file_id: The file_id for this entry
205
:param master_has_node: Does the master tree have this entry?
206
:param exp_other_paths: A list of other_path values.
207
:param iterator: The iterator to step
209
path, file_id, master_ie, other_values = next(iterator)
210
self.assertEqual((exp_path, exp_file_id), (path, file_id),
211
'Master entry did not match')
213
self.assertIsNot(None, master_ie, 'master should have an entry')
215
self.assertIs(None, master_ie, 'master should not have an entry')
216
self.assertEqual(len(exp_other_paths), len(other_values),
217
'Wrong number of other entries')
220
for path, ie in other_values:
221
other_paths.append(path)
223
other_file_ids.append(None)
225
other_file_ids.append(ie.file_id)
228
for path in exp_other_paths:
230
exp_file_ids.append(None)
232
exp_file_ids.append(file_id)
233
self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
234
self.assertEqual(exp_file_ids, other_file_ids,
235
"Other file_ids incorrect")
237
def lock_and_get_basis_and_root_id(self, tree):
239
self.addCleanup(tree.unlock)
240
basis_tree = tree.basis_tree()
241
basis_tree.lock_read()
242
self.addCleanup(basis_tree.unlock)
243
root_id = tree.path2id('')
244
return basis_tree, root_id
246
def test_simple_stepping(self):
247
tree = self.make_branch_and_tree('tree')
248
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
249
tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
251
tree.commit('first', rev_id='first-rev-id')
253
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
255
walker = _mod_tree.MultiWalker(tree, [basis_tree])
256
iterator = walker.iter_all()
257
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
258
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
259
self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
260
self.assertWalkerNext(u'b/c', 'c-id', True, [u'b/c'], iterator)
261
self.assertRaises(StopIteration, next, iterator)
263
def test_master_has_extra(self):
264
tree = self.make_branch_and_tree('tree')
265
self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
266
tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
268
tree.commit('first', rev_id='first-rev-id')
270
tree.add(['c'], ['c-id'])
271
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
273
walker = _mod_tree.MultiWalker(tree, [basis_tree])
274
iterator = walker.iter_all()
275
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
276
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
277
self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
278
self.assertWalkerNext(u'c', 'c-id', True, [None], iterator)
279
self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
280
self.assertRaises(StopIteration, next, iterator)
282
def test_master_renamed_to_earlier(self):
283
"""The record is still present, it just shows up early."""
284
tree = self.make_branch_and_tree('tree')
285
self.build_tree(['tree/a', 'tree/c', 'tree/d'])
286
tree.add(['a', 'c', 'd'], ['a-id', 'c-id', 'd-id'])
287
tree.commit('first', rev_id='first-rev-id')
288
tree.rename_one('d', 'b')
290
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
292
walker = _mod_tree.MultiWalker(tree, [basis_tree])
293
iterator = walker.iter_all()
294
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
295
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
296
self.assertWalkerNext(u'b', 'd-id', True, [u'd'], iterator)
297
self.assertWalkerNext(u'c', 'c-id', True, [u'c'], iterator)
298
self.assertRaises(StopIteration, next, iterator)
300
def test_master_renamed_to_later(self):
301
tree = self.make_branch_and_tree('tree')
302
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
303
tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
304
tree.commit('first', rev_id='first-rev-id')
305
tree.rename_one('b', 'e')
307
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
309
walker = _mod_tree.MultiWalker(tree, [basis_tree])
310
iterator = walker.iter_all()
311
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
312
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
313
self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
314
self.assertWalkerNext(u'e', 'b-id', True, [u'b'], iterator)
315
self.assertRaises(StopIteration, next, iterator)
317
def test_other_extra_in_middle(self):
318
tree = self.make_branch_and_tree('tree')
319
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
320
tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
321
tree.commit('first', rev_id='first-rev-id')
324
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
325
walker = _mod_tree.MultiWalker(tree, [basis_tree])
326
iterator = walker.iter_all()
327
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
328
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
329
self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
330
self.assertWalkerNext(u'b', 'b-id', False, [u'b'], iterator)
331
self.assertRaises(StopIteration, next, iterator)
333
def test_other_extra_at_end(self):
334
tree = self.make_branch_and_tree('tree')
335
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
336
tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
337
tree.commit('first', rev_id='first-rev-id')
340
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
341
walker = _mod_tree.MultiWalker(tree, [basis_tree])
342
iterator = walker.iter_all()
343
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
344
self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
345
self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
346
self.assertWalkerNext(u'd', 'd-id', False, [u'd'], iterator)
347
self.assertRaises(StopIteration, next, iterator)
349
def test_others_extra_at_end(self):
350
tree = self.make_branch_and_tree('tree')
351
self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
352
tree.add(['a', 'b', 'c', 'd', 'e'],
353
['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
354
tree.commit('first', rev_id='first-rev-id')
356
tree.commit('second', rev_id='second-rev-id')
358
tree.commit('third', rev_id='third-rev-id')
361
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
362
first_tree = tree.branch.repository.revision_tree('first-rev-id')
363
second_tree = tree.branch.repository.revision_tree('second-rev-id')
364
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
366
iterator = walker.iter_all()
367
self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
368
self.assertWalkerNext(u'a', 'a-id', True, [u'a', u'a', u'a'], iterator)
369
self.assertWalkerNext(u'b', 'b-id', True, [u'b', u'b', u'b'], iterator)
370
self.assertWalkerNext(u'c', 'c-id', False, [u'c', u'c', u'c'], iterator)
371
self.assertWalkerNext(u'd', 'd-id', False, [None, u'd', u'd'], iterator)
372
self.assertWalkerNext(u'e', 'e-id', False, [None, u'e', None], iterator)
373
self.assertRaises(StopIteration, next, iterator)
375
def test_different_file_id_in_others(self):
376
tree = self.make_branch_and_tree('tree')
377
self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
378
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
379
tree.commit('first', rev_id='first-rev-id')
381
tree.rename_one('b', 'c/d')
382
self.build_tree(['tree/b'])
383
tree.add(['b'], ['b2-id'])
384
tree.commit('second', rev_id='second-rev-id')
386
tree.rename_one('a', 'c/e')
387
self.build_tree(['tree/a'])
388
tree.add(['a'], ['a2-id'])
390
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
391
first_tree = tree.branch.repository.revision_tree('first-rev-id')
392
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
394
iterator = walker.iter_all()
395
self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
396
self.assertWalkerNext(u'a', 'a2-id', True, [None, None], iterator)
397
self.assertWalkerNext(u'b', 'b2-id', True, [u'b', None], iterator)
398
self.assertWalkerNext(u'c', 'c-id', True, [u'c', u'c'], iterator)
399
self.assertWalkerNext(u'c/d', 'b-id', True, [u'c/d', u'b'], iterator)
400
self.assertWalkerNext(u'c/e', 'a-id', True, [u'a', u'a'], iterator)
401
self.assertRaises(StopIteration, next, iterator)
403
def assertCmpByDirblock(self, cmp_val, path1, path2):
404
self.assertEqual(cmp_val,
405
_mod_tree.MultiWalker._cmp_path_by_dirblock(path1, path2))
407
def test__cmp_path_by_dirblock(self):
408
# We only support Unicode strings at this point
409
self.assertRaises(TypeError,
410
_mod_tree.MultiWalker._cmp_path_by_dirblock, '', 'b')
411
self.assertCmpByDirblock(0, u'', u'')
412
self.assertCmpByDirblock(0, u'a', u'a')
413
self.assertCmpByDirblock(0, u'a/b', u'a/b')
414
self.assertCmpByDirblock(0, u'a/b/c', u'a/b/c')
415
self.assertCmpByDirblock(1, u'a-a', u'a')
416
self.assertCmpByDirblock(-1, u'a-a', u'a/a')
417
self.assertCmpByDirblock(-1, u'a=a', u'a/a')
418
self.assertCmpByDirblock(1, u'a-a/a', u'a/a')
419
self.assertCmpByDirblock(1, u'a=a/a', u'a/a')
420
self.assertCmpByDirblock(1, u'a-a/a', u'a/a/a')
421
self.assertCmpByDirblock(1, u'a=a/a', u'a/a/a')
422
self.assertCmpByDirblock(1, u'a-a/a/a', u'a/a/a')
423
self.assertCmpByDirblock(1, u'a=a/a/a', u'a/a/a')
425
def assertPathToKey(self, expected, path):
426
self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
428
def test__path_to_key(self):
429
self.assertPathToKey(([u''], u''), u'')
430
self.assertPathToKey(([u''], u'a'), u'a')
431
self.assertPathToKey(([u'a'], u'b'), u'a/b')
432
self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')