/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2729.2.5 by Martin Pool
Move per-inventory tests from test_inv to tests.inventory_implementations
1
# Copyright (C) 2005, 2006, 2007 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,
20
    bzrdir,
21
    errors,
22
    inventory,
23
    osutils,
24
    repository,
25
    revision,
26
    )
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
27
from bzrlib.inventory import (CHKInventory, Inventory, ROOT_ID, InventoryFile,
2100.3.1 by Aaron Bentley
Start roundtripping tree-reference entries
28
    InventoryDirectory, InventoryEntry, TreeReference)
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.
29
from bzrlib.tests import (
30
    TestCase,
31
    TestCaseWithTransport,
32
    condition_isinstance,
33
    multiply_tests,
34
    split_suite_by_condition,
35
    )
4525.1.1 by Vincent Ladeuil
Finish the *_implementation to per_* test renaming
36
from bzrlib.tests.per_workingtree import workingtree_formats
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.
37
38
39
def load_tests(standard_tests, module, loader):
40
    """Parameterise some inventory tests."""
41
    to_adapt, result = split_suite_by_condition(standard_tests,
42
        condition_isinstance(TestDeltaApplication))
43
    scenarios = [
44
        ('Inventory', {'apply_delta':apply_inventory_Inventory}),
45
        ]
46
    # Working tree basis delta application
47
    # Repository add_inv_by_delta.
48
    # Reduce form of the per_repository test logic - that logic needs to be
49
    # be able to get /just/ repositories whereas these tests are fine with
50
    # just creating trees.
51
    formats = set()
52
    for _, format in repository.format_registry.iteritems():
53
        scenarios.append((str(format.__name__), {
54
            'apply_delta':apply_inventory_Repository_add_inventory_by_delta,
55
            'format':format}))
56
    for format in workingtree_formats():
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
57
        scenarios.append(
58
            (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.
59
            'apply_delta':apply_inventory_WT_basis,
60
            'format':format}))
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
61
        scenarios.append(
62
            (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.
63
            'apply_delta':apply_inventory_WT,
64
            'format':format}))
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.
65
    return multiply_tests(to_adapt, scenarios, result)
66
67
4634.35.19 by Andrew Bennetts
Fix test_inv.
68
def create_texts_for_inv(repo, inv):
69
    for path, ie in inv.iter_entries():
70
        if ie.text_size:
71
            lines = ['a' * ie.text_size]
72
        else:
73
            lines = []
74
        repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
75
    
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
def apply_inventory_Inventory(self, basis, delta):
77
    """Apply delta to basis and return the result.
78
    
79
    :param basis: An inventory to be used as the basis.
80
    :param delta: The inventory delta to apply:
81
    :return: An inventory resulting from the application.
82
    """
83
    basis.apply_delta(delta)
84
    return basis
85
86
4526.9.1 by Robert Collins
Add WorkingTree.apply_inventory_delta to the set of delta implementations interface tested.
87
def apply_inventory_WT(self, basis, delta):
88
    """Apply delta to basis and return the result.
89
90
    This sets the tree state to be basis, and then calls apply_inventory_delta.
91
    
92
    :param basis: An inventory to be used as the basis.
93
    :param delta: The inventory delta to apply:
94
    :return: An inventory resulting from the application.
95
    """
96
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
97
    control.create_repository()
98
    control.create_branch()
99
    tree = self.format.initialize(control)
100
    tree.lock_write()
101
    try:
102
        tree._write_inventory(basis)
103
    finally:
104
        tree.unlock()
105
    # Fresh object, reads disk again.
106
    tree = tree.bzrdir.open_workingtree()
107
    tree.lock_write()
108
    try:
109
        tree.apply_inventory_delta(delta)
110
    finally:
111
        tree.unlock()
112
    # reload tree - ensure we get what was written.
113
    tree = tree.bzrdir.open_workingtree()
114
    tree.lock_read()
115
    self.addCleanup(tree.unlock)
116
    # One could add 'tree._validate' here but that would cause 'early' failues 
117
    # as far as higher level code is concerned. Possibly adding an
118
    # expect_fail parameter to this function and if that is False then do a
119
    # validate call.
120
    return tree.inventory
121
122
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.
123
def apply_inventory_WT_basis(self, basis, delta):
124
    """Apply delta to basis and return the result.
125
126
    This sets the parent and then calls update_basis_by_delta.
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
127
    It also puts the basis in the repository under both 'basis' and 'result' to
128
    allow safety checks made by the WT to succeed, and finally ensures that all
129
    items in the delta with a new path are present in the WT before calling
130
    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.
131
    
132
    :param basis: An inventory to be used as the basis.
133
    :param delta: The inventory delta to apply:
134
    :return: An inventory resulting from the application.
135
    """
136
    control = self.make_bzrdir('tree', format=self.format._matchingbzrdir)
137
    control.create_repository()
138
    control.create_branch()
139
    tree = self.format.initialize(control)
140
    tree.lock_write()
141
    try:
142
        repo = tree.branch.repository
143
        repo.start_write_group()
144
        try:
145
            rev = revision.Revision('basis', timestamp=0, timezone=None,
146
                message="", committer="foo@example.com")
147
            basis.revision_id = 'basis'
4634.35.19 by Andrew Bennetts
Fix test_inv.
148
            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.
149
            repo.add_revision('basis', rev, basis)
150
            # Add a revision for the result, with the basis content - 
151
            # update_basis_by_delta doesn't check that the delta results in
152
            # result, and we want inconsistent deltas to get called on the
153
            # tree, or else the code isn't actually checked.
154
            rev = revision.Revision('result', timestamp=0, timezone=None,
155
                message="", committer="foo@example.com")
156
            basis.revision_id = 'result'
157
            repo.add_revision('result', rev, basis)
4634.35.19 by Andrew Bennetts
Fix test_inv.
158
            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.
159
        except:
160
            repo.abort_write_group()
161
            raise
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
162
        # Set the basis state as the trees current state
163
        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.
164
        # This reads basis from the repo and puts it into the tree's local
165
        # cache, if it has one.
166
        tree.set_parent_ids(['basis'])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
167
        paths = {}
168
        parents = set()
169
        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.
170
            if None in (new, entry):
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
171
                continue
172
            paths[new] = (entry.file_id, entry.kind)
173
            parents.add(osutils.dirname(new))
174
        parents = osutils.minimum_path_selection(parents)
175
        parents.discard('')
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
176
        # Put place holders in the tree to permit adding the other entries.
4505.5.6 by Robert Collins
Check for missing parents in deltas.
177
        for pos, parent in enumerate(parents):
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
178
            if not tree.path2id(parent):
4505.5.6 by Robert Collins
Check for missing parents in deltas.
179
                # add a synthetic directory in the tree so we can can put the
180
                # tree0 entries in place for dirstate.
181
                tree.add([parent], ["id%d" % pos], ["directory"])
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
182
        if paths:
183
            # Many deltas may cause this mini-apply to fail, but we want to see what
184
            # the delta application code says, not the prep that we do to deal with 
185
            # limitations of dirstate's update_basis code.
186
            for path, (file_id, kind) in sorted(paths.items()):
187
                try:
188
                    tree.add([path], [file_id], [kind])
189
                except (KeyboardInterrupt, SystemExit):
190
                    raise
191
                except:
192
                    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.
193
    finally:
194
        tree.unlock()
195
    # Fresh lock, reads disk again.
196
    tree.lock_write()
197
    try:
198
        tree.update_basis_by_delta('result', delta)
199
    finally:
200
        tree.unlock()
201
    # reload tree - ensure we get what was written.
202
    tree = tree.bzrdir.open_workingtree()
203
    basis_tree = tree.basis_tree()
204
    basis_tree.lock_read()
205
    self.addCleanup(basis_tree.unlock)
206
    # Note, that if the tree does not have a local cache, the trick above of
207
    # setting the result as the basis, will come back to bite us. That said,
208
    # all the implementations in bzr do have a local cache.
209
    return basis_tree.inventory
210
211
212
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta):
213
    """Apply delta to basis and return the result.
214
    
215
    This inserts basis as a whole inventory and then uses
216
    add_inventory_by_delta to add delta.
217
218
    :param basis: An inventory to be used as the basis.
219
    :param delta: The inventory delta to apply:
220
    :return: An inventory resulting from the application.
221
    """
222
    format = self.format()
223
    control = self.make_bzrdir('tree', format=format._matchingbzrdir)
224
    repo = format.initialize(control)
225
    repo.lock_write()
226
    try:
227
        repo.start_write_group()
228
        try:
229
            rev = revision.Revision('basis', timestamp=0, timezone=None,
230
                message="", committer="foo@example.com")
231
            basis.revision_id = 'basis'
4634.35.19 by Andrew Bennetts
Fix test_inv.
232
            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.
233
            repo.add_revision('basis', rev, basis)
4634.35.19 by Andrew Bennetts
Fix test_inv.
234
            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.
235
        except:
236
            repo.abort_write_group()
237
            raise
238
    finally:
239
        repo.unlock()
240
    repo.lock_write()
241
    try:
242
        repo.start_write_group()
243
        try:
244
            inv_sha1 = repo.add_inventory_by_delta('basis', delta,
245
                'result', ['basis'])
246
        except:
247
            repo.abort_write_group()
248
            raise
249
        else:
250
            repo.commit_write_group()
251
    finally:
252
        repo.unlock()
253
    # Fresh lock, reads disk again.
254
    repo = repo.bzrdir.open_repository()
255
    repo.lock_read()
256
    self.addCleanup(repo.unlock)
257
    return repo.get_inventory('result')
258
259
260
class TestDeltaApplication(TestCaseWithTransport):
261
 
262
    def get_empty_inventory(self, reference_inv=None):
263
        """Get an empty inventory.
264
265
        Note that tests should not depend on the revision of the root for
266
        setting up test conditions, as it has to be flexible to accomodate non
267
        rich root repositories.
268
269
        :param reference_inv: If not None, get the revision for the root from
270
            this inventory. This is useful for dealing with older repositories
271
            that routinely discarded the root entry data. If None, the root's
272
            revision is set to 'basis'.
273
        """
274
        inv = inventory.Inventory()
275
        if reference_inv is not None:
276
            inv.root.revision = reference_inv.root.revision
277
        else:
278
            inv.root.revision = 'basis'
279
        return inv
280
281
    def test_empty_delta(self):
282
        inv = self.get_empty_inventory()
283
        delta = []
284
        inv = self.apply_delta(self, inv, delta)
285
        inv2 = self.get_empty_inventory(inv)
286
        self.assertEqual([], inv2._make_delta(inv))
287
4526.9.22 by Robert Collins
Check fileids in inventory deltas are not None and are strings.
288
    def test_None_file_id(self):
289
        inv = self.get_empty_inventory()
290
        dir1 = inventory.InventoryDirectory(None, 'dir1', inv.root.file_id)
291
        dir1.revision = 'result'
292
        delta = [(None, u'dir1', None, dir1)]
293
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
294
            inv, delta)
295
296
    def test_unicode_file_id(self):
297
        inv = self.get_empty_inventory()
298
        dir1 = inventory.InventoryDirectory(u'dirid', 'dir1', inv.root.file_id)
299
        dir1.revision = 'result'
300
        delta = [(None, u'dir1', dir1.file_id, dir1)]
301
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
302
            inv, delta)
303
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.
304
    def test_repeated_file_id(self):
305
        inv = self.get_empty_inventory()
306
        file1 = inventory.InventoryFile('id', 'path1', inv.root.file_id)
307
        file1.revision = 'result'
308
        file1.text_size = 0
309
        file1.text_sha1 = ""
310
        file2 = inventory.InventoryFile('id', 'path2', inv.root.file_id)
311
        file2.revision = 'result'
312
        file2.text_size = 0
313
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
314
        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.
315
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
316
            inv, delta)
963 by Martin Pool
- add the start of a test for inventory file-id matching
317
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
318
    def test_repeated_new_path(self):
319
        inv = self.get_empty_inventory()
320
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
321
        file1.revision = 'result'
322
        file1.text_size = 0
323
        file1.text_sha1 = ""
324
        file2 = inventory.InventoryFile('id2', 'path', inv.root.file_id)
325
        file2.revision = 'result'
326
        file2.text_size = 0
327
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
328
        delta = [(None, u'path', 'id1', file1), (None, u'path', 'id2', file2)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
329
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
330
            inv, delta)
331
332
    def test_repeated_old_path(self):
333
        inv = self.get_empty_inventory()
334
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
335
        file1.revision = 'result'
336
        file1.text_size = 0
337
        file1.text_sha1 = ""
338
        # We can't *create* a source inventory with the same path, but
339
        # a badly generated partial delta might claim the same source twice.
340
        # This would be buggy in two ways: the path is repeated in the delta,
341
        # And the path for one of the file ids doesn't match the source
342
        # location. Alternatively, we could have a repeated fileid, but that
343
        # is separately checked for.
344
        file2 = inventory.InventoryFile('id2', 'path2', inv.root.file_id)
345
        file2.revision = 'result'
346
        file2.text_size = 0
347
        file2.text_sha1 = ""
348
        inv.add(file1)
349
        inv.add(file2)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
350
        delta = [(u'path', None, 'id1', None), (u'path', None, 'id2', None)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
351
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
352
            inv, delta)
353
354
    def test_mismatched_id_entry_id(self):
355
        inv = self.get_empty_inventory()
356
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
357
        file1.revision = 'result'
358
        file1.text_size = 0
359
        file1.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
360
        delta = [(None, u'path', 'id', file1)]
4505.5.4 by Robert Collins
Repeated path/id corruption detected.
361
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
362
            inv, delta)
363
4526.9.4 by Robert Collins
Look for trivial issues with new_path and entry being out of sync in deltas.
364
    def test_mismatched_new_path_entry_None(self):
365
        inv = self.get_empty_inventory()
366
        delta = [(None, u'path', 'id', None)]
367
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
368
            inv, delta)
369
370
    def test_mismatched_new_path_None_entry(self):
371
        inv = self.get_empty_inventory()
372
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
373
        file1.revision = 'result'
374
        file1.text_size = 0
375
        file1.text_sha1 = ""
376
        delta = [(u"path", None, 'id1', file1)]
377
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
378
            inv, delta)
379
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
380
    def test_parent_is_not_directory(self):
381
        inv = self.get_empty_inventory()
382
        file1 = inventory.InventoryFile('id1', 'path', inv.root.file_id)
383
        file1.revision = 'result'
384
        file1.text_size = 0
385
        file1.text_sha1 = ""
386
        file2 = inventory.InventoryFile('id2', 'path2', 'id1')
387
        file2.revision = 'result'
388
        file2.text_size = 0
389
        file2.text_sha1 = ""
390
        inv.add(file1)
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
391
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.5 by Robert Collins
Parents used in a delta must be directories.
392
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
393
            inv, delta)
394
4505.5.6 by Robert Collins
Check for missing parents in deltas.
395
    def test_parent_is_missing(self):
396
        inv = self.get_empty_inventory()
397
        file2 = inventory.InventoryFile('id2', 'path2', 'missingparent')
398
        file2.revision = 'result'
399
        file2.text_size = 0
400
        file2.text_sha1 = ""
4505.5.7 by Robert Collins
Handle unicode parents correctly in dirstate parent checking.
401
        delta = [(None, u'path/path2', 'id2', file2)]
4505.5.6 by Robert Collins
Check for missing parents in deltas.
402
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
403
            inv, delta)
404
4526.9.2 by Robert Collins
Handle deltas with new paths not matching the actual path.
405
    def test_new_parent_path_has_wrong_id(self):
406
        inv = self.get_empty_inventory()
407
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
408
        parent1.revision = 'result'
409
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
410
        parent2.revision = 'result'
411
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
412
        file1.revision = 'result'
413
        file1.text_size = 0
414
        file1.text_sha1 = ""
415
        inv.add(parent1)
416
        inv.add(parent2)
417
        # This delta claims that file1 is at dir/path, but actually its at
418
        # dir2/path if you follow the inventory parent structure.
419
        delta = [(None, u'dir/path', 'id', file1)]
420
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
421
            inv, delta)
422
4526.9.3 by Robert Collins
Handle mismatches between inventory delta paths and actual paths found by traversing parent pointers.
423
    def test_old_parent_path_is_wrong(self):
424
        inv = self.get_empty_inventory()
425
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
426
        parent1.revision = 'result'
427
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
428
        parent2.revision = 'result'
429
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
430
        file1.revision = 'result'
431
        file1.text_size = 0
432
        file1.text_sha1 = ""
433
        inv.add(parent1)
434
        inv.add(parent2)
435
        inv.add(file1)
436
        # This delta claims that file1 was at dir/path, but actually it was at
437
        # dir2/path if you follow the inventory parent structure.
438
        delta = [(u'dir/path', None, 'id', None)]
439
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
440
            inv, delta)
441
442
    def test_old_parent_path_is_for_other_id(self):
443
        inv = self.get_empty_inventory()
444
        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
445
        parent1.revision = 'result'
446
        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
447
        parent2.revision = 'result'
448
        file1 = inventory.InventoryFile('id', 'path', 'p-2')
449
        file1.revision = 'result'
450
        file1.text_size = 0
451
        file1.text_sha1 = ""
452
        file2 = inventory.InventoryFile('id2', 'path', 'p-1')
453
        file2.revision = 'result'
454
        file2.text_size = 0
455
        file2.text_sha1 = ""
456
        inv.add(parent1)
457
        inv.add(parent2)
458
        inv.add(file1)
459
        inv.add(file2)
460
        # This delta claims that file1 was at dir/path, but actually it was at
461
        # dir2/path if you follow the inventory parent structure. At dir/path
462
        # is another entry we should not delete.
463
        delta = [(u'dir/path', None, 'id', None)]
464
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
465
            inv, delta)
466
4526.9.5 by Robert Collins
Require that added ids in inventory deltas be new.
467
    def test_add_existing_id_new_path(self):
468
        inv = self.get_empty_inventory()
469
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
470
        parent1.revision = 'result'
471
        parent2 = inventory.InventoryDirectory('p-1', 'dir2', inv.root.file_id)
472
        parent2.revision = 'result'
473
        inv.add(parent1)
474
        delta = [(None, u'dir2', 'p-1', parent2)]
475
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
476
            inv, delta)
477
4526.9.8 by Robert Collins
Check that the paths deltas put entries into are not in use already.
478
    def test_add_new_id_existing_path(self):
479
        inv = self.get_empty_inventory()
480
        parent1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
481
        parent1.revision = 'result'
482
        parent2 = inventory.InventoryDirectory('p-2', 'dir1', inv.root.file_id)
483
        parent2.revision = 'result'
484
        inv.add(parent1)
485
        delta = [(None, u'dir1', 'p-2', parent2)]
486
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
487
            inv, delta)
488
4526.9.9 by Robert Collins
Add interface tests for dangling children in inventory deltas.
489
    def test_remove_dir_leaving_dangling_child(self):
490
        inv = self.get_empty_inventory()
491
        dir1 = inventory.InventoryDirectory('p-1', 'dir1', inv.root.file_id)
492
        dir1.revision = 'result'
493
        dir2 = inventory.InventoryDirectory('p-2', 'child1', 'p-1')
494
        dir2.revision = 'result'
495
        dir3 = inventory.InventoryDirectory('p-3', 'child2', 'p-1')
496
        dir3.revision = 'result'
497
        inv.add(dir1)
498
        inv.add(dir2)
499
        inv.add(dir3)
500
        delta = [(u'dir1', None, 'p-1', None),
501
            (u'dir1/child2', None, 'p-3', None)]
502
        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
503
            inv, delta)
504
969 by Martin Pool
- Add less-sucky is_within_any
505
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
506
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
507
508
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
509
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
510
        self.assertEqual(file.kind_character(), '')
511
512
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
513
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
514
        self.assertEqual(dir.kind_character(), '/')
515
516
    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
517
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
518
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
519
520
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
521
        left = 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
522
        left.text_sha1 = 123
523
        left.executable = True
524
        left.symlink_target='foo'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
525
        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
526
        right.text_sha1 = 321
527
        right.symlink_target='bar'
528
        self.assertEqual((False, False), left.detect_changes(right))
529
        self.assertEqual((False, False), right.detect_changes(left))
530
531
    def test_file_detect_changes(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
532
        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
533
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
534
        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
535
        right.text_sha1 = 123
536
        self.assertEqual((False, False), left.detect_changes(right))
537
        self.assertEqual((False, False), right.detect_changes(left))
538
        left.executable = True
539
        self.assertEqual((False, True), left.detect_changes(right))
540
        self.assertEqual((False, True), right.detect_changes(left))
541
        right.text_sha1 = 321
542
        self.assertEqual((True, True), left.detect_changes(right))
543
        self.assertEqual((True, True), right.detect_changes(left))
544
545
    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
546
        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
547
        left.text_sha1 = 123
548
        left.executable = True
549
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
550
        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
551
        right.text_sha1 = 321
552
        right.symlink_target='foo'
553
        self.assertEqual((False, False), left.detect_changes(right))
554
        self.assertEqual((False, False), right.detect_changes(left))
555
        left.symlink_target = 'different'
556
        self.assertEqual((True, False), left.detect_changes(right))
557
        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
558
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
559
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
560
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
561
        self.failUnless(file.has_text())
562
563
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
564
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
565
        self.failIf(dir.has_text())
566
567
    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
568
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
569
        self.failIf(link.has_text())
570
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)
571
    def test_make_entry(self):
572
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
573
            inventory.InventoryFile)
574
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
575
            inventory.InventoryLink)
576
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
577
            inventory.InventoryDirectory)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
578
1830.3.5 by John Arbash Meinel
make_entry refuses to create non-normalized entries.
579
    def test_make_entry_non_normalized(self):
580
        orig_normalized_filename = osutils.normalized_filename
581
582
        try:
583
            osutils.normalized_filename = osutils._accessible_normalized_filename
584
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
585
            self.assertEqual(u'\xe5', entry.name)
586
            self.assertIsInstance(entry, inventory.InventoryFile)
587
588
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
589
            self.assertRaises(errors.InvalidNormalization,
590
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
591
        finally:
592
            osutils.normalized_filename = orig_normalized_filename
593
594
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
595
class TestDescribeChanges(TestCase):
596
597
    def test_describe_change(self):
598
        # we need to test the following change combinations:
599
        # rename
600
        # reparent
601
        # modify
602
        # gone
603
        # added
604
        # renamed/reparented and modified
605
        # change kind (perhaps can't be done yet?)
606
        # also, merged in combination with all of these?
607
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
608
        old_a.text_sha1 = '123132'
609
        old_a.text_size = 0
610
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
611
        new_a.text_sha1 = '123132'
612
        new_a.text_size = 0
613
614
        self.assertChangeDescription('unchanged', old_a, new_a)
615
616
        new_a.text_size = 10
617
        new_a.text_sha1 = 'abcabc'
618
        self.assertChangeDescription('modified', old_a, new_a)
619
620
        self.assertChangeDescription('added', None, new_a)
621
        self.assertChangeDescription('removed', old_a, None)
622
        # perhaps a bit questionable but seems like the most reasonable thing...
623
        self.assertChangeDescription('unchanged', None, None)
624
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
625
        # 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
626
        # modification:
627
        new_a.name = 'newfilename'
628
        self.assertChangeDescription('modified and renamed', old_a, new_a)
629
630
        # reparenting is 'renaming'
631
        new_a.name = old_a.name
632
        new_a.parent_id = 'somedir-id'
633
        self.assertChangeDescription('modified and renamed', old_a, new_a)
634
635
        # reset the content values so its not modified
636
        new_a.text_size = old_a.text_size
637
        new_a.text_sha1 = old_a.text_sha1
638
        new_a.name = old_a.name
639
640
        new_a.name = 'newfilename'
641
        self.assertChangeDescription('renamed', old_a, new_a)
642
643
        # reparenting is 'renaming'
644
        new_a.name = old_a.name
645
        new_a.parent_id = 'somedir-id'
646
        self.assertChangeDescription('renamed', old_a, new_a)
647
648
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
649
        change = InventoryEntry.describe_change(old_ie, new_ie)
650
        self.assertEqual(expected_change, change)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
651
652
653
class TestCHKInventory(TestCaseWithTransport):
3735.2.99 by John Arbash Meinel
Merge bzr.dev 4034. Whitespace cleanup
654
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
655
    def get_chk_bytes(self):
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
656
        # The easiest way to get a CHK store is a development6 repository and
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
657
        # then work with the chk_bytes attribute directly.
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
658
        repo = self.make_repository(".", format="development6-rich-root")
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
659
        repo.lock_write()
660
        self.addCleanup(repo.unlock)
661
        repo.start_write_group()
662
        self.addCleanup(repo.abort_write_group)
663
        return repo.chk_bytes
664
665
    def read_bytes(self, chk_bytes, key):
666
        stream = chk_bytes.get_record_stream([key], 'unordered', True)
667
        return stream.next().get_bytes_as("fulltext")
668
669
    def test_deserialise_gives_CHKInventory(self):
670
        inv = Inventory()
671
        inv.revision_id = "revid"
672
        inv.root.revision = "rootrev"
673
        chk_bytes = self.get_chk_bytes()
674
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
675
        bytes = ''.join(chk_inv.to_lines())
676
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
677
        self.assertEqual("revid", new_inv.revision_id)
678
        self.assertEqual("directory", new_inv.root.kind)
679
        self.assertEqual(inv.root.file_id, new_inv.root.file_id)
680
        self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
681
        self.assertEqual(inv.root.name, new_inv.root.name)
682
        self.assertEqual("rootrev", new_inv.root.revision)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
683
        self.assertEqual('plain', new_inv._search_key_name)
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
684
685
    def test_deserialise_wrong_revid(self):
686
        inv = Inventory()
687
        inv.revision_id = "revid"
688
        inv.root.revision = "rootrev"
689
        chk_bytes = self.get_chk_bytes()
690
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
691
        bytes = ''.join(chk_inv.to_lines())
692
        self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
693
            bytes, ("revid2",))
694
695
    def test_captures_rev_root_byid(self):
696
        inv = Inventory()
697
        inv.revision_id = "foo"
698
        inv.root.revision = "bar"
699
        chk_bytes = self.get_chk_bytes()
700
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
701
        lines = chk_inv.to_lines()
702
        self.assertEqual([
703
            'chkinventory:\n',
704
            'revision_id: foo\n',
705
            '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.
706
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
707
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
708
            ], lines)
709
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
710
        self.assertEqual('plain', chk_inv._search_key_name)
711
712
    def test_captures_parent_id_basename_index(self):
713
        inv = Inventory()
714
        inv.revision_id = "foo"
715
        inv.root.revision = "bar"
716
        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.
717
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
718
        lines = chk_inv.to_lines()
719
        self.assertEqual([
720
            'chkinventory:\n',
721
            'revision_id: foo\n',
722
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
723
            'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
724
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
725
            ], lines)
726
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
727
        self.assertEqual('plain', chk_inv._search_key_name)
728
729
    def test_captures_search_key_name(self):
730
        inv = Inventory()
731
        inv.revision_id = "foo"
732
        inv.root.revision = "bar"
733
        chk_bytes = self.get_chk_bytes()
734
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
735
                                              search_key_name='hash-16-way')
736
        lines = chk_inv.to_lines()
737
        self.assertEqual([
738
            'chkinventory:\n',
739
            '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.
740
            'root_id: TREE_ROOT\n',
3735.17.8 by John Arbash Meinel
Most direct tests are now passing.
741
            '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.
742
            'revision_id: foo\n',
3735.17.11 by John Arbash Meinel
Actually format the inventories using line-based separation.
743
            'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
3735.16.7 by John Arbash Meinel
Start parameterizing CHKInventory and CHKSerializer so that we can
744
            ], lines)
745
        chk_inv = CHKInventory.deserialise(chk_bytes, ''.join(lines), ('foo',))
746
        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.
747
748
    def test_directory_children_on_demand(self):
749
        inv = Inventory()
750
        inv.revision_id = "revid"
751
        inv.root.revision = "rootrev"
752
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
753
        inv["fileid"].revision = "filerev"
754
        inv["fileid"].executable = True
755
        inv["fileid"].text_sha1 = "ffff"
756
        inv["fileid"].text_size = 1
757
        chk_bytes = self.get_chk_bytes()
758
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
759
        bytes = ''.join(chk_inv.to_lines())
760
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
761
        root_entry = new_inv[inv.root.file_id]
762
        self.assertEqual(None, root_entry._children)
763
        self.assertEqual(['file'], root_entry.children.keys())
764
        file_direct = new_inv["fileid"]
765
        file_found = root_entry.children['file']
766
        self.assertEqual(file_direct.kind, file_found.kind)
767
        self.assertEqual(file_direct.file_id, file_found.file_id)
768
        self.assertEqual(file_direct.parent_id, file_found.parent_id)
769
        self.assertEqual(file_direct.name, file_found.name)
770
        self.assertEqual(file_direct.revision, file_found.revision)
771
        self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
772
        self.assertEqual(file_direct.text_size, file_found.text_size)
773
        self.assertEqual(file_direct.executable, file_found.executable)
774
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
775
    def test_from_inventory_maximum_size(self):
776
        # from_inventory supports the maximum_size parameter.
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, 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.
782
        chk_inv.id_to_entry._ensure_root()
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
783
        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.
784
        self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
785
        p_id_basename = chk_inv.parent_id_basename_to_file_id
786
        p_id_basename._ensure_root()
787
        self.assertEqual(120, p_id_basename._root_node.maximum_size)
788
        self.assertEqual(2, p_id_basename._root_node._key_width)
3735.2.27 by Robert Collins
Use 4K pages for development3 repositories.
789
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
790
    def test___iter__(self):
791
        inv = Inventory()
792
        inv.revision_id = "revid"
793
        inv.root.revision = "rootrev"
794
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
795
        inv["fileid"].revision = "filerev"
796
        inv["fileid"].executable = True
797
        inv["fileid"].text_sha1 = "ffff"
798
        inv["fileid"].text_size = 1
799
        chk_bytes = self.get_chk_bytes()
800
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
801
        bytes = ''.join(chk_inv.to_lines())
802
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
803
        fileids = list(new_inv.__iter__())
804
        fileids.sort()
805
        self.assertEqual([inv.root.file_id, "fileid"], fileids)
806
807
    def test__len__(self):
808
        inv = Inventory()
809
        inv.revision_id = "revid"
810
        inv.root.revision = "rootrev"
811
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
812
        inv["fileid"].revision = "filerev"
813
        inv["fileid"].executable = True
814
        inv["fileid"].text_sha1 = "ffff"
815
        inv["fileid"].text_size = 1
816
        chk_bytes = self.get_chk_bytes()
817
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
818
        self.assertEqual(2, len(chk_inv))
819
820
    def test___getitem__(self):
821
        inv = Inventory()
822
        inv.revision_id = "revid"
823
        inv.root.revision = "rootrev"
824
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
825
        inv["fileid"].revision = "filerev"
826
        inv["fileid"].executable = True
827
        inv["fileid"].text_sha1 = "ffff"
828
        inv["fileid"].text_size = 1
829
        chk_bytes = self.get_chk_bytes()
830
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
831
        bytes = ''.join(chk_inv.to_lines())
832
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
833
        root_entry = new_inv[inv.root.file_id]
834
        file_entry = new_inv["fileid"]
835
        self.assertEqual("directory", root_entry.kind)
836
        self.assertEqual(inv.root.file_id, root_entry.file_id)
837
        self.assertEqual(inv.root.parent_id, root_entry.parent_id)
838
        self.assertEqual(inv.root.name, root_entry.name)
839
        self.assertEqual("rootrev", root_entry.revision)
840
        self.assertEqual("file", file_entry.kind)
841
        self.assertEqual("fileid", file_entry.file_id)
842
        self.assertEqual(inv.root.file_id, file_entry.parent_id)
843
        self.assertEqual("file", file_entry.name)
844
        self.assertEqual("filerev", file_entry.revision)
845
        self.assertEqual("ffff", file_entry.text_sha1)
846
        self.assertEqual(1, file_entry.text_size)
847
        self.assertEqual(True, file_entry.executable)
3735.2.53 by Robert Collins
Support Inventory.__getitem__ more consistently.
848
        self.assertRaises(errors.NoSuchId, new_inv.__getitem__, 'missing')
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
849
850
    def test_has_id_true(self):
851
        inv = Inventory()
852
        inv.revision_id = "revid"
853
        inv.root.revision = "rootrev"
854
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
855
        inv["fileid"].revision = "filerev"
856
        inv["fileid"].executable = True
857
        inv["fileid"].text_sha1 = "ffff"
858
        inv["fileid"].text_size = 1
859
        chk_bytes = self.get_chk_bytes()
860
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
861
        self.assertTrue(chk_inv.has_id('fileid'))
862
        self.assertTrue(chk_inv.has_id(inv.root.file_id))
863
864
    def test_has_id_not(self):
865
        inv = Inventory()
866
        inv.revision_id = "revid"
867
        inv.root.revision = "rootrev"
868
        chk_bytes = self.get_chk_bytes()
869
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
870
        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.
871
3735.2.12 by Robert Collins
Implement commit-via-deltas for split inventory repositories.
872
    def test_id2path(self):
873
        inv = Inventory()
874
        inv.revision_id = "revid"
875
        inv.root.revision = "rootrev"
876
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
877
        fileentry = InventoryFile("fileid", "file", "dirid")
878
        inv.add(direntry)
879
        inv.add(fileentry)
880
        inv["fileid"].revision = "filerev"
881
        inv["fileid"].executable = True
882
        inv["fileid"].text_sha1 = "ffff"
883
        inv["fileid"].text_size = 1
884
        inv["dirid"].revision = "filerev"
885
        chk_bytes = self.get_chk_bytes()
886
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
887
        bytes = ''.join(chk_inv.to_lines())
888
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
889
        self.assertEqual('', new_inv.id2path(inv.root.file_id))
890
        self.assertEqual('dir', new_inv.id2path('dirid'))
891
        self.assertEqual('dir/file', new_inv.id2path('fileid'))
892
893
    def test_path2id(self):
894
        inv = Inventory()
895
        inv.revision_id = "revid"
896
        inv.root.revision = "rootrev"
897
        direntry = InventoryDirectory("dirid", "dir", inv.root.file_id)
898
        fileentry = InventoryFile("fileid", "file", "dirid")
899
        inv.add(direntry)
900
        inv.add(fileentry)
901
        inv["fileid"].revision = "filerev"
902
        inv["fileid"].executable = True
903
        inv["fileid"].text_sha1 = "ffff"
904
        inv["fileid"].text_size = 1
905
        inv["dirid"].revision = "filerev"
906
        chk_bytes = self.get_chk_bytes()
907
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
908
        bytes = ''.join(chk_inv.to_lines())
909
        new_inv = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
910
        self.assertEqual(inv.root.file_id, new_inv.path2id(''))
911
        self.assertEqual('dirid', new_inv.path2id('dir'))
912
        self.assertEqual('fileid', new_inv.path2id('dir/file'))
913
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
914
    def test_create_by_apply_delta_sets_root(self):
915
        inv = Inventory()
916
        inv.revision_id = "revid"
917
        chk_bytes = self.get_chk_bytes()
918
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
919
        inv.add_path("", "directory", "myrootid", None)
920
        inv.revision_id = "expectedid"
921
        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.
922
        delta = [("", None, base_inv.root.file_id, None),
923
            (None, "",  "myrootid", inv.root)]
3735.2.57 by Jelmer Vernooij
Make sure CHKInventory._entry_cache gets initialized in create_by_apply_delta.
924
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
925
        self.assertEquals(reference_inv.root, new_inv.root)
926
3735.2.10 by Robert Collins
Teach CHKInventory how to make a new inventory from an inventory delta.
927
    def test_create_by_apply_delta_empty_add_child(self):
928
        inv = Inventory()
929
        inv.revision_id = "revid"
930
        inv.root.revision = "rootrev"
931
        chk_bytes = self.get_chk_bytes()
932
        base_inv = CHKInventory.from_inventory(chk_bytes, inv)
933
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
934
        a_entry.revision = "filerev"
935
        a_entry.executable = True
936
        a_entry.text_sha1 = "ffff"
937
        a_entry.text_size = 1
938
        inv.add(a_entry)
939
        inv.revision_id = "expectedid"
940
        reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
941
        delta = [(None, "A",  "A-id", a_entry)]
942
        new_inv = base_inv.create_by_apply_delta(delta, "expectedid")
943
        # new_inv should be the same as reference_inv.
944
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
945
        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.
946
        reference_inv.id_to_entry._ensure_root()
947
        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.
948
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
949
            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.
950
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
951
    def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
952
        inv = Inventory()
953
        inv.revision_id = "revid"
954
        inv.root.revision = "rootrev"
955
        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.
956
        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.
957
        a_entry = InventoryFile("A-id", "A", inv.root.file_id)
958
        a_entry.revision = "filerev"
959
        a_entry.executable = True
960
        a_entry.text_sha1 = "ffff"
961
        a_entry.text_size = 1
962
        inv.add(a_entry)
963
        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.
964
        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.
965
        delta = [(None, "A",  "A-id", a_entry)]
966
        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.
967
        reference_inv.id_to_entry._ensure_root()
968
        reference_inv.parent_id_basename_to_file_id._ensure_root()
969
        new_inv.id_to_entry._ensure_root()
970
        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.
971
        # new_inv should be the same as reference_inv.
972
        self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
973
        self.assertEqual(reference_inv.root_id, new_inv.root_id)
974
        self.assertEqual(reference_inv.id_to_entry._root_node._key,
975
            new_inv.id_to_entry._root_node._key)
976
        self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
977
            new_inv.parent_id_basename_to_file_id._root_node._key)
978
3735.2.33 by Robert Collins
Create a smoke-tested CHKInventory.iter_changes(CHKInventory) - incomplete in general but enough to start working with.
979
    def test_iter_changes(self):
980
        # Low level bootstrapping smoke test; comprehensive generic tests via
981
        # InterTree are coming.
982
        inv = Inventory()
983
        inv.revision_id = "revid"
984
        inv.root.revision = "rootrev"
985
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
986
        inv["fileid"].revision = "filerev"
987
        inv["fileid"].executable = True
988
        inv["fileid"].text_sha1 = "ffff"
989
        inv["fileid"].text_size = 1
990
        inv2 = Inventory()
991
        inv2.revision_id = "revid2"
992
        inv2.root.revision = "rootrev"
993
        inv2.add(InventoryFile("fileid", "file", inv.root.file_id))
994
        inv2["fileid"].revision = "filerev2"
995
        inv2["fileid"].executable = False
996
        inv2["fileid"].text_sha1 = "bbbb"
997
        inv2["fileid"].text_size = 2
998
        # get fresh objects.
999
        chk_bytes = self.get_chk_bytes()
1000
        chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1001
        bytes = ''.join(chk_inv.to_lines())
1002
        inv_1 = CHKInventory.deserialise(chk_bytes, bytes, ("revid",))
1003
        chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
1004
        bytes = ''.join(chk_inv2.to_lines())
1005
        inv_2 = CHKInventory.deserialise(chk_bytes, bytes, ("revid2",))
1006
        self.assertEqual([('fileid', (u'file', u'file'), True, (True, True),
1007
            ('TREE_ROOT', 'TREE_ROOT'), (u'file', u'file'), ('file', 'file'),
1008
            (False, True))],
1009
            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.
1010
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1011
    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.
1012
        inv = Inventory()
1013
        inv.revision_id = "revid"
1014
        inv.root.revision = "rootrev"
1015
        inv.add(InventoryFile("fileid", "file", inv.root.file_id))
1016
        inv["fileid"].revision = "filerev"
1017
        inv["fileid"].executable = True
1018
        inv["fileid"].text_sha1 = "ffff"
1019
        inv["fileid"].text_size = 1
1020
        # get fresh objects.
1021
        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.
1022
        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.
1023
        bytes = ''.join(tmp_inv.to_lines())
1024
        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.
1025
        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.
1026
        self.assertEqual(
1027
            {('', ''): 'TREE_ROOT', ('TREE_ROOT', 'file'): 'fileid'},
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
1028
            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
1029
1030
    def test_file_entry_to_bytes(self):
1031
        inv = CHKInventory(None)
1032
        ie = inventory.InventoryFile('file-id', 'filename', 'parent-id')
1033
        ie.executable = True
1034
        ie.revision = 'file-rev-id'
1035
        ie.text_sha1 = 'abcdefgh'
1036
        ie.text_size = 100
1037
        bytes = inv._entry_to_bytes(ie)
1038
        self.assertEqual('file: file-id\nparent-id\nfilename\n'
1039
                         'file-rev-id\nabcdefgh\n100\nY', bytes)
1040
        ie2 = inv._bytes_to_entry(bytes)
1041
        self.assertEqual(ie, ie2)
1042
        self.assertIsInstance(ie2.name, unicode)
1043
        self.assertEqual(('filename', 'file-id', 'file-rev-id'),
1044
                         inv._bytes_to_utf8name_key(bytes))
1045
1046
    def test_file2_entry_to_bytes(self):
1047
        inv = CHKInventory(None)
1048
        # \u30a9 == 'omega'
1049
        ie = inventory.InventoryFile('file-id', u'\u03a9name', 'parent-id')
1050
        ie.executable = False
1051
        ie.revision = 'file-rev-id'
1052
        ie.text_sha1 = '123456'
1053
        ie.text_size = 25
1054
        bytes = inv._entry_to_bytes(ie)
1055
        self.assertEqual('file: file-id\nparent-id\n\xce\xa9name\n'
1056
                         'file-rev-id\n123456\n25\nN', bytes)
1057
        ie2 = inv._bytes_to_entry(bytes)
1058
        self.assertEqual(ie, ie2)
1059
        self.assertIsInstance(ie2.name, unicode)
1060
        self.assertEqual(('\xce\xa9name', 'file-id', 'file-rev-id'),
1061
                         inv._bytes_to_utf8name_key(bytes))
1062
1063
    def test_dir_entry_to_bytes(self):
1064
        inv = CHKInventory(None)
1065
        ie = inventory.InventoryDirectory('dir-id', 'dirname', 'parent-id')
1066
        ie.revision = 'dir-rev-id'
1067
        bytes = inv._entry_to_bytes(ie)
1068
        self.assertEqual('dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1069
        ie2 = inv._bytes_to_entry(bytes)
1070
        self.assertEqual(ie, ie2)
1071
        self.assertIsInstance(ie2.name, unicode)
1072
        self.assertEqual(('dirname', 'dir-id', 'dir-rev-id'),
1073
                         inv._bytes_to_utf8name_key(bytes))
1074
1075
    def test_dir2_entry_to_bytes(self):
1076
        inv = CHKInventory(None)
1077
        ie = inventory.InventoryDirectory('dir-id', u'dir\u03a9name',
1078
                                          None)
1079
        ie.revision = 'dir-rev-id'
1080
        bytes = inv._entry_to_bytes(ie)
1081
        self.assertEqual('dir: dir-id\n\ndir\xce\xa9name\n'
1082
                         'dir-rev-id', bytes)
1083
        ie2 = inv._bytes_to_entry(bytes)
1084
        self.assertEqual(ie, ie2)
1085
        self.assertIsInstance(ie2.name, unicode)
1086
        self.assertIs(ie2.parent_id, None)
1087
        self.assertEqual(('dir\xce\xa9name', 'dir-id', 'dir-rev-id'),
1088
                         inv._bytes_to_utf8name_key(bytes))
1089
1090
    def test_symlink_entry_to_bytes(self):
1091
        inv = CHKInventory(None)
1092
        ie = inventory.InventoryLink('link-id', 'linkname', 'parent-id')
1093
        ie.revision = 'link-rev-id'
1094
        ie.symlink_target = u'target/path'
1095
        bytes = inv._entry_to_bytes(ie)
1096
        self.assertEqual('symlink: link-id\nparent-id\nlinkname\n'
1097
                         'link-rev-id\ntarget/path', bytes)
1098
        ie2 = inv._bytes_to_entry(bytes)
1099
        self.assertEqual(ie, ie2)
1100
        self.assertIsInstance(ie2.name, unicode)
1101
        self.assertIsInstance(ie2.symlink_target, unicode)
1102
        self.assertEqual(('linkname', 'link-id', 'link-rev-id'),
1103
                         inv._bytes_to_utf8name_key(bytes))
1104
1105
    def test_symlink2_entry_to_bytes(self):
1106
        inv = CHKInventory(None)
1107
        ie = inventory.InventoryLink('link-id', u'link\u03a9name', 'parent-id')
1108
        ie.revision = 'link-rev-id'
1109
        ie.symlink_target = u'target/\u03a9path'
1110
        bytes = inv._entry_to_bytes(ie)
1111
        self.assertEqual('symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1112
                         'link-rev-id\ntarget/\xce\xa9path', bytes)
1113
        ie2 = inv._bytes_to_entry(bytes)
1114
        self.assertEqual(ie, ie2)
1115
        self.assertIsInstance(ie2.name, unicode)
1116
        self.assertIsInstance(ie2.symlink_target, unicode)
1117
        self.assertEqual(('link\xce\xa9name', 'link-id', 'link-rev-id'),
1118
                         inv._bytes_to_utf8name_key(bytes))
1119
1120
    def test_tree_reference_entry_to_bytes(self):
1121
        inv = CHKInventory(None)
1122
        ie = inventory.TreeReference('tree-root-id', u'tree\u03a9name',
1123
                                     'parent-id')
1124
        ie.revision = 'tree-rev-id'
1125
        ie.reference_revision = 'ref-rev-id'
1126
        bytes = inv._entry_to_bytes(ie)
1127
        self.assertEqual('tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1128
                         'tree-rev-id\nref-rev-id', bytes)
1129
        ie2 = inv._bytes_to_entry(bytes)
1130
        self.assertEqual(ie, ie2)
1131
        self.assertIsInstance(ie2.name, unicode)
1132
        self.assertEqual(('tree\xce\xa9name', 'tree-root-id', 'tree-rev-id'),
1133
                         inv._bytes_to_utf8name_key(bytes))