/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,
37
    )
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.
38
from bzrlib.tests import (
39
    TestCase,
40
    TestCaseWithTransport,
41
    )
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
42
from bzrlib.tests.scenarios import load_tests_apply_scenarios
43
44
45
load_tests = load_tests_apply_scenarios
46
47
48
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.
49
    scenarios = [
50
        ('Inventory', {'apply_delta':apply_inventory_Inventory}),
51
        ]
52
    # Working tree basis delta application
53
    # Repository add_inv_by_delta.
54
    # Reduce form of the per_repository test logic - that logic needs to be
55
    # be able to get /just/ repositories whereas these tests are fine with
56
    # just creating trees.
57
    formats = set()
58
    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
59
        if format.supports_full_versioned_files:
60
            scenarios.append((str(format.__name__), {
61
                'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
62
                'format':format}))
5662.3.1 by Jelmer Vernooij
Add WorkingTreeFormatRegistry.
63
    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
64
        repo_fmt = format._matchingbzrdir.repository_format
65
        if not repo_fmt.supports_full_versioned_files:
66
            continue
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
67
        scenarios.append(
68
            (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.
69
            'apply_delta':apply_inventory_WT_basis,
70
            'format':format}))
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
71
        scenarios.append(
72
            (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.
73
            'apply_delta':apply_inventory_WT,
74
            'format':format}))
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
75
    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.
76
77
4634.35.19 by Andrew Bennetts
Fix test_inv.
78
def create_texts_for_inv(repo, inv):
79
    for path, ie in inv.iter_entries():
80
        if ie.text_size:
81
            lines = ['a' * ie.text_size]
82
        else:
83
            lines = []
84
        repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
85
5718.3.1 by Jelmer Vernooij
Skip more tests for repository formats that don't support the full
86
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.
87
def apply_inventory_Inventory(self, basis, delta):
88
    """Apply delta to basis and return the result.
89
    
90
    :param basis: An inventory to be used as the basis.
91
    :param delta: The inventory delta to apply:
92
    :return: An inventory resulting from the application.
93
    """
94
    basis.apply_delta(delta)
95
    return basis
96
97
4526.9.1 by Robert Collins
Add WorkingTree.apply_inventory_delta to the set of delta implementations interface tested.
98
def apply_inventory_WT(self, basis, delta):
99
    """Apply delta to basis and return the result.
100
101
    This sets the tree state to be basis, and then calls apply_inventory_delta.
102
    
103
    :param basis: An inventory to be used as the basis.
104
    :param delta: The inventory delta to apply:
105
    :return: An inventory resulting from the application.
106
    """
107
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
108
    control.create_repository()
109
    control.create_branch()
110
    tree = self.format.initialize(control)
111
    tree.lock_write()
112
    try:
113
        tree._write_inventory(basis)
114
    finally:
115
        tree.unlock()
116
    # Fresh object, reads disk again.
117
    tree = tree.bzrdir.open_workingtree()
118
    tree.lock_write()
119
    try:
120
        tree.apply_inventory_delta(delta)
121
    finally:
122
        tree.unlock()
123
    # reload tree - ensure we get what was written.
124
    tree = tree.bzrdir.open_workingtree()
125
    tree.lock_read()
126
    self.addCleanup(tree.unlock)
127
    # One could add 'tree._validate' here but that would cause 'early' failues 
128
    # as far as higher level code is concerned. Possibly adding an
129
    # expect_fail parameter to this function and if that is False then do a
130
    # validate call.
131
    return tree.inventory
132
133
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.
134
def apply_inventory_WT_basis(self, basis, delta):
135
    """Apply delta to basis and return the result.
136
137
    This sets the parent and then calls update_basis_by_delta.
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
138
    It also puts the basis in the repository under both 'basis' and 'result' to
139
    allow safety checks made by the WT to succeed, and finally ensures that all
140
    items in the delta with a new path are present in the WT before calling
141
    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.
142
    
143
    :param basis: An inventory to be used as the basis.
144
    :param delta: The inventory delta to apply:
145
    :return: An inventory resulting from the application.
146
    """
147
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
148
    control.create_repository()
149
    control.create_branch()
150
    tree = self.format.initialize(control)
151
    tree.lock_write()
152
    try:
153
        repo = tree.branch.repository
154
        repo.start_write_group()
155
        try:
156
            rev = revision.Revision('basis', timestamp=0, timezone=None,
157
                message="", committer="foo@example.com")
158
            basis.revision_id = 'basis'
4634.35.19 by Andrew Bennetts
Fix test_inv.
159
            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.
160
            repo.add_revision('basis', rev, basis)
161
            # Add a revision for the result, with the basis content - 
162
            # update_basis_by_delta doesn't check that the delta results in
163
            # result, and we want inconsistent deltas to get called on the
164
            # tree, or else the code isn't actually checked.
165
            rev = revision.Revision('result', timestamp=0, timezone=None,
166
                message="", committer="foo@example.com")
167
            basis.revision_id = 'result'
168
            repo.add_revision('result', rev, basis)
4634.35.19 by Andrew Bennetts
Fix test_inv.
169
            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.
170
        except:
171
            repo.abort_write_group()
172
            raise
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
173
        # Set the basis state as the trees current state
174
        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.
175
        # This reads basis from the repo and puts it into the tree's local
176
        # cache, if it has one.
177
        tree.set_parent_ids(['basis'])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
178
        paths = {}
179
        parents = set()
180
        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.
181
            if None in (new, entry):
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
182
                continue
183
            paths[new] = (entry.file_id, entry.kind)
184
            parents.add(osutils.dirname(new))
185
        parents = osutils.minimum_path_selection(parents)
186
        parents.discard('')
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
187
        # Put place holders in the tree to permit adding the other entries.
4505.5.6 by Robert Collins
Check for missing parents in deltas.
188
        for pos, parent in enumerate(parents):
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
189
            if not tree.path2id(parent):
4505.5.6 by Robert Collins
Check for missing parents in deltas.
190
                # add a synthetic directory in the tree so we can can put the
191
                # tree0 entries in place for dirstate.
192
                tree.add([parent], ["id%d" % pos], ["directory"])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
193
        if paths:
194
            # Many deltas may cause this mini-apply to fail, but we want to see what
195
            # the delta application code says, not the prep that we do to deal with 
196
            # limitations of dirstate's update_basis code.
197
            for path, (file_id, kind) in sorted(paths.items()):
198
                try:
199
                    tree.add([path], [file_id], [kind])
200
                except (KeyboardInterrupt, SystemExit):
201
                    raise
202
                except:
203
                    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.
204
    finally:
205
        tree.unlock()
206
    # Fresh lock, reads disk again.
207
    tree.lock_write()
208
    try:
209
        tree.update_basis_by_delta('result', delta)
210
    finally:
211
        tree.unlock()
212
    # reload tree - ensure we get what was written.
213
    tree = tree.bzrdir.open_workingtree()
214
    basis_tree = tree.basis_tree()
215
    basis_tree.lock_read()
216
    self.addCleanup(basis_tree.unlock)
217
    # Note, that if the tree does not have a local cache, the trick above of
218
    # setting the result as the basis, will come back to bite us. That said,
219
    # all the implementations in bzr do have a local cache.
220
    return basis_tree.inventory
221
222
223
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
224
    """Apply delta to basis and return the result.
225
    
226
    This inserts basis as a whole inventory and then uses
227
    add_inventory_by_delta to add delta.
228
229
    :param basis: An inventory to be used as the basis.
230
    :param delta: The inventory delta to apply:
231
    :return: An inventory resulting from the application.
232
    """
233
    format = self.format()
234
    control = self.make_bzrdir('tree', format=format._matchingbzrdir)
235
    repo = format.initialize(control)
236
    repo.lock_write()
237
    try:
238
        repo.start_write_group()
239
        try:
240
            rev = revision.Revision('basis', timestamp=0, timezone=None,
241
                message="", committer="foo@example.com")
242
            basis.revision_id = 'basis'
4634.35.19 by Andrew Bennetts
Fix test_inv.
243
            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.
244
            repo.add_revision('basis', rev, basis)
4634.35.19 by Andrew Bennetts
Fix test_inv.
245
            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.
246
        except:
247
            repo.abort_write_group()
248
            raise
249
    finally:
250
        repo.unlock()
251
    repo.lock_write()
252
    try:
253
        repo.start_write_group()
254
        try:
255
            inv_sha1 = repo.add_inventory_by_delta('basis', delta,
256
                'result', ['basis'])
257
        except:
258
            repo.abort_write_group()
259
            raise
260
        else:
261
            repo.commit_write_group()
262
    finally:
263
        repo.unlock()
264
    # Fresh lock, reads disk again.
265
    repo = repo.bzrdir.open_repository()
266
    repo.lock_read()
267
    self.addCleanup(repo.unlock)
268
    return repo.get_inventory('result')
269
270
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
271
class TestInventoryUpdates(TestCase):
272
273
    def test_creation_from_root_id(self):
274
        # iff a root id is passed to the constructor, a root directory is made
275
        inv = inventory.Inventory(root_id='tree-root')
276
        self.assertNotEqual(None, inv.root)
277
        self.assertEqual('tree-root', inv.root.file_id)
278
279
    def test_add_path_of_root(self):
280
        # if no root id is given at creation time, there is no root directory
281
        inv = inventory.Inventory(root_id=None)
282
        self.assertIs(None, inv.root)
283
        # add a root entry by adding its path
284
        ie = inv.add_path("", "directory", "my-root")
285
        ie.revision = 'test-rev'
286
        self.assertEqual("my-root", ie.file_id)
287
        self.assertIs(ie, inv.root)
288
289
    def test_add_path(self):
290
        inv = inventory.Inventory(root_id='tree_root')
291
        ie = inv.add_path('hello', 'file', 'hello-id')
292
        self.assertEqual('hello-id', ie.file_id)
293
        self.assertEqual('file', ie.kind)
294
295
    def test_copy(self):
296
        """Make sure copy() works and creates a deep copy."""
297
        inv = inventory.Inventory(root_id='some-tree-root')
298
        ie = inv.add_path('hello', 'file', 'hello-id')
299
        inv2 = inv.copy()
300
        inv.root.file_id = 'some-new-root'
301
        ie.name = 'file2'
302
        self.assertEqual('some-tree-root', inv2.root.file_id)
303
        self.assertEqual('hello', inv2['hello-id'].name)
304
305
    def test_copy_empty(self):
306
        """Make sure an empty inventory can be copied."""
307
        inv = inventory.Inventory(root_id=None)
308
        inv2 = inv.copy()
309
        self.assertIs(None, inv2.root)
310
311
    def test_copy_copies_root_revision(self):
312
        """Make sure the revision of the root gets copied."""
313
        inv = inventory.Inventory(root_id='someroot')
314
        inv.root.revision = 'therev'
315
        inv2 = inv.copy()
316
        self.assertEquals('someroot', inv2.root.file_id)
317
        self.assertEquals('therev', inv2.root.revision)
318
319
    def test_create_tree_reference(self):
320
        inv = inventory.Inventory('tree-root-123')
321
        inv.add(TreeReference('nested-id', 'nested', parent_id='tree-root-123',
322
                              revision='rev', reference_revision='rev2'))
323
324
    def test_error_encoding(self):
325
        inv = inventory.Inventory('tree-root')
326
        inv.add(InventoryFile('a-id', u'\u1234', 'tree-root'))
327
        e = self.assertRaises(errors.InconsistentDelta, inv.add,
328
            InventoryFile('b-id', u'\u1234', 'tree-root'))
329
        self.assertContainsRe(str(e), r'\\u1234')
330
331
    def test_add_recursive(self):
332
        parent = InventoryDirectory('src-id', 'src', 'tree-root')
333
        child = InventoryFile('hello-id', 'hello.c', 'src-id')
334
        parent.children[child.file_id] = child
335
        inv = inventory.Inventory('tree-root')
336
        inv.add(parent)
337
        self.assertEqual('src/hello.c', inv.id2path('hello-id'))
338
339
340
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.
341
class TestDeltaApplication(TestCaseWithTransport):
5559.2.2 by Martin Pool
Change to using standard load_tests_apply_scenarios.
342
343
    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.
344
 
345
    def get_empty_inventory(self, reference_inv=None):
346
        """Get an empty inventory.
347
348
        Note that tests should not depend on the revision of the root for
349
        setting up test conditions, as it has to be flexible to accomodate non
350
        rich root repositories.
351
352
        :param reference_inv: If not None, get the revision for the root from
353
            this inventory. This is useful for dealing with older repositories
354
            that routinely discarded the root entry data. If None, the root's
355
            revision is set to 'basis'.
356
        """
357
        inv = inventory.Inventory()
358
        if reference_inv is not None:
359
            inv.root.revision = reference_inv.root.revision
360
        else:
361
            inv.root.revision = 'basis'
362
        return inv
363
364
    def test_empty_delta(self):
365
        inv = self.get_empty_inventory()
366
        delta = []
367
        inv = self.apply_delta(self, inv, delta)
368
        inv2 = self.get_empty_inventory(inv)
369
        self.assertEqual([], inv2._make_delta(inv))
370
4526.9.22 by Robert Collins
Check fileids in inventory deltas are not None and are strings.
371
    def test_None_file_id(self):
372
        inv = self.get_empty_inventory()
373
        dir1 = inventory.InventoryDirectory(None, 'dir1', inv.root.file_id)
374
        dir1.revision = 'result'
375
        delta = [(None, u'dir1', None, dir1)]
376
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
377
            inv, delta)
378
379
    def test_unicode_file_id(self):
380
        inv = self.get_empty_inventory()
381
        dir1 = inventory.InventoryDirectory(u'dirid', 'dir1', inv.root.file_id)
382
        dir1.revision = 'result'
383
        delta = [(None, u'dir1', dir1.file_id, dir1)]
384
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
385
            inv, delta)
386
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.
387
    def test_repeated_file_id(self):
388
        inv = self.get_empty_inventory()
389
        file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
390
        file1.revision = 'result'
391
        file1.text_size = 0
392
        file1.text_sha1 = ""
393
        file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
394
        file2.revision = 'result'
395
        file2.text_size = 0
396
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
397
        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.
398
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
399
            inv, delta)
963 by Martin Pool
- add the start of a test for inventory file-id matching
400
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
401
    def test_repeated_new_path(self):
402
        inv = self.get_empty_inventory()
403
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
404
        file1.revision = 'result'
405
        file1.text_size = 0
406
        file1.text_sha1 = ""
407
        file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
408
        file2.revision = 'result'
409
        file2.text_size = 0
410
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
411
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
412
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
413
            inv, delta)
414
415
    def test_repeated_old_path(self):
416
        inv = self.get_empty_inventory()
417
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
418
        file1.revision = 'result'
419
        file1.text_size = 0
420
        file1.text_sha1 = ""
421
        # We can't *create* a source inventory with the same path, but
422
        # a badly generated partial delta might claim the same source twice.
423
        # This would be buggy in two ways: the path is repeated in the delta,
424
        # And the path for one of the file ids doesn't match the source
425
        # location. Alternatively, we could have a repeated fileid, but that
426
        # is separately checked for.
427
        file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
428
        file2.revision = 'result'
429
        file2.text_size = 0
430
        file2.text_sha1 = ""
431
        inv.add(file1)
432
        inv.add(file2)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
433
        delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
434
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
435
            inv, delta)
436
437
    def test_mismatched_id_entry_id(self):
438
        inv = self.get_empty_inventory()
439
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
440
        file1.revision = 'result'
441
        file1.text_size = 0
442
        file1.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
443
        delta = [(None, u'path', 'id', file1)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
444
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
445
            inv, delta)
446
4526.9.4 by Robert Collins
Look for trivial issues with new_path and entry being out of sync in deltas.
447
    def test_mismatched_new_path_entry_None(self):
448
        inv = self.get_empty_inventory()
449
        delta = [(None, u'path', 'id', None)]
450
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
451
            inv, delta)
452
453
    def test_mismatched_new_path_None_entry(self):
454
        inv = self.get_empty_inventory()
455
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
456
        file1.revision = 'result'
457
        file1.text_size = 0
458
        file1.text_sha1 = ""
459
        delta = [(u"path", None, 'id1', file1)]
460
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
461
            inv, delta)
462
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
463
    def test_parent_is_not_directory(self):
464
        inv = self.get_empty_inventory()
465
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
466
        file1.revision = 'result'
467
        file1.text_size = 0
468
        file1.text_sha1 = ""
469
        file2 = inventory.InventoryFile('id2', 'path2', 'id1')
470
        file2.revision = 'result'
471
        file2.text_size = 0
472
        file2.text_sha1 = ""
473
        inv.add(file1)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
474
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
475
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
476
            inv, delta)
477
4505.5.6 by Robert Collins
Check for missing parents in deltas.
478
    def test_parent_is_missing(self):
479
        inv = self.get_empty_inventory()
480
        file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
481
        file2.revision = 'result'
482
        file2.text_size = 0
483
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
484
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.6 by Robert Collins
Check for missing parents in deltas.
485
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
486
            inv, delta)
487
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
488
    def test_new_parent_path_has_wrong_id(self):
489
        inv = self.get_empty_inventory()
490
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
491
        parent1.revision = 'result'
492
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
493
        parent2.revision = 'result'
494
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
495
        file1.revision = 'result'
496
        file1.text_size = 0
497
        file1.text_sha1 = ""
498
        inv.add(parent1)
499
        inv.add(parent2)
500
        # This delta claims that file1 is at dir/path, but actually its at
501
        # dir2/path if you follow the inventory parent structure.
502
        delta = [(None, u'dir/path', 'id', file1)]
503
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
504
            inv, delta)
505
4526.9.3 by Robert Collins
Handle mismatches between inventory delta paths and actual paths found by traversing parent pointers.
506
    def test_old_parent_path_is_wrong(self):
507
        inv = self.get_empty_inventory()
508
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
509
        parent1.revision = 'result'
510
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
511
        parent2.revision = 'result'
512
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
513
        file1.revision = 'result'
514
        file1.text_size = 0
515
        file1.text_sha1 = ""
516
        inv.add(parent1)
517
        inv.add(parent2)
518
        inv.add(file1)
519
        # This delta claims that file1 was at dir/path, but actually it was at
520
        # dir2/path if you follow the inventory parent structure.
521
        delta = [(u'dir/path', None, 'id', None)]
522
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
523
            inv, delta)
524
525
    def test_old_parent_path_is_for_other_id(self):
526
        inv = self.get_empty_inventory()
527
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
528
        parent1.revision = 'result'
529
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
530
        parent2.revision = 'result'
531
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
532
        file1.revision = 'result'
533
        file1.text_size = 0
534
        file1.text_sha1 = ""
535
        file2 = inventory.InventoryFile('id2', 'path', 'p-1')
536
        file2.revision = 'result'
537
        file2.text_size = 0
538
        file2.text_sha1 = ""
539
        inv.add(parent1)
540
        inv.add(parent2)
541
        inv.add(file1)
542
        inv.add(file2)
543
        # This delta claims that file1 was at dir/path, but actually it was at
544
        # dir2/path if you follow the inventory parent structure. At dir/path
545
        # is another entry we should not delete.
546
        delta = [(u'dir/path', None, 'id', None)]
547
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
548
            inv, delta)
549
4526.9.5 by Robert Collins
Require that added ids in inventory deltas be new.
550
    def test_add_existing_id_new_path(self):
551
        inv = self.get_empty_inventory()
552
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
553
        parent1.revision = 'result'
554
        parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
555
        parent2.revision = 'result'
556
        inv.add(parent1)
557
        delta = [(None, u'dir2', 'p-1', parent2)]
558
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
559
            inv, delta)
560
4526.9.8 by Robert Collins
Check that the paths deltas put entries into are not in use already.
561
    def test_add_new_id_existing_path(self):
562
        inv = self.get_empty_inventory()
563
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
564
        parent1.revision = 'result'
565
        parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
566
        parent2.revision = 'result'
567
        inv.add(parent1)
568
        delta = [(None, u'dir1', 'p-2', parent2)]
569
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
570
            inv, delta)
571
4526.9.9 by Robert Collins
Add interface tests for dangling children in inventory deltas.
572
    def test_remove_dir_leaving_dangling_child(self):
573
        inv = self.get_empty_inventory()
574
        dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
575
        dir1.revision = 'result'
576
        dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
577
        dir2.revision = 'result'
578
        dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
579
        dir3.revision = 'result'
580
        inv.add(dir1)
581
        inv.add(dir2)
582
        inv.add(dir3)
583
        delta = [(u'dir1', None, 'p-1', None),
584
            (u'dir1/child2', None, 'p-3', None)]
585
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
586
            inv, delta)
587
969 by Martin Pool
- Add less-sucky is_within_any
588
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
589
class TestInventory(TestCase):
590
591
    def test_is_root(self):
592
        """Ensure our root-checking code is accurate."""
593
        inv = inventory.Inventory('TREE_ROOT')
594
        self.assertTrue(inv.is_root('TREE_ROOT'))
595
        self.assertFalse(inv.is_root('booga'))
596
        inv.root.file_id = 'booga'
597
        self.assertFalse(inv.is_root('TREE_ROOT'))
598
        self.assertTrue(inv.is_root('booga'))
599
        # works properly even if no root is set
600
        inv.root = None
601
        self.assertFalse(inv.is_root('TREE_ROOT'))
602
        self.assertFalse(inv.is_root('booga'))
603
5410.1.2 by Daniel Knittl-Frank
Add a test case for `entries()` on empty inventory
604
    def test_entries_for_empty_inventory(self):
605
        """Test that entries() will not fail for an empty inventory"""
5410.1.3 by Daniel Knittl-Frank
fix whitespace (only use spaces)
606
        inv = Inventory(root_id=None)
607
        self.assertEqual([], inv.entries())
5410.1.2 by Daniel Knittl-Frank
Add a test case for `entries()` on empty inventory
608
4634.51.7 by John Arbash Meinel
Finish adding CHKInventory as a permutation in per_inventory.
609
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
610
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
611
612
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
613
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
614
        self.assertEqual(file.kind_character(), '')
615
616
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
617
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
618
        self.assertEqual(dir.kind_character(), '/')
619
620
    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
621
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
622
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
623
624
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
625
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
626
        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
627
        self.assertEqual((False, False), left.detect_changes(right))
628
        self.assertEqual((False, False), right.detect_changes(left))
629
630
    def test_file_detect_changes(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
631
        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
632
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
633
        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
634
        right.text_sha1 = 123
635
        self.assertEqual((False, False), left.detect_changes(right))
636
        self.assertEqual((False, False), right.detect_changes(left))
637
        left.executable = True
638
        self.assertEqual((False, True), left.detect_changes(right))
639
        self.assertEqual((False, True), right.detect_changes(left))
640
        right.text_sha1 = 321
641
        self.assertEqual((True, True), left.detect_changes(right))
642
        self.assertEqual((True, True), right.detect_changes(left))
643
644
    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
645
        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
646
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
647
        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
648
        right.symlink_target='foo'
649
        self.assertEqual((False, False), left.detect_changes(right))
650
        self.assertEqual((False, False), right.detect_changes(left))
651
        left.symlink_target = 'different'
652
        self.assertEqual((True, False), left.detect_changes(right))
653
        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
654
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
655
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
656
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
657
        self.assertTrue(file.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
658
659
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
660
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
661
        self.assertFalse(dir.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
662
663
    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
664
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
665
        self.assertFalse(link.has_text())
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
666
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)
667
    def test_make_entry(self):
668
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
669
            inventory.InventoryFile)
670
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
671
            inventory.InventoryLink)
672
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
673
            inventory.InventoryDirectory)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
674
1830.3.5 by John Arbash Meinel
make_entry refuses to create non-normalized entries.
675
    def test_make_entry_non_normalized(self):
676
        orig_normalized_filename = osutils.normalized_filename
677
678
        try:
679
            osutils.normalized_filename = osutils._accessible_normalized_filename
680
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
681
            self.assertEqual(u'\xe5', entry.name)
682
            self.assertIsInstance(entry, inventory.InventoryFile)
683
684
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
685
            self.assertRaises(errors.InvalidNormalization,
686
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
687
        finally:
688
            osutils.normalized_filename = orig_normalized_filename
689
690
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
691
class TestDescribeChanges(TestCase):
692
693
    def test_describe_change(self):
694
        # we need to test the following change combinations:
695
        # rename
696
        # reparent
697
        # modify
698
        # gone
699
        # added
700
        # renamed/reparented and modified
701
        # change kind (perhaps can't be done yet?)
702
        # also, merged in combination with all of these?
703
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
704
        old_a.text_sha1 = '123132'
705
        old_a.text_size = 0
706
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
707
        new_a.text_sha1 = '123132'
708
        new_a.text_size = 0
709
710
        self.assertChangeDescription('unchanged', old_a, new_a)
711
712
        new_a.text_size = 10
713
        new_a.text_sha1 = 'abcabc'
714
        self.assertChangeDescription('modified', old_a, new_a)
715
716
        self.assertChangeDescription('added', None, new_a)
717
        self.assertChangeDescription('removed', old_a, None)
718
        # perhaps a bit questionable but seems like the most reasonable thing...
719
        self.assertChangeDescription('unchanged', None, None)
720
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
721
        # 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
722
        # modification:
723
        new_a.name = 'newfilename'
724
        self.assertChangeDescription('modified and renamed', old_a, new_a)
725
726
        # reparenting is 'renaming'
727
        new_a.name = old_a.name
728
        new_a.parent_id = 'somedir-id'
729
        self.assertChangeDescription('modified and renamed', old_a, new_a)
730
731
        # reset the content values so its not modified
732
        new_a.text_size = old_a.text_size
733
        new_a.text_sha1 = old_a.text_sha1
734
        new_a.name = old_a.name
735
736
        new_a.name = 'newfilename'
737
        self.assertChangeDescription('renamed', old_a, new_a)
738
739
        # reparenting is 'renaming'
740
        new_a.name = old_a.name
741
        new_a.parent_id = 'somedir-id'
742
        self.assertChangeDescription('renamed', old_a, new_a)
743
744
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
745
        change = InventoryEntry.describe_change(old_ie, new_ie)
746
        self.assertEqual(expected_change, change)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
747
748
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
749
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
3735.2.99 by John Arbash Meinel
Merge bzr.dev 4034. Whitespace cleanup
750
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
751
    def get_chk_bytes(self):
4634.51.1 by John Arbash Meinel
Switch away from creating a whole repository just to get a VF.
752
        factory = groupcompress.make_pack_factory(True, True, 1)
753
        trans = self.get_transport('')
754
        return factory(trans)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
755
756
    def read_bytes(self, chk_bytes, key):
757
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
758
        return stream.next().get_bytes_as("fulltext")
759
760
    def test_deserialise_gives_CHKInventory(self):
761
        inv = Inventory()
762
        inv.revision_id = "revid"
763
        inv.root.revision = "rootrev"
764
        chk_bytes = self.get_chk_bytes()
765
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
766
        bytes = ''.join(chk_inv.to_lines())
767
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
768
        self.assertEqual("revid", new_inv.revision_id)
769
        self.assertEqual("directory", new_inv.root.kind)
770
        self.assertEqual(inv.root.file_id, new_inv.root.file_id)
771
        self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
772
        self.assertEqual(inv.root.name, new_inv.root.name)
773
        self.assertEqual("rootrev", new_inv.root.revision)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
774
        self.assertEqual('plain', new_inv._search_key_name)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
775
776
    def test_deserialise_wrong_revid(self):
777
        inv = Inventory()
778
        inv.revision_id = "revid"
779
        inv.root.revision = "rootrev"
780
        chk_bytes = self.get_chk_bytes()
781
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
782
        bytes = ''.join(chk_inv.to_lines())
783
        self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
784
            bytes, ("revid2",))
785
786
    def test_captures_rev_root_byid(self):
787
        inv = Inventory()
788
        inv.revision_id = "foo"
789
        inv.root.revision = "bar"
790
        chk_bytes = self.get_chk_bytes()
791
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
792
        lines = chk_inv.to_lines()
793
        self.assertEqual([
794
            'chkinventory:\n',
795
            'revision_id: foo\n',
796
            '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.
797
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
798
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
799
            ], lines)
800
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
801
        self.assertEqual('plain', chk_inv._search_key_name)
802
803
    def test_captures_parent_id_basename_index(self):
804
        inv = Inventory()
805
        inv.revision_id = "foo"
806
        inv.root.revision = "bar"
807
        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.
808
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
809
        lines = chk_inv.to_lines()
810
        self.assertEqual([
811
            'chkinventory:\n',
812
            'revision_id: foo\n',
813
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
814
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
815
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
816
            ], lines)
817
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
818
        self.assertEqual('plain', chk_inv._search_key_name)
819
820
    def test_captures_search_key_name(self):
821
        inv = Inventory()
822
        inv.revision_id = "foo"
823
        inv.root.revision = "bar"
824
        chk_bytes = self.get_chk_bytes()
825
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
826
                                              search_key_name='hash-16-way')
827
        lines = chk_inv.to_lines()
828
        self.assertEqual([
829
            'chkinventory:\n',
830
            '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.
831
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
832
            '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.
833
            'revision_id: foo\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
834
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
835
            ], lines)
836
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
837
        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.
838
839
    def test_directory_children_on_demand(self):
840
        inv = Inventory()
841
        inv.revision_id = "revid"
842
        inv.root.revision = "rootrev"
843
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
844
        inv["fileid"].revision = "filerev"
845
        inv["fileid"].executable = True
846
        inv["fileid"].text_sha1 = "ffff"
847
        inv["fileid"].text_size = 1
848
        chk_bytes = self.get_chk_bytes()
849
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
850
        bytes = ''.join(chk_inv.to_lines())
851
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
852
        root_entry = new_inv[inv.root.file_id]
853
        self.assertEqual(None, root_entry._children)
854
        self.assertEqual(['file'], root_entry.children.keys())
855
        file_direct = new_inv["fileid"]
856
        file_found = root_entry.children['file']
857
        self.assertEqual(file_direct.kind, file_found.kind)
858
        self.assertEqual(file_direct.file_id, file_found.file_id)
859
        self.assertEqual(file_direct.parent_id, file_found.parent_id)
860
        self.assertEqual(file_direct.name, file_found.name)
861
        self.assertEqual(file_direct.revision, file_found.revision)
862
        self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
863
        self.assertEqual(file_direct.text_size, file_found.text_size)
864
        self.assertEqual(file_direct.executable, file_found.executable)
865
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
866
    def test_from_inventory_maximum_size(self):
867
        # from_inventory supports the maximum_size parameter.
868
        inv = Inventory()
869
        inv.revision_id = "revid"
870
        inv.root.revision = "rootrev"
871
        chk_bytes = self.get_chk_bytes()
872
        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.
873
        chk_inv.id_to_entry._ensure_root()
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
874
        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.
875
        self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
876
        p_id_basename = chk_inv.parent_id_basename_to_file_id
877
        p_id_basename._ensure_root()
878
        self.assertEqual(120, p_id_basename._root_node.maximum_size)
879
        self.assertEqual(2, p_id_basename._root_node._key_width)
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
880
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
881
    def test___iter__(self):
882
        inv = Inventory()
883
        inv.revision_id = "revid"
884
        inv.root.revision = "rootrev"
885
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
886
        inv["fileid"].revision = "filerev"
887
        inv["fileid"].executable = True
888
        inv["fileid"].text_sha1 = "ffff"
889
        inv["fileid"].text_size = 1
890
        chk_bytes = self.get_chk_bytes()
891
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
892
        bytes = ''.join(chk_inv.to_lines())
893
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
894
        fileids = list(new_inv.__iter__())
895
        fileids.sort()
896
        self.assertEqual([inv.root.file_id, "fileid"], fileids)
897
898
    def test__len__(self):
899
        inv = Inventory()
900
        inv.revision_id = "revid"
901
        inv.root.revision = "rootrev"
902
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
903
        inv["fileid"].revision = "filerev"
904
        inv["fileid"].executable = True
905
        inv["fileid"].text_sha1 = "ffff"
906
        inv["fileid"].text_size = 1
907
        chk_bytes = self.get_chk_bytes()
908
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
909
        self.assertEqual(2, len(chk_inv))
910
911
    def test___getitem__(self):
912
        inv = Inventory()
913
        inv.revision_id = "revid"
914
        inv.root.revision = "rootrev"
915
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
916
        inv["fileid"].revision = "filerev"
917
        inv["fileid"].executable = True
918
        inv["fileid"].text_sha1 = "ffff"
919
        inv["fileid"].text_size = 1
920
        chk_bytes = self.get_chk_bytes()
921
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
922
        bytes = ''.join(chk_inv.to_lines())
923
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
924
        root_entry = new_inv[inv.root.file_id]
925
        file_entry = new_inv["fileid"]
926
        self.assertEqual("directory", root_entry.kind)
927
        self.assertEqual(inv.root.file_id, root_entry.file_id)
928
        self.assertEqual(inv.root.parent_id, root_entry.parent_id)
929
        self.assertEqual(inv.root.name, root_entry.name)
930
        self.assertEqual("rootrev", root_entry.revision)
931
        self.assertEqual("file", file_entry.kind)
932
        self.assertEqual("fileid", file_entry.file_id)
933
        self.assertEqual(inv.root.file_id, file_entry.parent_id)
934
        self.assertEqual("file", file_entry.name)
935
        self.assertEqual("filerev", file_entry.revision)
936
        self.assertEqual("ffff", file_entry.text_sha1)
937
        self.assertEqual(1, file_entry.text_size)
938
        self.assertEqual(True, file_entry.executable)
3735.2.53 by Robert Collins
Support Inventory.__getitem__ more consistently.
939
        self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
940
941
    def test_has_id_true(self):
942
        inv = Inventory()
943
        inv.revision_id = "revid"
944
        inv.root.revision = "rootrev"
945
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
946
        inv["fileid"].revision = "filerev"
947
        inv["fileid"].executable = True
948
        inv["fileid"].text_sha1 = "ffff"
949
        inv["fileid"].text_size = 1
950
        chk_bytes = self.get_chk_bytes()
951
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
952
        self.assertTrue(chk_inv.has_id('fileid'))
953
        self.assertTrue(chk_inv.has_id(inv.root.file_id))
954
955
    def test_has_id_not(self):
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)
961
        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.
962
3735.2.12 by Robert Collins
Implement commit-via-deltas for split inventory repositories.
963
    def test_id2path(self):
964
        inv = Inventory()
965
        inv.revision_id = "revid"
966
        inv.root.revision = "rootrev"
967
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
968
        fileentry = InventoryFile("fileid", "file", "dirid")
969
        inv.add(direntry)
970
        inv.add(fileentry)
971
        inv["fileid"].revision = "filerev"
972
        inv["fileid"].executable = True
973
        inv["fileid"].text_sha1 = "ffff"
974
        inv["fileid"].text_size = 1
975
        inv["dirid"].revision = "filerev"
976
        chk_bytes = self.get_chk_bytes()
977
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
978
        bytes = ''.join(chk_inv.to_lines())
979
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
980
        self.assertEqual('', new_inv.id2path(inv.root.file_id))
981
        self.assertEqual('dir', new_inv.id2path('dirid'))
982
        self.assertEqual('dir/file', new_inv.id2path('fileid'))
983
984
    def test_path2id(self):
985
        inv = Inventory()
986
        inv.revision_id = "revid"
987
        inv.root.revision = "rootrev"
988
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
989
        fileentry = InventoryFile("fileid", "file", "dirid")
990
        inv.add(direntry)
991
        inv.add(fileentry)
992
        inv["fileid"].revision = "filerev"
993
        inv["fileid"].executable = True
994
        inv["fileid"].text_sha1 = "ffff"
995
        inv["fileid"].text_size = 1
996
        inv["dirid"].revision = "filerev"
997
        chk_bytes = self.get_chk_bytes()
998
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
999
        bytes = ''.join(chk_inv.to_lines())
1000
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1001
        self.assertEqual(inv.root.file_id, new_inv.path2id(''))
1002
        self.assertEqual('dirid', new_inv.path2id('dir'))
1003
        self.assertEqual('fileid', new_inv.path2id('dir/file'))
1004
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
1005
    def test_create_by_apply_delta_sets_root(self):
1006
        inv = Inventory()
1007
        inv.revision_id = "revid"
1008
        chk_bytes = self.get_chk_bytes()
1009
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1010
        inv.add_path("", "directory", "myrootid", None)
1011
        inv.revision_id = "expectedid"
1012
        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.
1013
        delta = [("", None, base_inv.root.file_id, None),
1014
            (None, "",  "myrootid", inv.root)]
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
1015
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1016
        self.assertEquals(reference_inv.root, new_inv.root)
1017
3735.2.10 by Robert Collins
Teach CHKInventory how to make a new inventory from an inventory delta.
1018
    def test_create_by_apply_delta_empty_add_child(self):
1019
        inv = Inventory()
1020
        inv.revision_id = "revid"
1021
        inv.root.revision = "rootrev"
1022
        chk_bytes = self.get_chk_bytes()
1023
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1024
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1025
        a_entry.revision = "filerev"
1026
        a_entry.executable = True
1027
        a_entry.text_sha1 = "ffff"
1028
        a_entry.text_size = 1
1029
        inv.add(a_entry)
1030
        inv.revision_id = "expectedid"
1031
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1032
        delta = [(None, "A",  "A-id", a_entry)]
1033
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
1034
        # new_inv should be the same as reference_inv.
1035
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1036
        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.
1037
        reference_inv.id_to_entry._ensure_root()
1038
        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.
1039
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
1040
            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.
1041
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1042
    def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
1043
        inv = Inventory()
1044
        inv.revision_id = "revid"
1045
        inv.root.revision = "rootrev"
1046
        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.
1047
        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.
1048
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
1049
        a_entry.revision = "filerev"
1050
        a_entry.executable = True
1051
        a_entry.text_sha1 = "ffff"
1052
        a_entry.text_size = 1
1053
        inv.add(a_entry)
1054
        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.
1055
        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.
1056
        delta = [(None, "A",  "A-id", a_entry)]
1057
        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.
1058
        reference_inv.id_to_entry._ensure_root()
1059
        reference_inv.parent_id_basename_to_file_id._ensure_root()
1060
        new_inv.id_to_entry._ensure_root()
1061
        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.
1062
        # new_inv should be the same as reference_inv.
1063
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1064
        self.assertEqual(reference_inv.root_id, new_inv.root_id)
1065
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
1066
            new_inv.id_to_entry._root_node._key)
1067
        self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
1068
            new_inv.parent_id_basename_to_file_id._root_node._key)
1069
3735.2.33 by Robert Collins
Create a smoke-tested CHKInventory.iter_changes(CHKInventory) - incomplete in general but enough to start working with.
1070
    def test_iter_changes(self):
1071
        # Low level bootstrapping smoke test; comprehensive generic tests via
1072
        # InterTree are coming.
1073
        inv = Inventory()
1074
        inv.revision_id = "revid"
1075
        inv.root.revision = "rootrev"
1076
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1077
        inv["fileid"].revision = "filerev"
1078
        inv["fileid"].executable = True
1079
        inv["fileid"].text_sha1 = "ffff"
1080
        inv["fileid"].text_size = 1
1081
        inv2 = Inventory()
1082
        inv2.revision_id = "revid2"
1083
        inv2.root.revision = "rootrev"
1084
        inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
1085
        inv2["fileid"].revision = "filerev2"
1086
        inv2["fileid"].executable = False
1087
        inv2["fileid"].text_sha1 = "bbbb"
1088
        inv2["fileid"].text_size = 2
1089
        # get fresh objects.
1090
        chk_bytes = self.get_chk_bytes()
1091
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1092
        bytes = ''.join(chk_inv.to_lines())
1093
        inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1094
        chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
1095
        bytes = ''.join(chk_inv2.to_lines())
1096
        inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
1097
        self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
1098
            ('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
1099
            (False, True))],
1100
            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.
1101
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1102
    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.
1103
        inv = Inventory()
1104
        inv.revision_id = "revid"
1105
        inv.root.revision = "rootrev"
1106
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1107
        inv["fileid"].revision = "filerev"
1108
        inv["fileid"].executable = True
1109
        inv["fileid"].text_sha1 = "ffff"
1110
        inv["fileid"].text_size = 1
1111
        # get fresh objects.
1112
        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.
1113
        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.
1114
        bytes = ''.join(tmp_inv.to_lines())
1115
        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.
1116
        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.
1117
        self.assertEqual(
1118
            {('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1119
            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
1120
1121
    def test_file_entry_to_bytes(self):
1122
        inv = CHKInventory(None)
1123
        ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
1124
        ie.executable = True
1125
        ie.revision = 'file-rev-id'
1126
        ie.text_sha1 = 'abcdefgh'
1127
        ie.text_size = 100
1128
        bytes = inv._entry_to_bytes(ie)
1129
        self.assertEqual('file: file-id\nparent-id\nfilename\n'
1130
                         'file-rev-id\nabcdefgh\n100\nY', bytes)
1131
        ie2 = inv._bytes_to_entry(bytes)
1132
        self.assertEqual(ie, ie2)
1133
        self.assertIsInstance(ie2.name, unicode)
1134
        self.assertEqual(('filename', 'file-id', 'file-rev-id'),
1135
                         inv._bytes_to_utf8name_key(bytes))
1136
1137
    def test_file2_entry_to_bytes(self):
1138
        inv = CHKInventory(None)
1139
        # \u30a9 == 'omega'
1140
        ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
1141
        ie.executable = False
1142
        ie.revision = 'file-rev-id'
1143
        ie.text_sha1 = '123456'
1144
        ie.text_size = 25
1145
        bytes = inv._entry_to_bytes(ie)
1146
        self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
1147
                         'file-rev-id\n123456\n25\nN', bytes)
1148
        ie2 = inv._bytes_to_entry(bytes)
1149
        self.assertEqual(ie, ie2)
1150
        self.assertIsInstance(ie2.name, unicode)
1151
        self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
1152
                         inv._bytes_to_utf8name_key(bytes))
1153
1154
    def test_dir_entry_to_bytes(self):
1155
        inv = CHKInventory(None)
1156
        ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
1157
        ie.revision = 'dir-rev-id'
1158
        bytes = inv._entry_to_bytes(ie)
1159
        self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1160
        ie2 = inv._bytes_to_entry(bytes)
1161
        self.assertEqual(ie, ie2)
1162
        self.assertIsInstance(ie2.name, unicode)
1163
        self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
1164
                         inv._bytes_to_utf8name_key(bytes))
1165
1166
    def test_dir2_entry_to_bytes(self):
1167
        inv = CHKInventory(None)
1168
        ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1169
                                          None)
1170
        ie.revision = 'dir-rev-id'
1171
        bytes = inv._entry_to_bytes(ie)
1172
        self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
1173
                         'dir-rev-id', bytes)
1174
        ie2 = inv._bytes_to_entry(bytes)
1175
        self.assertEqual(ie, ie2)
1176
        self.assertIsInstance(ie2.name, unicode)
1177
        self.assertIs(ie2.parent_id, None)
1178
        self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
1179
                         inv._bytes_to_utf8name_key(bytes))
1180
1181
    def test_symlink_entry_to_bytes(self):
1182
        inv = CHKInventory(None)
1183
        ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
1184
        ie.revision = 'link-rev-id'
1185
        ie.symlink_target = u'target/path'
1186
        bytes = inv._entry_to_bytes(ie)
1187
        self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
1188
                         'link-rev-id\ntarget/path', bytes)
1189
        ie2 = inv._bytes_to_entry(bytes)
1190
        self.assertEqual(ie, ie2)
1191
        self.assertIsInstance(ie2.name, unicode)
1192
        self.assertIsInstance(ie2.symlink_target, unicode)
1193
        self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
1194
                         inv._bytes_to_utf8name_key(bytes))
1195
1196
    def test_symlink2_entry_to_bytes(self):
1197
        inv = CHKInventory(None)
1198
        ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
1199
        ie.revision = 'link-rev-id'
1200
        ie.symlink_target = u'target/\u03a9path'
1201
        bytes = inv._entry_to_bytes(ie)
1202
        self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1203
                         'link-rev-id\ntarget/\xce\xa9path', bytes)
1204
        ie2 = inv._bytes_to_entry(bytes)
1205
        self.assertEqual(ie, ie2)
1206
        self.assertIsInstance(ie2.name, unicode)
1207
        self.assertIsInstance(ie2.symlink_target, unicode)
1208
        self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
1209
                         inv._bytes_to_utf8name_key(bytes))
1210
1211
    def test_tree_reference_entry_to_bytes(self):
1212
        inv = CHKInventory(None)
1213
        ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1214
                                     'parent-id')
1215
        ie.revision = 'tree-rev-id'
1216
        ie.reference_revision = 'ref-rev-id'
1217
        bytes = inv._entry_to_bytes(ie)
1218
        self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1219
                         'tree-rev-id\nref-rev-id', bytes)
1220
        ie2 = inv._bytes_to_entry(bytes)
1221
        self.assertEqual(ie, ie2)
1222
        self.assertIsInstance(ie2.name, unicode)
1223
        self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1224
                         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
1225
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1226
    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
1227
        inv = Inventory()
1228
        inv.revision_id = "revid"
1229
        inv.root.revision = "rootrev"
1230
        root_id = inv.root.file_id
1231
        inv.add(InventoryFile("fileid", u'f\xefle', root_id))
1232
        inv["fileid"].revision = "filerev"
1233
        inv["fileid"].text_sha1 = "ffff"
1234
        inv["fileid"].text_size = 0
1235
        inv.add(InventoryDirectory("dirid", u'dir-\N{EURO SIGN}', root_id))
1236
        inv.add(InventoryFile("childid", u'ch\xefld', "dirid"))
1237
        inv["childid"].revision = "filerev"
1238
        inv["childid"].text_sha1 = "ffff"
1239
        inv["childid"].text_size = 0
1240
        chk_bytes = self.get_chk_bytes()
1241
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1242
        bytes = ''.join(chk_inv.to_lines())
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1243
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1244
1245
    def test__preload_handles_utf8(self):
1246
        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
1247
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
1248
        self.assertFalse(new_inv._fully_cached)
1249
        new_inv._preload_cache()
1250
        self.assertEqual(
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1251
            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
1252
            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.
1253
        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
1254
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1255
                         sorted(ie_root._children.keys()))
1256
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
1257
        self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
1258
5726.2.1 by John Arbash Meinel
Fix bug #737234. Preload all entries for iter_entries_by_dir().
1259
    def test__preload_populates_cache(self):
1260
        inv = Inventory()
1261
        inv.revision_id = "revid"
1262
        inv.root.revision = "rootrev"
1263
        root_id = inv.root.file_id
1264
        inv.add(InventoryFile("fileid", "file", root_id))
1265
        inv["fileid"].revision = "filerev"
1266
        inv["fileid"].executable = True
1267
        inv["fileid"].text_sha1 = "ffff"
1268
        inv["fileid"].text_size = 1
1269
        inv.add(InventoryDirectory("dirid", "dir", root_id))
1270
        inv.add(InventoryFile("childid", "child", "dirid"))
1271
        inv["childid"].revision = "filerev"
1272
        inv["childid"].executable = False
1273
        inv["childid"].text_sha1 = "dddd"
1274
        inv["childid"].text_size = 1
1275
        chk_bytes = self.get_chk_bytes()
1276
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1277
        bytes = ''.join(chk_inv.to_lines())
1278
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1279
        self.assertEqual({}, new_inv._fileid_to_entry_cache)
1280
        self.assertFalse(new_inv._fully_cached)
1281
        new_inv._preload_cache()
1282
        self.assertEqual(
1283
            sorted([root_id, "fileid", "dirid", "childid"]),
1284
            sorted(new_inv._fileid_to_entry_cache.keys()))
1285
        self.assertTrue(new_inv._fully_cached)
1286
        ie_root = new_inv._fileid_to_entry_cache[root_id]
1287
        self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
1288
        ie_dir = new_inv._fileid_to_entry_cache['dirid']
1289
        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.
1290
5726.2.4 by John Arbash Meinel
_preload should also handle when some entries have already been expanded.
1291
    def test__preload_handles_partially_evaluated_inventory(self):
1292
        new_inv = self.make_basic_utf8_inventory()
1293
        ie = new_inv[new_inv.root_id]
1294
        self.assertIs(None, ie._children)
1295
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1296
                         sorted(ie.children.keys()))
1297
        # Accessing .children loads _children
1298
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1299
                         sorted(ie._children.keys()))
1300
        new_inv._preload_cache()
1301
        # No change
1302
        self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1303
                         sorted(ie._children.keys()))
1304
        ie_dir = new_inv["dirid"]
1305
        self.assertEqual([u'ch\xefld'],
1306
                         sorted(ie_dir._children.keys()))
1307
4634.51.2 by John Arbash Meinel
Start laying the groundwork for testing the expansion code
1308
1309
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1310
1311
    def get_chk_bytes(self):
1312
        factory = groupcompress.make_pack_factory(True, True, 1)
1313
        trans = self.get_transport('')
1314
        return factory(trans)
1315
1316
    def make_dir(self, inv, name, parent_id):
1317
        inv.add(inv.make_entry('directory', name, parent_id, name + '-id'))
1318
1319
    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.
1320
        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
1321
        ie.text_sha1 = osutils.sha_string(content)
1322
        ie.text_size = len(content)
1323
        inv.add(ie)
1324
1325
    def make_simple_inventory(self):
1326
        inv = Inventory('TREE_ROOT')
1327
        inv.revision_id = "revid"
1328
        inv.root.revision = "rootrev"
1329
        # /                 TREE_ROOT
1330
        # dir1/             dir1-id
1331
        #   sub-file1       sub-file1-id
1332
        #   sub-file2       sub-file2-id
1333
        #   sub-dir1/       sub-dir1-id
1334
        #     subsub-file1  subsub-file1-id
1335
        # dir2/             dir2-id
1336
        #   sub2-file1      sub2-file1-id
1337
        # top               top-id
1338
        self.make_dir(inv, 'dir1', 'TREE_ROOT')
1339
        self.make_dir(inv, 'dir2', 'TREE_ROOT')
1340
        self.make_dir(inv, 'sub-dir1', 'dir1-id')
1341
        self.make_file(inv, 'top', 'TREE_ROOT')
1342
        self.make_file(inv, 'sub-file1', 'dir1-id')
1343
        self.make_file(inv, 'sub-file2', 'dir1-id')
1344
        self.make_file(inv, 'subsub-file1', 'sub-dir1-id')
1345
        self.make_file(inv, 'sub2-file1', 'dir2-id')
1346
        chk_bytes = self.get_chk_bytes()
4634.51.8 by John Arbash Meinel
Catch a corner case that we were missing.
1347
        #  use a small maximum_size to force internal paging structures
1348
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
1349
                        maximum_size=100,
1350
                        search_key_name='hash-255-way')
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1351
        bytes = ''.join(chk_inv.to_lines())
1352
        return CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1353
1354
    def assert_Getitems(self, expected_fileids, inv, file_ids):
1355
        self.assertEqual(sorted(expected_fileids),
1356
                         sorted([ie.file_id for ie in inv._getitems(file_ids)]))
1357
4634.51.5 by John Arbash Meinel
Change the api a bit.
1358
    def assertExpand(self, all_ids, inv, file_ids):
1359
        (val_all_ids,
1360
         val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
1361
        self.assertEqual(set(all_ids), val_all_ids)
1362
        entries = inv._getitems(val_all_ids)
1363
        expected_children = {}
1364
        for entry in entries:
1365
            s = expected_children.setdefault(entry.parent_id, [])
1366
            s.append(entry.file_id)
1367
        val_children = dict((k, sorted(v)) for k, v
1368
                            in val_children.iteritems())
1369
        expected_children = dict((k, sorted(v)) for k, v
1370
                            in expected_children.iteritems())
1371
        self.assertEqual(expected_children, val_children)
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1372
1373
    def test_make_simple_inventory(self):
4634.51.2 by John Arbash Meinel
Start laying the groundwork for testing the expansion code
1374
        inv = self.make_simple_inventory()
1375
        layout = []
1376
        for path, entry in inv.iter_entries_by_dir():
1377
            layout.append((path, entry.file_id))
1378
        self.assertEqual([
1379
            ('', 'TREE_ROOT'),
1380
            ('dir1', 'dir1-id'),
1381
            ('dir2', 'dir2-id'),
1382
            ('top', 'top-id'),
1383
            ('dir1/sub-dir1', 'sub-dir1-id'),
1384
            ('dir1/sub-file1', 'sub-file1-id'),
1385
            ('dir1/sub-file2', 'sub-file2-id'),
1386
            ('dir1/sub-dir1/subsub-file1', 'subsub-file1-id'),
1387
            ('dir2/sub2-file1', 'sub2-file1-id'),
1388
            ], layout)
4634.51.3 by John Arbash Meinel
We have iteration to parents working, need to find children now.
1389
1390
    def test__getitems(self):
1391
        inv = self.make_simple_inventory()
1392
        # Reading from disk
1393
        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1394
        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1395
        self.assertFalse('sub-file2-id' in inv._fileid_to_entry_cache)
1396
        # From cache
1397
        self.assert_Getitems(['dir1-id'], inv, ['dir1-id'])
1398
        # Mixed
1399
        self.assert_Getitems(['dir1-id', 'sub-file2-id'], inv,
1400
                             ['dir1-id', 'sub-file2-id'])
1401
        self.assertTrue('dir1-id' in inv._fileid_to_entry_cache)
1402
        self.assertTrue('sub-file2-id' in inv._fileid_to_entry_cache)
1403
1404
    def test_single_file(self):
1405
        inv = self.make_simple_inventory()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1406
        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.
1407
1408
    def test_get_all_parents(self):
1409
        inv = self.make_simple_inventory()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1410
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1411
                           'subsub-file1-id',
1412
                          ], inv, ['subsub-file1-id'])
4634.51.4 by John Arbash Meinel
Implement an expansion function that works directly on the chk maps.
1413
1414
    def test_get_children(self):
1415
        inv = self.make_simple_inventory()
4634.51.5 by John Arbash Meinel
Change the api a bit.
1416
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1417
                           '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.
1418
                          ], inv, ['dir1-id'])
4634.51.8 by John Arbash Meinel
Catch a corner case that we were missing.
1419
1420
    def test_from_root(self):
1421
        inv = self.make_simple_inventory()
1422
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'dir2-id', 'sub-dir1-id',
1423
                           'sub-file1-id', 'sub-file2-id', 'sub2-file1-id',
1424
                           'subsub-file1-id', 'top-id'], inv, ['TREE_ROOT'])
1425
1426
    def test_top_level_file(self):
1427
        inv = self.make_simple_inventory()
1428
        self.assertExpand(['TREE_ROOT', 'top-id'], inv, ['top-id'])
1429
1430
    def test_subsub_file(self):
1431
        inv = self.make_simple_inventory()
1432
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id',
1433
                           'subsub-file1-id'], inv, ['subsub-file1-id'])
1434
1435
    def test_sub_and_root(self):
1436
        inv = self.make_simple_inventory()
1437
        self.assertExpand(['TREE_ROOT', 'dir1-id', 'sub-dir1-id', 'top-id',
1438
                           'subsub-file1-id'], inv, ['top-id', 'subsub-file1-id'])