/brz/remove-bazaar

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