1
# Copyright (C) 2005-2012, 2016 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 ..inventory import (
37
mutable_inventory_from_tree,
41
TestCaseWithTransport,
43
from .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, invalid_delta=True):
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, invalid_delta=True):
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
if not invalid_delta:
130
return tree.root_inventory
133
def _create_repo_revisions(repo, basis, delta, invalid_delta):
134
repo.start_write_group()
136
rev = revision.Revision('basis', timestamp=0, timezone=None,
137
message="", committer="foo@example.com")
138
basis.revision_id = 'basis'
139
create_texts_for_inv(repo, basis)
140
repo.add_revision('basis', rev, basis)
142
# We don't want to apply the delta to the basis, because we expect
143
# the delta is invalid.
145
result_inv.revision_id = 'result'
146
target_entries = None
148
result_inv = basis.create_by_apply_delta(delta, 'result')
149
create_texts_for_inv(repo, result_inv)
150
target_entries = list(result_inv.iter_entries_by_dir())
151
rev = revision.Revision('result', timestamp=0, timezone=None,
152
message="", committer="foo@example.com")
153
repo.add_revision('result', rev, result_inv)
154
repo.commit_write_group()
156
repo.abort_write_group()
158
return target_entries
161
def _get_basis_entries(tree):
162
basis_tree = tree.basis_tree()
163
basis_tree.lock_read()
164
basis_tree_entries = list(basis_tree.inventory.iter_entries_by_dir())
166
return basis_tree_entries
169
def _populate_different_tree(tree, basis, delta):
170
"""Put all entries into tree, but at a unique location."""
173
tree.add(['unique-dir'], ['unique-dir-id'], ['directory'])
174
for path, ie in basis.iter_entries_by_dir():
175
if ie.file_id in added_ids:
177
# We want a unique path for each of these, we use the file-id
178
tree.add(['unique-dir/' + ie.file_id], [ie.file_id], [ie.kind])
179
added_ids.add(ie.file_id)
180
for old_path, new_path, file_id, ie in delta:
181
if file_id in added_ids:
183
tree.add(['unique-dir/' + file_id], [file_id], [ie.kind])
186
def apply_inventory_WT_basis(test, basis, delta, invalid_delta=True):
187
"""Apply delta to basis and return the result.
189
This sets the parent and then calls update_basis_by_delta.
190
It also puts the basis in the repository under both 'basis' and 'result' to
191
allow safety checks made by the WT to succeed, and finally ensures that all
192
items in the delta with a new path are present in the WT before calling
193
update_basis_by_delta.
195
:param basis: An inventory to be used as the basis.
196
:param delta: The inventory delta to apply:
197
:return: An inventory resulting from the application.
199
control = test.make_bzrdir('tree', format=test.format._matchingbzrdir)
200
control.create_repository()
201
control.create_branch()
202
tree = test.format.initialize(control)
205
target_entries = _create_repo_revisions(tree.branch.repository, basis,
206
delta, invalid_delta)
207
# Set the basis state as the trees current state
208
tree._write_inventory(basis)
209
# This reads basis from the repo and puts it into the tree's local
210
# cache, if it has one.
211
tree.set_parent_ids(['basis'])
214
# Fresh lock, reads disk again.
217
tree.update_basis_by_delta('result', delta)
218
if not invalid_delta:
222
# reload tree - ensure we get what was written.
223
tree = tree.bzrdir.open_workingtree()
224
basis_tree = tree.basis_tree()
225
basis_tree.lock_read()
226
test.addCleanup(basis_tree.unlock)
227
basis_inv = basis_tree.root_inventory
229
basis_entries = list(basis_inv.iter_entries_by_dir())
230
test.assertEqual(target_entries, basis_entries)
234
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta,
236
"""Apply delta to basis and return the result.
238
This inserts basis as a whole inventory and then uses
239
add_inventory_by_delta to add delta.
241
:param basis: An inventory to be used as the basis.
242
:param delta: The inventory delta to apply:
243
:return: An inventory resulting from the application.
245
format = self.format()
246
control = self.make_bzrdir('tree', format=format._matchingbzrdir)
247
repo = format.initialize(control)
250
repo.start_write_group()
252
rev = revision.Revision('basis', timestamp=0, timezone=None,
253
message="", committer="foo@example.com")
254
basis.revision_id = 'basis'
255
create_texts_for_inv(repo, basis)
256
repo.add_revision('basis', rev, basis)
257
repo.commit_write_group()
259
repo.abort_write_group()
265
repo.start_write_group()
267
inv_sha1 = repo.add_inventory_by_delta('basis', delta,
270
repo.abort_write_group()
273
repo.commit_write_group()
276
# Fresh lock, reads disk again.
277
repo = repo.bzrdir.open_repository()
279
self.addCleanup(repo.unlock)
280
return repo.get_inventory('result')
283
class TestInventoryUpdates(TestCase):
285
def test_creation_from_root_id(self):
286
# iff a root id is passed to the constructor, a root directory is made
287
inv = inventory.Inventory(root_id='tree-root')
288
self.assertNotEqual(None, inv.root)
289
self.assertEqual('tree-root', inv.root.file_id)
291
def test_add_path_of_root(self):
292
# if no root id is given at creation time, there is no root directory
293
inv = inventory.Inventory(root_id=None)
294
self.assertIs(None, inv.root)
295
# add a root entry by adding its path
296
ie = inv.add_path("", "directory", "my-root")
297
ie.revision = 'test-rev'
298
self.assertEqual("my-root", ie.file_id)
299
self.assertIs(ie, inv.root)
301
def test_add_path(self):
302
inv = inventory.Inventory(root_id='tree_root')
303
ie = inv.add_path('hello', 'file', 'hello-id')
304
self.assertEqual('hello-id', ie.file_id)
305
self.assertEqual('file', ie.kind)
308
"""Make sure copy() works and creates a deep copy."""
309
inv = inventory.Inventory(root_id='some-tree-root')
310
ie = inv.add_path('hello', 'file', 'hello-id')
312
inv.root.file_id = 'some-new-root'
314
self.assertEqual('some-tree-root', inv2.root.file_id)
315
self.assertEqual('hello', inv2['hello-id'].name)
317
def test_copy_empty(self):
318
"""Make sure an empty inventory can be copied."""
319
inv = inventory.Inventory(root_id=None)
321
self.assertIs(None, inv2.root)
323
def test_copy_copies_root_revision(self):
324
"""Make sure the revision of the root gets copied."""
325
inv = inventory.Inventory(root_id='someroot')
326
inv.root.revision = 'therev'
328
self.assertEqual('someroot', inv2.root.file_id)
329
self.assertEqual('therev', inv2.root.revision)
331
def test_create_tree_reference(self):
332
inv = inventory.Inventory('tree-root-123')
333
inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
334
revision='rev', reference_revision='rev2'))
336
def test_error_encoding(self):
337
inv = inventory.Inventory('tree-root')
338
inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
339
e = self.assertRaises(errors.InconsistentDelta, inv.add,
340
InventoryFile('b-id', u'\u1234', 'tree-root'))
341
self.assertContainsRe(str(e), r'\\u1234')
343
def test_add_recursive(self):
344
parent = InventoryDirectory('src-id', 'src', 'tree-root')
345
child = InventoryFile('hello-id', 'hello.c', 'src-id')
346
parent.children[child.file_id] = child
347
inv = inventory.Inventory('tree-root')
349
self.assertEqual('src/hello.c', inv.id2path('hello-id'))
353
class TestDeltaApplication(TestCaseWithTransport):
355
scenarios = delta_application_scenarios()
357
def get_empty_inventory(self, reference_inv=None):
358
"""Get an empty inventory.
360
Note that tests should not depend on the revision of the root for
361
setting up test conditions, as it has to be flexible to accomodate non
362
rich root repositories.
364
:param reference_inv: If not None, get the revision for the root from
365
this inventory. This is useful for dealing with older repositories
366
that routinely discarded the root entry data. If None, the root's
367
revision is set to 'basis'.
369
inv = inventory.Inventory()
370
if reference_inv is not None:
371
inv.root.revision = reference_inv.root.revision
373
inv.root.revision = 'basis'
376
def make_file_ie(self, file_id='file-id', name='name', parent_id=None):
377
ie_file = inventory.InventoryFile(file_id, name, parent_id)
378
ie_file.revision = 'result'
379
ie_file.text_size = 0
380
ie_file.text_sha1 = ''
383
def test_empty_delta(self):
384
inv = self.get_empty_inventory()
386
inv = self.apply_delta(self, inv, delta)
387
inv2 = self.get_empty_inventory(inv)
388
self.assertEqual([], inv2._make_delta(inv))
390
def test_None_file_id(self):
391
inv = self.get_empty_inventory()
392
dir1 = inventory.InventoryDirectory(None, 'dir1', inv.root.file_id)
393
dir1.revision = 'result'
394
delta = [(None, u'dir1', None, dir1)]
395
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
398
def test_unicode_file_id(self):
399
inv = self.get_empty_inventory()
400
dir1 = inventory.InventoryDirectory(u'dirid', 'dir1', inv.root.file_id)
401
dir1.revision = 'result'
402
delta = [(None, u'dir1', dir1.file_id, dir1)]
403
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
406
def test_repeated_file_id(self):
407
inv = self.get_empty_inventory()
408
file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
409
file1.revision = 'result'
414
delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
415
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
418
def test_repeated_new_path(self):
419
inv = self.get_empty_inventory()
420
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
421
file1.revision = 'result'
425
file2.file_id = 'id2'
426
delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
427
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
430
def test_repeated_old_path(self):
431
inv = self.get_empty_inventory()
432
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
433
file1.revision = 'result'
436
# We can't *create* a source inventory with the same path, but
437
# a badly generated partial delta might claim the same source twice.
438
# This would be buggy in two ways: the path is repeated in the delta,
439
# And the path for one of the file ids doesn't match the source
440
# location. Alternatively, we could have a repeated fileid, but that
441
# is separately checked for.
442
file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
443
file2.revision = 'result'
448
delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
449
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
452
def test_mismatched_id_entry_id(self):
453
inv = self.get_empty_inventory()
454
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
455
file1.revision = 'result'
458
delta = [(None, u'path', 'id', file1)]
459
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
462
def test_mismatched_new_path_entry_None(self):
463
inv = self.get_empty_inventory()
464
delta = [(None, u'path', 'id', None)]
465
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
468
def test_mismatched_new_path_None_entry(self):
469
inv = self.get_empty_inventory()
470
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
471
file1.revision = 'result'
474
delta = [(u"path", None, 'id1', file1)]
475
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
478
def test_parent_is_not_directory(self):
479
inv = self.get_empty_inventory()
480
file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
481
file1.revision = 'result'
484
file2 = inventory.InventoryFile('id2', 'path2', 'id1')
485
file2.revision = 'result'
489
delta = [(None, u'path/path2', 'id2', file2)]
490
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
493
def test_parent_is_missing(self):
494
inv = self.get_empty_inventory()
495
file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
496
file2.revision = 'result'
499
delta = [(None, u'path/path2', 'id2', file2)]
500
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
503
def test_new_parent_path_has_wrong_id(self):
504
inv = self.get_empty_inventory()
505
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
506
parent1.revision = 'result'
507
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
508
parent2.revision = 'result'
509
file1 = inventory.InventoryFile('id', 'path', 'p-2')
510
file1.revision = 'result'
515
# This delta claims that file1 is at dir/path, but actually its at
516
# dir2/path if you follow the inventory parent structure.
517
delta = [(None, u'dir/path', 'id', file1)]
518
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
521
def test_old_parent_path_is_wrong(self):
522
inv = self.get_empty_inventory()
523
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
524
parent1.revision = 'result'
525
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
526
parent2.revision = 'result'
527
file1 = inventory.InventoryFile('id', 'path', 'p-2')
528
file1.revision = 'result'
534
# This delta claims that file1 was at dir/path, but actually it was at
535
# dir2/path if you follow the inventory parent structure.
536
delta = [(u'dir/path', None, 'id', None)]
537
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
540
def test_old_parent_path_is_for_other_id(self):
541
inv = self.get_empty_inventory()
542
parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
543
parent1.revision = 'result'
544
parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
545
parent2.revision = 'result'
546
file1 = inventory.InventoryFile('id', 'path', 'p-2')
547
file1.revision = 'result'
550
file2 = inventory.InventoryFile('id2', 'path', 'p-1')
551
file2.revision = 'result'
558
# This delta claims that file1 was at dir/path, but actually it was at
559
# dir2/path if you follow the inventory parent structure. At dir/path
560
# is another entry we should not delete.
561
delta = [(u'dir/path', None, 'id', None)]
562
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
565
def test_add_existing_id_new_path(self):
566
inv = self.get_empty_inventory()
567
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
568
parent1.revision = 'result'
569
parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
570
parent2.revision = 'result'
572
delta = [(None, u'dir2', 'p-1', parent2)]
573
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
576
def test_add_new_id_existing_path(self):
577
inv = self.get_empty_inventory()
578
parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
579
parent1.revision = 'result'
580
parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
581
parent2.revision = 'result'
583
delta = [(None, u'dir1', 'p-2', parent2)]
584
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
587
def test_remove_dir_leaving_dangling_child(self):
588
inv = self.get_empty_inventory()
589
dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
590
dir1.revision = 'result'
591
dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
592
dir2.revision = 'result'
593
dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
594
dir3.revision = 'result'
598
delta = [(u'dir1', None, 'p-1', None),
599
(u'dir1/child2', None, 'p-3', None)]
600
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
603
def test_add_file(self):
604
inv = self.get_empty_inventory()
605
file1 = inventory.InventoryFile('file-id', 'path', inv.root.file_id)
606
file1.revision = 'result'
609
delta = [(None, u'path', 'file-id', file1)]
610
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
611
self.assertEqual('file-id', res_inv['file-id'].file_id)
613
def test_remove_file(self):
614
inv = self.get_empty_inventory()
615
file1 = inventory.InventoryFile('file-id', 'path', inv.root.file_id)
616
file1.revision = 'result'
620
delta = [(u'path', None, 'file-id', None)]
621
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
622
self.assertEqual(None, res_inv.path2id('path'))
623
self.assertRaises(errors.NoSuchId, res_inv.id2path, 'file-id')
625
def test_rename_file(self):
626
inv = self.get_empty_inventory()
627
file1 = self.make_file_ie(name='path', parent_id=inv.root.file_id)
629
file2 = self.make_file_ie(name='path2', parent_id=inv.root.file_id)
630
delta = [(u'path', 'path2', 'file-id', file2)]
631
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
632
self.assertEqual(None, res_inv.path2id('path'))
633
self.assertEqual('file-id', res_inv.path2id('path2'))
635
def test_replaced_at_new_path(self):
636
inv = self.get_empty_inventory()
637
file1 = self.make_file_ie(file_id='id1', parent_id=inv.root.file_id)
639
file2 = self.make_file_ie(file_id='id2', parent_id=inv.root.file_id)
640
delta = [(u'name', None, 'id1', None),
641
(None, u'name', 'id2', file2)]
642
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
643
self.assertEqual('id2', res_inv.path2id('name'))
645
def test_rename_dir(self):
646
inv = self.get_empty_inventory()
647
dir1 = inventory.InventoryDirectory('dir-id', 'dir1', inv.root.file_id)
648
dir1.revision = 'basis'
649
file1 = self.make_file_ie(parent_id='dir-id')
652
dir2 = inventory.InventoryDirectory('dir-id', 'dir2', inv.root.file_id)
653
dir2.revision = 'result'
654
delta = [('dir1', 'dir2', 'dir-id', dir2)]
655
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
656
# The file should be accessible under the new path
657
self.assertEqual('file-id', res_inv.path2id('dir2/name'))
659
def test_renamed_dir_with_renamed_child(self):
660
inv = self.get_empty_inventory()
661
dir1 = inventory.InventoryDirectory('dir-id', 'dir1', inv.root.file_id)
662
dir1.revision = 'basis'
663
file1 = self.make_file_ie('file-id-1', 'name1', parent_id='dir-id')
664
file2 = self.make_file_ie('file-id-2', 'name2', parent_id='dir-id')
668
dir2 = inventory.InventoryDirectory('dir-id', 'dir2', inv.root.file_id)
669
dir2.revision = 'result'
670
file2b = self.make_file_ie('file-id-2', 'name2', inv.root.file_id)
671
delta = [('dir1', 'dir2', 'dir-id', dir2),
672
('dir1/name2', 'name2', 'file-id-2', file2b)]
673
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
674
# The file should be accessible under the new path
675
self.assertEqual('file-id-1', res_inv.path2id('dir2/name1'))
676
self.assertEqual(None, res_inv.path2id('dir2/name2'))
677
self.assertEqual('file-id-2', res_inv.path2id('name2'))
679
def test_is_root(self):
680
"""Ensure our root-checking code is accurate."""
681
inv = inventory.Inventory('TREE_ROOT')
682
self.assertTrue(inv.is_root('TREE_ROOT'))
683
self.assertFalse(inv.is_root('booga'))
684
inv.root.file_id = 'booga'
685
self.assertFalse(inv.is_root('TREE_ROOT'))
686
self.assertTrue(inv.is_root('booga'))
687
# works properly even if no root is set
689
self.assertFalse(inv.is_root('TREE_ROOT'))
690
self.assertFalse(inv.is_root('booga'))
692
def test_entries_for_empty_inventory(self):
693
"""Test that entries() will not fail for an empty inventory"""
694
inv = Inventory(root_id=None)
695
self.assertEqual([], inv.entries())
698
class TestInventoryEntry(TestCase):
700
def test_file_kind_character(self):
701
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
702
self.assertEqual(file.kind_character(), '')
704
def test_dir_kind_character(self):
705
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
706
self.assertEqual(dir.kind_character(), '/')
708
def test_link_kind_character(self):
709
dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
710
self.assertEqual(dir.kind_character(), '')
712
def test_dir_detect_changes(self):
713
left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
714
right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
715
self.assertEqual((False, False), left.detect_changes(right))
716
self.assertEqual((False, False), right.detect_changes(left))
718
def test_file_detect_changes(self):
719
left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
721
right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
722
right.text_sha1 = 123
723
self.assertEqual((False, False), left.detect_changes(right))
724
self.assertEqual((False, False), right.detect_changes(left))
725
left.executable = True
726
self.assertEqual((False, True), left.detect_changes(right))
727
self.assertEqual((False, True), right.detect_changes(left))
728
right.text_sha1 = 321
729
self.assertEqual((True, True), left.detect_changes(right))
730
self.assertEqual((True, True), right.detect_changes(left))
732
def test_symlink_detect_changes(self):
733
left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
734
left.symlink_target='foo'
735
right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
736
right.symlink_target='foo'
737
self.assertEqual((False, False), left.detect_changes(right))
738
self.assertEqual((False, False), right.detect_changes(left))
739
left.symlink_target = 'different'
740
self.assertEqual((True, False), left.detect_changes(right))
741
self.assertEqual((True, False), right.detect_changes(left))
743
def test_file_has_text(self):
744
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
745
self.assertTrue(file.has_text())
747
def test_directory_has_text(self):
748
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
749
self.assertFalse(dir.has_text())
751
def test_link_has_text(self):
752
link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
753
self.assertFalse(link.has_text())
755
def test_make_entry(self):
756
self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
757
inventory.InventoryFile)
758
self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
759
inventory.InventoryLink)
760
self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
761
inventory.InventoryDirectory)
763
def test_make_entry_non_normalized(self):
764
orig_normalized_filename = osutils.normalized_filename
767
osutils.normalized_filename = osutils._accessible_normalized_filename
768
entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
769
self.assertEqual(u'\xe5', entry.name)
770
self.assertIsInstance(entry, inventory.InventoryFile)
772
osutils.normalized_filename = osutils._inaccessible_normalized_filename
773
self.assertRaises(errors.InvalidNormalization,
774
inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
776
osutils.normalized_filename = orig_normalized_filename
779
class TestDescribeChanges(TestCase):
781
def test_describe_change(self):
782
# we need to test the following change combinations:
788
# renamed/reparented and modified
789
# change kind (perhaps can't be done yet?)
790
# also, merged in combination with all of these?
791
old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
792
old_a.text_sha1 = '123132'
794
new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
795
new_a.text_sha1 = '123132'
798
self.assertChangeDescription('unchanged', old_a, new_a)
801
new_a.text_sha1 = 'abcabc'
802
self.assertChangeDescription('modified', old_a, new_a)
804
self.assertChangeDescription('added', None, new_a)
805
self.assertChangeDescription('removed', old_a, None)
806
# perhaps a bit questionable but seems like the most reasonable thing...
807
self.assertChangeDescription('unchanged', None, None)
809
# in this case it's both renamed and modified; show a rename and
811
new_a.name = 'newfilename'
812
self.assertChangeDescription('modified and renamed', old_a, new_a)
814
# reparenting is 'renaming'
815
new_a.name = old_a.name
816
new_a.parent_id = 'somedir-id'
817
self.assertChangeDescription('modified and renamed', old_a, new_a)
819
# reset the content values so its not modified
820
new_a.text_size = old_a.text_size
821
new_a.text_sha1 = old_a.text_sha1
822
new_a.name = old_a.name
824
new_a.name = 'newfilename'
825
self.assertChangeDescription('renamed', old_a, new_a)
827
# reparenting is 'renaming'
828
new_a.name = old_a.name
829
new_a.parent_id = 'somedir-id'
830
self.assertChangeDescription('renamed', old_a, new_a)
832
def assertChangeDescription(self, expected_change, old_ie, new_ie):
833
change = InventoryEntry.describe_change(old_ie, new_ie)
834
self.assertEqual(expected_change, change)
837
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
839
def get_chk_bytes(self):
840
factory = groupcompress.make_pack_factory(True, True, 1)
841
trans = self.get_transport('')
842
return factory(trans)
844
def read_bytes(self, chk_bytes, key):
845
stream = chk_bytes.get_record_stream([key], 'unordered', True)
846
return stream.next().get_bytes_as("fulltext")
848
def test_deserialise_gives_CHKInventory(self):
850
inv.revision_id = "revid"
851
inv.root.revision = "rootrev"
852
chk_bytes = self.get_chk_bytes()
853
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
854
bytes = ''.join(chk_inv.to_lines())
855
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
856
self.assertEqual("revid", new_inv.revision_id)
857
self.assertEqual("directory", new_inv.root.kind)
858
self.assertEqual(inv.root.file_id, new_inv.root.file_id)
859
self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
860
self.assertEqual(inv.root.name, new_inv.root.name)
861
self.assertEqual("rootrev", new_inv.root.revision)
862
self.assertEqual('plain', new_inv._search_key_name)
864
def test_deserialise_wrong_revid(self):
866
inv.revision_id = "revid"
867
inv.root.revision = "rootrev"
868
chk_bytes = self.get_chk_bytes()
869
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
870
bytes = ''.join(chk_inv.to_lines())
871
self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
874
def test_captures_rev_root_byid(self):
876
inv.revision_id = "foo"
877
inv.root.revision = "bar"
878
chk_bytes = self.get_chk_bytes()
879
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
880
lines = chk_inv.to_lines()
883
'revision_id: foo\n',
884
'root_id: TREE_ROOT\n',
885
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
886
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
888
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
889
self.assertEqual('plain', chk_inv._search_key_name)
891
def test_captures_parent_id_basename_index(self):
893
inv.revision_id = "foo"
894
inv.root.revision = "bar"
895
chk_bytes = self.get_chk_bytes()
896
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
897
lines = chk_inv.to_lines()
900
'revision_id: foo\n',
901
'root_id: TREE_ROOT\n',
902
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
903
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
905
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
906
self.assertEqual('plain', chk_inv._search_key_name)
908
def test_captures_search_key_name(self):
910
inv.revision_id = "foo"
911
inv.root.revision = "bar"
912
chk_bytes = self.get_chk_bytes()
913
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
914
search_key_name='hash-16-way')
915
lines = chk_inv.to_lines()
918
'search_key_name: hash-16-way\n',
919
'root_id: TREE_ROOT\n',
920
'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
921
'revision_id: foo\n',
922
'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
924
chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
925
self.assertEqual('hash-16-way', chk_inv._search_key_name)
927
def test_directory_children_on_demand(self):
929
inv.revision_id = "revid"
930
inv.root.revision = "rootrev"
931
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
932
inv["fileid"].revision = "filerev"
933
inv["fileid"].executable = True
934
inv["fileid"].text_sha1 = "ffff"
935
inv["fileid"].text_size = 1
936
chk_bytes = self.get_chk_bytes()
937
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
938
bytes = ''.join(chk_inv.to_lines())
939
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
940
root_entry = new_inv[inv.root.file_id]
941
self.assertEqual(None, root_entry._children)
942
self.assertEqual({'file'}, set(root_entry.children))
943
file_direct = new_inv["fileid"]
944
file_found = root_entry.children['file']
945
self.assertEqual(file_direct.kind, file_found.kind)
946
self.assertEqual(file_direct.file_id, file_found.file_id)
947
self.assertEqual(file_direct.parent_id, file_found.parent_id)
948
self.assertEqual(file_direct.name, file_found.name)
949
self.assertEqual(file_direct.revision, file_found.revision)
950
self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
951
self.assertEqual(file_direct.text_size, file_found.text_size)
952
self.assertEqual(file_direct.executable, file_found.executable)
954
def test_from_inventory_maximum_size(self):
955
# from_inventory supports the maximum_size parameter.
957
inv.revision_id = "revid"
958
inv.root.revision = "rootrev"
959
chk_bytes = self.get_chk_bytes()
960
chk_inv = CHKInventory.from_inventory(chk_bytes, inv, 120)
961
chk_inv.id_to_entry._ensure_root()
962
self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
963
self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
964
p_id_basename = chk_inv.parent_id_basename_to_file_id
965
p_id_basename._ensure_root()
966
self.assertEqual(120, p_id_basename._root_node.maximum_size)
967
self.assertEqual(2, p_id_basename._root_node._key_width)
969
def test___iter__(self):
971
inv.revision_id = "revid"
972
inv.root.revision = "rootrev"
973
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
974
inv["fileid"].revision = "filerev"
975
inv["fileid"].executable = True
976
inv["fileid"].text_sha1 = "ffff"
977
inv["fileid"].text_size = 1
978
chk_bytes = self.get_chk_bytes()
979
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
980
bytes = ''.join(chk_inv.to_lines())
981
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
982
fileids = sorted(new_inv.__iter__())
983
self.assertEqual([inv.root.file_id, "fileid"], fileids)
985
def test__len__(self):
987
inv.revision_id = "revid"
988
inv.root.revision = "rootrev"
989
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
990
inv["fileid"].revision = "filerev"
991
inv["fileid"].executable = True
992
inv["fileid"].text_sha1 = "ffff"
993
inv["fileid"].text_size = 1
994
chk_bytes = self.get_chk_bytes()
995
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
996
self.assertEqual(2, len(chk_inv))
998
def test___getitem__(self):
1000
inv.revision_id = "revid"
1001
inv.root.revision = "rootrev"
1002
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1003
inv["fileid"].revision = "filerev"
1004
inv["fileid"].executable = True
1005
inv["fileid"].text_sha1 = "ffff"
1006
inv["fileid"].text_size = 1
1007
chk_bytes = self.get_chk_bytes()
1008
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1009
bytes = ''.join(chk_inv.to_lines())
1010
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1011
root_entry = new_inv[inv.root.file_id]
1012
file_entry = new_inv["fileid"]
1013
self.assertEqual("directory", root_entry.kind)
1014
self.assertEqual(inv.root.file_id, root_entry.file_id)
1015
self.assertEqual(inv.root.parent_id, root_entry.parent_id)
1016
self.assertEqual(inv.root.name, root_entry.name)
1017
self.assertEqual("rootrev", root_entry.revision)
1018
self.assertEqual("file", file_entry.kind)
1019
self.assertEqual("fileid", file_entry.file_id)
1020
self.assertEqual(inv.root.file_id, file_entry.parent_id)
1021
self.assertEqual("file", file_entry.name)
1022
self.assertEqual("filerev", file_entry.revision)
1023
self.assertEqual("ffff", file_entry.text_sha1)
1024
self.assertEqual(1, file_entry.text_size)
1025
self.assertEqual(True, file_entry.executable)
1026
self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
1028
def test_has_id_true(self):
1030
inv.revision_id = "revid"
1031
inv.root.revision = "rootrev"
1032
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1033
inv["fileid"].revision = "filerev"
1034
inv["fileid"].executable = True
1035
inv["fileid"].text_sha1 = "ffff"
1036
inv["fileid"].text_size = 1
1037
chk_bytes = self.get_chk_bytes()
1038
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1039
self.assertTrue(chk_inv.has_id('fileid'))
1040
self.assertTrue(chk_inv.has_id(inv.root.file_id))
1042
def test_has_id_not(self):
1044
inv.revision_id = "revid"
1045
inv.root.revision = "rootrev"
1046
chk_bytes = self.get_chk_bytes()
1047
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1048
self.assertFalse(chk_inv.has_id('fileid'))
1050
def test_id2path(self):
1052
inv.revision_id = "revid"
1053
inv.root.revision = "rootrev"
1054
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
1055
fileentry = InventoryFile("fileid", "file", "dirid")
1058
inv["fileid"].revision = "filerev"
1059
inv["fileid"].executable = True
1060
inv["fileid"].text_sha1 = "ffff"
1061
inv["fileid"].text_size = 1
1062
inv["dirid"].revision = "filerev"
1063
chk_bytes = self.get_chk_bytes()
1064
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1065
bytes = ''.join(chk_inv.to_lines())
1066
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1067
self.assertEqual('', new_inv.id2path(inv.root.file_id))
1068
self.assertEqual('dir', new_inv.id2path('dirid'))
1069
self.assertEqual('dir/file', new_inv.id2path('fileid'))
1071
def test_path2id(self):
1073
inv.revision_id = "revid"
1074
inv.root.revision = "rootrev"
1075
direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
1076
fileentry = InventoryFile("fileid", "file", "dirid")
1079
inv["fileid"].revision = "filerev"
1080
inv["fileid"].executable = True
1081
inv["fileid"].text_sha1 = "ffff"
1082
inv["fileid"].text_size = 1
1083
inv["dirid"].revision = "filerev"
1084
chk_bytes = self.get_chk_bytes()
1085
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1086
bytes = ''.join(chk_inv.to_lines())
1087
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1088
self.assertEqual(inv.root.file_id, new_inv.path2id(''))
1089
self.assertEqual('dirid', new_inv.path2id('dir'))
1090
self.assertEqual('fileid', new_inv.path2id('dir/file'))
1092
def test_create_by_apply_delta_sets_root(self):
1094
inv.revision_id = "revid"
1095
chk_bytes = self.get_chk_bytes()
1096
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1097
inv.add_path("", "directory", "myrootid", None)
1098
inv.revision_id = "expectedid"
1099
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1100
delta = [("", None, base_inv.root.file_id, None),
1101
(None, "", "myrootid", inv.root)]
1102
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1103
self.assertEqual(reference_inv.root, new_inv.root)
1105
def test_create_by_apply_delta_empty_add_child(self):
1107
inv.revision_id = "revid"
1108
inv.root.revision = "rootrev"
1109
chk_bytes = self.get_chk_bytes()
1110
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1111
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1112
a_entry.revision = "filerev"
1113
a_entry.executable = True
1114
a_entry.text_sha1 = "ffff"
1115
a_entry.text_size = 1
1117
inv.revision_id = "expectedid"
1118
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1119
delta = [(None, "A", "A-id", a_entry)]
1120
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1121
# new_inv should be the same as reference_inv.
1122
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1123
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1124
reference_inv.id_to_entry._ensure_root()
1125
new_inv.id_to_entry._ensure_root()
1126
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1127
new_inv.id_to_entry._root_node._key)
1129
def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
1131
inv.revision_id = "revid"
1132
inv.root.revision = "rootrev"
1133
chk_bytes = self.get_chk_bytes()
1134
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1135
a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1136
a_entry.revision = "filerev"
1137
a_entry.executable = True
1138
a_entry.text_sha1 = "ffff"
1139
a_entry.text_size = 1
1141
inv.revision_id = "expectedid"
1142
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1143
delta = [(None, "A", "A-id", a_entry)]
1144
new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1145
reference_inv.id_to_entry._ensure_root()
1146
reference_inv.parent_id_basename_to_file_id._ensure_root()
1147
new_inv.id_to_entry._ensure_root()
1148
new_inv.parent_id_basename_to_file_id._ensure_root()
1149
# new_inv should be the same as reference_inv.
1150
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1151
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1152
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1153
new_inv.id_to_entry._root_node._key)
1154
self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
1155
new_inv.parent_id_basename_to_file_id._root_node._key)
1157
def test_iter_changes(self):
1158
# Low level bootstrapping smoke test; comprehensive generic tests via
1159
# InterTree are coming.
1161
inv.revision_id = "revid"
1162
inv.root.revision = "rootrev"
1163
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1164
inv["fileid"].revision = "filerev"
1165
inv["fileid"].executable = True
1166
inv["fileid"].text_sha1 = "ffff"
1167
inv["fileid"].text_size = 1
1169
inv2.revision_id = "revid2"
1170
inv2.root.revision = "rootrev"
1171
inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
1172
inv2["fileid"].revision = "filerev2"
1173
inv2["fileid"].executable = False
1174
inv2["fileid"].text_sha1 = "bbbb"
1175
inv2["fileid"].text_size = 2
1176
# get fresh objects.
1177
chk_bytes = self.get_chk_bytes()
1178
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1179
bytes = ''.join(chk_inv.to_lines())
1180
inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1181
chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
1182
bytes = ''.join(chk_inv2.to_lines())
1183
inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
1184
self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
1185
('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
1187
list(inv_1.iter_changes(inv_2)))
1189
def test_parent_id_basename_to_file_id_index_enabled(self):
1191
inv.revision_id = "revid"
1192
inv.root.revision = "rootrev"
1193
inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1194
inv["fileid"].revision = "filerev"
1195
inv["fileid"].executable = True
1196
inv["fileid"].text_sha1 = "ffff"
1197
inv["fileid"].text_size = 1
1198
# get fresh objects.
1199
chk_bytes = self.get_chk_bytes()
1200
tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
1201
bytes = ''.join(tmp_inv.to_lines())
1202
chk_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1203
self.assertIsInstance(chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
1205
{('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
1206
dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
1208
def test_file_entry_to_bytes(self):
1209
inv = CHKInventory(None)
1210
ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
1211
ie.executable = True
1212
ie.revision = 'file-rev-id'
1213
ie.text_sha1 = 'abcdefgh'
1215
bytes = inv._entry_to_bytes(ie)
1216
self.assertEqual('file: file-id\nparent-id\nfilename\n'
1217
'file-rev-id\nabcdefgh\n100\nY', bytes)
1218
ie2 = inv._bytes_to_entry(bytes)
1219
self.assertEqual(ie, ie2)
1220
self.assertIsInstance(ie2.name, unicode)
1221
self.assertEqual(('filename', 'file-id', 'file-rev-id'),
1222
inv._bytes_to_utf8name_key(bytes))
1224
def test_file2_entry_to_bytes(self):
1225
inv = CHKInventory(None)
1227
ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
1228
ie.executable = False
1229
ie.revision = 'file-rev-id'
1230
ie.text_sha1 = '123456'
1232
bytes = inv._entry_to_bytes(ie)
1233
self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
1234
'file-rev-id\n123456\n25\nN', bytes)
1235
ie2 = inv._bytes_to_entry(bytes)
1236
self.assertEqual(ie, ie2)
1237
self.assertIsInstance(ie2.name, unicode)
1238
self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
1239
inv._bytes_to_utf8name_key(bytes))
1241
def test_dir_entry_to_bytes(self):
1242
inv = CHKInventory(None)
1243
ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
1244
ie.revision = 'dir-rev-id'
1245
bytes = inv._entry_to_bytes(ie)
1246
self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1247
ie2 = inv._bytes_to_entry(bytes)
1248
self.assertEqual(ie, ie2)
1249
self.assertIsInstance(ie2.name, unicode)
1250
self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
1251
inv._bytes_to_utf8name_key(bytes))
1253
def test_dir2_entry_to_bytes(self):
1254
inv = CHKInventory(None)
1255
ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1257
ie.revision = 'dir-rev-id'
1258
bytes = inv._entry_to_bytes(ie)
1259
self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
1260
'dir-rev-id', bytes)
1261
ie2 = inv._bytes_to_entry(bytes)
1262
self.assertEqual(ie, ie2)
1263
self.assertIsInstance(ie2.name, unicode)
1264
self.assertIs(ie2.parent_id, None)
1265
self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
1266
inv._bytes_to_utf8name_key(bytes))
1268
def test_symlink_entry_to_bytes(self):
1269
inv = CHKInventory(None)
1270
ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
1271
ie.revision = 'link-rev-id'
1272
ie.symlink_target = u'target/path'
1273
bytes = inv._entry_to_bytes(ie)
1274
self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
1275
'link-rev-id\ntarget/path', bytes)
1276
ie2 = inv._bytes_to_entry(bytes)
1277
self.assertEqual(ie, ie2)
1278
self.assertIsInstance(ie2.name, unicode)
1279
self.assertIsInstance(ie2.symlink_target, unicode)
1280
self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
1281
inv._bytes_to_utf8name_key(bytes))
1283
def test_symlink2_entry_to_bytes(self):
1284
inv = CHKInventory(None)
1285
ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
1286
ie.revision = 'link-rev-id'
1287
ie.symlink_target = u'target/\u03a9path'
1288
bytes = inv._entry_to_bytes(ie)
1289
self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1290
'link-rev-id\ntarget/\xce\xa9path', bytes)
1291
ie2 = inv._bytes_to_entry(bytes)
1292
self.assertEqual(ie, ie2)
1293
self.assertIsInstance(ie2.name, unicode)
1294
self.assertIsInstance(ie2.symlink_target, unicode)
1295
self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
1296
inv._bytes_to_utf8name_key(bytes))
1298
def test_tree_reference_entry_to_bytes(self):
1299
inv = CHKInventory(None)
1300
ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1302
ie.revision = 'tree-rev-id'
1303
ie.reference_revision = 'ref-rev-id'
1304
bytes = inv._entry_to_bytes(ie)
1305
self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1306
'tree-rev-id\nref-rev-id', bytes)
1307
ie2 = inv._bytes_to_entry(bytes)
1308
self.assertEqual(ie, ie2)
1309
self.assertIsInstance(ie2.name, unicode)
1310
self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1311
inv._bytes_to_utf8name_key(bytes))
1313
def make_basic_utf8_inventory(self):
1315
inv.revision_id = "revid"
1316
inv.root.revision = "rootrev"
1317
root_id = inv.root.file_id
1318
inv.add(InventoryFile("fileid", u'f\xefle', root_id))
1319
inv["fileid"].revision = "filerev"
1320
inv["fileid"].text_sha1 = "ffff"
1321
inv["fileid"].text_size = 0
1322
inv.add(InventoryDirectory("dirid", u'dir-\N{EURO SIGN}', root_id))
1323
inv.add(InventoryFile("childid", u'ch\xefld', "dirid"))
1324
inv["childid"].revision = "filerev"
1325
inv["childid"].text_sha1 = "ffff"
1326
inv["childid"].text_size = 0
1327
chk_bytes = self.get_chk_bytes()
1328
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1329
bytes = ''.join(chk_inv.to_lines())
1330
return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1332
def test__preload_handles_utf8(self):
1333
new_inv = self.make_basic_utf8_inventory()
1334
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1335
self.assertFalse(new_inv._fully_cached)
1336
new_inv._preload_cache()
1338
sorted([new_inv.root_id, "fileid", "dirid", "childid"]),
1339
sorted(new_inv._fileid_to_entry_cache.keys()))
1340
ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
1341
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1342
sorted(ie_root._children.keys()))
1343
ie_dir = new_inv._fileid_to_entry_cache['dirid']
1344
self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
1346
def test__preload_populates_cache(self):
1348
inv.revision_id = "revid"
1349
inv.root.revision = "rootrev"
1350
root_id = inv.root.file_id
1351
inv.add(InventoryFile("fileid", "file", root_id))
1352
inv["fileid"].revision = "filerev"
1353
inv["fileid"].executable = True
1354
inv["fileid"].text_sha1 = "ffff"
1355
inv["fileid"].text_size = 1
1356
inv.add(InventoryDirectory("dirid", "dir", root_id))
1357
inv.add(InventoryFile("childid", "child", "dirid"))
1358
inv["childid"].revision = "filerev"
1359
inv["childid"].executable = False
1360
inv["childid"].text_sha1 = "dddd"
1361
inv["childid"].text_size = 1
1362
chk_bytes = self.get_chk_bytes()
1363
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1364
bytes = ''.join(chk_inv.to_lines())
1365
new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1366
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1367
self.assertFalse(new_inv._fully_cached)
1368
new_inv._preload_cache()
1370
sorted([root_id, "fileid", "dirid", "childid"]),
1371
sorted(new_inv._fileid_to_entry_cache.keys()))
1372
self.assertTrue(new_inv._fully_cached)
1373
ie_root = new_inv._fileid_to_entry_cache[root_id]
1374
self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
1375
ie_dir = new_inv._fileid_to_entry_cache['dirid']
1376
self.assertEqual(['child'], sorted(ie_dir._children.keys()))
1378
def test__preload_handles_partially_evaluated_inventory(self):
1379
new_inv = self.make_basic_utf8_inventory()
1380
ie = new_inv[new_inv.root_id]
1381
self.assertIs(None, ie._children)
1382
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1383
sorted(ie.children.keys()))
1384
# Accessing .children loads _children
1385
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1386
sorted(ie._children.keys()))
1387
new_inv._preload_cache()
1389
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1390
sorted(ie._children.keys()))
1391
ie_dir = new_inv["dirid"]
1392
self.assertEqual([u'ch\xefld'],
1393
sorted(ie_dir._children.keys()))
1395
def test_filter_change_in_renamed_subfolder(self):
1396
inv = Inventory('tree-root')
1397
src_ie = inv.add_path('src', 'directory', 'src-id')
1398
inv.add_path('src/sub/', 'directory', 'sub-id')
1399
a_ie = inv.add_path('src/sub/a', 'file', 'a-id')
1400
a_ie.text_sha1 = osutils.sha_string('content\n')
1401
a_ie.text_size = len('content\n')
1402
chk_bytes = self.get_chk_bytes()
1403
inv = CHKInventory.from_inventory(chk_bytes, inv)
1404
inv = inv.create_by_apply_delta([
1405
("src/sub/a", "src/sub/a", "a-id", a_ie),
1406
("src", "src2", "src-id", src_ie),
1408
new_inv = inv.filter(['a-id', 'src-id'])
1412
('src/sub', 'sub-id'),
1413
('src/sub/a', 'a-id'),
1414
], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])
1416
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1418
def get_chk_bytes(self):
1419
factory = groupcompress.make_pack_factory(True, True, 1)
1420
trans = self.get_transport('')
1421
return factory(trans)
1423
def make_dir(self, inv, name, parent_id):
1424
inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
1426
def make_file(self, inv, name, parent_id, content='content\n'):
1427
ie = inv.make_entry('file', name, parent_id, name + '-id')
1428
ie.text_sha1 = osutils.sha_string(content)
1429
ie.text_size = len(content)
1432
def make_simple_inventory(self):
1433
inv = Inventory('TREE_ROOT')
1434
inv.revision_id = "revid"
1435
inv.root.revision = "rootrev"
1438
# sub-file1 sub-file1-id
1439
# sub-file2 sub-file2-id
1440
# sub-dir1/ sub-dir1-id
1441
# subsub-file1 subsub-file1-id
1443
# sub2-file1 sub2-file1-id
1445
self.make_dir(inv, 'dir1', 'TREE_ROOT')
1446
self.make_dir(inv, 'dir2', 'TREE_ROOT')
1447
self.make_dir(inv, 'sub-dir1', 'dir1-id')
1448
self.make_file(inv, 'top', 'TREE_ROOT')
1449
self.make_file(inv, 'sub-file1', 'dir1-id')
1450
self.make_file(inv, 'sub-file2', 'dir1-id')
1451
self.make_file(inv, 'subsub-file1', 'sub-dir1-id')
1452
self.make_file(inv, 'sub2-file1', 'dir2-id')
1453
chk_bytes = self.get_chk_bytes()
1454
# use a small maximum_size to force internal paging structures
1455
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
1457
search_key_name='hash-255-way')
1458
bytes = ''.join(chk_inv.to_lines())
1459
return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1461
def assert_Getitems(self, expected_fileids, inv, file_ids):
1462
self.assertEqual(sorted(expected_fileids),
1463
sorted([ie.file_id for ie in inv._getitems(file_ids)]))
1465
def assertExpand(self, all_ids, inv, file_ids):
1467
val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
1468
self.assertEqual(set(all_ids), val_all_ids)
1469
entries = inv._getitems(val_all_ids)
1470
expected_children = {}
1471
for entry in entries:
1472
s = expected_children.setdefault(entry.parent_id, [])
1473
s.append(entry.file_id)
1474
val_children = dict((k, sorted(v)) for k, v
1475
in val_children.items())
1476
expected_children = dict((k, sorted(v)) for k, v
1477
in expected_children.items())
1478
self.assertEqual(expected_children, val_children)
1480
def test_make_simple_inventory(self):
1481
inv = self.make_simple_inventory()
1483
for path, entry in inv.iter_entries_by_dir():
1484
layout.append((path, entry.file_id))
1487
('dir1', 'dir1-id'),
1488
('dir2', 'dir2-id'),
1490
('dir1/sub-dir1', 'sub-dir1-id'),
1491
('dir1/sub-file1', 'sub-file1-id'),
1492
('dir1/sub-file2', 'sub-file2-id'),
1493
('dir1/sub-dir1/subsub-file1', 'subsub-file1-id'),
1494
('dir2/sub2-file1', 'sub2-file1-id'),
1497
def test__getitems(self):
1498
inv = self.make_simple_inventory()
1500
self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1501
self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1502
self.assertFalse('sub-file2-id' in inv._fileid_to_entry_cache)
1504
self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1506
self.assert_Getitems(['dir1-id', 'sub-file2-id'], inv,
1507
['dir1-id', 'sub-file2-id'])
1508
self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1509
self.assertTrue('sub-file2-id' in inv._fileid_to_entry_cache)
1511
def test_single_file(self):
1512
inv = self.make_simple_inventory()
1513
self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1515
def test_get_all_parents(self):
1516
inv = self.make_simple_inventory()
1517
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1519
], inv, ['subsub-file1-id'])
1521
def test_get_children(self):
1522
inv = self.make_simple_inventory()
1523
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1524
'sub-file1-id', 'sub-file2-id', 'subsub-file1-id',
1525
], inv, ['dir1-id'])
1527
def test_from_root(self):
1528
inv = self.make_simple_inventory()
1529
self.assertExpand(['TREE_ROOT', 'dir1-id', 'dir2-id', 'sub-dir1-id',
1530
'sub-file1-id', 'sub-file2-id', 'sub2-file1-id',
1531
'subsub-file1-id', 'top-id'], inv, ['TREE_ROOT'])
1533
def test_top_level_file(self):
1534
inv = self.make_simple_inventory()
1535
self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1537
def test_subsub_file(self):
1538
inv = self.make_simple_inventory()
1539
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1540
'subsub-file1-id'], inv, ['subsub-file1-id'])
1542
def test_sub_and_root(self):
1543
inv = self.make_simple_inventory()
1544
self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
1545
'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])
1548
class TestMutableInventoryFromTree(TestCaseWithTransport):
1550
def test_empty(self):
1551
repository = self.make_repository('.')
1552
tree = repository.revision_tree(revision.NULL_REVISION)
1553
inv = mutable_inventory_from_tree(tree)
1554
self.assertEqual(revision.NULL_REVISION, inv.revision_id)
1555
self.assertEqual(0, len(inv))
1557
def test_some_files(self):
1558
wt = self.make_branch_and_tree('.')
1559
self.build_tree(['a'])
1560
wt.add(['a'], ['thefileid'])
1561
revid = wt.commit("commit")
1562
tree = wt.branch.repository.revision_tree(revid)
1563
inv = mutable_inventory_from_tree(tree)
1564
self.assertEqual(revid, inv.revision_id)
1565
self.assertEqual(2, len(inv))
1566
self.assertEqual("a", inv['thefileid'].name)
1567
# The inventory should be mutable and independent of
1569
self.assertFalse(tree.root_inventory['thefileid'].executable)
1570
inv['thefileid'].executable = True
1571
self.assertFalse(tree.root_inventory['thefileid'].executable)