/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Robert Collins
  • Date: 2009-07-17 00:49:02 UTC
  • mto: This revision was merged to the branch mainline in revision 4553.
  • Revision ID: robertc@robertcollins.net-20090717004902-w5zoag3sbg3ha0sj
Fixup broken test case to use a valid inventory delta

Show diffs side-by-side

added added

removed removed

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