~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-25 01:16:46 UTC
  • Revision ID: mbp@sourcefrog.net-20050325011646-e3f0af5d6bd1190c
- update version string
- put it in bzrlib

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
2
 
#
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.
7
 
#
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.
12
 
#
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
16
 
 
17
 
 
18
 
from bzrlib import (
19
 
    chk_map,
20
 
    groupcompress,
21
 
    errors,
22
 
    inventory,
23
 
    osutils,
24
 
    repository,
25
 
    revision,
26
 
    tests,
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
 
    )
39
 
from bzrlib.tests import (
40
 
    TestCase,
41
 
    TestCaseWithTransport,
42
 
    )
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():
50
 
    scenarios = [
51
 
        ('Inventory', {'apply_delta':apply_inventory_Inventory}),
52
 
        ]
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.
58
 
    formats = set()
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,
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
68
 
        scenarios.append(
69
 
            (str(format.__class__.__name__) + ".update_basis_by_delta", {
70
 
            'apply_delta':apply_inventory_WT_basis,
71
 
            'format':format}))
72
 
        scenarios.append(
73
 
            (str(format.__class__.__name__) + ".apply_inventory_delta", {
74
 
            'apply_delta':apply_inventory_WT,
75
 
            'format':format}))
76
 
    return scenarios
77
 
 
78
 
 
79
 
def create_texts_for_inv(repo, inv):
80
 
    for path, ie in inv.iter_entries():
81
 
        if ie.text_size:
82
 
            lines = ['a' * ie.text_size]
83
 
        else:
84
 
            lines = []
85
 
        repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
86
 
 
87
 
 
88
 
def apply_inventory_Inventory(self, basis, delta):
89
 
    """Apply delta to basis and return the result.
90
 
    
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.
94
 
    """
95
 
    basis.apply_delta(delta)
96
 
    return basis
97
 
 
98
 
 
99
 
def apply_inventory_WT(self, basis, delta):
100
 
    """Apply delta to basis and return the result.
101
 
 
102
 
    This sets the tree state to be basis, and then calls apply_inventory_delta.
103
 
    
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.
107
 
    """
108
 
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
109
 
    control.create_repository()
110
 
    control.create_branch()
111
 
    tree = self.format.initialize(control)
112
 
    tree.lock_write()
113
 
    try:
114
 
        tree._write_inventory(basis)
115
 
    finally:
116
 
        tree.unlock()
117
 
    # Fresh object, reads disk again.
118
 
    tree = tree.bzrdir.open_workingtree()
119
 
    tree.lock_write()
120
 
    try:
121
 
        tree.apply_inventory_delta(delta)
122
 
    finally:
123
 
        tree.unlock()
124
 
    # reload tree - ensure we get what was written.
125
 
    tree = tree.bzrdir.open_workingtree()
126
 
    tree.lock_read()
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
131
 
    # validate call.
132
 
    return tree.inventory
133
 
 
134
 
 
135
 
def apply_inventory_WT_basis(self, basis, delta):
136
 
    """Apply delta to basis and return the result.
137
 
 
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.
143
 
    
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.
147
 
    """
148
 
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
149
 
    control.create_repository()
150
 
    control.create_branch()
151
 
    tree = self.format.initialize(control)
152
 
    tree.lock_write()
153
 
    try:
154
 
        repo = tree.branch.repository
155
 
        repo.start_write_group()
156
 
        try:
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()
171
 
        except:
172
 
            repo.abort_write_group()
173
 
            raise
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'])
179
 
        paths = {}
180
 
        parents = set()
181
 
        for old, new, id, entry in delta:
182
 
            if None in (new, entry):
183
 
                continue
184
 
            paths[new] = (entry.file_id, entry.kind)
185
 
            parents.add(osutils.dirname(new))
186
 
        parents = osutils.minimum_path_selection(parents)
187
 
        parents.discard('')
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"])
194
 
        if paths:
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()):
199
 
                try:
200
 
                    tree.add([path], [file_id], [kind])
201
 
                except (KeyboardInterrupt, SystemExit):
202
 
                    raise
203
 
                except:
204
 
                    pass
205
 
    finally:
206
 
        tree.unlock()
207
 
    # Fresh lock, reads disk again.
208
 
    tree.lock_write()
209
 
    try:
210
 
        tree.update_basis_by_delta('result', delta)
211
 
    finally:
212
 
        tree.unlock()
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
222
 
 
223
 
 
224
 
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
225
 
    """Apply delta to basis and return the result.
226
 
    
227
 
    This inserts basis as a whole inventory and then uses
228
 
    add_inventory_by_delta to add delta.
229
 
 
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.
233
 
    """
234
 
    format = self.format()
235
 
    control = self.make_bzrdir('tree', format=format._matchingbzrdir)
236
 
    repo = format.initialize(control)
237
 
    repo.lock_write()
238
 
    try:
239
 
        repo.start_write_group()
240
 
        try:
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()
247
 
        except:
248
 
            repo.abort_write_group()
249
 
            raise
250
 
    finally:
251
 
        repo.unlock()
252
 
    repo.lock_write()
253
 
    try:
254
 
        repo.start_write_group()
255
 
        try:
256
 
            inv_sha1 = repo.add_inventory_by_delta('basis', delta,
257
 
                'result', ['basis'])
258
 
        except:
259
 
            repo.abort_write_group()
260
 
            raise
261
 
        else:
262
 
            repo.commit_write_group()
263
 
    finally:
264
 
        repo.unlock()
265
 
    # Fresh lock, reads disk again.
266
 
    repo = repo.bzrdir.open_repository()
267
 
    repo.lock_read()
268
 
    self.addCleanup(repo.unlock)
269
 
    return repo.get_inventory('result')
270
 
 
271
 
 
272
 
class TestInventoryUpdates(TestCase):
273
 
 
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)
279
 
 
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)
289
 
 
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)
295
 
 
296
 
    def test_copy(self):
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')
300
 
        inv2 = inv.copy()
301
 
        inv.root.file_id = 'some-new-root'
302
 
        ie.name = 'file2'
303
 
        self.assertEqual('some-tree-root', inv2.root.file_id)
304
 
        self.assertEqual('hello', inv2['hello-id'].name)
305
 
 
306
 
    def test_copy_empty(self):
307
 
        """Make sure an empty inventory can be copied."""
308
 
        inv = inventory.Inventory(root_id=None)
309
 
        inv2 = inv.copy()
310
 
        self.assertIs(None, inv2.root)
311
 
 
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'
316
 
        inv2 = inv.copy()
317
 
        self.assertEquals('someroot', inv2.root.file_id)
318
 
        self.assertEquals('therev', inv2.root.revision)
319
 
 
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'))
324
 
 
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')
331
 
 
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')
337
 
        inv.add(parent)
338
 
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
339
 
 
340
 
 
341
 
 
342
 
class TestDeltaApplication(TestCaseWithTransport):
343
 
 
344
 
    scenarios = delta_application_scenarios()
345
 
 
346
 
    def get_empty_inventory(self, reference_inv=None):
347
 
        """Get an empty inventory.
348
 
 
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.
352
 
 
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'.
357
 
        """
358
 
        inv = inventory.Inventory()
359
 
        if reference_inv is not None:
360
 
            inv.root.revision = reference_inv.root.revision
361
 
        else:
362
 
            inv.root.revision = 'basis'
363
 
        return inv
364
 
 
365
 
    def test_empty_delta(self):
366
 
        inv = self.get_empty_inventory()
367
 
        delta = []
368
 
        inv = self.apply_delta(self, inv, delta)
369
 
        inv2 = self.get_empty_inventory(inv)
370
 
        self.assertEqual([], inv2._make_delta(inv))
371
 
 
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,
378
 
            inv, delta)
379
 
 
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,
386
 
            inv, delta)
387
 
 
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'
392
 
        file1.text_size = 0
393
 
        file1.text_sha1 = ""
394
 
        file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
395
 
        file2.revision = 'result'
396
 
        file2.text_size = 0
397
 
        file2.text_sha1 = ""
398
 
        delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
399
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
400
 
            inv, delta)
401
 
 
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'
406
 
        file1.text_size = 0
407
 
        file1.text_sha1 = ""
408
 
        file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
409
 
        file2.revision = 'result'
410
 
        file2.text_size = 0
411
 
        file2.text_sha1 = ""
412
 
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
413
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
414
 
            inv, delta)
415
 
 
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'
420
 
        file1.text_size = 0
421
 
        file1.text_sha1 = ""
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'
430
 
        file2.text_size = 0
431
 
        file2.text_sha1 = ""
432
 
        inv.add(file1)
433
 
        inv.add(file2)
434
 
        delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
435
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
436
 
            inv, delta)
437
 
 
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'
442
 
        file1.text_size = 0
443
 
        file1.text_sha1 = ""
444
 
        delta = [(None, u'path', 'id', file1)]
445
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
446
 
            inv, delta)
447
 
 
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,
452
 
            inv, delta)
453
 
 
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'
458
 
        file1.text_size = 0
459
 
        file1.text_sha1 = ""
460
 
        delta = [(u"path", None, 'id1', file1)]
461
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
462
 
            inv, delta)
463
 
 
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'
468
 
        file1.text_size = 0
469
 
        file1.text_sha1 = ""
470
 
        file2 = inventory.InventoryFile('id2', 'path2', 'id1')
471
 
        file2.revision = 'result'
472
 
        file2.text_size = 0
473
 
        file2.text_sha1 = ""
474
 
        inv.add(file1)
475
 
        delta = [(None, u'path/path2', 'id2', file2)]
476
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
477
 
            inv, delta)
478
 
 
479
 
    def test_parent_is_missing(self):
480
 
        inv = self.get_empty_inventory()
481
 
        file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
482
 
        file2.revision = 'result'
483
 
        file2.text_size = 0
484
 
        file2.text_sha1 = ""
485
 
        delta = [(None, u'path/path2', 'id2', file2)]
486
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
487
 
            inv, delta)
488
 
 
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'
497
 
        file1.text_size = 0
498
 
        file1.text_sha1 = ""
499
 
        inv.add(parent1)
500
 
        inv.add(parent2)
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,
505
 
            inv, delta)
506
 
 
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'
515
 
        file1.text_size = 0
516
 
        file1.text_sha1 = ""
517
 
        inv.add(parent1)
518
 
        inv.add(parent2)
519
 
        inv.add(file1)
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,
524
 
            inv, delta)
525
 
 
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'
534
 
        file1.text_size = 0
535
 
        file1.text_sha1 = ""
536
 
        file2 = inventory.InventoryFile('id2', 'path', 'p-1')
537
 
        file2.revision = 'result'
538
 
        file2.text_size = 0
539
 
        file2.text_sha1 = ""
540
 
        inv.add(parent1)
541
 
        inv.add(parent2)
542
 
        inv.add(file1)
543
 
        inv.add(file2)
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,
549
 
            inv, delta)
550
 
 
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'
557
 
        inv.add(parent1)
558
 
        delta = [(None, u'dir2', 'p-1', parent2)]
559
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
560
 
            inv, delta)
561
 
 
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'
568
 
        inv.add(parent1)
569
 
        delta = [(None, u'dir1', 'p-2', parent2)]
570
 
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
571
 
            inv, delta)
572
 
 
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'
581
 
        inv.add(dir1)
582
 
        inv.add(dir2)
583
 
        inv.add(dir3)
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,
587
 
            inv, delta)
588
 
 
589
 
 
590
 
class TestInventory(TestCase):
591
 
 
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
601
 
        inv.root = None
602
 
        self.assertFalse(inv.is_root('TREE_ROOT'))
603
 
        self.assertFalse(inv.is_root('booga'))
604
 
 
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())
609
 
 
610
 
 
611
 
class TestInventoryEntry(TestCase):
612
 
 
613
 
    def test_file_kind_character(self):
614
 
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
615
 
        self.assertEqual(file.kind_character(), '')
616
 
 
617
 
    def test_dir_kind_character(self):
618
 
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
619
 
        self.assertEqual(dir.kind_character(), '/')
620
 
 
621
 
    def test_link_kind_character(self):
622
 
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
623
 
        self.assertEqual(dir.kind_character(), '')
624
 
 
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))
630
 
 
631
 
    def test_file_detect_changes(self):
632
 
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
633
 
        left.text_sha1 = 123
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))
644
 
 
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))
655
 
 
656
 
    def test_file_has_text(self):
657
 
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
658
 
        self.assertTrue(file.has_text())
659
 
 
660
 
    def test_directory_has_text(self):
661
 
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
662
 
        self.assertFalse(dir.has_text())
663
 
 
664
 
    def test_link_has_text(self):
665
 
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
666
 
        self.assertFalse(link.has_text())
667
 
 
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)
675
 
 
676
 
    def test_make_entry_non_normalized(self):
677
 
        orig_normalized_filename = osutils.normalized_filename
678
 
 
679
 
        try:
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)
684
 
 
685
 
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
686
 
            self.assertRaises(errors.InvalidNormalization,
687
 
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
688
 
        finally:
689
 
            osutils.normalized_filename = orig_normalized_filename
690
 
 
691
 
 
692
 
class TestDescribeChanges(TestCase):
693
 
 
694
 
    def test_describe_change(self):
695
 
        # we need to test the following change combinations:
696
 
        # rename
697
 
        # reparent
698
 
        # modify
699
 
        # gone
700
 
        # added
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'
706
 
        old_a.text_size = 0
707
 
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
708
 
        new_a.text_sha1 = '123132'
709
 
        new_a.text_size = 0
710
 
 
711
 
        self.assertChangeDescription('unchanged', old_a, new_a)
712
 
 
713
 
        new_a.text_size = 10
714
 
        new_a.text_sha1 = 'abcabc'
715
 
        self.assertChangeDescription('modified', old_a, new_a)
716
 
 
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)
721
 
 
722
 
        # in this case it's both renamed and modified; show a rename and
723
 
        # modification:
724
 
        new_a.name = 'newfilename'
725
 
        self.assertChangeDescription('modified and renamed', old_a, new_a)
726
 
 
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)
731
 
 
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
736
 
 
737
 
        new_a.name = 'newfilename'
738
 
        self.assertChangeDescription('renamed', old_a, new_a)
739
 
 
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)
744
 
 
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)
748
 
 
749
 
 
750
 
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
751
 
 
752
 
    def get_chk_bytes(self):
753
 
        factory = groupcompress.make_pack_factory(True, True, 1)
754
 
        trans = self.get_transport('')
755
 
        return factory(trans)
756
 
 
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")
760
 
 
761
 
    def test_deserialise_gives_CHKInventory(self):
762
 
        inv = Inventory()
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)
776
 
 
777
 
    def test_deserialise_wrong_revid(self):
778
 
        inv = Inventory()
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,
785
 
            bytes, ("revid2",))
786
 
 
787
 
    def test_captures_rev_root_byid(self):
788
 
        inv = Inventory()
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()
794
 
        self.assertEqual([
795
 
            'chkinventory:\n',
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',
800
 
            ], lines)
801
 
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
802
 
        self.assertEqual('plain', chk_inv._search_key_name)
803
 
 
804
 
    def test_captures_parent_id_basename_index(self):
805
 
        inv = Inventory()
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()
811
 
        self.assertEqual([
812
 
            'chkinventory:\n',
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',
817
 
            ], lines)
818
 
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
819
 
        self.assertEqual('plain', chk_inv._search_key_name)
820
 
 
821
 
    def test_captures_search_key_name(self):
822
 
        inv = Inventory()
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()
829
 
        self.assertEqual([
830
 
            'chkinventory:\n',
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',
836
 
            ], lines)
837
 
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
838
 
        self.assertEqual('hash-16-way', chk_inv._search_key_name)
839
 
 
840
 
    def test_directory_children_on_demand(self):
841
 
        inv = Inventory()
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)
866
 
 
867
 
    def test_from_inventory_maximum_size(self):
868
 
        # from_inventory supports the maximum_size parameter.
869
 
        inv = Inventory()
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)
881
 
 
882
 
    def test___iter__(self):
883
 
        inv = Inventory()
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__())
896
 
        fileids.sort()
897
 
        self.assertEqual([inv.root.file_id, "fileid"], fileids)
898
 
 
899
 
    def test__len__(self):
900
 
        inv = Inventory()
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))
911
 
 
912
 
    def test___getitem__(self):
913
 
        inv = Inventory()
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')
941
 
 
942
 
    def test_has_id_true(self):
943
 
        inv = Inventory()
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))
955
 
 
956
 
    def test_has_id_not(self):
957
 
        inv = Inventory()
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'))
963
 
 
964
 
    def test_id2path(self):
965
 
        inv = Inventory()
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")
970
 
        inv.add(direntry)
971
 
        inv.add(fileentry)
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'))
984
 
 
985
 
    def test_path2id(self):
986
 
        inv = Inventory()
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")
991
 
        inv.add(direntry)
992
 
        inv.add(fileentry)
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'))
1005
 
 
1006
 
    def test_create_by_apply_delta_sets_root(self):
1007
 
        inv = Inventory()
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)
1018
 
 
1019
 
    def test_create_by_apply_delta_empty_add_child(self):
1020
 
        inv = Inventory()
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
1030
 
        inv.add(a_entry)
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)
1042
 
 
1043
 
    def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
1044
 
        inv = Inventory()
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
1054
 
        inv.add(a_entry)
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)
1070
 
 
1071
 
    def test_iter_changes(self):
1072
 
        # Low level bootstrapping smoke test; comprehensive generic tests via
1073
 
        # InterTree are coming.
1074
 
        inv = Inventory()
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
1082
 
        inv2 = Inventory()
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'),
1100
 
            (False, True))],
1101
 
            list(inv_1.iter_changes(inv_2)))
1102
 
 
1103
 
    def test_parent_id_basename_to_file_id_index_enabled(self):
1104
 
        inv = Inventory()
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)
1118
 
        self.assertEqual(
1119
 
            {('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
1120
 
            dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
1121
 
 
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'
1128
 
        ie.text_size = 100
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))
1137
 
 
1138
 
    def test_file2_entry_to_bytes(self):
1139
 
        inv = CHKInventory(None)
1140
 
        # \u30a9 == 'omega'
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'
1145
 
        ie.text_size = 25
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))
1154
 
 
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))
1166
 
 
1167
 
    def test_dir2_entry_to_bytes(self):
1168
 
        inv = CHKInventory(None)
1169
 
        ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1170
 
                                          None)
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))
1181
 
 
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))
1196
 
 
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))
1211
 
 
1212
 
    def test_tree_reference_entry_to_bytes(self):
1213
 
        inv = CHKInventory(None)
1214
 
        ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1215
 
                                     'parent-id')
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))
1226
 
 
1227
 
    def make_basic_utf8_inventory(self):
1228
 
        inv = Inventory()
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",))
1245
 
 
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()
1251
 
        self.assertEqual(
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()))
1259
 
 
1260
 
    def test__preload_populates_cache(self):
1261
 
        inv = Inventory()
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()
1283
 
        self.assertEqual(
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()))
1291
 
 
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()
1302
 
        # No change
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()))
1308
 
 
1309
 
 
1310
 
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1311
 
 
1312
 
    def get_chk_bytes(self):
1313
 
        factory = groupcompress.make_pack_factory(True, True, 1)
1314
 
        trans = self.get_transport('')
1315
 
        return factory(trans)
1316
 
 
1317
 
    def make_dir(self, inv, name, parent_id):
1318
 
        inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
1319
 
 
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)
1324
 
        inv.add(ie)
1325
 
 
1326
 
    def make_simple_inventory(self):
1327
 
        inv = Inventory('TREE_ROOT')
1328
 
        inv.revision_id = "revid"
1329
 
        inv.root.revision = "rootrev"
1330
 
        # /                 TREE_ROOT
1331
 
        # dir1/             dir1-id
1332
 
        #   sub-file1       sub-file1-id
1333
 
        #   sub-file2       sub-file2-id
1334
 
        #   sub-dir1/       sub-dir1-id
1335
 
        #     subsub-file1  subsub-file1-id
1336
 
        # dir2/             dir2-id
1337
 
        #   sub2-file1      sub2-file1-id
1338
 
        # top               top-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,
1350
 
                        maximum_size=100,
1351
 
                        search_key_name='hash-255-way')
1352
 
        bytes = ''.join(chk_inv.to_lines())
1353
 
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1354
 
 
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)]))
1358
 
 
1359
 
    def assertExpand(self, all_ids, inv, file_ids):
1360
 
        (val_all_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)
1373
 
 
1374
 
    def test_make_simple_inventory(self):
1375
 
        inv = self.make_simple_inventory()
1376
 
        layout = []
1377
 
        for path, entry in inv.iter_entries_by_dir():
1378
 
            layout.append((path, entry.file_id))
1379
 
        self.assertEqual([
1380
 
            ('', 'TREE_ROOT'),
1381
 
            ('dir1', 'dir1-id'),
1382
 
            ('dir2', 'dir2-id'),
1383
 
            ('top', 'top-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'),
1389
 
            ], layout)
1390
 
 
1391
 
    def test__getitems(self):
1392
 
        inv = self.make_simple_inventory()
1393
 
        # Reading from disk
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)
1397
 
        # From cache
1398
 
        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1399
 
        # Mixed
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)
1404
 
 
1405
 
    def test_single_file(self):
1406
 
        inv = self.make_simple_inventory()
1407
 
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1408
 
 
1409
 
    def test_get_all_parents(self):
1410
 
        inv = self.make_simple_inventory()
1411
 
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1412
 
                           'subsub-file1-id',
1413
 
                          ], inv, ['subsub-file1-id'])
1414
 
 
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'])
1420
 
 
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'])
1426
 
 
1427
 
    def test_top_level_file(self):
1428
 
        inv = self.make_simple_inventory()
1429
 
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1430
 
 
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'])
1435
 
 
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'])
1440
 
 
1441
 
 
1442
 
class TestMutableInventoryFromTree(TestCaseWithTransport):
1443
 
 
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))
1450
 
 
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
1462
 
        # the original tree
1463
 
        self.assertFalse(tree.inventory['thefileid'].executable)
1464
 
        inv['thefileid'].executable = True
1465
 
        self.assertFalse(tree.inventory['thefileid'].executable)