~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
from bzrlib import (
19
19
    chk_map,
20
20
    groupcompress,
21
 
    bzrdir,
22
21
    errors,
23
22
    inventory,
24
23
    osutils,
25
24
    repository,
26
25
    revision,
27
26
    tests,
28
 
    )
29
 
from bzrlib.inventory import (CHKInventory, Inventory, ROOT_ID, InventoryFile,
30
 
    InventoryDirectory, InventoryEntry, TreeReference)
 
27
    workingtree,
 
28
    )
 
29
from bzrlib.inventory import (
 
30
    CHKInventory,
 
31
    Inventory,
 
32
    ROOT_ID,
 
33
    InventoryFile,
 
34
    InventoryDirectory,
 
35
    InventoryEntry,
 
36
    TreeReference,
 
37
    mutable_inventory_from_tree,
 
38
    )
31
39
from bzrlib.tests import (
32
40
    TestCase,
33
41
    TestCaseWithTransport,
34
 
    condition_isinstance,
35
 
    multiply_tests,
36
 
    split_suite_by_condition,
37
42
    )
38
 
from bzrlib.tests.per_workingtree import workingtree_formats
39
 
 
40
 
 
41
 
def load_tests(standard_tests, module, loader):
42
 
    """Parameterise some inventory tests."""
43
 
    to_adapt, result = split_suite_by_condition(standard_tests,
44
 
        condition_isinstance(TestDeltaApplication))
 
43
from bzrlib.tests.scenarios import load_tests_apply_scenarios
 
44
 
 
45
 
 
46
load_tests = load_tests_apply_scenarios
 
47
 
 
48
 
 
49
def delta_application_scenarios():
45
50
    scenarios = [
46
51
        ('Inventory', {'apply_delta':apply_inventory_Inventory}),
47
52
        ]
52
57
    # just creating trees.
53
58
    formats = set()
54
59
    for _, format in repository.format_registry.iteritems():
55
 
        scenarios.append((str(format.__name__), {
56
 
            'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
57
 
            'format':format}))
58
 
    for format in workingtree_formats():
 
60
        if format.supports_full_versioned_files:
 
61
            scenarios.append((str(format.__name__), {
 
62
                'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
 
63
                'format':format}))
 
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:
 
67
            continue
59
68
        scenarios.append(
60
69
            (str(format.__class__.__name__) + ".update_basis_by_delta", {
61
70
            'apply_delta':apply_inventory_WT_basis,
64
73
            (str(format.__class__.__name__) + ".apply_inventory_delta", {
65
74
            'apply_delta':apply_inventory_WT,
66
75
            'format':format}))
67
 
    return multiply_tests(to_adapt, scenarios, result)
 
76
    return scenarios
68
77
 
69
78
 
70
79
def create_texts_for_inv(repo, inv):
74
83
        else:
75
84
            lines = []
76
85
        repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
77
 
    
78
 
def apply_inventory_Inventory(self, basis, delta):
 
86
 
 
87
 
 
88
def apply_inventory_Inventory(self, basis, delta, invalid_delta=True):
79
89
    """Apply delta to basis and return the result.
80
 
    
 
90
 
81
91
    :param basis: An inventory to be used as the basis.
82
92
    :param delta: The inventory delta to apply:
83
93
    :return: An inventory resulting from the application.
86
96
    return basis
87
97
 
88
98
 
89
 
def apply_inventory_WT(self, basis, delta):
 
99
def apply_inventory_WT(self, basis, delta, invalid_delta=True):
90
100
    """Apply delta to basis and return the result.
91
101
 
92
102
    This sets the tree state to be basis, and then calls apply_inventory_delta.
93
 
    
 
103
 
94
104
    :param basis: An inventory to be used as the basis.
95
105
    :param delta: The inventory delta to apply:
96
106
    :return: An inventory resulting from the application.
115
125
    tree = tree.bzrdir.open_workingtree()
116
126
    tree.lock_read()
117
127
    self.addCleanup(tree.unlock)
118
 
    # One could add 'tree._validate' here but that would cause 'early' failues 
119
 
    # as far as higher level code is concerned. Possibly adding an
120
 
    # expect_fail parameter to this function and if that is False then do a
121
 
    # validate call.
 
128
    if not invalid_delta:
 
129
        tree._validate()
122
130
    return tree.inventory
123
131
 
124
132
 
125
 
def apply_inventory_WT_basis(self, basis, delta):
 
133
def _create_repo_revisions(repo, basis, delta, invalid_delta):
 
134
    repo.start_write_group()
 
135
    try:
 
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)
 
141
        if invalid_delta:
 
142
            # We don't want to apply the delta to the basis, because we expect
 
143
            # the delta is invalid.
 
144
            result_inv = basis
 
145
            result_inv.revision_id = 'result'
 
146
            target_entries = None
 
147
        else:
 
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()
 
155
    except:
 
156
        repo.abort_write_group()
 
157
        raise
 
158
    return target_entries
 
159
 
 
160
 
 
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())
 
165
    basis_tree.unlock()
 
166
    return basis_tree_entries
 
167
 
 
168
 
 
169
def _populate_different_tree(tree, basis, delta):
 
170
    """Put all entries into tree, but at a unique location."""
 
171
    added_ids = set()
 
172
    added_paths = set()
 
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:
 
176
            continue
 
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:
 
182
            continue
 
183
        tree.add(['unique-dir/' + file_id], [file_id], [ie.kind])
 
184
 
 
185
 
 
186
def apply_inventory_WT_basis(test, basis, delta, invalid_delta=True):
126
187
    """Apply delta to basis and return the result.
127
188
 
128
189
    This sets the parent and then calls update_basis_by_delta.
130
191
    allow safety checks made by the WT to succeed, and finally ensures that all
131
192
    items in the delta with a new path are present in the WT before calling
132
193
    update_basis_by_delta.
133
 
    
 
194
 
134
195
    :param basis: An inventory to be used as the basis.
135
196
    :param delta: The inventory delta to apply:
136
197
    :return: An inventory resulting from the application.
137
198
    """
138
 
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
 
199
    control = test.make_bzrdir('tree', format=test.format._matchingbzrdir)
139
200
    control.create_repository()
140
201
    control.create_branch()
141
 
    tree = self.format.initialize(control)
 
202
    tree = test.format.initialize(control)
142
203
    tree.lock_write()
143
204
    try:
144
 
        repo = tree.branch.repository
145
 
        repo.start_write_group()
146
 
        try:
147
 
            rev = revision.Revision('basis', timestamp=0, timezone=None,
148
 
                message="", committer="foo@example.com")
149
 
            basis.revision_id = 'basis'
150
 
            create_texts_for_inv(tree.branch.repository, basis)
151
 
            repo.add_revision('basis', rev, basis)
152
 
            # Add a revision for the result, with the basis content - 
153
 
            # update_basis_by_delta doesn't check that the delta results in
154
 
            # result, and we want inconsistent deltas to get called on the
155
 
            # tree, or else the code isn't actually checked.
156
 
            rev = revision.Revision('result', timestamp=0, timezone=None,
157
 
                message="", committer="foo@example.com")
158
 
            basis.revision_id = 'result'
159
 
            repo.add_revision('result', rev, basis)
160
 
            repo.commit_write_group()
161
 
        except:
162
 
            repo.abort_write_group()
163
 
            raise
 
205
        target_entries = _create_repo_revisions(tree.branch.repository, basis,
 
206
                                                delta, invalid_delta)
164
207
        # Set the basis state as the trees current state
165
208
        tree._write_inventory(basis)
166
209
        # This reads basis from the repo and puts it into the tree's local
167
210
        # cache, if it has one.
168
211
        tree.set_parent_ids(['basis'])
169
 
        paths = {}
170
 
        parents = set()
171
 
        for old, new, id, entry in delta:
172
 
            if None in (new, entry):
173
 
                continue
174
 
            paths[new] = (entry.file_id, entry.kind)
175
 
            parents.add(osutils.dirname(new))
176
 
        parents = osutils.minimum_path_selection(parents)
177
 
        parents.discard('')
178
 
        # Put place holders in the tree to permit adding the other entries.
179
 
        for pos, parent in enumerate(parents):
180
 
            if not tree.path2id(parent):
181
 
                # add a synthetic directory in the tree so we can can put the
182
 
                # tree0 entries in place for dirstate.
183
 
                tree.add([parent], ["id%d" % pos], ["directory"])
184
 
        if paths:
185
 
            # Many deltas may cause this mini-apply to fail, but we want to see what
186
 
            # the delta application code says, not the prep that we do to deal with 
187
 
            # limitations of dirstate's update_basis code.
188
 
            for path, (file_id, kind) in sorted(paths.items()):
189
 
                try:
190
 
                    tree.add([path], [file_id], [kind])
191
 
                except (KeyboardInterrupt, SystemExit):
192
 
                    raise
193
 
                except:
194
 
                    pass
195
212
    finally:
196
213
        tree.unlock()
197
214
    # Fresh lock, reads disk again.
198
215
    tree.lock_write()
199
216
    try:
200
217
        tree.update_basis_by_delta('result', delta)
 
218
        if not invalid_delta:
 
219
            tree._validate()
201
220
    finally:
202
221
        tree.unlock()
203
222
    # reload tree - ensure we get what was written.
204
223
    tree = tree.bzrdir.open_workingtree()
205
224
    basis_tree = tree.basis_tree()
206
225
    basis_tree.lock_read()
207
 
    self.addCleanup(basis_tree.unlock)
208
 
    # Note, that if the tree does not have a local cache, the trick above of
209
 
    # setting the result as the basis, will come back to bite us. That said,
210
 
    # all the implementations in bzr do have a local cache.
211
 
    return basis_tree.inventory
212
 
 
213
 
 
214
 
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
 
226
    test.addCleanup(basis_tree.unlock)
 
227
    basis_inv = basis_tree.inventory
 
228
    if target_entries:
 
229
        basis_entries = list(basis_inv.iter_entries_by_dir())
 
230
        test.assertEqual(target_entries, basis_entries)
 
231
    return basis_inv
 
232
 
 
233
 
 
234
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta,
 
235
                                                      invalid_delta=True):
215
236
    """Apply delta to basis and return the result.
216
237
    
217
238
    This inserts basis as a whole inventory and then uses
330
351
 
331
352
 
332
353
class TestDeltaApplication(TestCaseWithTransport):
 
354
 
 
355
    scenarios = delta_application_scenarios()
333
356
 
334
357
    def get_empty_inventory(self, reference_inv=None):
335
358
        """Get an empty inventory.
350
373
            inv.root.revision = 'basis'
351
374
        return inv
352
375
 
 
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 = ''
 
381
        return ie_file
 
382
 
353
383
    def test_empty_delta(self):
354
384
        inv = self.get_empty_inventory()
355
385
        delta = []
379
409
        file1.revision = 'result'
380
410
        file1.text_size = 0
381
411
        file1.text_sha1 = ""
382
 
        file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
383
 
        file2.revision = 'result'
384
 
        file2.text_size = 0
385
 
        file2.text_sha1 = ""
 
412
        file2 = file1.copy()
 
413
        file2.name = 'path2'
386
414
        delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
387
415
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
388
416
            inv, delta)
393
421
        file1.revision = 'result'
394
422
        file1.text_size = 0
395
423
        file1.text_sha1 = ""
396
 
        file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
397
 
        file2.revision = 'result'
398
 
        file2.text_size = 0
399
 
        file2.text_sha1 = ""
 
424
        file2 = file1.copy()
 
425
        file2.file_id = 'id2'
400
426
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
401
427
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
402
428
            inv, delta)
574
600
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
575
601
            inv, delta)
576
602
 
577
 
 
578
 
class TestInventory(TestCase):
 
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'
 
607
        file1.text_size = 0
 
608
        file1.text_sha1 = ''
 
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)
 
612
 
 
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'
 
617
        file1.text_size = 0
 
618
        file1.text_sha1 = ''
 
619
        inv.add(file1)
 
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')
 
624
 
 
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)
 
628
        inv.add(file1)
 
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'))
 
634
 
 
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)
 
638
        inv.add(file1)
 
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'))
 
644
 
 
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')
 
650
        inv.add(dir1)
 
651
        inv.add(file1)
 
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'))
 
658
 
 
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')
 
665
        inv.add(dir1)
 
666
        inv.add(file1)
 
667
        inv.add(file2)
 
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'))
579
678
 
580
679
    def test_is_root(self):
581
680
        """Ensure our root-checking code is accurate."""
590
689
        self.assertFalse(inv.is_root('TREE_ROOT'))
591
690
        self.assertFalse(inv.is_root('booga'))
592
691
 
 
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())
 
696
 
593
697
 
594
698
class TestInventoryEntry(TestCase):
595
699
 
607
711
 
608
712
    def test_dir_detect_changes(self):
609
713
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
610
 
        left.text_sha1 = 123
611
 
        left.executable = True
612
 
        left.symlink_target='foo'
613
714
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
614
 
        right.text_sha1 = 321
615
 
        right.symlink_target='bar'
616
715
        self.assertEqual((False, False), left.detect_changes(right))
617
716
        self.assertEqual((False, False), right.detect_changes(left))
618
717
 
632
731
 
633
732
    def test_symlink_detect_changes(self):
634
733
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
635
 
        left.text_sha1 = 123
636
 
        left.executable = True
637
734
        left.symlink_target='foo'
638
735
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
639
 
        right.text_sha1 = 321
640
736
        right.symlink_target='foo'
641
737
        self.assertEqual((False, False), left.detect_changes(right))
642
738
        self.assertEqual((False, False), right.detect_changes(left))
646
742
 
647
743
    def test_file_has_text(self):
648
744
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
649
 
        self.failUnless(file.has_text())
 
745
        self.assertTrue(file.has_text())
650
746
 
651
747
    def test_directory_has_text(self):
652
748
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
653
 
        self.failIf(dir.has_text())
 
749
        self.assertFalse(dir.has_text())
654
750
 
655
751
    def test_link_has_text(self):
656
752
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
657
 
        self.failIf(link.has_text())
 
753
        self.assertFalse(link.has_text())
658
754
 
659
755
    def test_make_entry(self):
660
756
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
1215
1311
        self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1216
1312
                         inv._bytes_to_utf8name_key(bytes))
1217
1313
 
 
1314
    def make_basic_utf8_inventory(self):
 
1315
        inv = Inventory()
 
1316
        inv.revision_id = "revid"
 
1317
        inv.root.revision = "rootrev"
 
1318
        root_id = inv.root.file_id
 
1319
        inv.add(InventoryFile("fileid", u'f\xefle', root_id))
 
1320
        inv["fileid"].revision = "filerev"
 
1321
        inv["fileid"].text_sha1 = "ffff"
 
1322
        inv["fileid"].text_size = 0
 
1323
        inv.add(InventoryDirectory("dirid", u'dir-\N{EURO SIGN}', root_id))
 
1324
        inv.add(InventoryFile("childid", u'ch\xefld', "dirid"))
 
1325
        inv["childid"].revision = "filerev"
 
1326
        inv["childid"].text_sha1 = "ffff"
 
1327
        inv["childid"].text_size = 0
 
1328
        chk_bytes = self.get_chk_bytes()
 
1329
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1330
        bytes = ''.join(chk_inv.to_lines())
 
1331
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1332
 
 
1333
    def test__preload_handles_utf8(self):
 
1334
        new_inv = self.make_basic_utf8_inventory()
 
1335
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
 
1336
        self.assertFalse(new_inv._fully_cached)
 
1337
        new_inv._preload_cache()
 
1338
        self.assertEqual(
 
1339
            sorted([new_inv.root_id, "fileid", "dirid", "childid"]),
 
1340
            sorted(new_inv._fileid_to_entry_cache.keys()))
 
1341
        ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
 
1342
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1343
                         sorted(ie_root._children.keys()))
 
1344
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
 
1345
        self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
 
1346
 
 
1347
    def test__preload_populates_cache(self):
 
1348
        inv = Inventory()
 
1349
        inv.revision_id = "revid"
 
1350
        inv.root.revision = "rootrev"
 
1351
        root_id = inv.root.file_id
 
1352
        inv.add(InventoryFile("fileid", "file", root_id))
 
1353
        inv["fileid"].revision = "filerev"
 
1354
        inv["fileid"].executable = True
 
1355
        inv["fileid"].text_sha1 = "ffff"
 
1356
        inv["fileid"].text_size = 1
 
1357
        inv.add(InventoryDirectory("dirid", "dir", root_id))
 
1358
        inv.add(InventoryFile("childid", "child", "dirid"))
 
1359
        inv["childid"].revision = "filerev"
 
1360
        inv["childid"].executable = False
 
1361
        inv["childid"].text_sha1 = "dddd"
 
1362
        inv["childid"].text_size = 1
 
1363
        chk_bytes = self.get_chk_bytes()
 
1364
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1365
        bytes = ''.join(chk_inv.to_lines())
 
1366
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1367
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
 
1368
        self.assertFalse(new_inv._fully_cached)
 
1369
        new_inv._preload_cache()
 
1370
        self.assertEqual(
 
1371
            sorted([root_id, "fileid", "dirid", "childid"]),
 
1372
            sorted(new_inv._fileid_to_entry_cache.keys()))
 
1373
        self.assertTrue(new_inv._fully_cached)
 
1374
        ie_root = new_inv._fileid_to_entry_cache[root_id]
 
1375
        self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
 
1376
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
 
1377
        self.assertEqual(['child'], sorted(ie_dir._children.keys()))
 
1378
 
 
1379
    def test__preload_handles_partially_evaluated_inventory(self):
 
1380
        new_inv = self.make_basic_utf8_inventory()
 
1381
        ie = new_inv[new_inv.root_id]
 
1382
        self.assertIs(None, ie._children)
 
1383
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1384
                         sorted(ie.children.keys()))
 
1385
        # Accessing .children loads _children
 
1386
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1387
                         sorted(ie._children.keys()))
 
1388
        new_inv._preload_cache()
 
1389
        # No change
 
1390
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1391
                         sorted(ie._children.keys()))
 
1392
        ie_dir = new_inv["dirid"]
 
1393
        self.assertEqual([u'ch\xefld'],
 
1394
                         sorted(ie_dir._children.keys()))
 
1395
 
 
1396
    def test_filter_change_in_renamed_subfolder(self):
 
1397
        inv = Inventory('tree-root')
 
1398
        src_ie = inv.add_path('src', 'directory', 'src-id')
 
1399
        inv.add_path('src/sub/', 'directory', 'sub-id')
 
1400
        a_ie = inv.add_path('src/sub/a', 'file', 'a-id')
 
1401
        a_ie.text_sha1 = osutils.sha_string('content\n')
 
1402
        a_ie.text_size = len('content\n')
 
1403
        chk_bytes = self.get_chk_bytes()
 
1404
        inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1405
        inv = inv.create_by_apply_delta([
 
1406
            ("src/sub/a", "src/sub/a", "a-id", a_ie),
 
1407
            ("src", "src2", "src-id", src_ie),
 
1408
            ], 'new-rev-2')
 
1409
        new_inv = inv.filter(['a-id', 'src-id'])
 
1410
        self.assertEqual([
 
1411
            ('', 'tree-root'),
 
1412
            ('src', 'src-id'),
 
1413
            ('src/sub', 'sub-id'),
 
1414
            ('src/sub/a', 'a-id'),
 
1415
            ], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])
1218
1416
 
1219
1417
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1220
1418
 
1346
1544
        inv = self.make_simple_inventory()
1347
1545
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
1348
1546
                           'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])
 
1547
 
 
1548
 
 
1549
class TestMutableInventoryFromTree(TestCaseWithTransport):
 
1550
 
 
1551
    def test_empty(self):
 
1552
        repository = self.make_repository('.')
 
1553
        tree = repository.revision_tree(revision.NULL_REVISION)
 
1554
        inv = mutable_inventory_from_tree(tree)
 
1555
        self.assertEquals(revision.NULL_REVISION, inv.revision_id)
 
1556
        self.assertEquals(0, len(inv))
 
1557
 
 
1558
    def test_some_files(self):
 
1559
        wt = self.make_branch_and_tree('.')
 
1560
        self.build_tree(['a'])
 
1561
        wt.add(['a'], ['thefileid'])
 
1562
        revid = wt.commit("commit")
 
1563
        tree = wt.branch.repository.revision_tree(revid)
 
1564
        inv = mutable_inventory_from_tree(tree)
 
1565
        self.assertEquals(revid, inv.revision_id)
 
1566
        self.assertEquals(2, len(inv))
 
1567
        self.assertEquals("a", inv['thefileid'].name)
 
1568
        # The inventory should be mutable and independent of
 
1569
        # the original tree
 
1570
        self.assertFalse(tree.inventory['thefileid'].executable)
 
1571
        inv['thefileid'].executable = True
 
1572
        self.assertFalse(tree.inventory['thefileid'].executable)