1
# Copyright (C) 2005, 2006, 2007 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
27
from bzrlib.inventory import (CHKInventory, Inventory, ROOT_ID, InventoryFile,
28
InventoryDirectory, InventoryEntry, TreeReference)
29
from bzrlib.tests import (
31
TestCaseWithTransport,
34
split_suite_by_condition,
36
from bzrlib.tests.per_workingtree import workingtree_formats
39
def load_tests(standard_tests, module, loader):
40
"""Parameterise some inventory tests."""
41
to_adapt, result = split_suite_by_condition(standard_tests,
42
condition_isinstance(TestDeltaApplication))
44
('Inventory', {'apply_delta':apply_inventory_Inventory}),
46
# Working tree basis delta application
47
# Repository add_inv_by_delta.
48
# Reduce form of the per_repository test logic - that logic needs to be
49
# be able to get /just/ repositories whereas these tests are fine with
50
# just creating trees.
52
for _, format in repository.format_registry.iteritems():
53
scenarios.append((str(format.__name__), {
54
'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
56
for format in workingtree_formats():
58
(str(format.__class__.__name__) + ".update_basis_by_delta", {
59
'apply_delta':apply_inventory_WT_basis,
62
(str(format.__class__.__name__) + ".apply_inventory_delta", {
63
'apply_delta':apply_inventory_WT,
65
return multiply_tests(to_adapt, scenarios, result)
68
def apply_inventory_Inventory(self, basis, delta):
69
"""Apply delta to basis and return the result.
71
:param basis: An inventory to be used as the basis.
72
:param delta: The inventory delta to apply:
73
:return: An inventory resulting from the application.
75
basis.apply_delta(delta)
79
def apply_inventory_WT(self, basis, delta):
80
"""Apply delta to basis and return the result.
82
This sets the tree state to be basis, and then calls apply_inventory_delta.
84
:param basis: An inventory to be used as the basis.
85
:param delta: The inventory delta to apply:
86
:return: An inventory resulting from the application.
88
control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
89
control.create_repository()
90
control.create_branch()
91
tree = self.format.initialize(control)
94
tree._write_inventory(basis)
97
# Fresh object, reads disk again.
98
tree = tree.bzrdir.open_workingtree()
101
tree.apply_inventory_delta(delta)
104
# reload tree - ensure we get what was written.
105
tree = tree.bzrdir.open_workingtree()
107
self.addCleanup(tree.unlock)
108
# One could add 'tree._validate' here but that would cause 'early' failues
109
# as far as higher level code is concerned. Possibly adding an
110
# expect_fail parameter to this function and if that is False then do a
112
return tree.inventory
115
def apply_inventory_WT_basis(self, basis, delta):
116
"""Apply delta to basis and return the result.
118
This sets the parent and then calls update_basis_by_delta.
119
It also puts the basis in the repository under both 'basis' and 'result' to
120
allow safety checks made by the WT to succeed, and finally ensures that all
121
items in the delta with a new path are present in the WT before calling
122
update_basis_by_delta.
124
:param basis: An inventory to be used as the basis.
125
:param delta: The inventory delta to apply:
126
:return: An inventory resulting from the application.
128
control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
129
control.create_repository()
130
control.create_branch()
131
tree = self.format.initialize(control)
134
repo = tree.branch.repository
135
repo.start_write_group()
137
rev = revision.Revision('basis', timestamp=0, timezone=None,
138
message="", committer="foo@example.com")
139
basis.revision_id = 'basis'
140
repo.add_revision('basis', rev, basis)
141
# Add a revision for the result, with the basis content -
142
# update_basis_by_delta doesn't check that the delta results in
143
# result, and we want inconsistent deltas to get called on the
144
# tree, or else the code isn't actually checked.
145
rev = revision.Revision('result', timestamp=0, timezone=None,
146
message="", committer="foo@example.com")
147
basis.revision_id = 'result'
148
repo.add_revision('result', rev, basis)
150
repo.abort_write_group()
153
repo.commit_write_group()
154
# Set the basis state as the trees current state
155
tree._write_inventory(basis)
156
# This reads basis from the repo and puts it into the tree's local
157
# cache, if it has one.
158
tree.set_parent_ids(['basis'])
161
for old, new, id, entry in delta:
162
if None in (new, entry):
164
paths[new] = (entry.file_id, entry.kind)
165
parents.add(osutils.dirname(new))
166
parents = osutils.minimum_path_selection(parents)
168
# Put place holders in the tree to permit adding the other entries.
169
for pos, parent in enumerate(parents):
170
if not tree.path2id(parent):
171
# add a synthetic directory in the tree so we can can put the
172
# tree0 entries in place for dirstate.
173
tree.add([parent], ["id%d" % pos], ["directory"])
175
# Many deltas may cause this mini-apply to fail, but we want to see what
176
# the delta application code says, not the prep that we do to deal with
177
# limitations of dirstate's update_basis code.
178
for path, (file_id, kind) in sorted(paths.items()):
180
tree.add([path], [file_id], [kind])
181
except (KeyboardInterrupt, SystemExit):
187
# Fresh lock, reads disk again.
190
tree.update_basis_by_delta('result', delta)
193
# reload tree - ensure we get what was written.
194
tree = tree.bzrdir.open_workingtree()
195
basis_tree = tree.basis_tree()
196
basis_tree.lock_read()
197
self.addCleanup(basis_tree.unlock)
198
# Note, that if the tree does not have a local cache, the trick above of
199
# setting the result as the basis, will come back to bite us. That said,
200
# all the implementations in bzr do have a local cache.
201
return basis_tree.inventory
204
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
205
"""Apply delta to basis and return the result.
207
This inserts basis as a whole inventory and then uses
208
add_inventory_by_delta to add delta.
210
:param basis: An inventory to be used as the basis.
211
:param delta: The inventory delta to apply:
212
:return: An inventory resulting from the application.
214
format = self.format()
215
control = self.make_bzrdir('tree', format=format._matchingbzrdir)
216
repo = format.initialize(control)
219
repo.start_write_group()
221
rev = revision.Revision('basis', timestamp=0, timezone=None,
222
message="", committer="foo@example.com")
223
basis.revision_id = 'basis'
224
repo.add_revision('basis', rev, basis)
226
repo.abort_write_group()
229
repo.commit_write_group()
234
repo.start_write_group()
236
inv_sha1 = repo.add_inventory_by_delta('basis', delta,
239
repo.abort_write_group()
242
repo.commit_write_group()
245
# Fresh lock, reads disk again.
246
repo = repo.bzrdir.open_repository()
248
self.addCleanup(repo.unlock)
249
return repo.get_inventory('result')
252
class TestDeltaApplication(TestCaseWithTransport):
254
def get_empty_inventory(self, reference_inv=None):
255
"""Get an empty inventory.
257
Note that tests should not depend on the revision of the root for
258
setting up test conditions, as it has to be flexible to accomodate non
259
rich root repositories.
261
:param reference_inv: If not None, get the revision for the root from
262
this inventory. This is useful for dealing with older repositories
263
that routinely discarded the root entry data. If None, the root's
264
revision is set to 'basis'.
266
inv = inventory.Inventory()
267
if reference_inv is not None:
268
inv.root.revision = reference_inv.root.revision
270
inv.root.revision = 'basis'
273
def test_empty_delta(self):
274
inv = self.get_empty_inventory()
276
inv = self.apply_delta(self, inv, delta)
277
inv2 = self.get_empty_inventory(inv)
278
self.assertEqual([], inv2._make_delta(inv))
280
def test_repeated_file_id(self):
281
inv = self.get_empty_inventory()
282
file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
283
file1.revision = 'result'
286
file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
287
file2.revision = 'result'
290
delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
291
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
294
def test_repeated_new_path(self):
295
inv = self.get_empty_inventory()
296
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
297
file1.revision = 'result'
300
file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
301
file2.revision = 'result'
304
delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
305
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
308
def test_repeated_old_path(self):
309
inv = self.get_empty_inventory()
310
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
311
file1.revision = 'result'
314
# We can't *create* a source inventory with the same path, but
315
# a badly generated partial delta might claim the same source twice.
316
# This would be buggy in two ways: the path is repeated in the delta,
317
# And the path for one of the file ids doesn't match the source
318
# location. Alternatively, we could have a repeated fileid, but that
319
# is separately checked for.
320
file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
321
file2.revision = 'result'
326
delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
327
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
330
def test_mismatched_id_entry_id(self):
331
inv = self.get_empty_inventory()
332
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
333
file1.revision = 'result'
336
delta = [(None, u'path', 'id', file1)]
337
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
340
def test_mismatched_new_path_entry_None(self):
341
inv = self.get_empty_inventory()
342
delta = [(None, u'path', 'id', None)]
343
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
346
def test_mismatched_new_path_None_entry(self):
347
inv = self.get_empty_inventory()
348
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
349
file1.revision = 'result'
352
delta = [(u"path", None, 'id1', file1)]
353
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
356
def test_parent_is_not_directory(self):
357
inv = self.get_empty_inventory()
358
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
359
file1.revision = 'result'
362
file2 = inventory.InventoryFile('id2', 'path2', 'id1')
363
file2.revision = 'result'
367
delta = [(None, u'path/path2', 'id2', file2)]
368
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
371
def test_parent_is_missing(self):
372
inv = self.get_empty_inventory()
373
file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
374
file2.revision = 'result'
377
delta = [(None, u'path/path2', 'id2', file2)]
378
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
381
def test_new_parent_path_has_wrong_id(self):
382
inv = self.get_empty_inventory()
383
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
384
parent1.revision = 'result'
385
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
386
parent2.revision = 'result'
387
file1 = inventory.InventoryFile('id', 'path', 'p-2')
388
file1.revision = 'result'
393
# This delta claims that file1 is at dir/path, but actually its at
394
# dir2/path if you follow the inventory parent structure.
395
delta = [(None, u'dir/path', 'id', file1)]
396
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
399
def test_old_parent_path_is_wrong(self):
400
inv = self.get_empty_inventory()
401
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
402
parent1.revision = 'result'
403
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
404
parent2.revision = 'result'
405
file1 = inventory.InventoryFile('id', 'path', 'p-2')
406
file1.revision = 'result'
412
# This delta claims that file1 was at dir/path, but actually it was at
413
# dir2/path if you follow the inventory parent structure.
414
delta = [(u'dir/path', None, 'id', None)]
415
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
418
def test_old_parent_path_is_for_other_id(self):
419
inv = self.get_empty_inventory()
420
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
421
parent1.revision = 'result'
422
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
423
parent2.revision = 'result'
424
file1 = inventory.InventoryFile('id', 'path', 'p-2')
425
file1.revision = 'result'
428
file2 = inventory.InventoryFile('id2', 'path', 'p-1')
429
file2.revision = 'result'
436
# This delta claims that file1 was at dir/path, but actually it was at
437
# dir2/path if you follow the inventory parent structure. At dir/path
438
# is another entry we should not delete.
439
delta = [(u'dir/path', None, 'id', None)]
440
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
443
def test_add_existing_id_new_path(self):
444
inv = self.get_empty_inventory()
445
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
446
parent1.revision = 'result'
447
parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
448
parent2.revision = 'result'
450
delta = [(None, u'dir2', 'p-1', parent2)]
451
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
454
def test_add_new_id_existing_path(self):
455
inv = self.get_empty_inventory()
456
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
457
parent1.revision = 'result'
458
parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
459
parent2.revision = 'result'
461
delta = [(None, u'dir1', 'p-2', parent2)]
462
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
465
def test_remove_dir_leaving_dangling_child(self):
466
inv = self.get_empty_inventory()
467
dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
468
dir1.revision = 'result'
469
dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
470
dir2.revision = 'result'
471
dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
472
dir3.revision = 'result'
476
delta = [(u'dir1', None, 'p-1', None),
477
(u'dir1/child2', None, 'p-3', None)]
478
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
482
class TestInventoryEntry(TestCase):
484
def test_file_kind_character(self):
485
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
486
self.assertEqual(file.kind_character(), '')
488
def test_dir_kind_character(self):
489
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
490
self.assertEqual(dir.kind_character(), '/')
492
def test_link_kind_character(self):
493
dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
494
self.assertEqual(dir.kind_character(), '')
496
def test_dir_detect_changes(self):
497
left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
499
left.executable = True
500
left.symlink_target='foo'
501
right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
502
right.text_sha1 = 321
503
right.symlink_target='bar'
504
self.assertEqual((False, False), left.detect_changes(right))
505
self.assertEqual((False, False), right.detect_changes(left))
507
def test_file_detect_changes(self):
508
left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
510
right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
511
right.text_sha1 = 123
512
self.assertEqual((False, False), left.detect_changes(right))
513
self.assertEqual((False, False), right.detect_changes(left))
514
left.executable = True
515
self.assertEqual((False, True), left.detect_changes(right))
516
self.assertEqual((False, True), right.detect_changes(left))
517
right.text_sha1 = 321
518
self.assertEqual((True, True), left.detect_changes(right))
519
self.assertEqual((True, True), right.detect_changes(left))
521
def test_symlink_detect_changes(self):
522
left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
524
left.executable = True
525
left.symlink_target='foo'
526
right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
527
right.text_sha1 = 321
528
right.symlink_target='foo'
529
self.assertEqual((False, False), left.detect_changes(right))
530
self.assertEqual((False, False), right.detect_changes(left))
531
left.symlink_target = 'different'
532
self.assertEqual((True, False), left.detect_changes(right))
533
self.assertEqual((True, False), right.detect_changes(left))
535
def test_file_has_text(self):
536
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
537
self.failUnless(file.has_text())
539
def test_directory_has_text(self):
540
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
541
self.failIf(dir.has_text())
543
def test_link_has_text(self):
544
link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
545
self.failIf(link.has_text())
547
def test_make_entry(self):
548
self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
549
inventory.InventoryFile)
550
self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
551
inventory.InventoryLink)
552
self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
553
inventory.InventoryDirectory)
555
def test_make_entry_non_normalized(self):
556
orig_normalized_filename = osutils.normalized_filename
559
osutils.normalized_filename = osutils._accessible_normalized_filename
560
entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
561
self.assertEqual(u'\xe5', entry.name)
562
self.assertIsInstance(entry, inventory.InventoryFile)
564
osutils.normalized_filename = osutils._inaccessible_normalized_filename
565
self.assertRaises(errors.InvalidNormalization,
566
inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
568
osutils.normalized_filename = orig_normalized_filename
571
class TestDescribeChanges(TestCase):
573
def test_describe_change(self):
574
# we need to test the following change combinations:
580
# renamed/reparented and modified
581
# change kind (perhaps can't be done yet?)
582
# also, merged in combination with all of these?
583
old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
584
old_a.text_sha1 = '123132'
586
new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
587
new_a.text_sha1 = '123132'
590
self.assertChangeDescription('unchanged', old_a, new_a)
593
new_a.text_sha1 = 'abcabc'
594
self.assertChangeDescription('modified', old_a, new_a)
596
self.assertChangeDescription('added', None, new_a)
597
self.assertChangeDescription('removed', old_a, None)
598
# perhaps a bit questionable but seems like the most reasonable thing...
599
self.assertChangeDescription('unchanged', None, None)
601
# in this case it's both renamed and modified; show a rename and
603
new_a.name = 'newfilename'
604
self.assertChangeDescription('modified and renamed', old_a, new_a)
606
# reparenting is 'renaming'
607
new_a.name = old_a.name
608
new_a.parent_id = 'somedir-id'
609
self.assertChangeDescription('modified and renamed', old_a, new_a)
611
# reset the content values so its not modified
612
new_a.text_size = old_a.text_size
613
new_a.text_sha1 = old_a.text_sha1
614
new_a.name = old_a.name
616
new_a.name = 'newfilename'
617
self.assertChangeDescription('renamed', old_a, new_a)
619
# reparenting is 'renaming'
620
new_a.name = old_a.name
621
new_a.parent_id = 'somedir-id'
622
self.assertChangeDescription('renamed', old_a, new_a)
624
def assertChangeDescription(self, expected_change, old_ie, new_ie):
625
change = InventoryEntry.describe_change(old_ie, new_ie)
626
self.assertEqual(expected_change, change)
629
class TestCHKInventory(TestCaseWithTransport):
631
def get_chk_bytes(self):
632
# The easiest way to get a CHK store is a development6 repository and
633
# then work with the chk_bytes attribute directly.
634
repo = self.make_repository(".", format="development6-rich-root")
636
self.addCleanup(repo.unlock)
637
repo.start_write_group()
638
self.addCleanup(repo.abort_write_group)
639
return repo.chk_bytes
641
def read_bytes(self, chk_bytes, key):
642
stream = chk_bytes.get_record_stream([key], 'unordered', True)
643
return stream.next().get_bytes_as("fulltext")
645
def test_deserialise_gives_CHKInventory(self):
647
inv.revision_id = "revid"
648
inv.root.revision = "rootrev"
649
chk_bytes = self.get_chk_bytes()
650
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
651
bytes = ''.join(chk_inv.to_lines())
652
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
653
self.assertEqual("revid", new_inv.revision_id)
654
self.assertEqual("directory", new_inv.root.kind)
655
self.assertEqual(inv.root.file_id, new_inv.root.file_id)
656
self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
657
self.assertEqual(inv.root.name, new_inv.root.name)
658
self.assertEqual("rootrev", new_inv.root.revision)
659
self.assertEqual('plain', new_inv._search_key_name)
661
def test_deserialise_wrong_revid(self):
663
inv.revision_id = "revid"
664
inv.root.revision = "rootrev"
665
chk_bytes = self.get_chk_bytes()
666
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
667
bytes = ''.join(chk_inv.to_lines())
668
self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
671
def test_captures_rev_root_byid(self):
673
inv.revision_id = "foo"
674
inv.root.revision = "bar"
675
chk_bytes = self.get_chk_bytes()
676
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
677
lines = chk_inv.to_lines()
680
'revision_id: foo\n',
681
'root_id: TREE_ROOT\n',
682
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
683
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
685
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
686
self.assertEqual('plain', chk_inv._search_key_name)
688
def test_captures_parent_id_basename_index(self):
690
inv.revision_id = "foo"
691
inv.root.revision = "bar"
692
chk_bytes = self.get_chk_bytes()
693
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
694
lines = chk_inv.to_lines()
697
'revision_id: foo\n',
698
'root_id: TREE_ROOT\n',
699
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
700
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
702
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
703
self.assertEqual('plain', chk_inv._search_key_name)
705
def test_captures_search_key_name(self):
707
inv.revision_id = "foo"
708
inv.root.revision = "bar"
709
chk_bytes = self.get_chk_bytes()
710
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
711
search_key_name='hash-16-way')
712
lines = chk_inv.to_lines()
715
'search_key_name: hash-16-way\n',
716
'root_id: TREE_ROOT\n',
717
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
718
'revision_id: foo\n',
719
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
721
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
722
self.assertEqual('hash-16-way', chk_inv._search_key_name)
724
def test_directory_children_on_demand(self):
726
inv.revision_id = "revid"
727
inv.root.revision = "rootrev"
728
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
729
inv["fileid"].revision = "filerev"
730
inv["fileid"].executable = True
731
inv["fileid"].text_sha1 = "ffff"
732
inv["fileid"].text_size = 1
733
chk_bytes = self.get_chk_bytes()
734
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
735
bytes = ''.join(chk_inv.to_lines())
736
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
737
root_entry = new_inv[inv.root.file_id]
738
self.assertEqual(None, root_entry._children)
739
self.assertEqual(['file'], root_entry.children.keys())
740
file_direct = new_inv["fileid"]
741
file_found = root_entry.children['file']
742
self.assertEqual(file_direct.kind, file_found.kind)
743
self.assertEqual(file_direct.file_id, file_found.file_id)
744
self.assertEqual(file_direct.parent_id, file_found.parent_id)
745
self.assertEqual(file_direct.name, file_found.name)
746
self.assertEqual(file_direct.revision, file_found.revision)
747
self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
748
self.assertEqual(file_direct.text_size, file_found.text_size)
749
self.assertEqual(file_direct.executable, file_found.executable)
751
def test_from_inventory_maximum_size(self):
752
# from_inventory supports the maximum_size parameter.
754
inv.revision_id = "revid"
755
inv.root.revision = "rootrev"
756
chk_bytes = self.get_chk_bytes()
757
chk_inv = CHKInventory.from_inventory(chk_bytes, inv, 120)
758
chk_inv.id_to_entry._ensure_root()
759
self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
760
self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
761
p_id_basename = chk_inv.parent_id_basename_to_file_id
762
p_id_basename._ensure_root()
763
self.assertEqual(120, p_id_basename._root_node.maximum_size)
764
self.assertEqual(2, p_id_basename._root_node._key_width)
766
def test___iter__(self):
768
inv.revision_id = "revid"
769
inv.root.revision = "rootrev"
770
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
771
inv["fileid"].revision = "filerev"
772
inv["fileid"].executable = True
773
inv["fileid"].text_sha1 = "ffff"
774
inv["fileid"].text_size = 1
775
chk_bytes = self.get_chk_bytes()
776
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
777
bytes = ''.join(chk_inv.to_lines())
778
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
779
fileids = list(new_inv.__iter__())
781
self.assertEqual([inv.root.file_id, "fileid"], fileids)
783
def test__len__(self):
785
inv.revision_id = "revid"
786
inv.root.revision = "rootrev"
787
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
788
inv["fileid"].revision = "filerev"
789
inv["fileid"].executable = True
790
inv["fileid"].text_sha1 = "ffff"
791
inv["fileid"].text_size = 1
792
chk_bytes = self.get_chk_bytes()
793
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
794
self.assertEqual(2, len(chk_inv))
796
def test___getitem__(self):
798
inv.revision_id = "revid"
799
inv.root.revision = "rootrev"
800
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
801
inv["fileid"].revision = "filerev"
802
inv["fileid"].executable = True
803
inv["fileid"].text_sha1 = "ffff"
804
inv["fileid"].text_size = 1
805
chk_bytes = self.get_chk_bytes()
806
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
807
bytes = ''.join(chk_inv.to_lines())
808
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
809
root_entry = new_inv[inv.root.file_id]
810
file_entry = new_inv["fileid"]
811
self.assertEqual("directory", root_entry.kind)
812
self.assertEqual(inv.root.file_id, root_entry.file_id)
813
self.assertEqual(inv.root.parent_id, root_entry.parent_id)
814
self.assertEqual(inv.root.name, root_entry.name)
815
self.assertEqual("rootrev", root_entry.revision)
816
self.assertEqual("file", file_entry.kind)
817
self.assertEqual("fileid", file_entry.file_id)
818
self.assertEqual(inv.root.file_id, file_entry.parent_id)
819
self.assertEqual("file", file_entry.name)
820
self.assertEqual("filerev", file_entry.revision)
821
self.assertEqual("ffff", file_entry.text_sha1)
822
self.assertEqual(1, file_entry.text_size)
823
self.assertEqual(True, file_entry.executable)
824
self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
826
def test_has_id_true(self):
828
inv.revision_id = "revid"
829
inv.root.revision = "rootrev"
830
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
831
inv["fileid"].revision = "filerev"
832
inv["fileid"].executable = True
833
inv["fileid"].text_sha1 = "ffff"
834
inv["fileid"].text_size = 1
835
chk_bytes = self.get_chk_bytes()
836
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
837
self.assertTrue(chk_inv.has_id('fileid'))
838
self.assertTrue(chk_inv.has_id(inv.root.file_id))
840
def test_has_id_not(self):
842
inv.revision_id = "revid"
843
inv.root.revision = "rootrev"
844
chk_bytes = self.get_chk_bytes()
845
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
846
self.assertFalse(chk_inv.has_id('fileid'))
848
def test_id2path(self):
850
inv.revision_id = "revid"
851
inv.root.revision = "rootrev"
852
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
853
fileentry = InventoryFile("fileid", "file", "dirid")
856
inv["fileid"].revision = "filerev"
857
inv["fileid"].executable = True
858
inv["fileid"].text_sha1 = "ffff"
859
inv["fileid"].text_size = 1
860
inv["dirid"].revision = "filerev"
861
chk_bytes = self.get_chk_bytes()
862
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
863
bytes = ''.join(chk_inv.to_lines())
864
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
865
self.assertEqual('', new_inv.id2path(inv.root.file_id))
866
self.assertEqual('dir', new_inv.id2path('dirid'))
867
self.assertEqual('dir/file', new_inv.id2path('fileid'))
869
def test_path2id(self):
871
inv.revision_id = "revid"
872
inv.root.revision = "rootrev"
873
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
874
fileentry = InventoryFile("fileid", "file", "dirid")
877
inv["fileid"].revision = "filerev"
878
inv["fileid"].executable = True
879
inv["fileid"].text_sha1 = "ffff"
880
inv["fileid"].text_size = 1
881
inv["dirid"].revision = "filerev"
882
chk_bytes = self.get_chk_bytes()
883
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
884
bytes = ''.join(chk_inv.to_lines())
885
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
886
self.assertEqual(inv.root.file_id, new_inv.path2id(''))
887
self.assertEqual('dirid', new_inv.path2id('dir'))
888
self.assertEqual('fileid', new_inv.path2id('dir/file'))
890
def test_create_by_apply_delta_sets_root(self):
892
inv.revision_id = "revid"
893
chk_bytes = self.get_chk_bytes()
894
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
895
inv.add_path("", "directory", "myrootid", None)
896
inv.revision_id = "expectedid"
897
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
898
delta = [(None, "", "myrootid", inv.root)]
899
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
900
self.assertEquals(reference_inv.root, new_inv.root)
902
def test_create_by_apply_delta_empty_add_child(self):
904
inv.revision_id = "revid"
905
inv.root.revision = "rootrev"
906
chk_bytes = self.get_chk_bytes()
907
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
908
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
909
a_entry.revision = "filerev"
910
a_entry.executable = True
911
a_entry.text_sha1 = "ffff"
912
a_entry.text_size = 1
914
inv.revision_id = "expectedid"
915
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
916
delta = [(None, "A", "A-id", a_entry)]
917
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
918
# new_inv should be the same as reference_inv.
919
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
920
self.assertEqual(reference_inv.root_id, new_inv.root_id)
921
reference_inv.id_to_entry._ensure_root()
922
new_inv.id_to_entry._ensure_root()
923
self.assertEqual(reference_inv.id_to_entry._root_node._key,
924
new_inv.id_to_entry._root_node._key)
926
def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
928
inv.revision_id = "revid"
929
inv.root.revision = "rootrev"
930
chk_bytes = self.get_chk_bytes()
931
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
932
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
933
a_entry.revision = "filerev"
934
a_entry.executable = True
935
a_entry.text_sha1 = "ffff"
936
a_entry.text_size = 1
938
inv.revision_id = "expectedid"
939
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
940
delta = [(None, "A", "A-id", a_entry)]
941
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
942
reference_inv.id_to_entry._ensure_root()
943
reference_inv.parent_id_basename_to_file_id._ensure_root()
944
new_inv.id_to_entry._ensure_root()
945
new_inv.parent_id_basename_to_file_id._ensure_root()
946
# new_inv should be the same as reference_inv.
947
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
948
self.assertEqual(reference_inv.root_id, new_inv.root_id)
949
self.assertEqual(reference_inv.id_to_entry._root_node._key,
950
new_inv.id_to_entry._root_node._key)
951
self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
952
new_inv.parent_id_basename_to_file_id._root_node._key)
954
def test_iter_changes(self):
955
# Low level bootstrapping smoke test; comprehensive generic tests via
956
# InterTree are coming.
958
inv.revision_id = "revid"
959
inv.root.revision = "rootrev"
960
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
961
inv["fileid"].revision = "filerev"
962
inv["fileid"].executable = True
963
inv["fileid"].text_sha1 = "ffff"
964
inv["fileid"].text_size = 1
966
inv2.revision_id = "revid2"
967
inv2.root.revision = "rootrev"
968
inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
969
inv2["fileid"].revision = "filerev2"
970
inv2["fileid"].executable = False
971
inv2["fileid"].text_sha1 = "bbbb"
972
inv2["fileid"].text_size = 2
974
chk_bytes = self.get_chk_bytes()
975
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
976
bytes = ''.join(chk_inv.to_lines())
977
inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
978
chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
979
bytes = ''.join(chk_inv2.to_lines())
980
inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
981
self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
982
('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
984
list(inv_1.iter_changes(inv_2)))
986
def test_parent_id_basename_to_file_id_index_enabled(self):
988
inv.revision_id = "revid"
989
inv.root.revision = "rootrev"
990
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
991
inv["fileid"].revision = "filerev"
992
inv["fileid"].executable = True
993
inv["fileid"].text_sha1 = "ffff"
994
inv["fileid"].text_size = 1
996
chk_bytes = self.get_chk_bytes()
997
tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
998
bytes = ''.join(tmp_inv.to_lines())
999
chk_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1000
self.assertIsInstance(chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
1002
{('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
1003
dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
1005
def test_file_entry_to_bytes(self):
1006
inv = CHKInventory(None)
1007
ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
1008
ie.executable = True
1009
ie.revision = 'file-rev-id'
1010
ie.text_sha1 = 'abcdefgh'
1012
bytes = inv._entry_to_bytes(ie)
1013
self.assertEqual('file: file-id\nparent-id\nfilename\n'
1014
'file-rev-id\nabcdefgh\n100\nY', bytes)
1015
ie2 = inv._bytes_to_entry(bytes)
1016
self.assertEqual(ie, ie2)
1017
self.assertIsInstance(ie2.name, unicode)
1018
self.assertEqual(('filename', 'file-id', 'file-rev-id'),
1019
inv._bytes_to_utf8name_key(bytes))
1021
def test_file2_entry_to_bytes(self):
1022
inv = CHKInventory(None)
1024
ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
1025
ie.executable = False
1026
ie.revision = 'file-rev-id'
1027
ie.text_sha1 = '123456'
1029
bytes = inv._entry_to_bytes(ie)
1030
self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
1031
'file-rev-id\n123456\n25\nN', bytes)
1032
ie2 = inv._bytes_to_entry(bytes)
1033
self.assertEqual(ie, ie2)
1034
self.assertIsInstance(ie2.name, unicode)
1035
self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
1036
inv._bytes_to_utf8name_key(bytes))
1038
def test_dir_entry_to_bytes(self):
1039
inv = CHKInventory(None)
1040
ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
1041
ie.revision = 'dir-rev-id'
1042
bytes = inv._entry_to_bytes(ie)
1043
self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1044
ie2 = inv._bytes_to_entry(bytes)
1045
self.assertEqual(ie, ie2)
1046
self.assertIsInstance(ie2.name, unicode)
1047
self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
1048
inv._bytes_to_utf8name_key(bytes))
1050
def test_dir2_entry_to_bytes(self):
1051
inv = CHKInventory(None)
1052
ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1054
ie.revision = 'dir-rev-id'
1055
bytes = inv._entry_to_bytes(ie)
1056
self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
1057
'dir-rev-id', bytes)
1058
ie2 = inv._bytes_to_entry(bytes)
1059
self.assertEqual(ie, ie2)
1060
self.assertIsInstance(ie2.name, unicode)
1061
self.assertIs(ie2.parent_id, None)
1062
self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
1063
inv._bytes_to_utf8name_key(bytes))
1065
def test_symlink_entry_to_bytes(self):
1066
inv = CHKInventory(None)
1067
ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
1068
ie.revision = 'link-rev-id'
1069
ie.symlink_target = u'target/path'
1070
bytes = inv._entry_to_bytes(ie)
1071
self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
1072
'link-rev-id\ntarget/path', bytes)
1073
ie2 = inv._bytes_to_entry(bytes)
1074
self.assertEqual(ie, ie2)
1075
self.assertIsInstance(ie2.name, unicode)
1076
self.assertIsInstance(ie2.symlink_target, unicode)
1077
self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
1078
inv._bytes_to_utf8name_key(bytes))
1080
def test_symlink2_entry_to_bytes(self):
1081
inv = CHKInventory(None)
1082
ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
1083
ie.revision = 'link-rev-id'
1084
ie.symlink_target = u'target/\u03a9path'
1085
bytes = inv._entry_to_bytes(ie)
1086
self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1087
'link-rev-id\ntarget/\xce\xa9path', bytes)
1088
ie2 = inv._bytes_to_entry(bytes)
1089
self.assertEqual(ie, ie2)
1090
self.assertIsInstance(ie2.name, unicode)
1091
self.assertIsInstance(ie2.symlink_target, unicode)
1092
self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
1093
inv._bytes_to_utf8name_key(bytes))
1095
def test_tree_reference_entry_to_bytes(self):
1096
inv = CHKInventory(None)
1097
ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1099
ie.revision = 'tree-rev-id'
1100
ie.reference_revision = 'ref-rev-id'
1101
bytes = inv._entry_to_bytes(ie)
1102
self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1103
'tree-rev-id\nref-rev-id', bytes)
1104
ie2 = inv._bytes_to_entry(bytes)
1105
self.assertEqual(ie, ie2)
1106
self.assertIsInstance(ie2.name, unicode)
1107
self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1108
inv._bytes_to_utf8name_key(bytes))