/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2005-2011 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
963 by Martin Pool
- add the start of a test for inventory file-id matching
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
963 by Martin Pool
- add the start of a test for inventory file-id matching
16
2729.2.5 by Martin Pool
Move per-inventory tests from test_inv to tests.inventory_implementations
17
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
18
from bzrlib import (
19
    chk_map,
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
20
    groupcompress,
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
21
    errors,
22
    inventory,
23
    osutils,
24
    repository,
25
    revision,
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
26
    tests,
5662.3.1 by Jelmer Vernooij
Add WorkingTreeFormatRegistry.
27
    workingtree,
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
28
    )
5579.3.1 by Jelmer Vernooij
Remove unused imports.
29
from bzrlib.inventory import (
30
    CHKInventory,
31
    Inventory,
32
    ROOT_ID,
33
    InventoryFile,
34
    InventoryDirectory,
35
    InventoryEntry,
36
    TreeReference,
5802.1.2 by Jelmer Vernooij
Add test for mutable_inventory_from_tree.
37
    mutable_inventory_from_tree,
5579.3.1 by Jelmer Vernooij
Remove unused imports.
38
    )
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
39
from bzrlib.tests import (
40
    TestCase,
41
    TestCaseWithTransport,
42
    )
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
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():
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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():
5718.3.1 by Jelmer Vernooij
Skip more tests for repository formats that don't support the full
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}))
5662.3.1 by Jelmer Vernooij
Add WorkingTreeFormatRegistry.
64
    for format in workingtree.format_registry._get_all():
5718.3.1 by Jelmer Vernooij
Skip more tests for repository formats that don't support the full
65
        repo_fmt = format._matchingbzrdir.repository_format
66
        if not repo_fmt.supports_full_versioned_files:
67
            continue
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
68
        scenarios.append(
69
            (str(format.__class__.__name__) + ".update_basis_by_delta", {
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
70
            'apply_delta':apply_inventory_WT_basis,
71
            'format':format}))
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
72
        scenarios.append(
73
            (str(format.__class__.__name__) + ".apply_inventory_delta", {
4526.9.1 by Robert Collins
Add WorkingTree.apply_inventory_delta to the set of delta implementations interface tested.
74
            'apply_delta':apply_inventory_WT,
75
            'format':format}))
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
76
    return scenarios
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
77
78
4634.35.19 by Andrew Bennetts
Fix test_inv.
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)
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
86
5718.3.1 by Jelmer Vernooij
Skip more tests for repository formats that don't support the full
87
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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
4526.9.1 by Robert Collins
Add WorkingTree.apply_inventory_delta to the set of delta implementations interface tested.
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
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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.
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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.
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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'
4634.35.19 by Andrew Bennetts
Fix test_inv.
160
            create_texts_for_inv(tree.branch.repository, basis)
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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)
4634.35.19 by Andrew Bennetts
Fix test_inv.
170
            repo.commit_write_group()
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
171
        except:
172
            repo.abort_write_group()
173
            raise
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
174
        # Set the basis state as the trees current state
175
        tree._write_inventory(basis)
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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'])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
179
        paths = {}
180
        parents = set()
181
        for old, new, id, entry in delta:
4526.9.4 by Robert Collins
Look for trivial issues with new_path and entry being out of sync in deltas.
182
            if None in (new, entry):
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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('')
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
188
        # Put place holders in the tree to permit adding the other entries.
4505.5.6 by Robert Collins
Check for missing parents in deltas.
189
        for pos, parent in enumerate(parents):
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
190
            if not tree.path2id(parent):
4505.5.6 by Robert Collins
Check for missing parents in deltas.
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"])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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'
4634.35.19 by Andrew Bennetts
Fix test_inv.
244
            create_texts_for_inv(repo, basis)
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
245
            repo.add_revision('basis', rev, basis)
4634.35.19 by Andrew Bennetts
Fix test_inv.
246
            repo.commit_write_group()
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
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
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
342
class TestDeltaApplication(TestCaseWithTransport):
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
343
344
    scenarios = delta_application_scenarios()
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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
4526.9.22 by Robert Collins
Check fileids in inventory deltas are not None and are strings.
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
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
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 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
398
        delta = [(None, u'path1', 'id', file1), (None, u'path2', 'id', file2)]
4505.5.3 by Robert Collins
Test infrastructure for testing all inventory delta applications and fix CHK inventories to reject repeated file ids in deltas.
399
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
400
            inv, delta)
963 by Martin Pool
- add the start of a test for inventory file-id matching
401
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
412
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
434
        delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
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 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
444
        delta = [(None, u'path', 'id', file1)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
445
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
446
            inv, delta)
447
4526.9.4 by Robert Collins
Look for trivial issues with new_path and entry being out of sync in deltas.
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
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
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)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
475
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
476
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
477
            inv, delta)
478
4505.5.6 by Robert Collins
Check for missing parents in deltas.
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 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
485
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.6 by Robert Collins
Check for missing parents in deltas.
486
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
487
            inv, delta)
488
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
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
4526.9.3 by Robert Collins
Handle mismatches between inventory delta paths and actual paths found by traversing parent pointers.
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
4526.9.5 by Robert Collins
Require that added ids in inventory deltas be new.
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
4526.9.8 by Robert Collins
Check that the paths deltas put entries into are not in use already.
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
4526.9.9 by Robert Collins
Add interface tests for dangling children in inventory deltas.
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
969 by Martin Pool
- Add less-sucky is_within_any
589
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
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
5410.1.2 by Daniel Knittl-Frank
Add a test case for `entries()` on empty inventory
605
    def test_entries_for_empty_inventory(self):
606
        """Test that entries() will not fail for an empty inventory"""
5410.1.3 by Daniel Knittl-Frank
fix whitespace (only use spaces)
607
        inv = Inventory(root_id=None)
608
        self.assertEqual([], inv.entries())
5410.1.2 by Daniel Knittl-Frank
Add a test case for `entries()` on empty inventory
609
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
610
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
611
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
612
613
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
614
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
615
        self.assertEqual(file.kind_character(), '')
616
617
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
618
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
619
        self.assertEqual(dir.kind_character(), '/')
620
621
    def test_link_kind_character(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
622
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
623
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
624
625
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
626
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
627
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
632
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
633
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
634
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
646
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
647
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
648
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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))
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
655
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
656
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
657
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
658
        self.assertTrue(file.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
659
660
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
661
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
662
        self.assertFalse(dir.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
663
664
    def test_link_has_text(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
665
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
666
        self.assertFalse(link.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
667
1713.1.11 by Robert Collins
refactor smart_add to pass around the parent inventory entry and use that, resulting in another 100bzrlib/inventory.py performance improvement, and making inventory writing the dominating factory in add. (Robert Collins)
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)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
675
1830.3.5 by John Arbash Meinel
make_entry refuses to create non-normalized entries.
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
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
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
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
722
        # in this case it's both renamed and modified; show a rename and
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
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)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
748
749
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
750
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
3735.2.99 by John Arbash Meinel
Merge bzr.dev 4034. Whitespace cleanup
751
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
752
    def get_chk_bytes(self):
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
753
        factory = groupcompress.make_pack_factory(True, True, 1)
754
        trans = self.get_transport('')
755
        return factory(trans)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
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)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
775
        self.assertEqual('plain', new_inv._search_key_name)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
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)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
793
        lines = chk_inv.to_lines()
794
        self.assertEqual([
795
            'chkinventory:\n',
796
            'revision_id: foo\n',
797
            'root_id: TREE_ROOT\n',
3735.2.132 by John Arbash Meinel
Remove references to parent_id_basename_index, now that we know we want it.
798
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
799
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
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()
3735.2.132 by John Arbash Meinel
Remove references to parent_id_basename_index, now that we know we want it.
809
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
810
        lines = chk_inv.to_lines()
811
        self.assertEqual([
812
            'chkinventory:\n',
813
            'revision_id: foo\n',
814
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
815
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
816
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
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',
3735.24.2 by John Arbash Meinel
Add a bit more strictness to the formatting, update the test case.
832
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
833
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.24.2 by John Arbash Meinel
Add a bit more strictness to the formatting, update the test case.
834
            'revision_id: foo\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
835
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
836
            ], lines)
837
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
838
        self.assertEqual('hash-16-way', chk_inv._search_key_name)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
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
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
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)
4413.5.10 by John Arbash Meinel
Clean upt the test_inv tests that assumed _root_node was real and not just a key.
874
        chk_inv.id_to_entry._ensure_root()
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
875
        self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
4413.5.10 by John Arbash Meinel
Clean upt the test_inv tests that assumed _root_node was real and not just a key.
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)
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
881
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
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)
3735.2.53 by Robert Collins
Support Inventory.__getitem__ more consistently.
940
        self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
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'))
3735.2.10 by Robert Collins
Teach CHKInventory how to make a new inventory from an inventory delta.
963
3735.2.12 by Robert Collins
Implement commit-via-deltas for split inventory repositories.
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
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
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)
4526.9.15 by Robert Collins
Fix broken CHK inventory test that was applying an inconsistend delta.
1014
        delta = [("", None, base_inv.root.file_id, None),
1015
            (None, "",  "myrootid", inv.root)]
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
1016
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1017
        self.assertEquals(reference_inv.root, new_inv.root)
1018
3735.2.10 by Robert Collins
Teach CHKInventory how to make a new inventory from an inventory delta.
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)
4413.5.10 by John Arbash Meinel
Clean upt the test_inv tests that assumed _root_node was real and not just a key.
1038
        reference_inv.id_to_entry._ensure_root()
1039
        new_inv.id_to_entry._ensure_root()
3735.2.10 by Robert Collins
Teach CHKInventory how to make a new inventory from an inventory delta.
1040
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
1041
            new_inv.id_to_entry._root_node._key)
3735.2.33 by Robert Collins
Create a smoke-tested CHKInventory.iter_changes(CHKInventory) - incomplete in general but enough to start working with.
1042
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
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()
3735.2.132 by John Arbash Meinel
Remove references to parent_id_basename_index, now that we know we want it.
1048
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
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"
3735.2.132 by John Arbash Meinel
Remove references to parent_id_basename_index, now that we know we want it.
1056
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1057
        delta = [(None, "A",  "A-id", a_entry)]
1058
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
4413.5.10 by John Arbash Meinel
Clean upt the test_inv tests that assumed _root_node was real and not just a key.
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()
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
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
3735.2.33 by Robert Collins
Create a smoke-tested CHKInventory.iter_changes(CHKInventory) - incomplete in general but enough to start working with.
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)))
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1102
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1103
    def test_parent_id_basename_to_file_id_index_enabled(self):
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
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()
3735.2.132 by John Arbash Meinel
Remove references to parent_id_basename_index, now that we know we want it.
1114
        tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1115
        bytes = ''.join(tmp_inv.to_lines())
1116
        chk_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1117
        self.assertIsInstance(chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1118
        self.assertEqual(
1119
            {('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1120
            dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
3735.36.12 by John Arbash Meinel
Add some direct tests for CHKInventory._entry_to_bytes
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))
5726.2.3 by John Arbash Meinel
Properly decode basename. In the map it is always stored as UTF-8, but
1226
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1227
    def make_basic_utf8_inventory(self):
5726.2.3 by John Arbash Meinel
Properly decode basename. In the map it is always stored as UTF-8, but
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())
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1244
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1245
1246
    def test__preload_handles_utf8(self):
1247
        new_inv = self.make_basic_utf8_inventory()
5726.2.3 by John Arbash Meinel
Properly decode basename. In the map it is always stored as UTF-8, but
1248
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
1249
        self.assertFalse(new_inv._fully_cached)
1250
        new_inv._preload_cache()
1251
        self.assertEqual(
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1252
            sorted([new_inv.root_id, "fileid", "dirid", "childid"]),
5726.2.3 by John Arbash Meinel
Properly decode basename. In the map it is always stored as UTF-8, but
1253
            sorted(new_inv._fileid_to_entry_cache.keys()))
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1254
        ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
5726.2.3 by John Arbash Meinel
Properly decode basename. In the map it is always stored as UTF-8, but
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
5726.2.1 by John Arbash Meinel
Fix bug #737234. Preload all entries for iter_entries_by_dir().
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()))
5609.27.1 by John Arbash Meinel
Backport the fix for bug #737234 to the 2.3 series.
1291
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
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
4634.51.2 by John Arbash Meinel
Start laying the groundwork for testing the expansion code
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'):
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1321
        ie = inv.make_entry('file', name, parent_id, name + '-id')
4634.51.2 by John Arbash Meinel
Start laying the groundwork for testing the expansion code
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()
4634.51.8 by John Arbash Meinel
Catch a corner case that we were missing.
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')
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
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
4634.51.5 by John Arbash Meinel
Change the api a bit.
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)
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1373
1374
    def test_make_simple_inventory(self):
4634.51.2 by John Arbash Meinel
Start laying the groundwork for testing the expansion code
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)
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
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()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1407
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1408
1409
    def test_get_all_parents(self):
1410
        inv = self.make_simple_inventory()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1411
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1412
                           'subsub-file1-id',
1413
                          ], inv, ['subsub-file1-id'])
4634.51.4 by John Arbash Meinel
Implement an expansion function that works directly on the chk maps.
1414
1415
    def test_get_children(self):
1416
        inv = self.make_simple_inventory()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1417
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1418
                           'sub-file1-id', 'sub-file2-id', 'subsub-file1-id',
4634.51.4 by John Arbash Meinel
Implement an expansion function that works directly on the chk maps.
1419
                          ], inv, ['dir1-id'])
4634.51.8 by John Arbash Meinel
Catch a corner case that we were missing.
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'])
5802.1.2 by Jelmer Vernooij
Add test for mutable_inventory_from_tree.
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)