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,
35
class TestErrors(TestCase):
37
def test_file_timestamp_unavailable(self):
38
e = FileTimestampUnavailable("/path/foo")
39
self.assertEqual("The filestamp for /path/foo is not available.",
43
class TestInterTree(TestCaseWithTransport):
45
def test_revision_tree_revision_tree(self):
46
# we should have an InterTree registered for RevisionTree to
48
tree = self.make_branch_and_tree('.')
49
rev_id = tree.commit('first post')
50
rev_id2 = tree.commit('second post', allow_pointless=True)
51
rev_tree = tree.branch.repository.revision_tree(rev_id)
52
rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
53
optimiser = InterTree.get(rev_tree, rev_tree2)
54
self.assertIsInstance(optimiser, InterTree)
55
optimiser = InterTree.get(rev_tree2, rev_tree)
56
self.assertIsInstance(optimiser, InterTree)
58
def test_working_tree_revision_tree(self):
59
# we should have an InterTree available for WorkingTree to
61
tree = self.make_branch_and_tree('.')
62
rev_id = tree.commit('first post')
63
rev_tree = tree.branch.repository.revision_tree(rev_id)
64
optimiser = InterTree.get(rev_tree, tree)
65
self.assertIsInstance(optimiser, InterTree)
66
optimiser = InterTree.get(tree, rev_tree)
67
self.assertIsInstance(optimiser, InterTree)
69
def test_working_tree_working_tree(self):
70
# we should have an InterTree available for WorkingTree to
72
tree = self.make_branch_and_tree('1')
73
tree2 = self.make_branch_and_tree('2')
74
optimiser = InterTree.get(tree, tree2)
75
self.assertIsInstance(optimiser, InterTree)
76
optimiser = InterTree.get(tree2, tree)
77
self.assertIsInstance(optimiser, InterTree)
80
class RecordingOptimiser(InterTree):
84
def compare(self, want_unchanged=False, specific_files=None,
85
extra_trees=None, require_versioned=False, include_root=False,
86
want_unversioned=False):
88
('compare', self.source, self.target, want_unchanged,
89
specific_files, extra_trees, require_versioned,
90
include_root, want_unversioned)
94
def is_compatible(klass, source, target):
98
class TestTree(TestCaseWithTransport):
100
def test_compare_calls_InterTree_compare(self):
101
"""This test tests the way Tree.compare() uses InterTree."""
102
old_optimisers = InterTree._optimisers
104
InterTree._optimisers = []
105
RecordingOptimiser.calls = []
106
InterTree.register_optimiser(RecordingOptimiser)
107
tree = self.make_branch_and_tree('1')
108
tree2 = self.make_branch_and_tree('2')
109
# do a series of calls:
111
tree.changes_from(tree2)
112
# pass in all optional arguments by position
113
tree.changes_from(tree2, 'unchanged', 'specific', 'extra',
115
# pass in all optional arguments by keyword
116
tree.changes_from(tree2,
117
specific_files='specific',
118
want_unchanged='unchanged',
120
require_versioned='require',
122
want_unversioned=True,
125
InterTree._optimisers = old_optimisers
128
('compare', tree2, tree, False, None, None, False, False, False),
129
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
130
'require', True, False),
131
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
132
'require', True, True),
133
], RecordingOptimiser.calls)
135
def test_changes_from_with_root(self):
136
"""Ensure the include_root option does what's expected."""
137
wt = self.make_branch_and_tree('.')
138
delta = wt.changes_from(wt.basis_tree())
139
self.assertEqual(len(delta.added), 0)
140
delta = wt.changes_from(wt.basis_tree(), include_root=True)
141
self.assertEqual(len(delta.added), 1)
142
self.assertEqual(delta.added[0][0], '')
144
def test_changes_from_with_require_versioned(self):
145
"""Ensure the require_versioned option does what's expected."""
146
wt = self.make_branch_and_tree('.')
147
self.build_tree(['known_file', 'unknown_file'])
150
self.assertRaises(errors.PathsNotVersionedError,
151
wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
152
'unknown_file'], require_versioned=True)
154
# we need to pass a known file with an unknown file to get this to
155
# fail when expected.
156
delta = wt.changes_from(wt.basis_tree(),
157
specific_files=['known_file', 'unknown_file'],
158
require_versioned=False)
159
self.assertEqual(len(delta.added), 1)
162
class TestMultiWalker(TestCaseWithTransport):
164
def assertStepOne(self, has_more, path, file_id, iterator):
165
retval = _mod_tree.MultiWalker._step_one(iterator)
167
self.assertIs(None, path)
168
self.assertIs(None, file_id)
169
self.assertEqual((False, None, None), retval)
171
self.assertEqual((has_more, path, file_id),
172
(retval[0], retval[1], retval[2].file_id))
174
def test__step_one_empty(self):
175
tree = self.make_branch_and_tree('empty')
176
repo = tree.branch.repository
177
empty_tree = repo.revision_tree(revision.NULL_REVISION)
179
iterator = empty_tree.iter_entries_by_dir()
180
self.assertStepOne(False, None, None, iterator)
181
self.assertStepOne(False, None, None, iterator)
183
def test__step_one(self):
184
tree = self.make_branch_and_tree('tree')
185
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
186
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
188
iterator = tree.iter_entries_by_dir()
190
self.addCleanup(tree.unlock)
192
root_id = tree.path2id('')
193
self.assertStepOne(True, '', root_id, iterator)
194
self.assertStepOne(True, 'a', b'a-id', iterator)
195
self.assertStepOne(True, 'b', b'b-id', iterator)
196
self.assertStepOne(True, 'b/c', b'c-id', iterator)
197
self.assertStepOne(False, None, None, iterator)
198
self.assertStepOne(False, None, None, iterator)
200
def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
201
exp_other_paths, iterator):
202
"""Check what happens when we step the iterator.
204
:param path: The path for this entry
205
:param file_id: The file_id for this entry
206
:param master_has_node: Does the master tree have this entry?
207
:param exp_other_paths: A list of other_path values.
208
:param iterator: The iterator to step
210
path, file_id, master_ie, other_values = next(iterator)
211
self.assertEqual((exp_path, exp_file_id), (path, file_id),
212
'Master entry did not match')
214
self.assertIsNot(None, master_ie, 'master should have an entry')
216
self.assertIs(None, master_ie, 'master should not have an entry')
217
self.assertEqual(len(exp_other_paths), len(other_values),
218
'Wrong number of other entries')
221
for path, ie in other_values:
222
other_paths.append(path)
224
other_file_ids.append(None)
226
other_file_ids.append(ie.file_id)
229
for path in exp_other_paths:
231
exp_file_ids.append(None)
233
exp_file_ids.append(file_id)
234
self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
235
self.assertEqual(exp_file_ids, other_file_ids,
236
"Other file_ids incorrect")
238
def lock_and_get_basis_and_root_id(self, tree):
240
self.addCleanup(tree.unlock)
241
basis_tree = tree.basis_tree()
242
basis_tree.lock_read()
243
self.addCleanup(basis_tree.unlock)
244
root_id = tree.path2id('')
245
return basis_tree, root_id
247
def test_simple_stepping(self):
248
tree = self.make_branch_and_tree('tree')
249
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
250
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
252
tree.commit('first', rev_id=b'first-rev-id')
254
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
256
walker = _mod_tree.MultiWalker(tree, [basis_tree])
257
iterator = walker.iter_all()
258
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
259
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
260
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
261
self.assertWalkerNext(u'b/c', b'c-id', True, [u'b/c'], iterator)
262
self.assertRaises(StopIteration, next, iterator)
264
def test_master_has_extra(self):
265
tree = self.make_branch_and_tree('tree')
266
self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
267
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
269
tree.commit('first', rev_id=b'first-rev-id')
271
tree.add(['c'], [b'c-id'])
272
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
274
walker = _mod_tree.MultiWalker(tree, [basis_tree])
275
iterator = walker.iter_all()
276
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
277
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
278
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
279
self.assertWalkerNext(u'c', b'c-id', True, [None], iterator)
280
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
281
self.assertRaises(StopIteration, next, iterator)
283
def test_master_renamed_to_earlier(self):
284
"""The record is still present, it just shows up early."""
285
tree = self.make_branch_and_tree('tree')
286
self.build_tree(['tree/a', 'tree/c', 'tree/d'])
287
tree.add(['a', 'c', 'd'], [b'a-id', b'c-id', b'd-id'])
288
tree.commit('first', rev_id=b'first-rev-id')
289
tree.rename_one('d', 'b')
291
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
293
walker = _mod_tree.MultiWalker(tree, [basis_tree])
294
iterator = walker.iter_all()
295
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
296
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
297
self.assertWalkerNext(u'b', b'd-id', True, [u'd'], iterator)
298
self.assertWalkerNext(u'c', b'c-id', True, [u'c'], iterator)
299
self.assertRaises(StopIteration, next, iterator)
301
def test_master_renamed_to_later(self):
302
tree = self.make_branch_and_tree('tree')
303
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
304
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
305
tree.commit('first', rev_id=b'first-rev-id')
306
tree.rename_one('b', 'e')
308
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
310
walker = _mod_tree.MultiWalker(tree, [basis_tree])
311
iterator = walker.iter_all()
312
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
313
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
314
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
315
self.assertWalkerNext(u'e', b'b-id', True, [u'b'], iterator)
316
self.assertRaises(StopIteration, next, iterator)
318
def test_other_extra_in_middle(self):
319
tree = self.make_branch_and_tree('tree')
320
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
321
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
322
tree.commit('first', rev_id=b'first-rev-id')
325
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
326
walker = _mod_tree.MultiWalker(tree, [basis_tree])
327
iterator = walker.iter_all()
328
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
329
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
330
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
331
self.assertWalkerNext(u'b', b'b-id', False, [u'b'], iterator)
332
self.assertRaises(StopIteration, next, iterator)
334
def test_other_extra_at_end(self):
335
tree = self.make_branch_and_tree('tree')
336
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
337
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
338
tree.commit('first', rev_id=b'first-rev-id')
341
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
342
walker = _mod_tree.MultiWalker(tree, [basis_tree])
343
iterator = walker.iter_all()
344
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
345
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
346
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
347
self.assertWalkerNext(u'd', b'd-id', False, [u'd'], iterator)
348
self.assertRaises(StopIteration, next, iterator)
350
def test_others_extra_at_end(self):
351
tree = self.make_branch_and_tree('tree')
352
self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
353
tree.add(['a', 'b', 'c', 'd', 'e'],
354
[b'a-id', b'b-id', b'c-id', b'd-id', b'e-id'])
355
tree.commit('first', rev_id=b'first-rev-id')
357
tree.commit('second', rev_id=b'second-rev-id')
359
tree.commit('third', rev_id=b'third-rev-id')
362
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
363
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
364
second_tree = tree.branch.repository.revision_tree(b'second-rev-id')
365
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
367
iterator = walker.iter_all()
368
self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
369
self.assertWalkerNext(u'a', b'a-id', True, [u'a', u'a', u'a'], iterator)
370
self.assertWalkerNext(u'b', b'b-id', True, [u'b', u'b', u'b'], iterator)
371
self.assertWalkerNext(u'c', b'c-id', False, [u'c', u'c', u'c'], iterator)
372
self.assertWalkerNext(u'd', b'd-id', False, [None, u'd', u'd'], iterator)
373
self.assertWalkerNext(u'e', b'e-id', False, [None, u'e', None], iterator)
374
self.assertRaises(StopIteration, next, iterator)
376
def test_different_file_id_in_others(self):
377
tree = self.make_branch_and_tree('tree')
378
self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
379
tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id'])
380
tree.commit('first', rev_id=b'first-rev-id')
382
tree.rename_one('b', 'c/d')
383
self.build_tree(['tree/b'])
384
tree.add(['b'], [b'b2-id'])
385
tree.commit('second', rev_id=b'second-rev-id')
387
tree.rename_one('a', 'c/e')
388
self.build_tree(['tree/a'])
389
tree.add(['a'], [b'a2-id'])
391
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
392
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
393
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
395
iterator = walker.iter_all()
396
self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
397
self.assertWalkerNext(u'a', b'a2-id', True, [None, None], iterator)
398
self.assertWalkerNext(u'b', b'b2-id', True, [u'b', None], iterator)
399
self.assertWalkerNext(u'c', b'c-id', True, [u'c', u'c'], iterator)
400
self.assertWalkerNext(u'c/d', b'b-id', True, [u'c/d', u'b'], iterator)
401
self.assertWalkerNext(u'c/e', b'a-id', True, [u'a', u'a'], iterator)
402
self.assertRaises(StopIteration, next, iterator)
404
def assertCmpByDirblock(self, cmp_val, path1, path2):
405
self.assertEqual(cmp_val,
406
_mod_tree.MultiWalker._cmp_path_by_dirblock(path1, path2))
408
def test__cmp_path_by_dirblock(self):
409
# We only support Unicode strings at this point
410
self.assertRaises(TypeError,
411
_mod_tree.MultiWalker._cmp_path_by_dirblock, '', 'b')
412
self.assertCmpByDirblock(0, u'', u'')
413
self.assertCmpByDirblock(0, u'a', u'a')
414
self.assertCmpByDirblock(0, u'a/b', u'a/b')
415
self.assertCmpByDirblock(0, u'a/b/c', u'a/b/c')
416
self.assertCmpByDirblock(1, u'a-a', u'a')
417
self.assertCmpByDirblock(-1, u'a-a', u'a/a')
418
self.assertCmpByDirblock(-1, u'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')
421
self.assertCmpByDirblock(1, u'a-a/a', u'a/a/a')
422
self.assertCmpByDirblock(1, u'a=a/a', u'a/a/a')
423
self.assertCmpByDirblock(1, u'a-a/a/a', u'a/a/a')
424
self.assertCmpByDirblock(1, u'a=a/a/a', u'a/a/a')
426
def assertPathToKey(self, expected, path):
427
self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
429
def test__path_to_key(self):
430
self.assertPathToKey(([u''], u''), u'')
431
self.assertPathToKey(([u''], u'a'), u'a')
432
self.assertPathToKey(([u'a'], u'b'), u'a/b')
433
self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')
436
class FindPreviousPathsTests(TestCaseWithTransport):
439
tree = self.make_branch_and_tree('tree')
440
self.build_tree(['tree/b'])
442
revid1 = tree.commit('first')
443
tree1 = tree.branch.repository.revision_tree(revid1)
445
tree0 = tree.branch.repository.revision_tree(revision.NULL_REVISION)
447
self.assertEqual({'b': None}, find_previous_paths(tree1, tree0, ['b']))
449
def test_find_previous_paths(self):
450
tree = self.make_branch_and_tree('tree')
451
self.build_tree(['tree/b'])
453
revid1 = tree.commit('first')
454
tree1 = tree.branch.repository.revision_tree(revid1)
456
tree.rename_one('b', 'c')
457
self.build_tree(['tree/b'])
459
revid2 = tree.commit('second')
460
tree2 = tree.branch.repository.revision_tree(revid2)
462
self.assertEqual({'c': 'b', 'b': None},
463
find_previous_paths(tree2, tree1, ['b', 'c']))