/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_inv.py

  • Committer: Martin
  • Date: 2017-06-10 01:57:00 UTC
  • mto: This revision was merged to the branch mainline in revision 6679.
  • Revision ID: gzlist@googlemail.com-20170610015700-o3xeuyaqry2obiay
Go back to native str for urls and many other py3 changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2012, 2016 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 .. import (
 
19
    chk_map,
 
20
    groupcompress,
 
21
    errors,
 
22
    inventory,
 
23
    osutils,
 
24
    repository,
 
25
    revision,
 
26
    tests,
 
27
    workingtree,
 
28
    )
 
29
from ..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 . import (
 
40
    TestCase,
 
41
    TestCaseWithTransport,
 
42
    )
 
43
from .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, invalid_delta=True):
 
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, invalid_delta=True):
 
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
    if not invalid_delta:
 
129
        tree._validate()
 
130
    return tree.root_inventory
 
131
 
 
132
 
 
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):
 
187
    """Apply delta to basis and return the result.
 
188
 
 
189
    This sets the parent and then calls update_basis_by_delta.
 
190
    It also puts the basis in the repository under both 'basis' and 'result' to
 
191
    allow safety checks made by the WT to succeed, and finally ensures that all
 
192
    items in the delta with a new path are present in the WT before calling
 
193
    update_basis_by_delta.
 
194
 
 
195
    :param basis: An inventory to be used as the basis.
 
196
    :param delta: The inventory delta to apply:
 
197
    :return: An inventory resulting from the application.
 
198
    """
 
199
    control = test.make_bzrdir('tree', format=test.format._matchingbzrdir)
 
200
    control.create_repository()
 
201
    control.create_branch()
 
202
    tree = test.format.initialize(control)
 
203
    tree.lock_write()
 
204
    try:
 
205
        target_entries = _create_repo_revisions(tree.branch.repository, basis,
 
206
                                                delta, invalid_delta)
 
207
        # Set the basis state as the trees current state
 
208
        tree._write_inventory(basis)
 
209
        # This reads basis from the repo and puts it into the tree's local
 
210
        # cache, if it has one.
 
211
        tree.set_parent_ids(['basis'])
 
212
    finally:
 
213
        tree.unlock()
 
214
    # Fresh lock, reads disk again.
 
215
    tree.lock_write()
 
216
    try:
 
217
        tree.update_basis_by_delta('result', delta)
 
218
        if not invalid_delta:
 
219
            tree._validate()
 
220
    finally:
 
221
        tree.unlock()
 
222
    # reload tree - ensure we get what was written.
 
223
    tree = tree.bzrdir.open_workingtree()
 
224
    basis_tree = tree.basis_tree()
 
225
    basis_tree.lock_read()
 
226
    test.addCleanup(basis_tree.unlock)
 
227
    basis_inv = basis_tree.root_inventory
 
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):
 
236
    """Apply delta to basis and return the result.
 
237
    
 
238
    This inserts basis as a whole inventory and then uses
 
239
    add_inventory_by_delta to add delta.
 
240
 
 
241
    :param basis: An inventory to be used as the basis.
 
242
    :param delta: The inventory delta to apply:
 
243
    :return: An inventory resulting from the application.
 
244
    """
 
245
    format = self.format()
 
246
    control = self.make_bzrdir('tree', format=format._matchingbzrdir)
 
247
    repo = format.initialize(control)
 
248
    repo.lock_write()
 
249
    try:
 
250
        repo.start_write_group()
 
251
        try:
 
252
            rev = revision.Revision('basis', timestamp=0, timezone=None,
 
253
                message="", committer="foo@example.com")
 
254
            basis.revision_id = 'basis'
 
255
            create_texts_for_inv(repo, basis)
 
256
            repo.add_revision('basis', rev, basis)
 
257
            repo.commit_write_group()
 
258
        except:
 
259
            repo.abort_write_group()
 
260
            raise
 
261
    finally:
 
262
        repo.unlock()
 
263
    repo.lock_write()
 
264
    try:
 
265
        repo.start_write_group()
 
266
        try:
 
267
            inv_sha1 = repo.add_inventory_by_delta('basis', delta,
 
268
                'result', ['basis'])
 
269
        except:
 
270
            repo.abort_write_group()
 
271
            raise
 
272
        else:
 
273
            repo.commit_write_group()
 
274
    finally:
 
275
        repo.unlock()
 
276
    # Fresh lock, reads disk again.
 
277
    repo = repo.bzrdir.open_repository()
 
278
    repo.lock_read()
 
279
    self.addCleanup(repo.unlock)
 
280
    return repo.get_inventory('result')
 
281
 
 
282
 
 
283
class TestInventoryUpdates(TestCase):
 
284
 
 
285
    def test_creation_from_root_id(self):
 
286
        # iff a root id is passed to the constructor, a root directory is made
 
287
        inv = inventory.Inventory(root_id='tree-root')
 
288
        self.assertNotEqual(None, inv.root)
 
289
        self.assertEqual('tree-root', inv.root.file_id)
 
290
 
 
291
    def test_add_path_of_root(self):
 
292
        # if no root id is given at creation time, there is no root directory
 
293
        inv = inventory.Inventory(root_id=None)
 
294
        self.assertIs(None, inv.root)
 
295
        # add a root entry by adding its path
 
296
        ie = inv.add_path("", "directory", "my-root")
 
297
        ie.revision = 'test-rev'
 
298
        self.assertEqual("my-root", ie.file_id)
 
299
        self.assertIs(ie, inv.root)
 
300
 
 
301
    def test_add_path(self):
 
302
        inv = inventory.Inventory(root_id='tree_root')
 
303
        ie = inv.add_path('hello', 'file', 'hello-id')
 
304
        self.assertEqual('hello-id', ie.file_id)
 
305
        self.assertEqual('file', ie.kind)
 
306
 
 
307
    def test_copy(self):
 
308
        """Make sure copy() works and creates a deep copy."""
 
309
        inv = inventory.Inventory(root_id='some-tree-root')
 
310
        ie = inv.add_path('hello', 'file', 'hello-id')
 
311
        inv2 = inv.copy()
 
312
        inv.root.file_id = 'some-new-root'
 
313
        ie.name = 'file2'
 
314
        self.assertEqual('some-tree-root', inv2.root.file_id)
 
315
        self.assertEqual('hello', inv2['hello-id'].name)
 
316
 
 
317
    def test_copy_empty(self):
 
318
        """Make sure an empty inventory can be copied."""
 
319
        inv = inventory.Inventory(root_id=None)
 
320
        inv2 = inv.copy()
 
321
        self.assertIs(None, inv2.root)
 
322
 
 
323
    def test_copy_copies_root_revision(self):
 
324
        """Make sure the revision of the root gets copied."""
 
325
        inv = inventory.Inventory(root_id='someroot')
 
326
        inv.root.revision = 'therev'
 
327
        inv2 = inv.copy()
 
328
        self.assertEqual('someroot', inv2.root.file_id)
 
329
        self.assertEqual('therev', inv2.root.revision)
 
330
 
 
331
    def test_create_tree_reference(self):
 
332
        inv = inventory.Inventory('tree-root-123')
 
333
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
 
334
                              revision='rev', reference_revision='rev2'))
 
335
 
 
336
    def test_error_encoding(self):
 
337
        inv = inventory.Inventory('tree-root')
 
338
        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
 
339
        e = self.assertRaises(errors.InconsistentDelta, inv.add,
 
340
            InventoryFile('b-id', u'\u1234', 'tree-root'))
 
341
        self.assertContainsRe(str(e), r'\\u1234')
 
342
 
 
343
    def test_add_recursive(self):
 
344
        parent = InventoryDirectory('src-id', 'src', 'tree-root')
 
345
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
 
346
        parent.children[child.file_id] = child
 
347
        inv = inventory.Inventory('tree-root')
 
348
        inv.add(parent)
 
349
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
 
350
 
 
351
 
 
352
 
 
353
class TestDeltaApplication(TestCaseWithTransport):
 
354
 
 
355
    scenarios = delta_application_scenarios()
 
356
 
 
357
    def get_empty_inventory(self, reference_inv=None):
 
358
        """Get an empty inventory.
 
359
 
 
360
        Note that tests should not depend on the revision of the root for
 
361
        setting up test conditions, as it has to be flexible to accomodate non
 
362
        rich root repositories.
 
363
 
 
364
        :param reference_inv: If not None, get the revision for the root from
 
365
            this inventory. This is useful for dealing with older repositories
 
366
            that routinely discarded the root entry data. If None, the root's
 
367
            revision is set to 'basis'.
 
368
        """
 
369
        inv = inventory.Inventory()
 
370
        if reference_inv is not None:
 
371
            inv.root.revision = reference_inv.root.revision
 
372
        else:
 
373
            inv.root.revision = 'basis'
 
374
        return inv
 
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
 
 
383
    def test_empty_delta(self):
 
384
        inv = self.get_empty_inventory()
 
385
        delta = []
 
386
        inv = self.apply_delta(self, inv, delta)
 
387
        inv2 = self.get_empty_inventory(inv)
 
388
        self.assertEqual([], inv2._make_delta(inv))
 
389
 
 
390
    def test_None_file_id(self):
 
391
        inv = self.get_empty_inventory()
 
392
        dir1 = inventory.InventoryDirectory(None, 'dir1', inv.root.file_id)
 
393
        dir1.revision = 'result'
 
394
        delta = [(None, u'dir1', None, dir1)]
 
395
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
396
            inv, delta)
 
397
 
 
398
    def test_unicode_file_id(self):
 
399
        inv = self.get_empty_inventory()
 
400
        dir1 = inventory.InventoryDirectory(u'dirid', 'dir1', inv.root.file_id)
 
401
        dir1.revision = 'result'
 
402
        delta = [(None, u'dir1', dir1.file_id, dir1)]
 
403
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
404
            inv, delta)
 
405
 
 
406
    def test_repeated_file_id(self):
 
407
        inv = self.get_empty_inventory()
 
408
        file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
 
409
        file1.revision = 'result'
 
410
        file1.text_size = 0
 
411
        file1.text_sha1 = ""
 
412
        file2 = file1.copy()
 
413
        file2.name = 'path2'
 
414
        delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
 
415
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
416
            inv, delta)
 
417
 
 
418
    def test_repeated_new_path(self):
 
419
        inv = self.get_empty_inventory()
 
420
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
 
421
        file1.revision = 'result'
 
422
        file1.text_size = 0
 
423
        file1.text_sha1 = ""
 
424
        file2 = file1.copy()
 
425
        file2.file_id = 'id2'
 
426
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
 
427
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
428
            inv, delta)
 
429
 
 
430
    def test_repeated_old_path(self):
 
431
        inv = self.get_empty_inventory()
 
432
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
 
433
        file1.revision = 'result'
 
434
        file1.text_size = 0
 
435
        file1.text_sha1 = ""
 
436
        # We can't *create* a source inventory with the same path, but
 
437
        # a badly generated partial delta might claim the same source twice.
 
438
        # This would be buggy in two ways: the path is repeated in the delta,
 
439
        # And the path for one of the file ids doesn't match the source
 
440
        # location. Alternatively, we could have a repeated fileid, but that
 
441
        # is separately checked for.
 
442
        file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
 
443
        file2.revision = 'result'
 
444
        file2.text_size = 0
 
445
        file2.text_sha1 = ""
 
446
        inv.add(file1)
 
447
        inv.add(file2)
 
448
        delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
 
449
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
450
            inv, delta)
 
451
 
 
452
    def test_mismatched_id_entry_id(self):
 
453
        inv = self.get_empty_inventory()
 
454
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
 
455
        file1.revision = 'result'
 
456
        file1.text_size = 0
 
457
        file1.text_sha1 = ""
 
458
        delta = [(None, u'path', 'id', file1)]
 
459
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
460
            inv, delta)
 
461
 
 
462
    def test_mismatched_new_path_entry_None(self):
 
463
        inv = self.get_empty_inventory()
 
464
        delta = [(None, u'path', 'id', None)]
 
465
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
466
            inv, delta)
 
467
 
 
468
    def test_mismatched_new_path_None_entry(self):
 
469
        inv = self.get_empty_inventory()
 
470
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
 
471
        file1.revision = 'result'
 
472
        file1.text_size = 0
 
473
        file1.text_sha1 = ""
 
474
        delta = [(u"path", None, 'id1', file1)]
 
475
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
476
            inv, delta)
 
477
 
 
478
    def test_parent_is_not_directory(self):
 
479
        inv = self.get_empty_inventory()
 
480
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
 
481
        file1.revision = 'result'
 
482
        file1.text_size = 0
 
483
        file1.text_sha1 = ""
 
484
        file2 = inventory.InventoryFile('id2', 'path2', 'id1')
 
485
        file2.revision = 'result'
 
486
        file2.text_size = 0
 
487
        file2.text_sha1 = ""
 
488
        inv.add(file1)
 
489
        delta = [(None, u'path/path2', 'id2', file2)]
 
490
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
491
            inv, delta)
 
492
 
 
493
    def test_parent_is_missing(self):
 
494
        inv = self.get_empty_inventory()
 
495
        file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
 
496
        file2.revision = 'result'
 
497
        file2.text_size = 0
 
498
        file2.text_sha1 = ""
 
499
        delta = [(None, u'path/path2', 'id2', file2)]
 
500
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
501
            inv, delta)
 
502
 
 
503
    def test_new_parent_path_has_wrong_id(self):
 
504
        inv = self.get_empty_inventory()
 
505
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
 
506
        parent1.revision = 'result'
 
507
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
 
508
        parent2.revision = 'result'
 
509
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
 
510
        file1.revision = 'result'
 
511
        file1.text_size = 0
 
512
        file1.text_sha1 = ""
 
513
        inv.add(parent1)
 
514
        inv.add(parent2)
 
515
        # This delta claims that file1 is at dir/path, but actually its at
 
516
        # dir2/path if you follow the inventory parent structure.
 
517
        delta = [(None, u'dir/path', 'id', file1)]
 
518
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
519
            inv, delta)
 
520
 
 
521
    def test_old_parent_path_is_wrong(self):
 
522
        inv = self.get_empty_inventory()
 
523
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
 
524
        parent1.revision = 'result'
 
525
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
 
526
        parent2.revision = 'result'
 
527
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
 
528
        file1.revision = 'result'
 
529
        file1.text_size = 0
 
530
        file1.text_sha1 = ""
 
531
        inv.add(parent1)
 
532
        inv.add(parent2)
 
533
        inv.add(file1)
 
534
        # This delta claims that file1 was at dir/path, but actually it was at
 
535
        # dir2/path if you follow the inventory parent structure.
 
536
        delta = [(u'dir/path', None, 'id', None)]
 
537
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
538
            inv, delta)
 
539
 
 
540
    def test_old_parent_path_is_for_other_id(self):
 
541
        inv = self.get_empty_inventory()
 
542
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
 
543
        parent1.revision = 'result'
 
544
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
 
545
        parent2.revision = 'result'
 
546
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
 
547
        file1.revision = 'result'
 
548
        file1.text_size = 0
 
549
        file1.text_sha1 = ""
 
550
        file2 = inventory.InventoryFile('id2', 'path', 'p-1')
 
551
        file2.revision = 'result'
 
552
        file2.text_size = 0
 
553
        file2.text_sha1 = ""
 
554
        inv.add(parent1)
 
555
        inv.add(parent2)
 
556
        inv.add(file1)
 
557
        inv.add(file2)
 
558
        # This delta claims that file1 was at dir/path, but actually it was at
 
559
        # dir2/path if you follow the inventory parent structure. At dir/path
 
560
        # is another entry we should not delete.
 
561
        delta = [(u'dir/path', None, 'id', None)]
 
562
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
563
            inv, delta)
 
564
 
 
565
    def test_add_existing_id_new_path(self):
 
566
        inv = self.get_empty_inventory()
 
567
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
 
568
        parent1.revision = 'result'
 
569
        parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
 
570
        parent2.revision = 'result'
 
571
        inv.add(parent1)
 
572
        delta = [(None, u'dir2', 'p-1', parent2)]
 
573
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
574
            inv, delta)
 
575
 
 
576
    def test_add_new_id_existing_path(self):
 
577
        inv = self.get_empty_inventory()
 
578
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
 
579
        parent1.revision = 'result'
 
580
        parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
 
581
        parent2.revision = 'result'
 
582
        inv.add(parent1)
 
583
        delta = [(None, u'dir1', 'p-2', parent2)]
 
584
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
585
            inv, delta)
 
586
 
 
587
    def test_remove_dir_leaving_dangling_child(self):
 
588
        inv = self.get_empty_inventory()
 
589
        dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
 
590
        dir1.revision = 'result'
 
591
        dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
 
592
        dir2.revision = 'result'
 
593
        dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
 
594
        dir3.revision = 'result'
 
595
        inv.add(dir1)
 
596
        inv.add(dir2)
 
597
        inv.add(dir3)
 
598
        delta = [(u'dir1', None, 'p-1', None),
 
599
            (u'dir1/child2', None, 'p-3', None)]
 
600
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
 
601
            inv, delta)
 
602
 
 
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'))
 
678
 
 
679
    def test_is_root(self):
 
680
        """Ensure our root-checking code is accurate."""
 
681
        inv = inventory.Inventory('TREE_ROOT')
 
682
        self.assertTrue(inv.is_root('TREE_ROOT'))
 
683
        self.assertFalse(inv.is_root('booga'))
 
684
        inv.root.file_id = 'booga'
 
685
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
686
        self.assertTrue(inv.is_root('booga'))
 
687
        # works properly even if no root is set
 
688
        inv.root = None
 
689
        self.assertFalse(inv.is_root('TREE_ROOT'))
 
690
        self.assertFalse(inv.is_root('booga'))
 
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
 
 
697
 
 
698
class TestInventoryEntry(TestCase):
 
699
 
 
700
    def test_file_kind_character(self):
 
701
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
702
        self.assertEqual(file.kind_character(), '')
 
703
 
 
704
    def test_dir_kind_character(self):
 
705
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
706
        self.assertEqual(dir.kind_character(), '/')
 
707
 
 
708
    def test_link_kind_character(self):
 
709
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
710
        self.assertEqual(dir.kind_character(), '')
 
711
 
 
712
    def test_dir_detect_changes(self):
 
713
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
714
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
715
        self.assertEqual((False, False), left.detect_changes(right))
 
716
        self.assertEqual((False, False), right.detect_changes(left))
 
717
 
 
718
    def test_file_detect_changes(self):
 
719
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
720
        left.text_sha1 = 123
 
721
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
722
        right.text_sha1 = 123
 
723
        self.assertEqual((False, False), left.detect_changes(right))
 
724
        self.assertEqual((False, False), right.detect_changes(left))
 
725
        left.executable = True
 
726
        self.assertEqual((False, True), left.detect_changes(right))
 
727
        self.assertEqual((False, True), right.detect_changes(left))
 
728
        right.text_sha1 = 321
 
729
        self.assertEqual((True, True), left.detect_changes(right))
 
730
        self.assertEqual((True, True), right.detect_changes(left))
 
731
 
 
732
    def test_symlink_detect_changes(self):
 
733
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
734
        left.symlink_target='foo'
 
735
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
736
        right.symlink_target='foo'
 
737
        self.assertEqual((False, False), left.detect_changes(right))
 
738
        self.assertEqual((False, False), right.detect_changes(left))
 
739
        left.symlink_target = 'different'
 
740
        self.assertEqual((True, False), left.detect_changes(right))
 
741
        self.assertEqual((True, False), right.detect_changes(left))
 
742
 
 
743
    def test_file_has_text(self):
 
744
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
745
        self.assertTrue(file.has_text())
 
746
 
 
747
    def test_directory_has_text(self):
 
748
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
749
        self.assertFalse(dir.has_text())
 
750
 
 
751
    def test_link_has_text(self):
 
752
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
753
        self.assertFalse(link.has_text())
 
754
 
 
755
    def test_make_entry(self):
 
756
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
757
            inventory.InventoryFile)
 
758
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
759
            inventory.InventoryLink)
 
760
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
761
            inventory.InventoryDirectory)
 
762
 
 
763
    def test_make_entry_non_normalized(self):
 
764
        orig_normalized_filename = osutils.normalized_filename
 
765
 
 
766
        try:
 
767
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
768
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
769
            self.assertEqual(u'\xe5', entry.name)
 
770
            self.assertIsInstance(entry, inventory.InventoryFile)
 
771
 
 
772
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
773
            self.assertRaises(errors.InvalidNormalization,
 
774
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
775
        finally:
 
776
            osutils.normalized_filename = orig_normalized_filename
 
777
 
 
778
 
 
779
class TestDescribeChanges(TestCase):
 
780
 
 
781
    def test_describe_change(self):
 
782
        # we need to test the following change combinations:
 
783
        # rename
 
784
        # reparent
 
785
        # modify
 
786
        # gone
 
787
        # added
 
788
        # renamed/reparented and modified
 
789
        # change kind (perhaps can't be done yet?)
 
790
        # also, merged in combination with all of these?
 
791
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
792
        old_a.text_sha1 = '123132'
 
793
        old_a.text_size = 0
 
794
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
795
        new_a.text_sha1 = '123132'
 
796
        new_a.text_size = 0
 
797
 
 
798
        self.assertChangeDescription('unchanged', old_a, new_a)
 
799
 
 
800
        new_a.text_size = 10
 
801
        new_a.text_sha1 = 'abcabc'
 
802
        self.assertChangeDescription('modified', old_a, new_a)
 
803
 
 
804
        self.assertChangeDescription('added', None, new_a)
 
805
        self.assertChangeDescription('removed', old_a, None)
 
806
        # perhaps a bit questionable but seems like the most reasonable thing...
 
807
        self.assertChangeDescription('unchanged', None, None)
 
808
 
 
809
        # in this case it's both renamed and modified; show a rename and
 
810
        # modification:
 
811
        new_a.name = 'newfilename'
 
812
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
813
 
 
814
        # reparenting is 'renaming'
 
815
        new_a.name = old_a.name
 
816
        new_a.parent_id = 'somedir-id'
 
817
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
818
 
 
819
        # reset the content values so its not modified
 
820
        new_a.text_size = old_a.text_size
 
821
        new_a.text_sha1 = old_a.text_sha1
 
822
        new_a.name = old_a.name
 
823
 
 
824
        new_a.name = 'newfilename'
 
825
        self.assertChangeDescription('renamed', old_a, new_a)
 
826
 
 
827
        # reparenting is 'renaming'
 
828
        new_a.name = old_a.name
 
829
        new_a.parent_id = 'somedir-id'
 
830
        self.assertChangeDescription('renamed', old_a, new_a)
 
831
 
 
832
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
833
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
834
        self.assertEqual(expected_change, change)
 
835
 
 
836
 
 
837
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
 
838
 
 
839
    def get_chk_bytes(self):
 
840
        factory = groupcompress.make_pack_factory(True, True, 1)
 
841
        trans = self.get_transport('')
 
842
        return factory(trans)
 
843
 
 
844
    def read_bytes(self, chk_bytes, key):
 
845
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
 
846
        return stream.next().get_bytes_as("fulltext")
 
847
 
 
848
    def test_deserialise_gives_CHKInventory(self):
 
849
        inv = Inventory()
 
850
        inv.revision_id = "revid"
 
851
        inv.root.revision = "rootrev"
 
852
        chk_bytes = self.get_chk_bytes()
 
853
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
854
        bytes = ''.join(chk_inv.to_lines())
 
855
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
856
        self.assertEqual("revid", new_inv.revision_id)
 
857
        self.assertEqual("directory", new_inv.root.kind)
 
858
        self.assertEqual(inv.root.file_id, new_inv.root.file_id)
 
859
        self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
 
860
        self.assertEqual(inv.root.name, new_inv.root.name)
 
861
        self.assertEqual("rootrev", new_inv.root.revision)
 
862
        self.assertEqual('plain', new_inv._search_key_name)
 
863
 
 
864
    def test_deserialise_wrong_revid(self):
 
865
        inv = Inventory()
 
866
        inv.revision_id = "revid"
 
867
        inv.root.revision = "rootrev"
 
868
        chk_bytes = self.get_chk_bytes()
 
869
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
870
        bytes = ''.join(chk_inv.to_lines())
 
871
        self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
 
872
            bytes, ("revid2",))
 
873
 
 
874
    def test_captures_rev_root_byid(self):
 
875
        inv = Inventory()
 
876
        inv.revision_id = "foo"
 
877
        inv.root.revision = "bar"
 
878
        chk_bytes = self.get_chk_bytes()
 
879
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
880
        lines = chk_inv.to_lines()
 
881
        self.assertEqual([
 
882
            'chkinventory:\n',
 
883
            'revision_id: foo\n',
 
884
            'root_id: TREE_ROOT\n',
 
885
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
 
886
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
 
887
            ], lines)
 
888
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
 
889
        self.assertEqual('plain', chk_inv._search_key_name)
 
890
 
 
891
    def test_captures_parent_id_basename_index(self):
 
892
        inv = Inventory()
 
893
        inv.revision_id = "foo"
 
894
        inv.root.revision = "bar"
 
895
        chk_bytes = self.get_chk_bytes()
 
896
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
897
        lines = chk_inv.to_lines()
 
898
        self.assertEqual([
 
899
            'chkinventory:\n',
 
900
            'revision_id: foo\n',
 
901
            'root_id: TREE_ROOT\n',
 
902
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
 
903
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
 
904
            ], lines)
 
905
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
 
906
        self.assertEqual('plain', chk_inv._search_key_name)
 
907
 
 
908
    def test_captures_search_key_name(self):
 
909
        inv = Inventory()
 
910
        inv.revision_id = "foo"
 
911
        inv.root.revision = "bar"
 
912
        chk_bytes = self.get_chk_bytes()
 
913
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
 
914
                                              search_key_name='hash-16-way')
 
915
        lines = chk_inv.to_lines()
 
916
        self.assertEqual([
 
917
            'chkinventory:\n',
 
918
            'search_key_name: hash-16-way\n',
 
919
            'root_id: TREE_ROOT\n',
 
920
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
 
921
            'revision_id: foo\n',
 
922
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
 
923
            ], lines)
 
924
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
 
925
        self.assertEqual('hash-16-way', chk_inv._search_key_name)
 
926
 
 
927
    def test_directory_children_on_demand(self):
 
928
        inv = Inventory()
 
929
        inv.revision_id = "revid"
 
930
        inv.root.revision = "rootrev"
 
931
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
932
        inv["fileid"].revision = "filerev"
 
933
        inv["fileid"].executable = True
 
934
        inv["fileid"].text_sha1 = "ffff"
 
935
        inv["fileid"].text_size = 1
 
936
        chk_bytes = self.get_chk_bytes()
 
937
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
938
        bytes = ''.join(chk_inv.to_lines())
 
939
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
940
        root_entry = new_inv[inv.root.file_id]
 
941
        self.assertEqual(None, root_entry._children)
 
942
        self.assertEqual({'file'}, set(root_entry.children))
 
943
        file_direct = new_inv["fileid"]
 
944
        file_found = root_entry.children['file']
 
945
        self.assertEqual(file_direct.kind, file_found.kind)
 
946
        self.assertEqual(file_direct.file_id, file_found.file_id)
 
947
        self.assertEqual(file_direct.parent_id, file_found.parent_id)
 
948
        self.assertEqual(file_direct.name, file_found.name)
 
949
        self.assertEqual(file_direct.revision, file_found.revision)
 
950
        self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
 
951
        self.assertEqual(file_direct.text_size, file_found.text_size)
 
952
        self.assertEqual(file_direct.executable, file_found.executable)
 
953
 
 
954
    def test_from_inventory_maximum_size(self):
 
955
        # from_inventory supports the maximum_size parameter.
 
956
        inv = Inventory()
 
957
        inv.revision_id = "revid"
 
958
        inv.root.revision = "rootrev"
 
959
        chk_bytes = self.get_chk_bytes()
 
960
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv, 120)
 
961
        chk_inv.id_to_entry._ensure_root()
 
962
        self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
 
963
        self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
 
964
        p_id_basename = chk_inv.parent_id_basename_to_file_id
 
965
        p_id_basename._ensure_root()
 
966
        self.assertEqual(120, p_id_basename._root_node.maximum_size)
 
967
        self.assertEqual(2, p_id_basename._root_node._key_width)
 
968
 
 
969
    def test___iter__(self):
 
970
        inv = Inventory()
 
971
        inv.revision_id = "revid"
 
972
        inv.root.revision = "rootrev"
 
973
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
974
        inv["fileid"].revision = "filerev"
 
975
        inv["fileid"].executable = True
 
976
        inv["fileid"].text_sha1 = "ffff"
 
977
        inv["fileid"].text_size = 1
 
978
        chk_bytes = self.get_chk_bytes()
 
979
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
980
        bytes = ''.join(chk_inv.to_lines())
 
981
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
982
        fileids = sorted(new_inv.__iter__())
 
983
        self.assertEqual([inv.root.file_id, "fileid"], fileids)
 
984
 
 
985
    def test__len__(self):
 
986
        inv = Inventory()
 
987
        inv.revision_id = "revid"
 
988
        inv.root.revision = "rootrev"
 
989
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
990
        inv["fileid"].revision = "filerev"
 
991
        inv["fileid"].executable = True
 
992
        inv["fileid"].text_sha1 = "ffff"
 
993
        inv["fileid"].text_size = 1
 
994
        chk_bytes = self.get_chk_bytes()
 
995
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
996
        self.assertEqual(2, len(chk_inv))
 
997
 
 
998
    def test___getitem__(self):
 
999
        inv = Inventory()
 
1000
        inv.revision_id = "revid"
 
1001
        inv.root.revision = "rootrev"
 
1002
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
1003
        inv["fileid"].revision = "filerev"
 
1004
        inv["fileid"].executable = True
 
1005
        inv["fileid"].text_sha1 = "ffff"
 
1006
        inv["fileid"].text_size = 1
 
1007
        chk_bytes = self.get_chk_bytes()
 
1008
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1009
        bytes = ''.join(chk_inv.to_lines())
 
1010
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1011
        root_entry = new_inv[inv.root.file_id]
 
1012
        file_entry = new_inv["fileid"]
 
1013
        self.assertEqual("directory", root_entry.kind)
 
1014
        self.assertEqual(inv.root.file_id, root_entry.file_id)
 
1015
        self.assertEqual(inv.root.parent_id, root_entry.parent_id)
 
1016
        self.assertEqual(inv.root.name, root_entry.name)
 
1017
        self.assertEqual("rootrev", root_entry.revision)
 
1018
        self.assertEqual("file", file_entry.kind)
 
1019
        self.assertEqual("fileid", file_entry.file_id)
 
1020
        self.assertEqual(inv.root.file_id, file_entry.parent_id)
 
1021
        self.assertEqual("file", file_entry.name)
 
1022
        self.assertEqual("filerev", file_entry.revision)
 
1023
        self.assertEqual("ffff", file_entry.text_sha1)
 
1024
        self.assertEqual(1, file_entry.text_size)
 
1025
        self.assertEqual(True, file_entry.executable)
 
1026
        self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
 
1027
 
 
1028
    def test_has_id_true(self):
 
1029
        inv = Inventory()
 
1030
        inv.revision_id = "revid"
 
1031
        inv.root.revision = "rootrev"
 
1032
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
1033
        inv["fileid"].revision = "filerev"
 
1034
        inv["fileid"].executable = True
 
1035
        inv["fileid"].text_sha1 = "ffff"
 
1036
        inv["fileid"].text_size = 1
 
1037
        chk_bytes = self.get_chk_bytes()
 
1038
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1039
        self.assertTrue(chk_inv.has_id('fileid'))
 
1040
        self.assertTrue(chk_inv.has_id(inv.root.file_id))
 
1041
 
 
1042
    def test_has_id_not(self):
 
1043
        inv = Inventory()
 
1044
        inv.revision_id = "revid"
 
1045
        inv.root.revision = "rootrev"
 
1046
        chk_bytes = self.get_chk_bytes()
 
1047
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1048
        self.assertFalse(chk_inv.has_id('fileid'))
 
1049
 
 
1050
    def test_id2path(self):
 
1051
        inv = Inventory()
 
1052
        inv.revision_id = "revid"
 
1053
        inv.root.revision = "rootrev"
 
1054
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
 
1055
        fileentry = InventoryFile("fileid", "file", "dirid")
 
1056
        inv.add(direntry)
 
1057
        inv.add(fileentry)
 
1058
        inv["fileid"].revision = "filerev"
 
1059
        inv["fileid"].executable = True
 
1060
        inv["fileid"].text_sha1 = "ffff"
 
1061
        inv["fileid"].text_size = 1
 
1062
        inv["dirid"].revision = "filerev"
 
1063
        chk_bytes = self.get_chk_bytes()
 
1064
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1065
        bytes = ''.join(chk_inv.to_lines())
 
1066
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1067
        self.assertEqual('', new_inv.id2path(inv.root.file_id))
 
1068
        self.assertEqual('dir', new_inv.id2path('dirid'))
 
1069
        self.assertEqual('dir/file', new_inv.id2path('fileid'))
 
1070
 
 
1071
    def test_path2id(self):
 
1072
        inv = Inventory()
 
1073
        inv.revision_id = "revid"
 
1074
        inv.root.revision = "rootrev"
 
1075
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
 
1076
        fileentry = InventoryFile("fileid", "file", "dirid")
 
1077
        inv.add(direntry)
 
1078
        inv.add(fileentry)
 
1079
        inv["fileid"].revision = "filerev"
 
1080
        inv["fileid"].executable = True
 
1081
        inv["fileid"].text_sha1 = "ffff"
 
1082
        inv["fileid"].text_size = 1
 
1083
        inv["dirid"].revision = "filerev"
 
1084
        chk_bytes = self.get_chk_bytes()
 
1085
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1086
        bytes = ''.join(chk_inv.to_lines())
 
1087
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1088
        self.assertEqual(inv.root.file_id, new_inv.path2id(''))
 
1089
        self.assertEqual('dirid', new_inv.path2id('dir'))
 
1090
        self.assertEqual('fileid', new_inv.path2id('dir/file'))
 
1091
 
 
1092
    def test_create_by_apply_delta_sets_root(self):
 
1093
        inv = Inventory()
 
1094
        inv.revision_id = "revid"
 
1095
        chk_bytes = self.get_chk_bytes()
 
1096
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1097
        inv.add_path("", "directory", "myrootid", None)
 
1098
        inv.revision_id = "expectedid"
 
1099
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1100
        delta = [("", None, base_inv.root.file_id, None),
 
1101
            (None, "",  "myrootid", inv.root)]
 
1102
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
 
1103
        self.assertEqual(reference_inv.root, new_inv.root)
 
1104
 
 
1105
    def test_create_by_apply_delta_empty_add_child(self):
 
1106
        inv = Inventory()
 
1107
        inv.revision_id = "revid"
 
1108
        inv.root.revision = "rootrev"
 
1109
        chk_bytes = self.get_chk_bytes()
 
1110
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1111
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
 
1112
        a_entry.revision = "filerev"
 
1113
        a_entry.executable = True
 
1114
        a_entry.text_sha1 = "ffff"
 
1115
        a_entry.text_size = 1
 
1116
        inv.add(a_entry)
 
1117
        inv.revision_id = "expectedid"
 
1118
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1119
        delta = [(None, "A",  "A-id", a_entry)]
 
1120
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
 
1121
        # new_inv should be the same as reference_inv.
 
1122
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
 
1123
        self.assertEqual(reference_inv.root_id, new_inv.root_id)
 
1124
        reference_inv.id_to_entry._ensure_root()
 
1125
        new_inv.id_to_entry._ensure_root()
 
1126
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
 
1127
            new_inv.id_to_entry._root_node._key)
 
1128
 
 
1129
    def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
 
1130
        inv = Inventory()
 
1131
        inv.revision_id = "revid"
 
1132
        inv.root.revision = "rootrev"
 
1133
        chk_bytes = self.get_chk_bytes()
 
1134
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1135
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
 
1136
        a_entry.revision = "filerev"
 
1137
        a_entry.executable = True
 
1138
        a_entry.text_sha1 = "ffff"
 
1139
        a_entry.text_size = 1
 
1140
        inv.add(a_entry)
 
1141
        inv.revision_id = "expectedid"
 
1142
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1143
        delta = [(None, "A",  "A-id", a_entry)]
 
1144
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
 
1145
        reference_inv.id_to_entry._ensure_root()
 
1146
        reference_inv.parent_id_basename_to_file_id._ensure_root()
 
1147
        new_inv.id_to_entry._ensure_root()
 
1148
        new_inv.parent_id_basename_to_file_id._ensure_root()
 
1149
        # new_inv should be the same as reference_inv.
 
1150
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
 
1151
        self.assertEqual(reference_inv.root_id, new_inv.root_id)
 
1152
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
 
1153
            new_inv.id_to_entry._root_node._key)
 
1154
        self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
 
1155
            new_inv.parent_id_basename_to_file_id._root_node._key)
 
1156
 
 
1157
    def test_iter_changes(self):
 
1158
        # Low level bootstrapping smoke test; comprehensive generic tests via
 
1159
        # InterTree are coming.
 
1160
        inv = Inventory()
 
1161
        inv.revision_id = "revid"
 
1162
        inv.root.revision = "rootrev"
 
1163
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
1164
        inv["fileid"].revision = "filerev"
 
1165
        inv["fileid"].executable = True
 
1166
        inv["fileid"].text_sha1 = "ffff"
 
1167
        inv["fileid"].text_size = 1
 
1168
        inv2 = Inventory()
 
1169
        inv2.revision_id = "revid2"
 
1170
        inv2.root.revision = "rootrev"
 
1171
        inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
 
1172
        inv2["fileid"].revision = "filerev2"
 
1173
        inv2["fileid"].executable = False
 
1174
        inv2["fileid"].text_sha1 = "bbbb"
 
1175
        inv2["fileid"].text_size = 2
 
1176
        # get fresh objects.
 
1177
        chk_bytes = self.get_chk_bytes()
 
1178
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1179
        bytes = ''.join(chk_inv.to_lines())
 
1180
        inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1181
        chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
 
1182
        bytes = ''.join(chk_inv2.to_lines())
 
1183
        inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
 
1184
        self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
 
1185
            ('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
 
1186
            (False, True))],
 
1187
            list(inv_1.iter_changes(inv_2)))
 
1188
 
 
1189
    def test_parent_id_basename_to_file_id_index_enabled(self):
 
1190
        inv = Inventory()
 
1191
        inv.revision_id = "revid"
 
1192
        inv.root.revision = "rootrev"
 
1193
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
 
1194
        inv["fileid"].revision = "filerev"
 
1195
        inv["fileid"].executable = True
 
1196
        inv["fileid"].text_sha1 = "ffff"
 
1197
        inv["fileid"].text_size = 1
 
1198
        # get fresh objects.
 
1199
        chk_bytes = self.get_chk_bytes()
 
1200
        tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1201
        bytes = ''.join(tmp_inv.to_lines())
 
1202
        chk_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1203
        self.assertIsInstance(chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
 
1204
        self.assertEqual(
 
1205
            {('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
 
1206
            dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
 
1207
 
 
1208
    def test_file_entry_to_bytes(self):
 
1209
        inv = CHKInventory(None)
 
1210
        ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
 
1211
        ie.executable = True
 
1212
        ie.revision = 'file-rev-id'
 
1213
        ie.text_sha1 = 'abcdefgh'
 
1214
        ie.text_size = 100
 
1215
        bytes = inv._entry_to_bytes(ie)
 
1216
        self.assertEqual('file: file-id\nparent-id\nfilename\n'
 
1217
                         'file-rev-id\nabcdefgh\n100\nY', bytes)
 
1218
        ie2 = inv._bytes_to_entry(bytes)
 
1219
        self.assertEqual(ie, ie2)
 
1220
        self.assertIsInstance(ie2.name, unicode)
 
1221
        self.assertEqual(('filename', 'file-id', 'file-rev-id'),
 
1222
                         inv._bytes_to_utf8name_key(bytes))
 
1223
 
 
1224
    def test_file2_entry_to_bytes(self):
 
1225
        inv = CHKInventory(None)
 
1226
        # \u30a9 == 'omega'
 
1227
        ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
 
1228
        ie.executable = False
 
1229
        ie.revision = 'file-rev-id'
 
1230
        ie.text_sha1 = '123456'
 
1231
        ie.text_size = 25
 
1232
        bytes = inv._entry_to_bytes(ie)
 
1233
        self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
 
1234
                         'file-rev-id\n123456\n25\nN', bytes)
 
1235
        ie2 = inv._bytes_to_entry(bytes)
 
1236
        self.assertEqual(ie, ie2)
 
1237
        self.assertIsInstance(ie2.name, unicode)
 
1238
        self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
 
1239
                         inv._bytes_to_utf8name_key(bytes))
 
1240
 
 
1241
    def test_dir_entry_to_bytes(self):
 
1242
        inv = CHKInventory(None)
 
1243
        ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
 
1244
        ie.revision = 'dir-rev-id'
 
1245
        bytes = inv._entry_to_bytes(ie)
 
1246
        self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
 
1247
        ie2 = inv._bytes_to_entry(bytes)
 
1248
        self.assertEqual(ie, ie2)
 
1249
        self.assertIsInstance(ie2.name, unicode)
 
1250
        self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
 
1251
                         inv._bytes_to_utf8name_key(bytes))
 
1252
 
 
1253
    def test_dir2_entry_to_bytes(self):
 
1254
        inv = CHKInventory(None)
 
1255
        ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
 
1256
                                          None)
 
1257
        ie.revision = 'dir-rev-id'
 
1258
        bytes = inv._entry_to_bytes(ie)
 
1259
        self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
 
1260
                         'dir-rev-id', bytes)
 
1261
        ie2 = inv._bytes_to_entry(bytes)
 
1262
        self.assertEqual(ie, ie2)
 
1263
        self.assertIsInstance(ie2.name, unicode)
 
1264
        self.assertIs(ie2.parent_id, None)
 
1265
        self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
 
1266
                         inv._bytes_to_utf8name_key(bytes))
 
1267
 
 
1268
    def test_symlink_entry_to_bytes(self):
 
1269
        inv = CHKInventory(None)
 
1270
        ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
 
1271
        ie.revision = 'link-rev-id'
 
1272
        ie.symlink_target = u'target/path'
 
1273
        bytes = inv._entry_to_bytes(ie)
 
1274
        self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
 
1275
                         'link-rev-id\ntarget/path', bytes)
 
1276
        ie2 = inv._bytes_to_entry(bytes)
 
1277
        self.assertEqual(ie, ie2)
 
1278
        self.assertIsInstance(ie2.name, unicode)
 
1279
        self.assertIsInstance(ie2.symlink_target, unicode)
 
1280
        self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
 
1281
                         inv._bytes_to_utf8name_key(bytes))
 
1282
 
 
1283
    def test_symlink2_entry_to_bytes(self):
 
1284
        inv = CHKInventory(None)
 
1285
        ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
 
1286
        ie.revision = 'link-rev-id'
 
1287
        ie.symlink_target = u'target/\u03a9path'
 
1288
        bytes = inv._entry_to_bytes(ie)
 
1289
        self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
 
1290
                         'link-rev-id\ntarget/\xce\xa9path', bytes)
 
1291
        ie2 = inv._bytes_to_entry(bytes)
 
1292
        self.assertEqual(ie, ie2)
 
1293
        self.assertIsInstance(ie2.name, unicode)
 
1294
        self.assertIsInstance(ie2.symlink_target, unicode)
 
1295
        self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
 
1296
                         inv._bytes_to_utf8name_key(bytes))
 
1297
 
 
1298
    def test_tree_reference_entry_to_bytes(self):
 
1299
        inv = CHKInventory(None)
 
1300
        ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
 
1301
                                     'parent-id')
 
1302
        ie.revision = 'tree-rev-id'
 
1303
        ie.reference_revision = 'ref-rev-id'
 
1304
        bytes = inv._entry_to_bytes(ie)
 
1305
        self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
 
1306
                         'tree-rev-id\nref-rev-id', bytes)
 
1307
        ie2 = inv._bytes_to_entry(bytes)
 
1308
        self.assertEqual(ie, ie2)
 
1309
        self.assertIsInstance(ie2.name, unicode)
 
1310
        self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
 
1311
                         inv._bytes_to_utf8name_key(bytes))
 
1312
 
 
1313
    def make_basic_utf8_inventory(self):
 
1314
        inv = Inventory()
 
1315
        inv.revision_id = "revid"
 
1316
        inv.root.revision = "rootrev"
 
1317
        root_id = inv.root.file_id
 
1318
        inv.add(InventoryFile("fileid", u'f\xefle', root_id))
 
1319
        inv["fileid"].revision = "filerev"
 
1320
        inv["fileid"].text_sha1 = "ffff"
 
1321
        inv["fileid"].text_size = 0
 
1322
        inv.add(InventoryDirectory("dirid", u'dir-\N{EURO SIGN}', root_id))
 
1323
        inv.add(InventoryFile("childid", u'ch\xefld', "dirid"))
 
1324
        inv["childid"].revision = "filerev"
 
1325
        inv["childid"].text_sha1 = "ffff"
 
1326
        inv["childid"].text_size = 0
 
1327
        chk_bytes = self.get_chk_bytes()
 
1328
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1329
        bytes = ''.join(chk_inv.to_lines())
 
1330
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1331
 
 
1332
    def test__preload_handles_utf8(self):
 
1333
        new_inv = self.make_basic_utf8_inventory()
 
1334
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
 
1335
        self.assertFalse(new_inv._fully_cached)
 
1336
        new_inv._preload_cache()
 
1337
        self.assertEqual(
 
1338
            sorted([new_inv.root_id, "fileid", "dirid", "childid"]),
 
1339
            sorted(new_inv._fileid_to_entry_cache.keys()))
 
1340
        ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
 
1341
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1342
                         sorted(ie_root._children.keys()))
 
1343
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
 
1344
        self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
 
1345
 
 
1346
    def test__preload_populates_cache(self):
 
1347
        inv = Inventory()
 
1348
        inv.revision_id = "revid"
 
1349
        inv.root.revision = "rootrev"
 
1350
        root_id = inv.root.file_id
 
1351
        inv.add(InventoryFile("fileid", "file", root_id))
 
1352
        inv["fileid"].revision = "filerev"
 
1353
        inv["fileid"].executable = True
 
1354
        inv["fileid"].text_sha1 = "ffff"
 
1355
        inv["fileid"].text_size = 1
 
1356
        inv.add(InventoryDirectory("dirid", "dir", root_id))
 
1357
        inv.add(InventoryFile("childid", "child", "dirid"))
 
1358
        inv["childid"].revision = "filerev"
 
1359
        inv["childid"].executable = False
 
1360
        inv["childid"].text_sha1 = "dddd"
 
1361
        inv["childid"].text_size = 1
 
1362
        chk_bytes = self.get_chk_bytes()
 
1363
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1364
        bytes = ''.join(chk_inv.to_lines())
 
1365
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1366
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
 
1367
        self.assertFalse(new_inv._fully_cached)
 
1368
        new_inv._preload_cache()
 
1369
        self.assertEqual(
 
1370
            sorted([root_id, "fileid", "dirid", "childid"]),
 
1371
            sorted(new_inv._fileid_to_entry_cache.keys()))
 
1372
        self.assertTrue(new_inv._fully_cached)
 
1373
        ie_root = new_inv._fileid_to_entry_cache[root_id]
 
1374
        self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
 
1375
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
 
1376
        self.assertEqual(['child'], sorted(ie_dir._children.keys()))
 
1377
 
 
1378
    def test__preload_handles_partially_evaluated_inventory(self):
 
1379
        new_inv = self.make_basic_utf8_inventory()
 
1380
        ie = new_inv[new_inv.root_id]
 
1381
        self.assertIs(None, ie._children)
 
1382
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1383
                         sorted(ie.children.keys()))
 
1384
        # Accessing .children loads _children
 
1385
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1386
                         sorted(ie._children.keys()))
 
1387
        new_inv._preload_cache()
 
1388
        # No change
 
1389
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
 
1390
                         sorted(ie._children.keys()))
 
1391
        ie_dir = new_inv["dirid"]
 
1392
        self.assertEqual([u'ch\xefld'],
 
1393
                         sorted(ie_dir._children.keys()))
 
1394
 
 
1395
    def test_filter_change_in_renamed_subfolder(self):
 
1396
        inv = Inventory('tree-root')
 
1397
        src_ie = inv.add_path('src', 'directory', 'src-id')
 
1398
        inv.add_path('src/sub/', 'directory', 'sub-id')
 
1399
        a_ie = inv.add_path('src/sub/a', 'file', 'a-id')
 
1400
        a_ie.text_sha1 = osutils.sha_string('content\n')
 
1401
        a_ie.text_size = len('content\n')
 
1402
        chk_bytes = self.get_chk_bytes()
 
1403
        inv = CHKInventory.from_inventory(chk_bytes, inv)
 
1404
        inv = inv.create_by_apply_delta([
 
1405
            ("src/sub/a", "src/sub/a", "a-id", a_ie),
 
1406
            ("src", "src2", "src-id", src_ie),
 
1407
            ], 'new-rev-2')
 
1408
        new_inv = inv.filter(['a-id', 'src-id'])
 
1409
        self.assertEqual([
 
1410
            ('', 'tree-root'),
 
1411
            ('src', 'src-id'),
 
1412
            ('src/sub', 'sub-id'),
 
1413
            ('src/sub/a', 'a-id'),
 
1414
            ], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])
 
1415
 
 
1416
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
 
1417
 
 
1418
    def get_chk_bytes(self):
 
1419
        factory = groupcompress.make_pack_factory(True, True, 1)
 
1420
        trans = self.get_transport('')
 
1421
        return factory(trans)
 
1422
 
 
1423
    def make_dir(self, inv, name, parent_id):
 
1424
        inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
 
1425
 
 
1426
    def make_file(self, inv, name, parent_id, content='content\n'):
 
1427
        ie = inv.make_entry('file', name, parent_id, name + '-id')
 
1428
        ie.text_sha1 = osutils.sha_string(content)
 
1429
        ie.text_size = len(content)
 
1430
        inv.add(ie)
 
1431
 
 
1432
    def make_simple_inventory(self):
 
1433
        inv = Inventory('TREE_ROOT')
 
1434
        inv.revision_id = "revid"
 
1435
        inv.root.revision = "rootrev"
 
1436
        # /                 TREE_ROOT
 
1437
        # dir1/             dir1-id
 
1438
        #   sub-file1       sub-file1-id
 
1439
        #   sub-file2       sub-file2-id
 
1440
        #   sub-dir1/       sub-dir1-id
 
1441
        #     subsub-file1  subsub-file1-id
 
1442
        # dir2/             dir2-id
 
1443
        #   sub2-file1      sub2-file1-id
 
1444
        # top               top-id
 
1445
        self.make_dir(inv, 'dir1', 'TREE_ROOT')
 
1446
        self.make_dir(inv, 'dir2', 'TREE_ROOT')
 
1447
        self.make_dir(inv, 'sub-dir1', 'dir1-id')
 
1448
        self.make_file(inv, 'top', 'TREE_ROOT')
 
1449
        self.make_file(inv, 'sub-file1', 'dir1-id')
 
1450
        self.make_file(inv, 'sub-file2', 'dir1-id')
 
1451
        self.make_file(inv, 'subsub-file1', 'sub-dir1-id')
 
1452
        self.make_file(inv, 'sub2-file1', 'dir2-id')
 
1453
        chk_bytes = self.get_chk_bytes()
 
1454
        #  use a small maximum_size to force internal paging structures
 
1455
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
 
1456
                        maximum_size=100,
 
1457
                        search_key_name='hash-255-way')
 
1458
        bytes = ''.join(chk_inv.to_lines())
 
1459
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
 
1460
 
 
1461
    def assert_Getitems(self, expected_fileids, inv, file_ids):
 
1462
        self.assertEqual(sorted(expected_fileids),
 
1463
                         sorted([ie.file_id for ie in inv._getitems(file_ids)]))
 
1464
 
 
1465
    def assertExpand(self, all_ids, inv, file_ids):
 
1466
        (val_all_ids,
 
1467
         val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
 
1468
        self.assertEqual(set(all_ids), val_all_ids)
 
1469
        entries = inv._getitems(val_all_ids)
 
1470
        expected_children = {}
 
1471
        for entry in entries:
 
1472
            s = expected_children.setdefault(entry.parent_id, [])
 
1473
            s.append(entry.file_id)
 
1474
        val_children = dict((k, sorted(v)) for k, v
 
1475
                            in val_children.items())
 
1476
        expected_children = dict((k, sorted(v)) for k, v
 
1477
                            in expected_children.items())
 
1478
        self.assertEqual(expected_children, val_children)
 
1479
 
 
1480
    def test_make_simple_inventory(self):
 
1481
        inv = self.make_simple_inventory()
 
1482
        layout = []
 
1483
        for path, entry in inv.iter_entries_by_dir():
 
1484
            layout.append((path, entry.file_id))
 
1485
        self.assertEqual([
 
1486
            ('', 'TREE_ROOT'),
 
1487
            ('dir1', 'dir1-id'),
 
1488
            ('dir2', 'dir2-id'),
 
1489
            ('top', 'top-id'),
 
1490
            ('dir1/sub-dir1', 'sub-dir1-id'),
 
1491
            ('dir1/sub-file1', 'sub-file1-id'),
 
1492
            ('dir1/sub-file2', 'sub-file2-id'),
 
1493
            ('dir1/sub-dir1/subsub-file1', 'subsub-file1-id'),
 
1494
            ('dir2/sub2-file1', 'sub2-file1-id'),
 
1495
            ], layout)
 
1496
 
 
1497
    def test__getitems(self):
 
1498
        inv = self.make_simple_inventory()
 
1499
        # Reading from disk
 
1500
        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
 
1501
        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
 
1502
        self.assertFalse('sub-file2-id' in inv._fileid_to_entry_cache)
 
1503
        # From cache
 
1504
        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
 
1505
        # Mixed
 
1506
        self.assert_Getitems(['dir1-id', 'sub-file2-id'], inv,
 
1507
                             ['dir1-id', 'sub-file2-id'])
 
1508
        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
 
1509
        self.assertTrue('sub-file2-id' in inv._fileid_to_entry_cache)
 
1510
 
 
1511
    def test_single_file(self):
 
1512
        inv = self.make_simple_inventory()
 
1513
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
 
1514
 
 
1515
    def test_get_all_parents(self):
 
1516
        inv = self.make_simple_inventory()
 
1517
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
 
1518
                           'subsub-file1-id',
 
1519
                          ], inv, ['subsub-file1-id'])
 
1520
 
 
1521
    def test_get_children(self):
 
1522
        inv = self.make_simple_inventory()
 
1523
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
 
1524
                           'sub-file1-id', 'sub-file2-id', 'subsub-file1-id',
 
1525
                          ], inv, ['dir1-id'])
 
1526
 
 
1527
    def test_from_root(self):
 
1528
        inv = self.make_simple_inventory()
 
1529
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'dir2-id', 'sub-dir1-id',
 
1530
                           'sub-file1-id', 'sub-file2-id', 'sub2-file1-id',
 
1531
                           'subsub-file1-id', 'top-id'], inv, ['TREE_ROOT'])
 
1532
 
 
1533
    def test_top_level_file(self):
 
1534
        inv = self.make_simple_inventory()
 
1535
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
 
1536
 
 
1537
    def test_subsub_file(self):
 
1538
        inv = self.make_simple_inventory()
 
1539
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
 
1540
                           'subsub-file1-id'], inv, ['subsub-file1-id'])
 
1541
 
 
1542
    def test_sub_and_root(self):
 
1543
        inv = self.make_simple_inventory()
 
1544
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
 
1545
                           'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])
 
1546
 
 
1547
 
 
1548
class TestMutableInventoryFromTree(TestCaseWithTransport):
 
1549
 
 
1550
    def test_empty(self):
 
1551
        repository = self.make_repository('.')
 
1552
        tree = repository.revision_tree(revision.NULL_REVISION)
 
1553
        inv = mutable_inventory_from_tree(tree)
 
1554
        self.assertEqual(revision.NULL_REVISION, inv.revision_id)
 
1555
        self.assertEqual(0, len(inv))
 
1556
 
 
1557
    def test_some_files(self):
 
1558
        wt = self.make_branch_and_tree('.')
 
1559
        self.build_tree(['a'])
 
1560
        wt.add(['a'], ['thefileid'])
 
1561
        revid = wt.commit("commit")
 
1562
        tree = wt.branch.repository.revision_tree(revid)
 
1563
        inv = mutable_inventory_from_tree(tree)
 
1564
        self.assertEqual(revid, inv.revision_id)
 
1565
        self.assertEqual(2, len(inv))
 
1566
        self.assertEqual("a", inv['thefileid'].name)
 
1567
        # The inventory should be mutable and independent of
 
1568
        # the original tree
 
1569
        self.assertFalse(tree.root_inventory['thefileid'].executable)
 
1570
        inv['thefileid'].executable = True
 
1571
        self.assertFalse(tree.root_inventory['thefileid'].executable)