/brz/remove-bazaar

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