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,
36
class TestErrors(TestCase):
38
def test_file_timestamp_unavailable(self):
39
e = FileTimestampUnavailable("/path/foo")
40
self.assertEqual("The filestamp for /path/foo is not available.",
44
class TestInterTree(TestCaseWithTransport):
46
def test_revision_tree_revision_tree(self):
47
# we should have an InterTree registered for RevisionTree to
49
tree = self.make_branch_and_tree('.')
50
rev_id = tree.commit('first post')
51
rev_id2 = tree.commit('second post', allow_pointless=True)
52
rev_tree = tree.branch.repository.revision_tree(rev_id)
53
rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
54
optimiser = InterTree.get(rev_tree, rev_tree2)
55
self.assertIsInstance(optimiser, InterTree)
56
optimiser = InterTree.get(rev_tree2, rev_tree)
57
self.assertIsInstance(optimiser, InterTree)
59
def test_working_tree_revision_tree(self):
60
# we should have an InterTree available for WorkingTree to
62
tree = self.make_branch_and_tree('.')
63
rev_id = tree.commit('first post')
64
rev_tree = tree.branch.repository.revision_tree(rev_id)
65
optimiser = InterTree.get(rev_tree, tree)
66
self.assertIsInstance(optimiser, InterTree)
67
optimiser = InterTree.get(tree, rev_tree)
68
self.assertIsInstance(optimiser, InterTree)
70
def test_working_tree_working_tree(self):
71
# we should have an InterTree available for WorkingTree to
73
tree = self.make_branch_and_tree('1')
74
tree2 = self.make_branch_and_tree('2')
75
optimiser = InterTree.get(tree, tree2)
76
self.assertIsInstance(optimiser, InterTree)
77
optimiser = InterTree.get(tree2, tree)
78
self.assertIsInstance(optimiser, InterTree)
81
class RecordingOptimiser(InterTree):
85
def compare(self, want_unchanged=False, specific_files=None,
86
extra_trees=None, require_versioned=False, include_root=False,
87
want_unversioned=False):
89
('compare', self.source, self.target, want_unchanged,
90
specific_files, extra_trees, require_versioned,
91
include_root, want_unversioned)
95
def is_compatible(klass, source, target):
99
class TestTree(TestCaseWithTransport):
101
def test_compare_calls_InterTree_compare(self):
102
"""This test tests the way Tree.compare() uses InterTree."""
103
old_optimisers = InterTree._optimisers
105
InterTree._optimisers = []
106
RecordingOptimiser.calls = []
107
InterTree.register_optimiser(RecordingOptimiser)
108
tree = self.make_branch_and_tree('1')
109
tree2 = self.make_branch_and_tree('2')
110
# do a series of calls:
112
tree.changes_from(tree2)
113
# pass in all optional arguments by position
114
tree.changes_from(tree2, 'unchanged', 'specific', 'extra',
116
# pass in all optional arguments by keyword
117
tree.changes_from(tree2,
118
specific_files='specific',
119
want_unchanged='unchanged',
121
require_versioned='require',
123
want_unversioned=True,
126
InterTree._optimisers = old_optimisers
129
('compare', tree2, tree, False, None, None, False, False, False),
130
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
131
'require', True, False),
132
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
133
'require', True, True),
134
], RecordingOptimiser.calls)
136
def test_changes_from_with_root(self):
137
"""Ensure the include_root option does what's expected."""
138
wt = self.make_branch_and_tree('.')
139
delta = wt.changes_from(wt.basis_tree())
140
self.assertEqual(len(delta.added), 0)
141
delta = wt.changes_from(wt.basis_tree(), include_root=True)
142
self.assertEqual(len(delta.added), 1)
143
self.assertEqual(delta.added[0][0], '')
145
def test_changes_from_with_require_versioned(self):
146
"""Ensure the require_versioned option does what's expected."""
147
wt = self.make_branch_and_tree('.')
148
self.build_tree(['known_file', 'unknown_file'])
151
self.assertRaises(errors.PathsNotVersionedError,
152
wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
153
'unknown_file'], require_versioned=True)
155
# we need to pass a known file with an unknown file to get this to
156
# fail when expected.
157
delta = wt.changes_from(wt.basis_tree(),
158
specific_files=['known_file', 'unknown_file'],
159
require_versioned=False)
160
self.assertEqual(len(delta.added), 1)
163
class TestMultiWalker(TestCaseWithTransport):
165
def assertStepOne(self, has_more, path, file_id, iterator):
166
retval = _mod_tree.MultiWalker._step_one(iterator)
168
self.assertIs(None, path)
169
self.assertIs(None, file_id)
170
self.assertEqual((False, None, None), retval)
172
self.assertEqual((has_more, path, file_id),
173
(retval[0], retval[1], retval[2].file_id))
175
def test__step_one_empty(self):
176
tree = self.make_branch_and_tree('empty')
177
repo = tree.branch.repository
178
empty_tree = repo.revision_tree(revision.NULL_REVISION)
180
iterator = empty_tree.iter_entries_by_dir()
181
self.assertStepOne(False, None, None, iterator)
182
self.assertStepOne(False, None, None, iterator)
184
def test__step_one(self):
185
tree = self.make_branch_and_tree('tree')
186
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
187
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
189
iterator = tree.iter_entries_by_dir()
191
self.addCleanup(tree.unlock)
193
root_id = tree.path2id('')
194
self.assertStepOne(True, '', root_id, iterator)
195
self.assertStepOne(True, 'a', b'a-id', iterator)
196
self.assertStepOne(True, 'b', b'b-id', iterator)
197
self.assertStepOne(True, 'b/c', b'c-id', iterator)
198
self.assertStepOne(False, None, None, iterator)
199
self.assertStepOne(False, None, None, iterator)
201
def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
202
exp_other_paths, iterator):
203
"""Check what happens when we step the iterator.
205
:param path: The path for this entry
206
:param file_id: The file_id for this entry
207
:param master_has_node: Does the master tree have this entry?
208
:param exp_other_paths: A list of other_path values.
209
:param iterator: The iterator to step
211
path, file_id, master_ie, other_values = next(iterator)
212
self.assertEqual((exp_path, exp_file_id), (path, file_id),
213
'Master entry did not match')
215
self.assertIsNot(None, master_ie, 'master should have an entry')
217
self.assertIs(None, master_ie, 'master should not have an entry')
218
self.assertEqual(len(exp_other_paths), len(other_values),
219
'Wrong number of other entries')
222
for path, ie in other_values:
223
other_paths.append(path)
225
other_file_ids.append(None)
227
other_file_ids.append(ie.file_id)
230
for path in exp_other_paths:
232
exp_file_ids.append(None)
234
exp_file_ids.append(file_id)
235
self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
236
self.assertEqual(exp_file_ids, other_file_ids,
237
"Other file_ids incorrect")
239
def lock_and_get_basis_and_root_id(self, tree):
241
self.addCleanup(tree.unlock)
242
basis_tree = tree.basis_tree()
243
basis_tree.lock_read()
244
self.addCleanup(basis_tree.unlock)
245
root_id = tree.path2id('')
246
return basis_tree, root_id
248
def test_simple_stepping(self):
249
tree = self.make_branch_and_tree('tree')
250
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
251
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
253
tree.commit('first', rev_id=b'first-rev-id')
255
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
257
walker = _mod_tree.MultiWalker(tree, [basis_tree])
258
iterator = walker.iter_all()
259
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
260
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
261
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
262
self.assertWalkerNext(u'b/c', b'c-id', True, [u'b/c'], iterator)
263
self.assertRaises(StopIteration, next, iterator)
265
def test_master_has_extra(self):
266
tree = self.make_branch_and_tree('tree')
267
self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
268
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
270
tree.commit('first', rev_id=b'first-rev-id')
272
tree.add(['c'], [b'c-id'])
273
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
275
walker = _mod_tree.MultiWalker(tree, [basis_tree])
276
iterator = walker.iter_all()
277
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
278
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
279
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
280
self.assertWalkerNext(u'c', b'c-id', True, [None], iterator)
281
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
282
self.assertRaises(StopIteration, next, iterator)
284
def test_master_renamed_to_earlier(self):
285
"""The record is still present, it just shows up early."""
286
tree = self.make_branch_and_tree('tree')
287
self.build_tree(['tree/a', 'tree/c', 'tree/d'])
288
tree.add(['a', 'c', 'd'], [b'a-id', b'c-id', b'd-id'])
289
tree.commit('first', rev_id=b'first-rev-id')
290
tree.rename_one('d', 'b')
292
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
294
walker = _mod_tree.MultiWalker(tree, [basis_tree])
295
iterator = walker.iter_all()
296
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
297
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
298
self.assertWalkerNext(u'b', b'd-id', True, [u'd'], iterator)
299
self.assertWalkerNext(u'c', b'c-id', True, [u'c'], iterator)
300
self.assertRaises(StopIteration, next, iterator)
302
def test_master_renamed_to_later(self):
303
tree = self.make_branch_and_tree('tree')
304
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
305
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
306
tree.commit('first', rev_id=b'first-rev-id')
307
tree.rename_one('b', 'e')
309
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', b'a-id', True, [u'a'], iterator)
315
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
316
self.assertWalkerNext(u'e', b'b-id', True, [u'b'], iterator)
317
self.assertRaises(StopIteration, next, iterator)
319
def test_other_extra_in_middle(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'], [b'a-id', b'b-id', b'd-id'])
323
tree.commit('first', rev_id=b'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', b'a-id', True, [u'a'], iterator)
331
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
332
self.assertWalkerNext(u'b', b'b-id', False, [u'b'], iterator)
333
self.assertRaises(StopIteration, next, iterator)
335
def test_other_extra_at_end(self):
336
tree = self.make_branch_and_tree('tree')
337
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
338
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
339
tree.commit('first', rev_id=b'first-rev-id')
342
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
343
walker = _mod_tree.MultiWalker(tree, [basis_tree])
344
iterator = walker.iter_all()
345
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
346
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
347
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
348
self.assertWalkerNext(u'd', b'd-id', False, [u'd'], iterator)
349
self.assertRaises(StopIteration, next, iterator)
351
def test_others_extra_at_end(self):
352
tree = self.make_branch_and_tree('tree')
353
self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
354
tree.add(['a', 'b', 'c', 'd', 'e'],
355
[b'a-id', b'b-id', b'c-id', b'd-id', b'e-id'])
356
tree.commit('first', rev_id=b'first-rev-id')
358
tree.commit('second', rev_id=b'second-rev-id')
360
tree.commit('third', rev_id=b'third-rev-id')
363
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
364
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
365
second_tree = tree.branch.repository.revision_tree(b'second-rev-id')
366
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
368
iterator = walker.iter_all()
369
self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
370
self.assertWalkerNext(u'a', b'a-id', True, [u'a', u'a', u'a'], iterator)
371
self.assertWalkerNext(u'b', b'b-id', True, [u'b', u'b', u'b'], iterator)
372
self.assertWalkerNext(u'c', b'c-id', False, [u'c', u'c', u'c'], iterator)
373
self.assertWalkerNext(u'd', b'd-id', False, [None, u'd', u'd'], iterator)
374
self.assertWalkerNext(u'e', b'e-id', False, [None, u'e', None], iterator)
375
self.assertRaises(StopIteration, next, iterator)
377
def test_different_file_id_in_others(self):
378
tree = self.make_branch_and_tree('tree')
379
self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
380
tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id'])
381
tree.commit('first', rev_id=b'first-rev-id')
383
tree.rename_one('b', 'c/d')
384
self.build_tree(['tree/b'])
385
tree.add(['b'], [b'b2-id'])
386
tree.commit('second', rev_id=b'second-rev-id')
388
tree.rename_one('a', 'c/e')
389
self.build_tree(['tree/a'])
390
tree.add(['a'], [b'a2-id'])
392
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
393
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
394
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
396
iterator = walker.iter_all()
397
self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
398
self.assertWalkerNext(u'a', b'a2-id', True, [None, None], iterator)
399
self.assertWalkerNext(u'b', b'b2-id', True, [u'b', None], iterator)
400
self.assertWalkerNext(u'c', b'c-id', True, [u'c', u'c'], iterator)
401
self.assertWalkerNext(u'c/d', b'b-id', True, [u'c/d', u'b'], iterator)
402
self.assertWalkerNext(u'c/e', b'a-id', True, [u'a', u'a'], iterator)
403
self.assertRaises(StopIteration, next, iterator)
405
def assertLtByDirblock(self, lt_val, path1, path2):
406
self.assertEqual(lt_val,
407
_mod_tree.MultiWalker._lt_path_by_dirblock(path1, path2))
409
def test__lt_path_by_dirblock(self):
410
# We only support Unicode strings at this point
411
self.assertRaises(TypeError,
412
_mod_tree.MultiWalker._lt_path_by_dirblock, b'', b'b')
413
self.assertLtByDirblock(False, u'', u'')
414
self.assertLtByDirblock(False, u'a', u'a')
415
self.assertLtByDirblock(False, u'a/b', u'a/b')
416
self.assertLtByDirblock(False, u'a/b/c', u'a/b/c')
417
self.assertLtByDirblock(False, u'a-a', u'a')
418
self.assertLtByDirblock(True, u'a-a', u'a/a')
419
self.assertLtByDirblock(True, u'a=a', u'a/a')
420
self.assertLtByDirblock(False, u'a-a/a', u'a/a')
421
self.assertLtByDirblock(False, u'a=a/a', u'a/a')
422
self.assertLtByDirblock(False, u'a-a/a', u'a/a/a')
423
self.assertLtByDirblock(False, u'a=a/a', u'a/a/a')
424
self.assertLtByDirblock(False, u'a-a/a/a', u'a/a/a')
425
self.assertLtByDirblock(False, u'a=a/a/a', u'a/a/a')
427
def assertPathToKey(self, expected, path):
428
self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
430
def test__path_to_key(self):
431
self.assertPathToKey(([u''], u''), u'')
432
self.assertPathToKey(([u''], u'a'), u'a')
433
self.assertPathToKey(([u'a'], u'b'), u'a/b')
434
self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')
437
class FindPreviousPathsTests(TestCaseWithTransport):
440
tree = self.make_branch_and_tree('tree')
441
self.build_tree(['tree/b'])
443
revid1 = tree.commit('first')
444
tree1 = tree.branch.repository.revision_tree(revid1)
446
tree0 = tree.branch.repository.revision_tree(revision.NULL_REVISION)
448
self.assertEqual({'b': None}, find_previous_paths(tree1, tree0, ['b']))
450
def test_find_previous_paths(self):
451
tree = self.make_branch_and_tree('tree')
452
self.build_tree(['tree/b'])
454
revid1 = tree.commit('first')
455
tree1 = tree.branch.repository.revision_tree(revid1)
457
tree.rename_one('b', 'c')
458
self.build_tree(['tree/b'])
460
revid2 = tree.commit('second')
461
tree2 = tree.branch.repository.revision_tree(revid2)
463
self.assertEqual({'c': 'b', 'b': None},
464
find_previous_paths(tree2, tree1, ['b', 'c']))
467
class GetCanonicalPath(TestCaseWithTransport):
469
def test_existing_case(self):
470
# Test that we can find a file from a path with different case
471
tree = self.make_branch_and_tree('tree')
472
self.build_tree(['tree/b'])
476
get_canonical_path(tree, 'b', lambda x: x.lower()))
479
get_canonical_path(tree, 'B', lambda x: x.lower()))
481
def test_nonexistant_preserves_case(self):
482
tree = self.make_branch_and_tree('tree')
485
get_canonical_path(tree, 'b', lambda x: x.lower()))
488
get_canonical_path(tree, 'B', lambda x: x.lower()))
490
def test_in_directory_with_case(self):
491
tree = self.make_branch_and_tree('tree')
492
self.build_tree(['tree/a/', 'tree/a/b'])
493
tree.add(['a', 'a/b'])
496
get_canonical_path(tree, 'a/b', lambda x: x.lower()))
499
get_canonical_path(tree, 'A/B', lambda x: x.lower()))
502
get_canonical_path(tree, 'A/b', lambda x: x.lower()))
505
get_canonical_path(tree, 'A/C', lambda x: x.lower()))