/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3735.2.1 by Robert Collins
Add the concept of CHK lookups to Repository.
1
# Copyright (C) 2008 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
3735.36.3 by John Arbash Meinel
Add the new address for FSF to the new files.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3735.2.1 by Robert Collins
Add the concept of CHK lookups to Repository.
16
17
"""Tests for repositories that support CHK indices."""
18
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
19
from bzrlib import (
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
20
    btree_index,
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
21
    errors,
22
    osutils,
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
23
    repository,
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
24
    )
6282.6.40 by Jelmer Vernooij
Fix tests.
25
from bzrlib.remote import RemoteRepository
3735.2.1 by Robert Collins
Add the concept of CHK lookups to Repository.
26
from bzrlib.versionedfile import VersionedFiles
6282.6.40 by Jelmer Vernooij
Fix tests.
27
from bzrlib.tests import TestNotApplicable
3735.2.4 by Robert Collins
Test RemoteRepository with and with-out chk index backing formats.
28
from bzrlib.tests.per_repository_chk import TestCaseWithRepositoryCHK
29
30
31
class TestCHKSupport(TestCaseWithRepositoryCHK):
3735.2.1 by Robert Collins
Add the concept of CHK lookups to Repository.
32
33
    def test_chk_bytes_attribute_is_VersionedFiles(self):
34
        repo = self.make_repository('.')
35
        self.assertIsInstance(repo.chk_bytes, VersionedFiles)
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
36
37
    def test_add_bytes_to_chk_bytes_store(self):
38
        repo = self.make_repository('.')
39
        repo.lock_write()
40
        try:
41
            repo.start_write_group()
42
            try:
43
                sha1, len, _ = repo.chk_bytes.add_lines((None,),
44
                    None, ["foo\n", "bar\n"], random_id=True)
45
                self.assertEqual('4e48e2c9a3d2ca8a708cb0cc545700544efb5021',
46
                    sha1)
47
                self.assertEqual(
48
                    set([('sha1:4e48e2c9a3d2ca8a708cb0cc545700544efb5021',)]),
49
                    repo.chk_bytes.keys())
50
            except:
51
                repo.abort_write_group()
52
                raise
53
            else:
54
                repo.commit_write_group()
55
        finally:
56
            repo.unlock()
57
        # And after an unlock/lock pair
58
        repo.lock_read()
59
        try:
60
            self.assertEqual(
61
                set([('sha1:4e48e2c9a3d2ca8a708cb0cc545700544efb5021',)]),
62
                repo.chk_bytes.keys())
63
        finally:
64
            repo.unlock()
65
        # and reopening
66
        repo = repo.bzrdir.open_repository()
67
        repo.lock_read()
68
        try:
69
            self.assertEqual(
70
                set([('sha1:4e48e2c9a3d2ca8a708cb0cc545700544efb5021',)]),
71
                repo.chk_bytes.keys())
72
        finally:
73
            repo.unlock()
74
75
    def test_pack_preserves_chk_bytes_store(self):
3735.2.77 by John Arbash Meinel
Fix 'test_pack_preserves_chk_bytes_store'.
76
        leaf_lines = ["chkleaf:\n", "0\n", "1\n", "0\n", "\n"]
77
        leaf_sha1 = osutils.sha_strings(leaf_lines)
78
        node_lines = ["chknode:\n", "0\n", "1\n", "1\n", "foo\n",
79
                      "\x00sha1:%s\n" % (leaf_sha1,)]
80
        node_sha1 = osutils.sha_strings(node_lines)
81
        expected_set = set([('sha1:' + leaf_sha1,), ('sha1:' + node_sha1,)])
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
82
        repo = self.make_repository('.')
83
        repo.lock_write()
84
        try:
85
            repo.start_write_group()
86
            try:
3735.2.26 by Robert Collins
CHKInventory migrated to new CHKMap code.
87
                # Internal node pointing at a leaf.
3735.2.77 by John Arbash Meinel
Fix 'test_pack_preserves_chk_bytes_store'.
88
                repo.chk_bytes.add_lines((None,), None, node_lines, random_id=True)
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
89
            except:
90
                repo.abort_write_group()
91
                raise
92
            else:
93
                repo.commit_write_group()
94
            repo.start_write_group()
95
            try:
3735.2.26 by Robert Collins
CHKInventory migrated to new CHKMap code.
96
                # Leaf in a separate pack.
3735.2.77 by John Arbash Meinel
Fix 'test_pack_preserves_chk_bytes_store'.
97
                repo.chk_bytes.add_lines((None,), None, leaf_lines, random_id=True)
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
98
            except:
99
                repo.abort_write_group()
100
                raise
101
            else:
102
                repo.commit_write_group()
103
            repo.pack()
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
104
            self.assertEqual(expected_set, repo.chk_bytes.keys())
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
105
        finally:
106
            repo.unlock()
107
        # and reopening
108
        repo = repo.bzrdir.open_repository()
109
        repo.lock_read()
110
        try:
3735.2.18 by Robert Collins
Partial multi-layer chk dictionary trees.
111
            self.assertEqual(expected_set, repo.chk_bytes.keys())
3735.2.6 by Robert Collins
Basic add-and-pack of CHK content from within a repository.
112
        finally:
113
            repo.unlock()
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
114
4634.71.1 by John Arbash Meinel
Work around bug #402623 by allowing BTreeGraphIndex(...,unlimited_cache=True).
115
    def test_chk_bytes_are_fully_buffered(self):
116
        repo = self.make_repository('.')
117
        repo.lock_write()
118
        self.addCleanup(repo.unlock)
119
        repo.start_write_group()
120
        try:
121
            sha1, len, _ = repo.chk_bytes.add_lines((None,),
122
                None, ["foo\n", "bar\n"], random_id=True)
123
            self.assertEqual('4e48e2c9a3d2ca8a708cb0cc545700544efb5021',
124
                sha1)
125
            self.assertEqual(
126
                set([('sha1:4e48e2c9a3d2ca8a708cb0cc545700544efb5021',)]),
127
                repo.chk_bytes.keys())
128
        except:
129
            repo.abort_write_group()
130
            raise
131
        else:
132
            repo.commit_write_group()
133
        # This may not always be correct if we change away from BTreeGraphIndex
134
        # in the future. But for now, lets check that chk_bytes are fully
135
        # buffered
136
        index = repo.chk_bytes._index._graph_index._indices[0]
137
        self.assertIsInstance(index, btree_index.BTreeGraphIndex)
138
        self.assertIs(type(index._leaf_node_cache), dict)
139
        # Re-opening the repository should also have a repo with everything
140
        # fully buffered
141
        repo2 = repository.Repository.open(self.get_url())
142
        repo2.lock_read()
143
        self.addCleanup(repo2.unlock)
144
        index = repo2.chk_bytes._index._graph_index._indices[0]
145
        self.assertIsInstance(index, btree_index.BTreeGraphIndex)
146
        self.assertIs(type(index._leaf_node_cache), dict)
147
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
148
149
class TestCommitWriteGroupIntegrityCheck(TestCaseWithRepositoryCHK):
150
    """Tests that commit_write_group prevents various kinds of invalid data
151
    from being committed to a CHK repository.
152
    """
153
154
    def reopen_repo_and_resume_write_group(self, repo):
155
        resume_tokens = repo.suspend_write_group()
156
        repo.unlock()
157
        reopened_repo = repo.bzrdir.open_repository()
158
        reopened_repo.lock_write()
159
        self.addCleanup(reopened_repo.unlock)
160
        reopened_repo.resume_write_group(resume_tokens)
161
        return reopened_repo
162
163
    def test_missing_chk_root_for_inventory(self):
164
        """commit_write_group fails with BzrCheckError when the chk root record
165
        for a new inventory is missing.
166
        """
167
        repo = self.make_repository('damaged-repo')
168
        builder = self.make_branch_builder('simple-branch')
169
        builder.build_snapshot('A-id', None, [
170
            ('add', ('', 'root-id', 'directory', None)),
171
            ('add', ('file', 'file-id', 'file', 'content\n'))])
172
        b = builder.get_branch()
173
        b.lock_read()
174
        self.addCleanup(b.unlock)
175
        repo.lock_write()
176
        repo.start_write_group()
177
        # Now, add the objects manually
178
        text_keys = [('file-id', 'A-id'), ('root-id', 'A-id')]
179
        # Directly add the texts, inventory, and revision object for 'A-id' --
180
        # but don't add the chk_bytes.
181
        src_repo = b.repository
182
        repo.texts.insert_record_stream(src_repo.texts.get_record_stream(
183
            text_keys, 'unordered', True))
184
        repo.inventories.insert_record_stream(
185
            src_repo.inventories.get_record_stream(
186
                [('A-id',)], 'unordered', True))
187
        repo.revisions.insert_record_stream(
188
            src_repo.revisions.get_record_stream(
189
                [('A-id',)], 'unordered', True))
190
        # Make sure the presence of the missing data in a fallback does not
191
        # avoid the error.
192
        repo.add_fallback_repository(b.repository)
193
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
194
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
195
        self.assertRaises(
196
            errors.BzrCheckError, reopened_repo.commit_write_group)
197
        reopened_repo.abort_write_group()
198
199
    def test_missing_chk_root_for_unchanged_inventory(self):
200
        """commit_write_group fails with BzrCheckError when the chk root record
201
        for a new inventory is missing, even if the parent inventory is present
202
        and has identical content (i.e. the same chk root).
203
        
204
        A stacked repository containing only a revision with an identical
205
        inventory to its parent will still have the chk root records for those
206
        inventories.
207
208
        (In principle the chk records are unnecessary in this case, but in
209
        practice bzr 2.0rc1 (at least) expects to find them.)
210
        """
211
        repo = self.make_repository('damaged-repo')
212
        # Make a branch where the last two revisions have identical
213
        # inventories.
214
        builder = self.make_branch_builder('simple-branch')
215
        builder.build_snapshot('A-id', None, [
216
            ('add', ('', 'root-id', 'directory', None)),
217
            ('add', ('file', 'file-id', 'file', 'content\n'))])
218
        builder.build_snapshot('B-id', None, [])
219
        builder.build_snapshot('C-id', None, [])
220
        b = builder.get_branch()
221
        b.lock_read()
222
        self.addCleanup(b.unlock)
223
        # check our setup: B-id and C-id should have identical chk root keys.
224
        inv_b = b.repository.get_inventory('B-id')
225
        inv_c = b.repository.get_inventory('C-id')
6282.6.40 by Jelmer Vernooij
Fix tests.
226
        if not isinstance(repo, RemoteRepository):
227
            # Remote repositories always return plain inventories
228
            self.assertEqual(inv_b.id_to_entry.key(), inv_c.id_to_entry.key())
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
229
        # Now, manually insert objects for a stacked repo with only revision
230
        # C-id:
231
        # We need ('revisions', 'C-id'), ('inventories', 'C-id'),
232
        # ('inventories', 'B-id'), and the corresponding chk roots for those
233
        # inventories.
234
        repo.lock_write()
235
        repo.start_write_group()
236
        src_repo = b.repository
237
        repo.inventories.insert_record_stream(
238
            src_repo.inventories.get_record_stream(
239
                [('B-id',), ('C-id',)], 'unordered', True))
240
        repo.revisions.insert_record_stream(
241
            src_repo.revisions.get_record_stream(
242
                [('C-id',)], 'unordered', True))
243
        # Make sure the presence of the missing data in a fallback does not
244
        # avoid the error.
245
        repo.add_fallback_repository(b.repository)
246
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
247
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
248
        self.assertRaises(
249
            errors.BzrCheckError, reopened_repo.commit_write_group)
250
        reopened_repo.abort_write_group()
251
252
    def test_missing_chk_leaf_for_inventory(self):
253
        """commit_write_group fails with BzrCheckError when the chk root record
254
        for a parent inventory of a new revision is missing.
255
        """
256
        repo = self.make_repository('damaged-repo')
6282.6.40 by Jelmer Vernooij
Fix tests.
257
        if isinstance(repo, RemoteRepository):
258
            raise TestNotApplicable(
259
                "Unable to obtain CHKInventory from remote repo")
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
260
        b = self.make_branch_with_multiple_chk_nodes()
261
        src_repo = b.repository
262
        src_repo.lock_read()
263
        self.addCleanup(src_repo.unlock)
264
        # Now, manually insert objects for a stacked repo with only revision
265
        # C-id, *except* drop the non-root chk records.
266
        inv_b = src_repo.get_inventory('B-id')
267
        inv_c = src_repo.get_inventory('C-id')
268
        chk_root_keys_only = [
269
            inv_b.id_to_entry.key(), inv_b.parent_id_basename_to_file_id.key(),
270
            inv_c.id_to_entry.key(), inv_c.parent_id_basename_to_file_id.key()]
271
        all_chks = src_repo.chk_bytes.keys()
272
        # Pick a non-root key to drop
273
        key_to_drop = all_chks.difference(chk_root_keys_only).pop()
274
        all_chks.discard(key_to_drop)
275
        repo.lock_write()
276
        repo.start_write_group()
277
        repo.chk_bytes.insert_record_stream(
278
            src_repo.chk_bytes.get_record_stream(
279
                all_chks, 'unordered', True))
280
        repo.texts.insert_record_stream(
281
            src_repo.texts.get_record_stream(
282
                src_repo.texts.keys(), 'unordered', True))
283
        repo.inventories.insert_record_stream(
284
            src_repo.inventories.get_record_stream(
285
                [('B-id',), ('C-id',)], 'unordered', True))
286
        repo.revisions.insert_record_stream(
287
            src_repo.revisions.get_record_stream(
288
                [('C-id',)], 'unordered', True))
289
        # Make sure the presence of the missing data in a fallback does not
290
        # avoid the error.
291
        repo.add_fallback_repository(b.repository)
292
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
293
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
294
        self.assertRaises(
295
            errors.BzrCheckError, reopened_repo.commit_write_group)
296
        reopened_repo.abort_write_group()
297
298
    def test_missing_chk_root_for_parent_inventory(self):
299
        """commit_write_group fails with BzrCheckError when the chk root record
300
        for a parent inventory of a new revision is missing.
301
        """
302
        repo = self.make_repository('damaged-repo')
6282.6.40 by Jelmer Vernooij
Fix tests.
303
        if isinstance(repo, RemoteRepository):
304
            raise TestNotApplicable(
305
                "Unable to obtain CHKInventory from remote repo")
4634.35.10 by Andrew Bennetts
Move tests to per_repository_chk.
306
        b = self.make_branch_with_multiple_chk_nodes()
307
        b.lock_read()
308
        self.addCleanup(b.unlock)
309
        # Now, manually insert objects for a stacked repo with only revision
310
        # C-id, *except* the chk root entry for the parent inventory.
311
        # We need ('revisions', 'C-id'), ('inventories', 'C-id'),
312
        # ('inventories', 'B-id'), and the corresponding chk roots for those
313
        # inventories.
314
        inv_c = b.repository.get_inventory('C-id')
315
        chk_keys_for_c_only = [
316
            inv_c.id_to_entry.key(), inv_c.parent_id_basename_to_file_id.key()]
317
        repo.lock_write()
318
        repo.start_write_group()
319
        src_repo = b.repository
320
        repo.chk_bytes.insert_record_stream(
321
            src_repo.chk_bytes.get_record_stream(
322
                chk_keys_for_c_only, 'unordered', True))
323
        repo.inventories.insert_record_stream(
324
            src_repo.inventories.get_record_stream(
325
                [('B-id',), ('C-id',)], 'unordered', True))
326
        repo.revisions.insert_record_stream(
327
            src_repo.revisions.get_record_stream(
328
                [('C-id',)], 'unordered', True))
329
        # Make sure the presence of the missing data in a fallback does not
330
        # avoid the error.
331
        repo.add_fallback_repository(b.repository)
332
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
333
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
334
        self.assertRaises(
335
            errors.BzrCheckError, reopened_repo.commit_write_group)
336
        reopened_repo.abort_write_group()
337
338
    def make_branch_with_multiple_chk_nodes(self):
339
        # add and modify files with very long file-ids, so that the chk map
340
        # will need more than just a root node.
341
        builder = self.make_branch_builder('simple-branch')
342
        file_adds = []
343
        file_modifies = []
344
        for char in 'abc':
345
            name = char * 10000
346
            file_adds.append(
347
                ('add', ('file-' + name, 'file-%s-id' % name, 'file',
348
                         'content %s\n' % name)))
349
            file_modifies.append(
350
                ('modify', ('file-%s-id' % name, 'new content %s\n' % name)))
351
        builder.build_snapshot('A-id', None, [
352
            ('add', ('', 'root-id', 'directory', None))] +
353
            file_adds)
354
        builder.build_snapshot('B-id', None, [])
355
        builder.build_snapshot('C-id', None, file_modifies)
356
        return builder.get_branch()
357
        
358
    def test_missing_text_record(self):
359
        """commit_write_group fails with BzrCheckError when a text is missing.
360
        """
361
        repo = self.make_repository('damaged-repo')
362
        b = self.make_branch_with_multiple_chk_nodes()
363
        src_repo = b.repository
364
        src_repo.lock_read()
365
        self.addCleanup(src_repo.unlock)
366
        # Now, manually insert objects for a stacked repo with only revision
367
        # C-id, *except* drop one changed text.
368
        all_texts = src_repo.texts.keys()
369
        all_texts.remove(('file-%s-id' % ('c'*10000,), 'C-id'))
370
        repo.lock_write()
371
        repo.start_write_group()
372
        repo.chk_bytes.insert_record_stream(
373
            src_repo.chk_bytes.get_record_stream(
374
                src_repo.chk_bytes.keys(), 'unordered', True))
375
        repo.texts.insert_record_stream(
376
            src_repo.texts.get_record_stream(
377
                all_texts, 'unordered', True))
378
        repo.inventories.insert_record_stream(
379
            src_repo.inventories.get_record_stream(
380
                [('B-id',), ('C-id',)], 'unordered', True))
381
        repo.revisions.insert_record_stream(
382
            src_repo.revisions.get_record_stream(
383
                [('C-id',)], 'unordered', True))
384
        # Make sure the presence of the missing data in a fallback does not
385
        # avoid the error.
386
        repo.add_fallback_repository(b.repository)
387
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
388
        reopened_repo = self.reopen_repo_and_resume_write_group(repo)
389
        self.assertRaises(
390
            errors.BzrCheckError, reopened_repo.commit_write_group)
391
        reopened_repo.abort_write_group()
392
393
394
395