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,
131
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
132
'require', True, False),
133
('compare', tree2, tree, 'unchanged', 'specific', 'extra',
134
'require', True, True),
135
], RecordingOptimiser.calls)
137
def test_changes_from_with_root(self):
138
"""Ensure the include_root option does what's expected."""
139
wt = self.make_branch_and_tree('.')
140
delta = wt.changes_from(wt.basis_tree())
141
self.assertEqual(len(delta.added), 0)
142
delta = wt.changes_from(wt.basis_tree(), include_root=True)
143
self.assertEqual(len(delta.added), 1)
144
self.assertEqual(delta.added[0][0], '')
146
def test_changes_from_with_require_versioned(self):
147
"""Ensure the require_versioned option does what's expected."""
148
wt = self.make_branch_and_tree('.')
149
self.build_tree(['known_file', 'unknown_file'])
153
errors.PathsNotVersionedError,
154
wt.changes_from, wt.basis_tree(), wt,
155
specific_files=['known_file', 'unknown_file'],
156
require_versioned=True)
158
# we need to pass a known file with an unknown file to get this to
159
# fail when expected.
160
delta = wt.changes_from(wt.basis_tree(),
161
specific_files=['known_file', 'unknown_file'],
162
require_versioned=False)
163
self.assertEqual(len(delta.added), 1)
166
class TestMultiWalker(TestCaseWithTransport):
168
def assertStepOne(self, has_more, path, file_id, iterator):
169
retval = _mod_tree.MultiWalker._step_one(iterator)
171
self.assertIs(None, path)
172
self.assertIs(None, file_id)
173
self.assertEqual((False, None, None), retval)
175
self.assertEqual((has_more, path, file_id),
176
(retval[0], retval[1], retval[2].file_id))
178
def test__step_one_empty(self):
179
tree = self.make_branch_and_tree('empty')
180
repo = tree.branch.repository
181
empty_tree = repo.revision_tree(revision.NULL_REVISION)
183
iterator = empty_tree.iter_entries_by_dir()
184
self.assertStepOne(False, None, None, iterator)
185
self.assertStepOne(False, None, None, iterator)
187
def test__step_one(self):
188
tree = self.make_branch_and_tree('tree')
189
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
190
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
192
iterator = tree.iter_entries_by_dir()
194
self.addCleanup(tree.unlock)
196
root_id = tree.path2id('')
197
self.assertStepOne(True, '', root_id, iterator)
198
self.assertStepOne(True, 'a', b'a-id', iterator)
199
self.assertStepOne(True, 'b', b'b-id', iterator)
200
self.assertStepOne(True, 'b/c', b'c-id', iterator)
201
self.assertStepOne(False, None, None, iterator)
202
self.assertStepOne(False, None, None, iterator)
204
def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
205
exp_other_paths, iterator):
206
"""Check what happens when we step the iterator.
208
:param path: The path for this entry
209
:param file_id: The file_id for this entry
210
:param master_has_node: Does the master tree have this entry?
211
:param exp_other_paths: A list of other_path values.
212
:param iterator: The iterator to step
214
path, file_id, master_ie, other_values = next(iterator)
215
self.assertEqual((exp_path, exp_file_id), (path, file_id),
216
'Master entry did not match')
218
self.assertIsNot(None, master_ie, 'master should have an entry')
220
self.assertIs(None, master_ie, 'master should not have an entry')
221
self.assertEqual(len(exp_other_paths), len(other_values),
222
'Wrong number of other entries')
225
for path, ie in other_values:
226
other_paths.append(path)
228
other_file_ids.append(None)
230
other_file_ids.append(ie.file_id)
233
for path in exp_other_paths:
235
exp_file_ids.append(None)
237
exp_file_ids.append(file_id)
238
self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
239
self.assertEqual(exp_file_ids, other_file_ids,
240
"Other file_ids incorrect")
242
def lock_and_get_basis_and_root_id(self, tree):
244
self.addCleanup(tree.unlock)
245
basis_tree = tree.basis_tree()
246
basis_tree.lock_read()
247
self.addCleanup(basis_tree.unlock)
248
root_id = tree.path2id('')
249
return basis_tree, root_id
251
def test_simple_stepping(self):
252
tree = self.make_branch_and_tree('tree')
253
self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
254
tree.add(['a', 'b', 'b/c'], [b'a-id', b'b-id', b'c-id'])
256
tree.commit('first', rev_id=b'first-rev-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', b'a-id', True, [u'a'], iterator)
264
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
265
self.assertWalkerNext(u'b/c', b'c-id', True, [u'b/c'], iterator)
266
self.assertRaises(StopIteration, next, iterator)
268
def test_master_has_extra(self):
269
tree = self.make_branch_and_tree('tree')
270
self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
271
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
273
tree.commit('first', rev_id=b'first-rev-id')
275
tree.add(['c'], [b'c-id'])
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', b'a-id', True, [u'a'], iterator)
282
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
283
self.assertWalkerNext(u'c', b'c-id', True, [None], iterator)
284
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
285
self.assertRaises(StopIteration, next, iterator)
287
def test_master_renamed_to_earlier(self):
288
"""The record is still present, it just shows up early."""
289
tree = self.make_branch_and_tree('tree')
290
self.build_tree(['tree/a', 'tree/c', 'tree/d'])
291
tree.add(['a', 'c', 'd'], [b'a-id', b'c-id', b'd-id'])
292
tree.commit('first', rev_id=b'first-rev-id')
293
tree.rename_one('d', 'b')
295
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
297
walker = _mod_tree.MultiWalker(tree, [basis_tree])
298
iterator = walker.iter_all()
299
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
300
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
301
self.assertWalkerNext(u'b', b'd-id', True, [u'd'], iterator)
302
self.assertWalkerNext(u'c', b'c-id', True, [u'c'], iterator)
303
self.assertRaises(StopIteration, next, iterator)
305
def test_master_renamed_to_later(self):
306
tree = self.make_branch_and_tree('tree')
307
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
308
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
309
tree.commit('first', rev_id=b'first-rev-id')
310
tree.rename_one('b', 'e')
312
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
314
walker = _mod_tree.MultiWalker(tree, [basis_tree])
315
iterator = walker.iter_all()
316
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
317
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
318
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
319
self.assertWalkerNext(u'e', b'b-id', True, [u'b'], iterator)
320
self.assertRaises(StopIteration, next, iterator)
322
def test_other_extra_in_middle(self):
323
tree = self.make_branch_and_tree('tree')
324
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
325
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
326
tree.commit('first', rev_id=b'first-rev-id')
329
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
330
walker = _mod_tree.MultiWalker(tree, [basis_tree])
331
iterator = walker.iter_all()
332
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
333
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
334
self.assertWalkerNext(u'd', b'd-id', True, [u'd'], iterator)
335
self.assertWalkerNext(u'b', b'b-id', False, [u'b'], iterator)
336
self.assertRaises(StopIteration, next, iterator)
338
def test_other_extra_at_end(self):
339
tree = self.make_branch_and_tree('tree')
340
self.build_tree(['tree/a', 'tree/b', 'tree/d'])
341
tree.add(['a', 'b', 'd'], [b'a-id', b'b-id', b'd-id'])
342
tree.commit('first', rev_id=b'first-rev-id')
345
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
346
walker = _mod_tree.MultiWalker(tree, [basis_tree])
347
iterator = walker.iter_all()
348
self.assertWalkerNext(u'', root_id, True, [u''], iterator)
349
self.assertWalkerNext(u'a', b'a-id', True, [u'a'], iterator)
350
self.assertWalkerNext(u'b', b'b-id', True, [u'b'], iterator)
351
self.assertWalkerNext(u'd', b'd-id', False, [u'd'], iterator)
352
self.assertRaises(StopIteration, next, iterator)
354
def test_others_extra_at_end(self):
355
tree = self.make_branch_and_tree('tree')
356
self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
357
tree.add(['a', 'b', 'c', 'd', 'e'],
358
[b'a-id', b'b-id', b'c-id', b'd-id', b'e-id'])
359
tree.commit('first', rev_id=b'first-rev-id')
361
tree.commit('second', rev_id=b'second-rev-id')
363
tree.commit('third', rev_id=b'third-rev-id')
366
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
367
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
368
second_tree = tree.branch.repository.revision_tree(b'second-rev-id')
369
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
371
iterator = walker.iter_all()
372
self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
373
self.assertWalkerNext(u'a', b'a-id', True,
374
[u'a', u'a', u'a'], iterator)
375
self.assertWalkerNext(u'b', b'b-id', True,
376
[u'b', u'b', u'b'], iterator)
377
self.assertWalkerNext(u'c', b'c-id', False,
378
[u'c', u'c', u'c'], iterator)
379
self.assertWalkerNext(u'd', b'd-id', False,
380
[None, u'd', u'd'], iterator)
381
self.assertWalkerNext(u'e', b'e-id', False,
382
[None, u'e', None], iterator)
383
self.assertRaises(StopIteration, next, iterator)
385
def test_different_file_id_in_others(self):
386
tree = self.make_branch_and_tree('tree')
387
self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
388
tree.add(['a', 'b', 'c'], [b'a-id', b'b-id', b'c-id'])
389
tree.commit('first', rev_id=b'first-rev-id')
391
tree.rename_one('b', 'c/d')
392
self.build_tree(['tree/b'])
393
tree.add(['b'], [b'b2-id'])
394
tree.commit('second', rev_id=b'second-rev-id')
396
tree.rename_one('a', 'c/e')
397
self.build_tree(['tree/a'])
398
tree.add(['a'], [b'a2-id'])
400
basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
401
first_tree = tree.branch.repository.revision_tree(b'first-rev-id')
402
walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
404
iterator = walker.iter_all()
405
self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
406
self.assertWalkerNext(u'a', b'a2-id', True, [None, None], iterator)
407
self.assertWalkerNext(u'b', b'b2-id', True, [u'b', None], iterator)
408
self.assertWalkerNext(u'c', b'c-id', True, [u'c', u'c'], iterator)
409
self.assertWalkerNext(u'c/d', b'b-id', True, [u'c/d', u'b'], iterator)
410
self.assertWalkerNext(u'c/e', b'a-id', True, [u'a', u'a'], iterator)
411
self.assertRaises(StopIteration, next, iterator)
413
def assertLtByDirblock(self, lt_val, path1, path2):
415
lt_val, _mod_tree.MultiWalker._lt_path_by_dirblock(path1, path2))
417
def test__lt_path_by_dirblock(self):
418
# We only support Unicode strings at this point
420
TypeError, _mod_tree.MultiWalker._lt_path_by_dirblock, b'', b'b')
421
self.assertLtByDirblock(False, u'', u'')
422
self.assertLtByDirblock(False, u'a', u'a')
423
self.assertLtByDirblock(False, u'a/b', u'a/b')
424
self.assertLtByDirblock(False, u'a/b/c', u'a/b/c')
425
self.assertLtByDirblock(False, u'a-a', u'a')
426
self.assertLtByDirblock(True, u'a-a', u'a/a')
427
self.assertLtByDirblock(True, u'a=a', u'a/a')
428
self.assertLtByDirblock(False, u'a-a/a', u'a/a')
429
self.assertLtByDirblock(False, u'a=a/a', u'a/a')
430
self.assertLtByDirblock(False, u'a-a/a', u'a/a/a')
431
self.assertLtByDirblock(False, u'a=a/a', u'a/a/a')
432
self.assertLtByDirblock(False, u'a-a/a/a', u'a/a/a')
433
self.assertLtByDirblock(False, u'a=a/a/a', u'a/a/a')
435
def assertPathToKey(self, expected, path):
436
self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
438
def test__path_to_key(self):
439
self.assertPathToKey(([u''], u''), u'')
440
self.assertPathToKey(([u''], u'a'), u'a')
441
self.assertPathToKey(([u'a'], u'b'), u'a/b')
442
self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')
445
class FindPreviousPathsTests(TestCaseWithTransport):
448
tree = self.make_branch_and_tree('tree')
449
self.build_tree(['tree/b'])
451
revid1 = tree.commit('first')
452
tree1 = tree.branch.repository.revision_tree(revid1)
454
tree0 = tree.branch.repository.revision_tree(revision.NULL_REVISION)
456
self.assertEqual({'b': None}, find_previous_paths(tree1, tree0, ['b']))
458
def test_find_previous_paths(self):
459
tree = self.make_branch_and_tree('tree')
460
self.build_tree(['tree/b'])
462
revid1 = tree.commit('first')
463
tree1 = tree.branch.repository.revision_tree(revid1)
465
tree.rename_one('b', 'c')
466
self.build_tree(['tree/b'])
468
revid2 = tree.commit('second')
469
tree2 = tree.branch.repository.revision_tree(revid2)
471
self.assertEqual({'c': 'b', 'b': None},
472
find_previous_paths(tree2, tree1, ['b', 'c']))
475
class GetCanonicalPath(TestCaseWithTransport):
477
def test_existing_case(self):
478
# Test that we can find a file from a path with different case
479
tree = self.make_branch_and_tree('tree')
480
self.build_tree(['tree/b'])
484
get_canonical_path(tree, 'b', lambda x: x.lower()))
487
get_canonical_path(tree, 'B', lambda x: x.lower()))
489
def test_nonexistant_preserves_case(self):
490
tree = self.make_branch_and_tree('tree')
493
get_canonical_path(tree, 'b', lambda x: x.lower()))
496
get_canonical_path(tree, 'B', lambda x: x.lower()))
498
def test_in_directory_with_case(self):
499
tree = self.make_branch_and_tree('tree')
500
self.build_tree(['tree/a/', 'tree/a/b'])
501
tree.add(['a', 'a/b'])
504
get_canonical_path(tree, 'a/b', lambda x: x.lower()))
507
get_canonical_path(tree, 'A/B', lambda x: x.lower()))
510
get_canonical_path(tree, 'A/b', lambda x: x.lower()))
513
get_canonical_path(tree, 'A/C', lambda x: x.lower()))