/brz/remove-bazaar

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