1
# Copyright (C) 2005-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
29
from bzrlib.inventory import (
37
mutable_inventory_from_tree,
39
from bzrlib.tests import (
41
TestCaseWithTransport,
43
from bzrlib.tests.scenarios import load_tests_apply_scenarios
46
load_tests = load_tests_apply_scenarios
49
def delta_application_scenarios():
51
('Inventory', {'apply_delta':apply_inventory_Inventory}),
53
# Working tree basis delta application
54
# Repository add_inv_by_delta.
55
# Reduce form of the per_repository test logic - that logic needs to be
56
# be able to get /just/ repositories whereas these tests are fine with
57
# just creating trees.
59
for _, format in repository.format_registry.iteritems():
60
if format.supports_full_versioned_files:
61
scenarios.append((str(format.__name__), {
62
'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
64
for format in workingtree.format_registry._get_all():
65
repo_fmt = format._matchingbzrdir.repository_format
66
if not repo_fmt.supports_full_versioned_files:
69
(str(format.__class__.__name__) + ".update_basis_by_delta", {
70
'apply_delta':apply_inventory_WT_basis,
73
(str(format.__class__.__name__) + ".apply_inventory_delta", {
74
'apply_delta':apply_inventory_WT,
79
def create_texts_for_inv(repo, inv):
80
for path, ie in inv.iter_entries():
82
lines = ['a' * ie.text_size]
85
repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
88
def apply_inventory_Inventory(self, basis, delta):
89
"""Apply delta to basis and return the result.
91
:param basis: An inventory to be used as the basis.
92
:param delta: The inventory delta to apply:
93
:return: An inventory resulting from the application.
95
basis.apply_delta(delta)
99
def apply_inventory_WT(self, basis, delta):
100
"""Apply delta to basis and return the result.
102
This sets the tree state to be basis, and then calls apply_inventory_delta.
104
:param basis: An inventory to be used as the basis.
105
:param delta: The inventory delta to apply:
106
:return: An inventory resulting from the application.
108
control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
109
control.create_repository()
110
control.create_branch()
111
tree = self.format.initialize(control)
114
tree._write_inventory(basis)
117
# Fresh object, reads disk again.
118
tree = tree.bzrdir.open_workingtree()
121
tree.apply_inventory_delta(delta)
124
# reload tree - ensure we get what was written.
125
tree = tree.bzrdir.open_workingtree()
127
self.addCleanup(tree.unlock)
128
# One could add 'tree._validate' here but that would cause 'early' failues
129
# as far as higher level code is concerned. Possibly adding an
130
# expect_fail parameter to this function and if that is False then do a
132
return tree.inventory
135
def apply_inventory_WT_basis(self, basis, delta):
136
"""Apply delta to basis and return the result.
138
This sets the parent and then calls update_basis_by_delta.
139
It also puts the basis in the repository under both 'basis' and 'result' to
140
allow safety checks made by the WT to succeed, and finally ensures that all
141
items in the delta with a new path are present in the WT before calling
142
update_basis_by_delta.
144
:param basis: An inventory to be used as the basis.
145
:param delta: The inventory delta to apply:
146
:return: An inventory resulting from the application.
148
control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
149
control.create_repository()
150
control.create_branch()
151
tree = self.format.initialize(control)
154
repo = tree.branch.repository
155
repo.start_write_group()
157
rev = revision.Revision('basis', timestamp=0, timezone=None,
158
message="", committer="foo@example.com")
159
basis.revision_id = 'basis'
160
create_texts_for_inv(tree.branch.repository, basis)
161
repo.add_revision('basis', rev, basis)
162
# Add a revision for the result, with the basis content -
163
# update_basis_by_delta doesn't check that the delta results in
164
# result, and we want inconsistent deltas to get called on the
165
# tree, or else the code isn't actually checked.
166
rev = revision.Revision('result', timestamp=0, timezone=None,
167
message="", committer="foo@example.com")
168
basis.revision_id = 'result'
169
repo.add_revision('result', rev, basis)
170
repo.commit_write_group()
172
repo.abort_write_group()
174
# Set the basis state as the trees current state
175
tree._write_inventory(basis)
176
# This reads basis from the repo and puts it into the tree's local
177
# cache, if it has one.
178
tree.set_parent_ids(['basis'])
181
for old, new, id, entry in delta:
182
if None in (new, entry):
184
paths[new] = (entry.file_id, entry.kind)
185
parents.add(osutils.dirname(new))
186
parents = osutils.minimum_path_selection(parents)
188
# Put place holders in the tree to permit adding the other entries.
189
for pos, parent in enumerate(parents):
190
if not tree.path2id(parent):
191
# add a synthetic directory in the tree so we can can put the
192
# tree0 entries in place for dirstate.
193
tree.add([parent], ["id%d" % pos], ["directory"])
195
# Many deltas may cause this mini-apply to fail, but we want to see what
196
# the delta application code says, not the prep that we do to deal with
197
# limitations of dirstate's update_basis code.
198
for path, (file_id, kind) in sorted(paths.items()):
200
tree.add([path], [file_id], [kind])
201
except (KeyboardInterrupt, SystemExit):
207
# Fresh lock, reads disk again.
210
tree.update_basis_by_delta('result', delta)
213
# reload tree - ensure we get what was written.
214
tree = tree.bzrdir.open_workingtree()
215
basis_tree = tree.basis_tree()
216
basis_tree.lock_read()
217
self.addCleanup(basis_tree.unlock)
218
# Note, that if the tree does not have a local cache, the trick above of
219
# setting the result as the basis, will come back to bite us. That said,
220
# all the implementations in bzr do have a local cache.
221
return basis_tree.inventory
224
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
225
"""Apply delta to basis and return the result.
227
This inserts basis as a whole inventory and then uses
228
add_inventory_by_delta to add delta.
230
:param basis: An inventory to be used as the basis.
231
:param delta: The inventory delta to apply:
232
:return: An inventory resulting from the application.
234
format = self.format()
235
control = self.make_bzrdir('tree', format=format._matchingbzrdir)
236
repo = format.initialize(control)
239
repo.start_write_group()
241
rev = revision.Revision('basis', timestamp=0, timezone=None,
242
message="", committer="foo@example.com")
243
basis.revision_id = 'basis'
244
create_texts_for_inv(repo, basis)
245
repo.add_revision('basis', rev, basis)
246
repo.commit_write_group()
248
repo.abort_write_group()
254
repo.start_write_group()
256
inv_sha1 = repo.add_inventory_by_delta('basis', delta,
259
repo.abort_write_group()
262
repo.commit_write_group()
265
# Fresh lock, reads disk again.
266
repo = repo.bzrdir.open_repository()
268
self.addCleanup(repo.unlock)
269
return repo.get_inventory('result')
272
class TestInventoryUpdates(TestCase):
274
def test_creation_from_root_id(self):
275
# iff a root id is passed to the constructor, a root directory is made
276
inv = inventory.Inventory(root_id='tree-root')
277
self.assertNotEqual(None, inv.root)
278
self.assertEqual('tree-root', inv.root.file_id)
280
def test_add_path_of_root(self):
281
# if no root id is given at creation time, there is no root directory
282
inv = inventory.Inventory(root_id=None)
283
self.assertIs(None, inv.root)
284
# add a root entry by adding its path
285
ie = inv.add_path("", "directory", "my-root")
286
ie.revision = 'test-rev'
287
self.assertEqual("my-root", ie.file_id)
288
self.assertIs(ie, inv.root)
290
def test_add_path(self):
291
inv = inventory.Inventory(root_id='tree_root')
292
ie = inv.add_path('hello', 'file', 'hello-id')
293
self.assertEqual('hello-id', ie.file_id)
294
self.assertEqual('file', ie.kind)
297
"""Make sure copy() works and creates a deep copy."""
298
inv = inventory.Inventory(root_id='some-tree-root')
299
ie = inv.add_path('hello', 'file', 'hello-id')
301
inv.root.file_id = 'some-new-root'
303
self.assertEqual('some-tree-root', inv2.root.file_id)
304
self.assertEqual('hello', inv2['hello-id'].name)
306
def test_copy_empty(self):
307
"""Make sure an empty inventory can be copied."""
308
inv = inventory.Inventory(root_id=None)
310
self.assertIs(None, inv2.root)
312
def test_copy_copies_root_revision(self):
313
"""Make sure the revision of the root gets copied."""
314
inv = inventory.Inventory(root_id='someroot')
315
inv.root.revision = 'therev'
317
self.assertEquals('someroot', inv2.root.file_id)
318
self.assertEquals('therev', inv2.root.revision)
320
def test_create_tree_reference(self):
321
inv = inventory.Inventory('tree-root-123')
322
inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
323
revision='rev', reference_revision='rev2'))
325
def test_error_encoding(self):
326
inv = inventory.Inventory('tree-root')
327
inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
328
e = self.assertRaises(errors.InconsistentDelta, inv.add,
329
InventoryFile('b-id', u'\u1234', 'tree-root'))
330
self.assertContainsRe(str(e), r'\\u1234')
332
def test_add_recursive(self):
333
parent = InventoryDirectory('src-id', 'src', 'tree-root')
334
child = InventoryFile('hello-id', 'hello.c', 'src-id')
335
parent.children[child.file_id] = child
336
inv = inventory.Inventory('tree-root')
338
self.assertEqual('src/hello.c', inv.id2path('hello-id'))
342
class TestDeltaApplication(TestCaseWithTransport):
344
scenarios = delta_application_scenarios()
346
def get_empty_inventory(self, reference_inv=None):
347
"""Get an empty inventory.
349
Note that tests should not depend on the revision of the root for
350
setting up test conditions, as it has to be flexible to accomodate non
351
rich root repositories.
353
:param reference_inv: If not None, get the revision for the root from
354
this inventory. This is useful for dealing with older repositories
355
that routinely discarded the root entry data. If None, the root's
356
revision is set to 'basis'.
358
inv = inventory.Inventory()
359
if reference_inv is not None:
360
inv.root.revision = reference_inv.root.revision
362
inv.root.revision = 'basis'
365
def test_empty_delta(self):
366
inv = self.get_empty_inventory()
368
inv = self.apply_delta(self, inv, delta)
369
inv2 = self.get_empty_inventory(inv)
370
self.assertEqual([], inv2._make_delta(inv))
372
def test_None_file_id(self):
373
inv = self.get_empty_inventory()
374
dir1 = inventory.InventoryDirectory(None, 'dir1', inv.root.file_id)
375
dir1.revision = 'result'
376
delta = [(None, u'dir1', None, dir1)]
377
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
380
def test_unicode_file_id(self):
381
inv = self.get_empty_inventory()
382
dir1 = inventory.InventoryDirectory(u'dirid', 'dir1', inv.root.file_id)
383
dir1.revision = 'result'
384
delta = [(None, u'dir1', dir1.file_id, dir1)]
385
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
388
def test_repeated_file_id(self):
389
inv = self.get_empty_inventory()
390
file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
391
file1.revision = 'result'
394
file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
395
file2.revision = 'result'
398
delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
399
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
402
def test_repeated_new_path(self):
403
inv = self.get_empty_inventory()
404
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
405
file1.revision = 'result'
408
file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
409
file2.revision = 'result'
412
delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
413
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
416
def test_repeated_old_path(self):
417
inv = self.get_empty_inventory()
418
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
419
file1.revision = 'result'
422
# We can't *create* a source inventory with the same path, but
423
# a badly generated partial delta might claim the same source twice.
424
# This would be buggy in two ways: the path is repeated in the delta,
425
# And the path for one of the file ids doesn't match the source
426
# location. Alternatively, we could have a repeated fileid, but that
427
# is separately checked for.
428
file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
429
file2.revision = 'result'
434
delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
435
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
438
def test_mismatched_id_entry_id(self):
439
inv = self.get_empty_inventory()
440
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
441
file1.revision = 'result'
444
delta = [(None, u'path', 'id', file1)]
445
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
448
def test_mismatched_new_path_entry_None(self):
449
inv = self.get_empty_inventory()
450
delta = [(None, u'path', 'id', None)]
451
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
454
def test_mismatched_new_path_None_entry(self):
455
inv = self.get_empty_inventory()
456
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
457
file1.revision = 'result'
460
delta = [(u"path", None, 'id1', file1)]
461
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
464
def test_parent_is_not_directory(self):
465
inv = self.get_empty_inventory()
466
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
467
file1.revision = 'result'
470
file2 = inventory.InventoryFile('id2', 'path2', 'id1')
471
file2.revision = 'result'
475
delta = [(None, u'path/path2', 'id2', file2)]
476
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
479
def test_parent_is_missing(self):
480
inv = self.get_empty_inventory()
481
file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
482
file2.revision = 'result'
485
delta = [(None, u'path/path2', 'id2', file2)]
486
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
489
def test_new_parent_path_has_wrong_id(self):
490
inv = self.get_empty_inventory()
491
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
492
parent1.revision = 'result'
493
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
494
parent2.revision = 'result'
495
file1 = inventory.InventoryFile('id', 'path', 'p-2')
496
file1.revision = 'result'
501
# This delta claims that file1 is at dir/path, but actually its at
502
# dir2/path if you follow the inventory parent structure.
503
delta = [(None, u'dir/path', 'id', file1)]
504
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
507
def test_old_parent_path_is_wrong(self):
508
inv = self.get_empty_inventory()
509
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
510
parent1.revision = 'result'
511
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
512
parent2.revision = 'result'
513
file1 = inventory.InventoryFile('id', 'path', 'p-2')
514
file1.revision = 'result'
520
# This delta claims that file1 was at dir/path, but actually it was at
521
# dir2/path if you follow the inventory parent structure.
522
delta = [(u'dir/path', None, 'id', None)]
523
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
526
def test_old_parent_path_is_for_other_id(self):
527
inv = self.get_empty_inventory()
528
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
529
parent1.revision = 'result'
530
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
531
parent2.revision = 'result'
532
file1 = inventory.InventoryFile('id', 'path', 'p-2')
533
file1.revision = 'result'
536
file2 = inventory.InventoryFile('id2', 'path', 'p-1')
537
file2.revision = 'result'
544
# This delta claims that file1 was at dir/path, but actually it was at
545
# dir2/path if you follow the inventory parent structure. At dir/path
546
# is another entry we should not delete.
547
delta = [(u'dir/path', None, 'id', None)]
548
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
551
def test_add_existing_id_new_path(self):
552
inv = self.get_empty_inventory()
553
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
554
parent1.revision = 'result'
555
parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
556
parent2.revision = 'result'
558
delta = [(None, u'dir2', 'p-1', parent2)]
559
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
562
def test_add_new_id_existing_path(self):
563
inv = self.get_empty_inventory()
564
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
565
parent1.revision = 'result'
566
parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
567
parent2.revision = 'result'
569
delta = [(None, u'dir1', 'p-2', parent2)]
570
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
573
def test_remove_dir_leaving_dangling_child(self):
574
inv = self.get_empty_inventory()
575
dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
576
dir1.revision = 'result'
577
dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
578
dir2.revision = 'result'
579
dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
580
dir3.revision = 'result'
584
delta = [(u'dir1', None, 'p-1', None),
585
(u'dir1/child2', None, 'p-3', None)]
586
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
590
class TestInventory(TestCase):
592
def test_is_root(self):
593
"""Ensure our root-checking code is accurate."""
594
inv = inventory.Inventory('TREE_ROOT')
595
self.assertTrue(inv.is_root('TREE_ROOT'))
596
self.assertFalse(inv.is_root('booga'))
597
inv.root.file_id = 'booga'
598
self.assertFalse(inv.is_root('TREE_ROOT'))
599
self.assertTrue(inv.is_root('booga'))
600
# works properly even if no root is set
602
self.assertFalse(inv.is_root('TREE_ROOT'))
603
self.assertFalse(inv.is_root('booga'))
605
def test_entries_for_empty_inventory(self):
606
"""Test that entries() will not fail for an empty inventory"""
607
inv = Inventory(root_id=None)
608
self.assertEqual([], inv.entries())
611
class TestInventoryEntry(TestCase):
613
def test_file_kind_character(self):
614
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
615
self.assertEqual(file.kind_character(), '')
617
def test_dir_kind_character(self):
618
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
619
self.assertEqual(dir.kind_character(), '/')
621
def test_link_kind_character(self):
622
dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
623
self.assertEqual(dir.kind_character(), '')
625
def test_dir_detect_changes(self):
626
left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
627
right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
628
self.assertEqual((False, False), left.detect_changes(right))
629
self.assertEqual((False, False), right.detect_changes(left))
631
def test_file_detect_changes(self):
632
left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
634
right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
635
right.text_sha1 = 123
636
self.assertEqual((False, False), left.detect_changes(right))
637
self.assertEqual((False, False), right.detect_changes(left))
638
left.executable = True
639
self.assertEqual((False, True), left.detect_changes(right))
640
self.assertEqual((False, True), right.detect_changes(left))
641
right.text_sha1 = 321
642
self.assertEqual((True, True), left.detect_changes(right))
643
self.assertEqual((True, True), right.detect_changes(left))
645
def test_symlink_detect_changes(self):
646
left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
647
left.symlink_target='foo'
648
right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
649
right.symlink_target='foo'
650
self.assertEqual((False, False), left.detect_changes(right))
651
self.assertEqual((False, False), right.detect_changes(left))
652
left.symlink_target = 'different'
653
self.assertEqual((True, False), left.detect_changes(right))
654
self.assertEqual((True, False), right.detect_changes(left))
656
def test_file_has_text(self):
657
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
658
self.assertTrue(file.has_text())
660
def test_directory_has_text(self):
661
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
662
self.assertFalse(dir.has_text())
664
def test_link_has_text(self):
665
link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
666
self.assertFalse(link.has_text())
668
def test_make_entry(self):
669
self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
670
inventory.InventoryFile)
671
self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
672
inventory.InventoryLink)
673
self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
674
inventory.InventoryDirectory)
676
def test_make_entry_non_normalized(self):
677
orig_normalized_filename = osutils.normalized_filename
680
osutils.normalized_filename = osutils._accessible_normalized_filename
681
entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
682
self.assertEqual(u'\xe5', entry.name)
683
self.assertIsInstance(entry, inventory.InventoryFile)
685
osutils.normalized_filename = osutils._inaccessible_normalized_filename
686
self.assertRaises(errors.InvalidNormalization,
687
inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
689
osutils.normalized_filename = orig_normalized_filename
692
class TestDescribeChanges(TestCase):
694
def test_describe_change(self):
695
# we need to test the following change combinations:
701
# renamed/reparented and modified
702
# change kind (perhaps can't be done yet?)
703
# also, merged in combination with all of these?
704
old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
705
old_a.text_sha1 = '123132'
707
new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
708
new_a.text_sha1 = '123132'
711
self.assertChangeDescription('unchanged', old_a, new_a)
714
new_a.text_sha1 = 'abcabc'
715
self.assertChangeDescription('modified', old_a, new_a)
717
self.assertChangeDescription('added', None, new_a)
718
self.assertChangeDescription('removed', old_a, None)
719
# perhaps a bit questionable but seems like the most reasonable thing...
720
self.assertChangeDescription('unchanged', None, None)
722
# in this case it's both renamed and modified; show a rename and
724
new_a.name = 'newfilename'
725
self.assertChangeDescription('modified and renamed', old_a, new_a)
727
# reparenting is 'renaming'
728
new_a.name = old_a.name
729
new_a.parent_id = 'somedir-id'
730
self.assertChangeDescription('modified and renamed', old_a, new_a)
732
# reset the content values so its not modified
733
new_a.text_size = old_a.text_size
734
new_a.text_sha1 = old_a.text_sha1
735
new_a.name = old_a.name
737
new_a.name = 'newfilename'
738
self.assertChangeDescription('renamed', old_a, new_a)
740
# reparenting is 'renaming'
741
new_a.name = old_a.name
742
new_a.parent_id = 'somedir-id'
743
self.assertChangeDescription('renamed', old_a, new_a)
745
def assertChangeDescription(self, expected_change, old_ie, new_ie):
746
change = InventoryEntry.describe_change(old_ie, new_ie)
747
self.assertEqual(expected_change, change)
750
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
752
def get_chk_bytes(self):
753
factory = groupcompress.make_pack_factory(True, True, 1)
754
trans = self.get_transport('')
755
return factory(trans)
757
def read_bytes(self, chk_bytes, key):
758
stream = chk_bytes.get_record_stream([key], 'unordered', True)
759
return stream.next().get_bytes_as("fulltext")
761
def test_deserialise_gives_CHKInventory(self):
763
inv.revision_id = "revid"
764
inv.root.revision = "rootrev"
765
chk_bytes = self.get_chk_bytes()
766
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
767
bytes = ''.join(chk_inv.to_lines())
768
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
769
self.assertEqual("revid", new_inv.revision_id)
770
self.assertEqual("directory", new_inv.root.kind)
771
self.assertEqual(inv.root.file_id, new_inv.root.file_id)
772
self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
773
self.assertEqual(inv.root.name, new_inv.root.name)
774
self.assertEqual("rootrev", new_inv.root.revision)
775
self.assertEqual('plain', new_inv._search_key_name)
777
def test_deserialise_wrong_revid(self):
779
inv.revision_id = "revid"
780
inv.root.revision = "rootrev"
781
chk_bytes = self.get_chk_bytes()
782
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
783
bytes = ''.join(chk_inv.to_lines())
784
self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
787
def test_captures_rev_root_byid(self):
789
inv.revision_id = "foo"
790
inv.root.revision = "bar"
791
chk_bytes = self.get_chk_bytes()
792
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
793
lines = chk_inv.to_lines()
796
'revision_id: foo\n',
797
'root_id: TREE_ROOT\n',
798
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
799
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
801
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
802
self.assertEqual('plain', chk_inv._search_key_name)
804
def test_captures_parent_id_basename_index(self):
806
inv.revision_id = "foo"
807
inv.root.revision = "bar"
808
chk_bytes = self.get_chk_bytes()
809
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
810
lines = chk_inv.to_lines()
813
'revision_id: foo\n',
814
'root_id: TREE_ROOT\n',
815
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
816
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
818
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
819
self.assertEqual('plain', chk_inv._search_key_name)
821
def test_captures_search_key_name(self):
823
inv.revision_id = "foo"
824
inv.root.revision = "bar"
825
chk_bytes = self.get_chk_bytes()
826
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
827
search_key_name='hash-16-way')
828
lines = chk_inv.to_lines()
831
'search_key_name: hash-16-way\n',
832
'root_id: TREE_ROOT\n',
833
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
834
'revision_id: foo\n',
835
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
837
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
838
self.assertEqual('hash-16-way', chk_inv._search_key_name)
840
def test_directory_children_on_demand(self):
842
inv.revision_id = "revid"
843
inv.root.revision = "rootrev"
844
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
845
inv["fileid"].revision = "filerev"
846
inv["fileid"].executable = True
847
inv["fileid"].text_sha1 = "ffff"
848
inv["fileid"].text_size = 1
849
chk_bytes = self.get_chk_bytes()
850
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
851
bytes = ''.join(chk_inv.to_lines())
852
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
853
root_entry = new_inv[inv.root.file_id]
854
self.assertEqual(None, root_entry._children)
855
self.assertEqual(['file'], root_entry.children.keys())
856
file_direct = new_inv["fileid"]
857
file_found = root_entry.children['file']
858
self.assertEqual(file_direct.kind, file_found.kind)
859
self.assertEqual(file_direct.file_id, file_found.file_id)
860
self.assertEqual(file_direct.parent_id, file_found.parent_id)
861
self.assertEqual(file_direct.name, file_found.name)
862
self.assertEqual(file_direct.revision, file_found.revision)
863
self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
864
self.assertEqual(file_direct.text_size, file_found.text_size)
865
self.assertEqual(file_direct.executable, file_found.executable)
867
def test_from_inventory_maximum_size(self):
868
# from_inventory supports the maximum_size parameter.
870
inv.revision_id = "revid"
871
inv.root.revision = "rootrev"
872
chk_bytes = self.get_chk_bytes()
873
chk_inv = CHKInventory.from_inventory(chk_bytes, inv, 120)
874
chk_inv.id_to_entry._ensure_root()
875
self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
876
self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
877
p_id_basename = chk_inv.parent_id_basename_to_file_id
878
p_id_basename._ensure_root()
879
self.assertEqual(120, p_id_basename._root_node.maximum_size)
880
self.assertEqual(2, p_id_basename._root_node._key_width)
882
def test___iter__(self):
884
inv.revision_id = "revid"
885
inv.root.revision = "rootrev"
886
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
887
inv["fileid"].revision = "filerev"
888
inv["fileid"].executable = True
889
inv["fileid"].text_sha1 = "ffff"
890
inv["fileid"].text_size = 1
891
chk_bytes = self.get_chk_bytes()
892
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
893
bytes = ''.join(chk_inv.to_lines())
894
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
895
fileids = list(new_inv.__iter__())
897
self.assertEqual([inv.root.file_id, "fileid"], fileids)
899
def test__len__(self):
901
inv.revision_id = "revid"
902
inv.root.revision = "rootrev"
903
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
904
inv["fileid"].revision = "filerev"
905
inv["fileid"].executable = True
906
inv["fileid"].text_sha1 = "ffff"
907
inv["fileid"].text_size = 1
908
chk_bytes = self.get_chk_bytes()
909
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
910
self.assertEqual(2, len(chk_inv))
912
def test___getitem__(self):
914
inv.revision_id = "revid"
915
inv.root.revision = "rootrev"
916
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
917
inv["fileid"].revision = "filerev"
918
inv["fileid"].executable = True
919
inv["fileid"].text_sha1 = "ffff"
920
inv["fileid"].text_size = 1
921
chk_bytes = self.get_chk_bytes()
922
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
923
bytes = ''.join(chk_inv.to_lines())
924
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
925
root_entry = new_inv[inv.root.file_id]
926
file_entry = new_inv["fileid"]
927
self.assertEqual("directory", root_entry.kind)
928
self.assertEqual(inv.root.file_id, root_entry.file_id)
929
self.assertEqual(inv.root.parent_id, root_entry.parent_id)
930
self.assertEqual(inv.root.name, root_entry.name)
931
self.assertEqual("rootrev", root_entry.revision)
932
self.assertEqual("file", file_entry.kind)
933
self.assertEqual("fileid", file_entry.file_id)
934
self.assertEqual(inv.root.file_id, file_entry.parent_id)
935
self.assertEqual("file", file_entry.name)
936
self.assertEqual("filerev", file_entry.revision)
937
self.assertEqual("ffff", file_entry.text_sha1)
938
self.assertEqual(1, file_entry.text_size)
939
self.assertEqual(True, file_entry.executable)
940
self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
942
def test_has_id_true(self):
944
inv.revision_id = "revid"
945
inv.root.revision = "rootrev"
946
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
947
inv["fileid"].revision = "filerev"
948
inv["fileid"].executable = True
949
inv["fileid"].text_sha1 = "ffff"
950
inv["fileid"].text_size = 1
951
chk_bytes = self.get_chk_bytes()
952
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
953
self.assertTrue(chk_inv.has_id('fileid'))
954
self.assertTrue(chk_inv.has_id(inv.root.file_id))
956
def test_has_id_not(self):
958
inv.revision_id = "revid"
959
inv.root.revision = "rootrev"
960
chk_bytes = self.get_chk_bytes()
961
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
962
self.assertFalse(chk_inv.has_id('fileid'))
964
def test_id2path(self):
966
inv.revision_id = "revid"
967
inv.root.revision = "rootrev"
968
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
969
fileentry = InventoryFile("fileid", "file", "dirid")
972
inv["fileid"].revision = "filerev"
973
inv["fileid"].executable = True
974
inv["fileid"].text_sha1 = "ffff"
975
inv["fileid"].text_size = 1
976
inv["dirid"].revision = "filerev"
977
chk_bytes = self.get_chk_bytes()
978
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
979
bytes = ''.join(chk_inv.to_lines())
980
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
981
self.assertEqual('', new_inv.id2path(inv.root.file_id))
982
self.assertEqual('dir', new_inv.id2path('dirid'))
983
self.assertEqual('dir/file', new_inv.id2path('fileid'))
985
def test_path2id(self):
987
inv.revision_id = "revid"
988
inv.root.revision = "rootrev"
989
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
990
fileentry = InventoryFile("fileid", "file", "dirid")
993
inv["fileid"].revision = "filerev"
994
inv["fileid"].executable = True
995
inv["fileid"].text_sha1 = "ffff"
996
inv["fileid"].text_size = 1
997
inv["dirid"].revision = "filerev"
998
chk_bytes = self.get_chk_bytes()
999
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1000
bytes = ''.join(chk_inv.to_lines())
1001
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1002
self.assertEqual(inv.root.file_id, new_inv.path2id(''))
1003
self.assertEqual('dirid', new_inv.path2id('dir'))
1004
self.assertEqual('fileid', new_inv.path2id('dir/file'))
1006
def test_create_by_apply_delta_sets_root(self):
1008
inv.revision_id = "revid"
1009
chk_bytes = self.get_chk_bytes()
1010
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1011
inv.add_path("", "directory", "myrootid", None)
1012
inv.revision_id = "expectedid"
1013
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1014
delta = [("", None, base_inv.root.file_id, None),
1015
(None, "", "myrootid", inv.root)]
1016
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1017
self.assertEquals(reference_inv.root, new_inv.root)
1019
def test_create_by_apply_delta_empty_add_child(self):
1021
inv.revision_id = "revid"
1022
inv.root.revision = "rootrev"
1023
chk_bytes = self.get_chk_bytes()
1024
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1025
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1026
a_entry.revision = "filerev"
1027
a_entry.executable = True
1028
a_entry.text_sha1 = "ffff"
1029
a_entry.text_size = 1
1031
inv.revision_id = "expectedid"
1032
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1033
delta = [(None, "A", "A-id", a_entry)]
1034
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1035
# new_inv should be the same as reference_inv.
1036
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1037
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1038
reference_inv.id_to_entry._ensure_root()
1039
new_inv.id_to_entry._ensure_root()
1040
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1041
new_inv.id_to_entry._root_node._key)
1043
def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
1045
inv.revision_id = "revid"
1046
inv.root.revision = "rootrev"
1047
chk_bytes = self.get_chk_bytes()
1048
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1049
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1050
a_entry.revision = "filerev"
1051
a_entry.executable = True
1052
a_entry.text_sha1 = "ffff"
1053
a_entry.text_size = 1
1055
inv.revision_id = "expectedid"
1056
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1057
delta = [(None, "A", "A-id", a_entry)]
1058
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1059
reference_inv.id_to_entry._ensure_root()
1060
reference_inv.parent_id_basename_to_file_id._ensure_root()
1061
new_inv.id_to_entry._ensure_root()
1062
new_inv.parent_id_basename_to_file_id._ensure_root()
1063
# new_inv should be the same as reference_inv.
1064
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1065
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1066
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1067
new_inv.id_to_entry._root_node._key)
1068
self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
1069
new_inv.parent_id_basename_to_file_id._root_node._key)
1071
def test_iter_changes(self):
1072
# Low level bootstrapping smoke test; comprehensive generic tests via
1073
# InterTree are coming.
1075
inv.revision_id = "revid"
1076
inv.root.revision = "rootrev"
1077
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1078
inv["fileid"].revision = "filerev"
1079
inv["fileid"].executable = True
1080
inv["fileid"].text_sha1 = "ffff"
1081
inv["fileid"].text_size = 1
1083
inv2.revision_id = "revid2"
1084
inv2.root.revision = "rootrev"
1085
inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
1086
inv2["fileid"].revision = "filerev2"
1087
inv2["fileid"].executable = False
1088
inv2["fileid"].text_sha1 = "bbbb"
1089
inv2["fileid"].text_size = 2
1090
# get fresh objects.
1091
chk_bytes = self.get_chk_bytes()
1092
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1093
bytes = ''.join(chk_inv.to_lines())
1094
inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1095
chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
1096
bytes = ''.join(chk_inv2.to_lines())
1097
inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
1098
self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
1099
('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
1101
list(inv_1.iter_changes(inv_2)))
1103
def test_parent_id_basename_to_file_id_index_enabled(self):
1105
inv.revision_id = "revid"
1106
inv.root.revision = "rootrev"
1107
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1108
inv["fileid"].revision = "filerev"
1109
inv["fileid"].executable = True
1110
inv["fileid"].text_sha1 = "ffff"
1111
inv["fileid"].text_size = 1
1112
# get fresh objects.
1113
chk_bytes = self.get_chk_bytes()
1114
tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
1115
bytes = ''.join(tmp_inv.to_lines())
1116
chk_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1117
self.assertIsInstance(chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
1119
{('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
1120
dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
1122
def test_file_entry_to_bytes(self):
1123
inv = CHKInventory(None)
1124
ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
1125
ie.executable = True
1126
ie.revision = 'file-rev-id'
1127
ie.text_sha1 = 'abcdefgh'
1129
bytes = inv._entry_to_bytes(ie)
1130
self.assertEqual('file: file-id\nparent-id\nfilename\n'
1131
'file-rev-id\nabcdefgh\n100\nY', bytes)
1132
ie2 = inv._bytes_to_entry(bytes)
1133
self.assertEqual(ie, ie2)
1134
self.assertIsInstance(ie2.name, unicode)
1135
self.assertEqual(('filename', 'file-id', 'file-rev-id'),
1136
inv._bytes_to_utf8name_key(bytes))
1138
def test_file2_entry_to_bytes(self):
1139
inv = CHKInventory(None)
1141
ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
1142
ie.executable = False
1143
ie.revision = 'file-rev-id'
1144
ie.text_sha1 = '123456'
1146
bytes = inv._entry_to_bytes(ie)
1147
self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
1148
'file-rev-id\n123456\n25\nN', bytes)
1149
ie2 = inv._bytes_to_entry(bytes)
1150
self.assertEqual(ie, ie2)
1151
self.assertIsInstance(ie2.name, unicode)
1152
self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
1153
inv._bytes_to_utf8name_key(bytes))
1155
def test_dir_entry_to_bytes(self):
1156
inv = CHKInventory(None)
1157
ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
1158
ie.revision = 'dir-rev-id'
1159
bytes = inv._entry_to_bytes(ie)
1160
self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1161
ie2 = inv._bytes_to_entry(bytes)
1162
self.assertEqual(ie, ie2)
1163
self.assertIsInstance(ie2.name, unicode)
1164
self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
1165
inv._bytes_to_utf8name_key(bytes))
1167
def test_dir2_entry_to_bytes(self):
1168
inv = CHKInventory(None)
1169
ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1171
ie.revision = 'dir-rev-id'
1172
bytes = inv._entry_to_bytes(ie)
1173
self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
1174
'dir-rev-id', bytes)
1175
ie2 = inv._bytes_to_entry(bytes)
1176
self.assertEqual(ie, ie2)
1177
self.assertIsInstance(ie2.name, unicode)
1178
self.assertIs(ie2.parent_id, None)
1179
self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
1180
inv._bytes_to_utf8name_key(bytes))
1182
def test_symlink_entry_to_bytes(self):
1183
inv = CHKInventory(None)
1184
ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
1185
ie.revision = 'link-rev-id'
1186
ie.symlink_target = u'target/path'
1187
bytes = inv._entry_to_bytes(ie)
1188
self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
1189
'link-rev-id\ntarget/path', bytes)
1190
ie2 = inv._bytes_to_entry(bytes)
1191
self.assertEqual(ie, ie2)
1192
self.assertIsInstance(ie2.name, unicode)
1193
self.assertIsInstance(ie2.symlink_target, unicode)
1194
self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
1195
inv._bytes_to_utf8name_key(bytes))
1197
def test_symlink2_entry_to_bytes(self):
1198
inv = CHKInventory(None)
1199
ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
1200
ie.revision = 'link-rev-id'
1201
ie.symlink_target = u'target/\u03a9path'
1202
bytes = inv._entry_to_bytes(ie)
1203
self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1204
'link-rev-id\ntarget/\xce\xa9path', bytes)
1205
ie2 = inv._bytes_to_entry(bytes)
1206
self.assertEqual(ie, ie2)
1207
self.assertIsInstance(ie2.name, unicode)
1208
self.assertIsInstance(ie2.symlink_target, unicode)
1209
self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
1210
inv._bytes_to_utf8name_key(bytes))
1212
def test_tree_reference_entry_to_bytes(self):
1213
inv = CHKInventory(None)
1214
ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1216
ie.revision = 'tree-rev-id'
1217
ie.reference_revision = 'ref-rev-id'
1218
bytes = inv._entry_to_bytes(ie)
1219
self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1220
'tree-rev-id\nref-rev-id', bytes)
1221
ie2 = inv._bytes_to_entry(bytes)
1222
self.assertEqual(ie, ie2)
1223
self.assertIsInstance(ie2.name, unicode)
1224
self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1225
inv._bytes_to_utf8name_key(bytes))
1227
def make_basic_utf8_inventory(self):
1229
inv.revision_id = "revid"
1230
inv.root.revision = "rootrev"
1231
root_id = inv.root.file_id
1232
inv.add(InventoryFile("fileid", u'f\xefle', root_id))
1233
inv["fileid"].revision = "filerev"
1234
inv["fileid"].text_sha1 = "ffff"
1235
inv["fileid"].text_size = 0
1236
inv.add(InventoryDirectory("dirid", u'dir-\N{EURO SIGN}', root_id))
1237
inv.add(InventoryFile("childid", u'ch\xefld', "dirid"))
1238
inv["childid"].revision = "filerev"
1239
inv["childid"].text_sha1 = "ffff"
1240
inv["childid"].text_size = 0
1241
chk_bytes = self.get_chk_bytes()
1242
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1243
bytes = ''.join(chk_inv.to_lines())
1244
return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1246
def test__preload_handles_utf8(self):
1247
new_inv = self.make_basic_utf8_inventory()
1248
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1249
self.assertFalse(new_inv._fully_cached)
1250
new_inv._preload_cache()
1252
sorted([new_inv.root_id, "fileid", "dirid", "childid"]),
1253
sorted(new_inv._fileid_to_entry_cache.keys()))
1254
ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
1255
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1256
sorted(ie_root._children.keys()))
1257
ie_dir = new_inv._fileid_to_entry_cache['dirid']
1258
self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
1260
def test__preload_populates_cache(self):
1262
inv.revision_id = "revid"
1263
inv.root.revision = "rootrev"
1264
root_id = inv.root.file_id
1265
inv.add(InventoryFile("fileid", "file", root_id))
1266
inv["fileid"].revision = "filerev"
1267
inv["fileid"].executable = True
1268
inv["fileid"].text_sha1 = "ffff"
1269
inv["fileid"].text_size = 1
1270
inv.add(InventoryDirectory("dirid", "dir", root_id))
1271
inv.add(InventoryFile("childid", "child", "dirid"))
1272
inv["childid"].revision = "filerev"
1273
inv["childid"].executable = False
1274
inv["childid"].text_sha1 = "dddd"
1275
inv["childid"].text_size = 1
1276
chk_bytes = self.get_chk_bytes()
1277
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1278
bytes = ''.join(chk_inv.to_lines())
1279
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1280
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1281
self.assertFalse(new_inv._fully_cached)
1282
new_inv._preload_cache()
1284
sorted([root_id, "fileid", "dirid", "childid"]),
1285
sorted(new_inv._fileid_to_entry_cache.keys()))
1286
self.assertTrue(new_inv._fully_cached)
1287
ie_root = new_inv._fileid_to_entry_cache[root_id]
1288
self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
1289
ie_dir = new_inv._fileid_to_entry_cache['dirid']
1290
self.assertEqual(['child'], sorted(ie_dir._children.keys()))
1292
def test__preload_handles_partially_evaluated_inventory(self):
1293
new_inv = self.make_basic_utf8_inventory()
1294
ie = new_inv[new_inv.root_id]
1295
self.assertIs(None, ie._children)
1296
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1297
sorted(ie.children.keys()))
1298
# Accessing .children loads _children
1299
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1300
sorted(ie._children.keys()))
1301
new_inv._preload_cache()
1303
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1304
sorted(ie._children.keys()))
1305
ie_dir = new_inv["dirid"]
1306
self.assertEqual([u'ch\xefld'],
1307
sorted(ie_dir._children.keys()))
1310
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1312
def get_chk_bytes(self):
1313
factory = groupcompress.make_pack_factory(True, True, 1)
1314
trans = self.get_transport('')
1315
return factory(trans)
1317
def make_dir(self, inv, name, parent_id):
1318
inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
1320
def make_file(self, inv, name, parent_id, content='content\n'):
1321
ie = inv.make_entry('file', name, parent_id, name + '-id')
1322
ie.text_sha1 = osutils.sha_string(content)
1323
ie.text_size = len(content)
1326
def make_simple_inventory(self):
1327
inv = Inventory('TREE_ROOT')
1328
inv.revision_id = "revid"
1329
inv.root.revision = "rootrev"
1332
# sub-file1 sub-file1-id
1333
# sub-file2 sub-file2-id
1334
# sub-dir1/ sub-dir1-id
1335
# subsub-file1 subsub-file1-id
1337
# sub2-file1 sub2-file1-id
1339
self.make_dir(inv, 'dir1', 'TREE_ROOT')
1340
self.make_dir(inv, 'dir2', 'TREE_ROOT')
1341
self.make_dir(inv, 'sub-dir1', 'dir1-id')
1342
self.make_file(inv, 'top', 'TREE_ROOT')
1343
self.make_file(inv, 'sub-file1', 'dir1-id')
1344
self.make_file(inv, 'sub-file2', 'dir1-id')
1345
self.make_file(inv, 'subsub-file1', 'sub-dir1-id')
1346
self.make_file(inv, 'sub2-file1', 'dir2-id')
1347
chk_bytes = self.get_chk_bytes()
1348
# use a small maximum_size to force internal paging structures
1349
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
1351
search_key_name='hash-255-way')
1352
bytes = ''.join(chk_inv.to_lines())
1353
return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1355
def assert_Getitems(self, expected_fileids, inv, file_ids):
1356
self.assertEqual(sorted(expected_fileids),
1357
sorted([ie.file_id for ie in inv._getitems(file_ids)]))
1359
def assertExpand(self, all_ids, inv, file_ids):
1361
val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
1362
self.assertEqual(set(all_ids), val_all_ids)
1363
entries = inv._getitems(val_all_ids)
1364
expected_children = {}
1365
for entry in entries:
1366
s = expected_children.setdefault(entry.parent_id, [])
1367
s.append(entry.file_id)
1368
val_children = dict((k, sorted(v)) for k, v
1369
in val_children.iteritems())
1370
expected_children = dict((k, sorted(v)) for k, v
1371
in expected_children.iteritems())
1372
self.assertEqual(expected_children, val_children)
1374
def test_make_simple_inventory(self):
1375
inv = self.make_simple_inventory()
1377
for path, entry in inv.iter_entries_by_dir():
1378
layout.append((path, entry.file_id))
1381
('dir1', 'dir1-id'),
1382
('dir2', 'dir2-id'),
1384
('dir1/sub-dir1', 'sub-dir1-id'),
1385
('dir1/sub-file1', 'sub-file1-id'),
1386
('dir1/sub-file2', 'sub-file2-id'),
1387
('dir1/sub-dir1/subsub-file1', 'subsub-file1-id'),
1388
('dir2/sub2-file1', 'sub2-file1-id'),
1391
def test__getitems(self):
1392
inv = self.make_simple_inventory()
1394
self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1395
self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1396
self.assertFalse('sub-file2-id' in inv._fileid_to_entry_cache)
1398
self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1400
self.assert_Getitems(['dir1-id', 'sub-file2-id'], inv,
1401
['dir1-id', 'sub-file2-id'])
1402
self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1403
self.assertTrue('sub-file2-id' in inv._fileid_to_entry_cache)
1405
def test_single_file(self):
1406
inv = self.make_simple_inventory()
1407
self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1409
def test_get_all_parents(self):
1410
inv = self.make_simple_inventory()
1411
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1413
], inv, ['subsub-file1-id'])
1415
def test_get_children(self):
1416
inv = self.make_simple_inventory()
1417
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1418
'sub-file1-id', 'sub-file2-id', 'subsub-file1-id',
1419
], inv, ['dir1-id'])
1421
def test_from_root(self):
1422
inv = self.make_simple_inventory()
1423
self.assertExpand(['TREE_ROOT', 'dir1-id', 'dir2-id', 'sub-dir1-id',
1424
'sub-file1-id', 'sub-file2-id', 'sub2-file1-id',
1425
'subsub-file1-id', 'top-id'], inv, ['TREE_ROOT'])
1427
def test_top_level_file(self):
1428
inv = self.make_simple_inventory()
1429
self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1431
def test_subsub_file(self):
1432
inv = self.make_simple_inventory()
1433
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1434
'subsub-file1-id'], inv, ['subsub-file1-id'])
1436
def test_sub_and_root(self):
1437
inv = self.make_simple_inventory()
1438
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
1439
'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])
1442
class TestMutableInventoryFromTree(TestCaseWithTransport):
1444
def test_empty(self):
1445
repository = self.make_repository('.')
1446
tree = repository.revision_tree(revision.NULL_REVISION)
1447
inv = mutable_inventory_from_tree(tree)
1448
self.assertEquals(revision.NULL_REVISION, inv.revision_id)
1449
self.assertEquals(0, len(inv))
1451
def test_some_files(self):
1452
wt = self.make_branch_and_tree('.')
1453
self.build_tree(['a'])
1454
wt.add(['a'], ['thefileid'])
1455
revid = wt.commit("commit")
1456
tree = wt.branch.repository.revision_tree(revid)
1457
inv = mutable_inventory_from_tree(tree)
1458
self.assertEquals(revid, inv.revision_id)
1459
self.assertEquals(2, len(inv))
1460
self.assertEquals("a", inv['thefileid'].name)
1461
# The inventory should be mutable and independent of
1463
self.assertFalse(tree.inventory['thefileid'].executable)
1464
inv['thefileid'].executable = True
1465
self.assertFalse(tree.inventory['thefileid'].executable)