/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4597.1.6 by John Arbash Meinel
Add a test that inventory texts are preserved during pack.
1
# Copyright (C) 2008, 2009 Canonical Ltd
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
16
17
"""Tests for pack repositories.
18
19
These tests are repeated for all pack-based repository formats.
20
"""
21
3582.3.4 by Martin Pool
Use cStringIO rather than StringIO
22
from cStringIO import StringIO
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
23
from stat import S_ISDIR
24
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
25
from bzrlib.btree_index import BTreeGraphIndex
26
from bzrlib.index import GraphIndex
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
27
from bzrlib import (
28
    bzrdir,
29
    errors,
30
    inventory,
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
31
    osutils,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
32
    progress,
33
    repository,
34
    revision as _mod_revision,
35
    symbol_versioning,
36
    tests,
37
    ui,
38
    upgrade,
39
    workingtree,
40
    )
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
41
from bzrlib.repofmt import (
42
    pack_repo,
43
    groupcompress_repo,
44
    )
4597.1.10 by John Arbash Meinel
Fix some tests that were failing because we checked against RepositoryFormatCHK1
45
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
46
from bzrlib.smart import (
47
    client,
48
    server,
49
    )
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
50
from bzrlib.tests import (
51
    TestCase,
52
    TestCaseWithTransport,
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
53
    TestNotApplicable,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
54
    TestSkipped,
55
    )
56
from bzrlib.transport import (
57
    fakenfs,
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
58
    memory,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
59
    get_transport,
60
    )
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
61
from bzrlib.tests.per_repository import TestCaseWithRepository
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
62
63
64
class TestPackRepository(TestCaseWithTransport):
65
    """Tests to be repeated across all pack-based formats.
66
67
    The following are populated from the test scenario:
68
69
    :ivar format_name: Registered name fo the format to test.
70
    :ivar format_string: On-disk format marker.
71
    :ivar format_supports_external_lookups: Boolean.
72
    """
73
74
    def get_format(self):
75
        return bzrdir.format_registry.make_bzrdir(self.format_name)
76
77
    def test_attribute__fetch_order(self):
3606.7.3 by John Arbash Meinel
We don't have to fetch in topological order, as long as we fix all of the delta logic pieces.
78
        """Packs do not need ordered data retrieval."""
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
79
        format = self.get_format()
80
        repo = self.make_repository('.', format=format)
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
81
        self.assertEqual('unordered', repo._format._fetch_order)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
82
83
    def test_attribute__fetch_uses_deltas(self):
84
        """Packs reuse deltas."""
85
        format = self.get_format()
86
        repo = self.make_repository('.', format=format)
4597.1.10 by John Arbash Meinel
Fix some tests that were failing because we checked against RepositoryFormatCHK1
87
        if isinstance(format.repository_format, RepositoryFormat2a):
4265.1.4 by John Arbash Meinel
Special case the CHK1 format to allow it to not fetch using deltas.
88
            # TODO: This is currently a workaround. CHK format repositories
89
            #       ignore the 'deltas' flag, but during conversions, we can't
90
            #       do unordered delta fetches. Remove this clause once we
91
            #       improve the inter-format fetching.
92
            self.assertEqual(False, repo._format._fetch_uses_deltas)
93
        else:
94
            self.assertEqual(True, repo._format._fetch_uses_deltas)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
95
96
    def test_disk_layout(self):
97
        format = self.get_format()
98
        repo = self.make_repository('.', format=format)
99
        # in case of side effects of locking.
100
        repo.lock_write()
101
        repo.unlock()
102
        t = repo.bzrdir.get_repository_transport(None)
103
        self.check_format(t)
104
        # XXX: no locks left when unlocked at the moment
105
        # self.assertEqualDiff('', t.get('lock').read())
106
        self.check_databases(t)
107
108
    def check_format(self, t):
109
        self.assertEqualDiff(
110
            self.format_string, # from scenario
111
            t.get('format').read())
112
113
    def assertHasNoKndx(self, t, knit_name):
114
        """Assert that knit_name has no index on t."""
115
        self.assertFalse(t.has(knit_name + '.kndx'))
116
117
    def assertHasNoKnit(self, t, knit_name):
118
        """Assert that knit_name exists on t."""
119
        # no default content
120
        self.assertFalse(t.has(knit_name + '.knit'))
121
122
    def check_databases(self, t):
123
        """check knit content for a repository."""
124
        # check conversion worked
125
        self.assertHasNoKndx(t, 'inventory')
126
        self.assertHasNoKnit(t, 'inventory')
127
        self.assertHasNoKndx(t, 'revisions')
128
        self.assertHasNoKnit(t, 'revisions')
129
        self.assertHasNoKndx(t, 'signatures')
130
        self.assertHasNoKnit(t, 'signatures')
131
        self.assertFalse(t.has('knits'))
132
        # revision-indexes file-container directory
133
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
134
            list(self.index_class(t, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
135
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
136
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
137
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
138
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
139
140
    def test_shared_disk_layout(self):
141
        format = self.get_format()
142
        repo = self.make_repository('.', shared=True, format=format)
143
        # we want:
144
        t = repo.bzrdir.get_repository_transport(None)
145
        self.check_format(t)
146
        # XXX: no locks left when unlocked at the moment
147
        # self.assertEqualDiff('', t.get('lock').read())
148
        # We should have a 'shared-storage' marker file.
149
        self.assertEqualDiff('', t.get('shared-storage').read())
150
        self.check_databases(t)
151
152
    def test_shared_no_tree_disk_layout(self):
153
        format = self.get_format()
154
        repo = self.make_repository('.', shared=True, format=format)
155
        repo.set_make_working_trees(False)
156
        # we want:
157
        t = repo.bzrdir.get_repository_transport(None)
158
        self.check_format(t)
159
        # XXX: no locks left when unlocked at the moment
160
        # self.assertEqualDiff('', t.get('lock').read())
161
        # We should have a 'shared-storage' marker file.
162
        self.assertEqualDiff('', t.get('shared-storage').read())
163
        # We should have a marker for the no-working-trees flag.
164
        self.assertEqualDiff('', t.get('no-working-trees').read())
165
        # The marker should go when we toggle the setting.
166
        repo.set_make_working_trees(True)
167
        self.assertFalse(t.has('no-working-trees'))
168
        self.check_databases(t)
169
170
    def test_adding_revision_creates_pack_indices(self):
171
        format = self.get_format()
172
        tree = self.make_branch_and_tree('.', format=format)
173
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
174
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
175
            list(self.index_class(trans, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
176
        tree.commit('foobarbaz')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
177
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
178
        index_nodes = list(index.iter_all_entries())
179
        self.assertEqual(1, len(index_nodes))
180
        node = index_nodes[0]
181
        name = node[1][0]
182
        # the pack sizes should be listed in the index
183
        pack_value = node[2]
184
        sizes = [int(digits) for digits in pack_value.split(' ')]
185
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
186
            stat = trans.stat('indices/%s%s' % (name, suffix))
187
            self.assertEqual(size, stat.st_size)
188
189
    def test_pulling_nothing_leads_to_no_new_names(self):
190
        format = self.get_format()
191
        tree1 = self.make_branch_and_tree('1', format=format)
192
        tree2 = self.make_branch_and_tree('2', format=format)
193
        tree1.branch.repository.fetch(tree2.branch.repository)
194
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
195
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
196
            list(self.index_class(trans, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
197
198
    def test_commit_across_pack_shape_boundary_autopacks(self):
199
        format = self.get_format()
200
        tree = self.make_branch_and_tree('.', format=format)
201
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
202
        # This test could be a little cheaper by replacing the packs
203
        # attribute on the repository to allow a different pack distribution
204
        # and max packs policy - so we are checking the policy is honoured
205
        # in the test. But for now 11 commits is not a big deal in a single
206
        # test.
207
        for x in range(9):
208
            tree.commit('commit %s' % x)
209
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
210
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
211
        self.assertEqual(9, len(list(index.iter_all_entries())))
212
        # insert some files in obsolete_packs which should be removed by pack.
213
        trans.put_bytes('obsolete_packs/foo', '123')
214
        trans.put_bytes('obsolete_packs/bar', '321')
215
        # committing one more should coalesce to 1 of 10.
216
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
217
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
218
        self.assertEqual(1, len(list(index.iter_all_entries())))
219
        # packing should not damage data
220
        tree = tree.bzrdir.open_workingtree()
221
        check_result = tree.branch.repository.check(
222
            [tree.branch.last_revision()])
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)
223
        nb_files = 5 # .pack, .rix, .iix, .tix, .six
224
        if tree.branch.repository._format.supports_chks:
225
            nb_files += 1 # .cix
226
        # We should have 10 x nb_files files in the obsolete_packs directory.
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
227
        obsolete_files = list(trans.list_dir('obsolete_packs'))
228
        self.assertFalse('foo' in obsolete_files)
229
        self.assertFalse('bar' in obsolete_files)
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)
230
        self.assertEqual(10 * nb_files, len(obsolete_files))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
231
        # XXX: Todo check packs obsoleted correctly - old packs and indices
232
        # in the obsolete_packs directory.
233
        large_pack_name = list(index.iter_all_entries())[0][1][0]
234
        # finally, committing again should not touch the large pack.
235
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
236
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
237
        self.assertEqual(2, len(list(index.iter_all_entries())))
238
        pack_names = [node[1][0] for node in index.iter_all_entries()]
239
        self.assertTrue(large_pack_name in pack_names)
240
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
241
    def test_commit_write_group_returns_new_pack_names(self):
242
        format = self.get_format()
243
        tree = self.make_branch_and_tree('foo', format=format)
244
        tree.commit('first post')
245
        repo = tree.branch.repository
246
        repo.lock_write()
247
        try:
248
            repo.start_write_group()
249
            try:
250
                inv = inventory.Inventory(revision_id="A")
251
                inv.root.revision = "A"
252
                repo.texts.add_lines((inv.root.file_id, "A"), [], [])
253
                rev = _mod_revision.Revision(timestamp=0, timezone=None,
254
                    committer="Foo Bar <foo@example.com>", message="Message",
255
                    revision_id="A")
256
                rev.parent_ids = ()
257
                repo.add_revision("A", rev, inv=inv)
258
            except:
259
                repo.abort_write_group()
260
                raise
261
            else:
262
                old_names = repo._pack_collection._names.keys()
263
                result = repo.commit_write_group()
264
                cur_names = repo._pack_collection._names.keys()
265
                new_names = list(set(cur_names) - set(old_names))
266
                self.assertEqual(new_names, result)
267
        finally:
268
            repo.unlock()
269
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
270
    def test_fail_obsolete_deletion(self):
271
        # failing to delete obsolete packs is not fatal
272
        format = self.get_format()
273
        server = fakenfs.FakeNFSServer()
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
274
        self.start_server(server)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
275
        transport = get_transport(server.get_url())
276
        bzrdir = self.get_format().initialize_on_transport(transport)
277
        repo = bzrdir.create_repository()
278
        repo_transport = bzrdir.get_repository_transport(None)
279
        self.assertTrue(repo_transport.has('obsolete_packs'))
280
        # these files are in use by another client and typically can't be deleted
281
        repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
282
        repo._pack_collection._clear_obsolete_packs()
283
        self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
284
285
    def test_pack_after_two_commits_packs_everything(self):
286
        format = self.get_format()
287
        tree = self.make_branch_and_tree('.', format=format)
288
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
289
        tree.commit('start')
290
        tree.commit('more work')
291
        tree.branch.repository.pack()
292
        # there should be 1 pack:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
293
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
294
        self.assertEqual(1, len(list(index.iter_all_entries())))
295
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
296
4597.1.6 by John Arbash Meinel
Add a test that inventory texts are preserved during pack.
297
    def test_pack_preserves_all_inventories(self):
298
        # This is related to bug:
299
        #   https://bugs.launchpad.net/bzr/+bug/412198
300
        # Stacked repositories need to keep the inventory for parents, even
301
        # after a pack operation. However, it is harder to test that, then just
302
        # test that all inventory texts are preserved.
303
        format = self.get_format()
304
        builder = self.make_branch_builder('source', format=format)
305
        builder.start_series()
306
        builder.build_snapshot('A-id', None, [
307
            ('add', ('', 'root-id', 'directory', None))])
308
        builder.build_snapshot('B-id', None, [
309
            ('add', ('file', 'file-id', 'file', 'B content\n'))])
310
        builder.build_snapshot('C-id', None, [
311
            ('modify', ('file-id', 'C content\n'))])
312
        builder.finish_series()
313
        b = builder.get_branch()
314
        b.lock_read()
315
        self.addCleanup(b.unlock)
316
        repo = self.make_repository('repo', shared=True, format=format)
317
        repo.lock_write()
318
        self.addCleanup(repo.unlock)
319
        repo.fetch(b.repository, revision_id='B-id')
320
        inv = b.repository.iter_inventories(['C-id']).next()
321
        repo.start_write_group()
322
        repo.add_inventory('C-id', inv, ['B-id'])
323
        repo.commit_write_group()
324
        self.assertEqual([('A-id',), ('B-id',), ('C-id',)],
325
                         sorted(repo.inventories.keys()))
326
        repo.pack()
327
        self.assertEqual([('A-id',), ('B-id',), ('C-id',)],
328
                         sorted(repo.inventories.keys()))
329
        # Content should be preserved as well
330
        self.assertEqual(inv, repo.iter_inventories(['C-id']).next())
331
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
332
    def test_pack_layout(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)
333
        # Test that the ordering of revisions in pack repositories is
334
        # tip->ancestor
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
335
        format = self.get_format()
336
        tree = self.make_branch_and_tree('.', format=format)
337
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
338
        tree.commit('start', rev_id='1')
339
        tree.commit('more work', rev_id='2')
340
        tree.branch.repository.pack()
341
        tree.lock_read()
342
        self.addCleanup(tree.unlock)
343
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
344
            tree.branch.repository._pack_collection.names()[0])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
345
        # revision access tends to be tip->ancestor, so ordering that way on
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
346
        # disk is a good idea.
347
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
4597.1.10 by John Arbash Meinel
Fix some tests that were failing because we checked against RepositoryFormatCHK1
348
            if type(format.repository_format) is RepositoryFormat2a:
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
349
                # group_start, group_len, internal_start, internal_len
350
                pos = map(int, val.split())
351
            else:
352
                # eol_flag, start, len
353
                pos = int(val[1:].split()[0])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
354
            if key == ('1',):
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
355
                pos_1 = pos
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
356
            else:
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
357
                pos_2 = pos
358
        self.assertTrue(pos_2 < pos_1, 'rev 1 came before rev 2 %s > %s'
359
                                       % (pos_1, pos_2))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
360
361
    def test_pack_repositories_support_multiple_write_locks(self):
362
        format = self.get_format()
363
        self.make_repository('.', shared=True, format=format)
364
        r1 = repository.Repository.open('.')
365
        r2 = repository.Repository.open('.')
366
        r1.lock_write()
367
        self.addCleanup(r1.unlock)
368
        r2.lock_write()
369
        r2.unlock()
370
371
    def _add_text(self, repo, fileid):
372
        """Add a text to the repository within a write group."""
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)
373
        repo.texts.add_lines((fileid, 'samplerev+'+fileid), [],
374
            ['smaplerev+'+fileid])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
375
376
    def test_concurrent_writers_merge_new_packs(self):
377
        format = self.get_format()
378
        self.make_repository('.', shared=True, format=format)
379
        r1 = repository.Repository.open('.')
380
        r2 = repository.Repository.open('.')
381
        r1.lock_write()
382
        try:
383
            # access enough data to load the names list
384
            list(r1.all_revision_ids())
385
            r2.lock_write()
386
            try:
387
                # access enough data to load the names list
388
                list(r2.all_revision_ids())
389
                r1.start_write_group()
390
                try:
391
                    r2.start_write_group()
392
                    try:
393
                        self._add_text(r1, 'fileidr1')
394
                        self._add_text(r2, 'fileidr2')
395
                    except:
396
                        r2.abort_write_group()
397
                        raise
398
                except:
399
                    r1.abort_write_group()
400
                    raise
401
                # both r1 and r2 have open write groups with data in them
402
                # created while the other's write group was open.
403
                # Commit both which requires a merge to the pack-names.
404
                try:
405
                    r1.commit_write_group()
406
                except:
407
                    r1.abort_write_group()
408
                    r2.abort_write_group()
409
                    raise
410
                r2.commit_write_group()
411
                # tell r1 to reload from disk
412
                r1._pack_collection.reset()
413
                # Now both repositories should know about both names
414
                r1._pack_collection.ensure_loaded()
415
                r2._pack_collection.ensure_loaded()
416
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
417
                self.assertEqual(2, len(r1._pack_collection.names()))
418
            finally:
419
                r2.unlock()
420
        finally:
421
            r1.unlock()
422
423
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
424
        format = self.get_format()
425
        self.make_repository('.', shared=True, format=format)
426
        r1 = repository.Repository.open('.')
427
        r2 = repository.Repository.open('.')
428
        # add a pack to drop
429
        r1.lock_write()
430
        try:
431
            r1.start_write_group()
432
            try:
433
                self._add_text(r1, 'fileidr1')
434
            except:
435
                r1.abort_write_group()
436
                raise
437
            else:
438
                r1.commit_write_group()
439
            r1._pack_collection.ensure_loaded()
440
            name_to_drop = r1._pack_collection.all_packs()[0].name
441
        finally:
442
            r1.unlock()
443
        r1.lock_write()
444
        try:
445
            # access enough data to load the names list
446
            list(r1.all_revision_ids())
447
            r2.lock_write()
448
            try:
449
                # access enough data to load the names list
450
                list(r2.all_revision_ids())
451
                r1._pack_collection.ensure_loaded()
452
                try:
453
                    r2.start_write_group()
454
                    try:
455
                        # in r1, drop the pack
456
                        r1._pack_collection._remove_pack_from_memory(
457
                            r1._pack_collection.get_pack_by_name(name_to_drop))
458
                        # in r2, add a pack
459
                        self._add_text(r2, 'fileidr2')
460
                    except:
461
                        r2.abort_write_group()
462
                        raise
463
                except:
464
                    r1._pack_collection.reset()
465
                    raise
466
                # r1 has a changed names list, and r2 an open write groups with
467
                # changes.
468
                # save r1, and then commit the r2 write group, which requires a
469
                # merge to the pack-names, which should not reinstate
470
                # name_to_drop
471
                try:
472
                    r1._pack_collection._save_pack_names()
473
                    r1._pack_collection.reset()
474
                except:
475
                    r2.abort_write_group()
476
                    raise
477
                try:
478
                    r2.commit_write_group()
479
                except:
480
                    r2.abort_write_group()
481
                    raise
482
                # Now both repositories should now about just one name.
483
                r1._pack_collection.ensure_loaded()
484
                r2._pack_collection.ensure_loaded()
485
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
486
                self.assertEqual(1, len(r1._pack_collection.names()))
487
                self.assertFalse(name_to_drop in r1._pack_collection.names())
488
            finally:
489
                r2.unlock()
490
        finally:
491
            r1.unlock()
492
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
493
    def test_concurrent_pack_triggers_reload(self):
494
        # create 2 packs, which we will then collapse
495
        tree = self.make_branch_and_tree('tree')
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
496
        tree.lock_write()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
497
        try:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
498
            rev1 = tree.commit('one')
499
            rev2 = tree.commit('two')
500
            r2 = repository.Repository.open('tree')
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
501
            r2.lock_read()
502
            try:
503
                # Now r2 has read the pack-names file, but will need to reload
504
                # it after r1 has repacked
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
505
                tree.branch.repository.pack()
506
                self.assertEqual({rev2:(rev1,)}, r2.get_parent_map([rev2]))
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
507
            finally:
508
                r2.unlock()
509
        finally:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
510
            tree.unlock()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
511
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
512
    def test_concurrent_pack_during_get_record_reloads(self):
513
        tree = self.make_branch_and_tree('tree')
514
        tree.lock_write()
515
        try:
516
            rev1 = tree.commit('one')
517
            rev2 = tree.commit('two')
3789.2.14 by John Arbash Meinel
Update AggregateIndex to pass the reload_func into _DirectPackAccess
518
            keys = [(rev1,), (rev2,)]
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
519
            r2 = repository.Repository.open('tree')
520
            r2.lock_read()
521
            try:
522
                # At this point, we will start grabbing a record stream, and
523
                # trigger a repack mid-way
524
                packed = False
525
                result = {}
526
                record_stream = r2.revisions.get_record_stream(keys,
527
                                    'unordered', False)
528
                for record in record_stream:
529
                    result[record.key] = record
530
                    if not packed:
531
                        tree.branch.repository.pack()
532
                        packed = True
533
                # The first record will be found in the original location, but
534
                # after the pack, we have to reload to find the next record
3789.2.14 by John Arbash Meinel
Update AggregateIndex to pass the reload_func into _DirectPackAccess
535
                self.assertEqual(sorted(keys), sorted(result.keys()))
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
536
            finally:
537
                r2.unlock()
538
        finally:
539
            tree.unlock()
540
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
541
    def test_lock_write_does_not_physically_lock(self):
542
        repo = self.make_repository('.', format=self.get_format())
543
        repo.lock_write()
544
        self.addCleanup(repo.unlock)
545
        self.assertFalse(repo.get_physical_lock_status())
546
547
    def prepare_for_break_lock(self):
548
        # Setup the global ui factory state so that a break-lock method call
549
        # will find usable input in the input stream.
550
        old_factory = ui.ui_factory
551
        def restoreFactory():
552
            ui.ui_factory = old_factory
553
        self.addCleanup(restoreFactory)
4449.3.27 by Martin Pool
More test updates to use CannedInputUIFactory
554
        ui.ui_factory = ui.CannedInputUIFactory([True])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
555
556
    def test_break_lock_breaks_physical_lock(self):
557
        repo = self.make_repository('.', format=self.get_format())
558
        repo._pack_collection.lock_names()
3650.4.1 by Aaron Bentley
Fix test kipple in test_break_lock_breaks_physical_lock
559
        repo.control_files.leave_in_place()
560
        repo.unlock()
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
561
        repo2 = repository.Repository.open('.')
562
        self.assertTrue(repo.get_physical_lock_status())
563
        self.prepare_for_break_lock()
564
        repo2.break_lock()
565
        self.assertFalse(repo.get_physical_lock_status())
566
567
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
568
        repo = self.make_repository('.', format=self.get_format())
569
        repo._pack_collection.lock_names()
570
        self.assertTrue(repo.get_physical_lock_status())
571
        repo2 = repository.Repository.open('.')
572
        self.prepare_for_break_lock()
573
        repo2.break_lock()
574
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
575
576
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
577
        # we want two repositories at this point:
578
        # one with a revision that is a ghost in the other
579
        # repository.
580
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
581
        # 'references' is present in both repositories, and 'tip' is present
582
        # just in has_ghost.
583
        # has_ghost       missing_ghost
584
        #------------------------------
585
        # 'ghost'             -
586
        # 'references'    'references'
587
        # 'tip'               -
588
        # In this test we fetch 'tip' which should not fetch 'ghost'
589
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
590
        missing_ghost = self.make_repository('missing_ghost',
591
            format=self.get_format())
592
593
        def add_commit(repo, revision_id, parent_ids):
594
            repo.lock_write()
595
            repo.start_write_group()
596
            inv = inventory.Inventory(revision_id=revision_id)
597
            inv.root.revision = revision_id
598
            root_id = inv.root.file_id
599
            sha1 = repo.add_inventory(revision_id, inv, [])
600
            repo.texts.add_lines((root_id, revision_id), [], [])
601
            rev = _mod_revision.Revision(timestamp=0,
602
                                         timezone=None,
603
                                         committer="Foo Bar <foo@example.com>",
604
                                         message="Message",
605
                                         inventory_sha1=sha1,
606
                                         revision_id=revision_id)
607
            rev.parent_ids = parent_ids
608
            repo.add_revision(revision_id, rev)
609
            repo.commit_write_group()
610
            repo.unlock()
611
        add_commit(has_ghost, 'ghost', [])
612
        add_commit(has_ghost, 'references', ['ghost'])
613
        add_commit(missing_ghost, 'references', ['ghost'])
614
        add_commit(has_ghost, 'tip', ['references'])
615
        missing_ghost.fetch(has_ghost, 'tip')
616
        # missing ghost now has tip and not ghost.
617
        rev = missing_ghost.get_revision('tip')
618
        inv = missing_ghost.get_inventory('tip')
619
        self.assertRaises(errors.NoSuchRevision,
620
            missing_ghost.get_revision, 'ghost')
621
        self.assertRaises(errors.NoSuchRevision,
622
            missing_ghost.get_inventory, 'ghost')
623
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
624
    def make_write_ready_repo(self):
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
625
        format = self.get_format()
4597.1.10 by John Arbash Meinel
Fix some tests that were failing because we checked against RepositoryFormatCHK1
626
        if isinstance(format.repository_format, RepositoryFormat2a):
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
627
            raise TestNotApplicable("No missing compression parents")
628
        repo = self.make_repository('.', format=format)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
629
        repo.lock_write()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
630
        self.addCleanup(repo.unlock)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
631
        repo.start_write_group()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
632
        self.addCleanup(repo.abort_write_group)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
633
        return repo
634
635
    def test_missing_inventories_compression_parent_prevents_commit(self):
636
        repo = self.make_write_ready_repo()
637
        key = ('junk',)
638
        repo.inventories._index._missing_compression_parents.add(key)
639
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
640
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
641
642
    def test_missing_revisions_compression_parent_prevents_commit(self):
643
        repo = self.make_write_ready_repo()
644
        key = ('junk',)
645
        repo.revisions._index._missing_compression_parents.add(key)
646
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
647
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
648
649
    def test_missing_signatures_compression_parent_prevents_commit(self):
650
        repo = self.make_write_ready_repo()
651
        key = ('junk',)
652
        repo.signatures._index._missing_compression_parents.add(key)
653
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
654
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
655
656
    def test_missing_text_compression_parent_prevents_commit(self):
657
        repo = self.make_write_ready_repo()
658
        key = ('some', 'junk')
659
        repo.texts._index._missing_compression_parents.add(key)
660
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
661
        e = self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
662
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
663
    def test_supports_external_lookups(self):
664
        repo = self.make_repository('.', format=self.get_format())
665
        self.assertEqual(self.format_supports_external_lookups,
666
            repo._format.supports_external_lookups)
667
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
668
    def test_abort_write_group_does_not_raise_when_suppressed(self):
669
        """Similar to per_repository.test_write_group's test of the same name.
670
671
        Also requires that the exception is logged.
672
        """
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
673
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
674
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
675
        token = repo.lock_write()
676
        self.addCleanup(repo.unlock)
677
        repo.start_write_group()
678
        # Damage the repository on the filesystem
679
        self.get_transport('').rename('repo', 'foo')
680
        # abort_write_group will not raise an error
681
        self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
682
        # But it does log an error
683
        log_file = self._get_log(keep_log_file=True)
684
        self.assertContainsRe(log_file, 'abort_write_group failed')
685
        self.assertContainsRe(log_file, r'INFO  bzr: ERROR \(ignored\):')
686
        if token is not None:
687
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
688
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
689
    def test_abort_write_group_does_raise_when_not_suppressed(self):
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
690
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
691
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
692
        token = repo.lock_write()
693
        self.addCleanup(repo.unlock)
694
        repo.start_write_group()
695
        # Damage the repository on the filesystem
696
        self.get_transport('').rename('repo', 'foo')
697
        # abort_write_group will not raise an error
698
        self.assertRaises(Exception, repo.abort_write_group)
699
        if token is not None:
700
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
701
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
702
    def test_suspend_write_group(self):
703
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
704
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
705
        token = repo.lock_write()
706
        self.addCleanup(repo.unlock)
707
        repo.start_write_group()
708
        repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
709
        wg_tokens = repo.suspend_write_group()
710
        expected_pack_name = wg_tokens[0] + '.pack'
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
711
        expected_names = [wg_tokens[0] + ext for ext in
712
                            ('.rix', '.iix', '.tix', '.six')]
713
        if repo.chk_bytes is not None:
714
            expected_names.append(wg_tokens[0] + '.cix')
715
        expected_names.append(expected_pack_name)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
716
        upload_transport = repo._pack_collection._upload_transport
717
        limbo_files = upload_transport.list_dir('')
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
718
        self.assertEqual(sorted(expected_names), sorted(limbo_files))
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
719
        md5 = osutils.md5(upload_transport.get_bytes(expected_pack_name))
720
        self.assertEqual(wg_tokens[0], md5.hexdigest())
721
4343.3.8 by John Arbash Meinel
Some cleanup passes.
722
    def test_resume_chk_bytes(self):
723
        self.vfs_transport_factory = memory.MemoryServer
724
        repo = self.make_repository('repo', format=self.get_format())
725
        if repo.chk_bytes is None:
726
            raise TestNotApplicable('no chk_bytes for this repository')
727
        token = repo.lock_write()
728
        self.addCleanup(repo.unlock)
729
        repo.start_write_group()
730
        text = 'a bit of text\n'
731
        key = ('sha1:' + osutils.sha_string(text),)
732
        repo.chk_bytes.add_lines(key, (), [text])
733
        wg_tokens = repo.suspend_write_group()
734
        same_repo = repo.bzrdir.open_repository()
735
        same_repo.lock_write()
736
        self.addCleanup(same_repo.unlock)
737
        same_repo.resume_write_group(wg_tokens)
738
        self.assertEqual([key], list(same_repo.chk_bytes.keys()))
739
        self.assertEqual(
740
            text, same_repo.chk_bytes.get_record_stream([key],
741
                'unordered', True).next().get_bytes_as('fulltext'))
742
        same_repo.abort_write_group()
743
        self.assertEqual([], list(same_repo.chk_bytes.keys()))
744
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
745
    def test_resume_write_group_then_abort(self):
746
        # Create a repo, start a write group, insert some data, suspend.
747
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
748
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
749
        token = repo.lock_write()
750
        self.addCleanup(repo.unlock)
751
        repo.start_write_group()
752
        text_key = ('file-id', 'revid')
753
        repo.texts.add_lines(text_key, (), ['lines'])
754
        wg_tokens = repo.suspend_write_group()
755
        # Get a fresh repository object for the repo on the filesystem.
756
        same_repo = repo.bzrdir.open_repository()
757
        # Resume
758
        same_repo.lock_write()
759
        self.addCleanup(same_repo.unlock)
760
        same_repo.resume_write_group(wg_tokens)
761
        same_repo.abort_write_group()
762
        self.assertEqual(
763
            [], same_repo._pack_collection._upload_transport.list_dir(''))
764
        self.assertEqual(
765
            [], same_repo._pack_collection._pack_transport.list_dir(''))
766
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
767
    def test_commit_resumed_write_group(self):
768
        self.vfs_transport_factory = memory.MemoryServer
769
        repo = self.make_repository('repo', format=self.get_format())
770
        token = repo.lock_write()
771
        self.addCleanup(repo.unlock)
772
        repo.start_write_group()
773
        text_key = ('file-id', 'revid')
774
        repo.texts.add_lines(text_key, (), ['lines'])
775
        wg_tokens = repo.suspend_write_group()
776
        # Get a fresh repository object for the repo on the filesystem.
777
        same_repo = repo.bzrdir.open_repository()
778
        # Resume
779
        same_repo.lock_write()
780
        self.addCleanup(same_repo.unlock)
781
        same_repo.resume_write_group(wg_tokens)
782
        same_repo.commit_write_group()
783
        expected_pack_name = wg_tokens[0] + '.pack'
784
        expected_names = [wg_tokens[0] + ext for ext in
785
                            ('.rix', '.iix', '.tix', '.six')]
786
        if repo.chk_bytes is not None:
787
            expected_names.append(wg_tokens[0] + '.cix')
788
        self.assertEqual(
789
            [], same_repo._pack_collection._upload_transport.list_dir(''))
790
        index_names = repo._pack_collection._index_transport.list_dir('')
791
        self.assertEqual(sorted(expected_names), sorted(index_names))
792
        pack_names = repo._pack_collection._pack_transport.list_dir('')
793
        self.assertEqual([expected_pack_name], pack_names)
794
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
795
    def test_resume_malformed_token(self):
796
        self.vfs_transport_factory = memory.MemoryServer
797
        # Make a repository with a suspended write group
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
798
        repo = self.make_repository('repo', format=self.get_format())
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
799
        token = repo.lock_write()
800
        self.addCleanup(repo.unlock)
801
        repo.start_write_group()
802
        text_key = ('file-id', 'revid')
803
        repo.texts.add_lines(text_key, (), ['lines'])
804
        wg_tokens = repo.suspend_write_group()
805
        # Make a new repository
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
806
        new_repo = self.make_repository('new_repo', format=self.get_format())
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
807
        token = new_repo.lock_write()
808
        self.addCleanup(new_repo.unlock)
809
        hacked_wg_token = (
810
            '../../../../repo/.bzr/repository/upload/' + wg_tokens[0])
811
        self.assertRaises(
4002.1.7 by Andrew Bennetts
Rename UnresumableWriteGroups to UnresumableWriteGroup.
812
            errors.UnresumableWriteGroup,
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
813
            new_repo.resume_write_group, [hacked_wg_token])
814
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
815
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
816
class TestPackRepositoryStacking(TestCaseWithTransport):
817
818
    """Tests for stacking pack repositories"""
819
820
    def setUp(self):
821
        if not self.format_supports_external_lookups:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
822
            raise TestNotApplicable("%r doesn't support stacking"
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
823
                % (self.format_name,))
824
        super(TestPackRepositoryStacking, self).setUp()
825
826
    def get_format(self):
827
        return bzrdir.format_registry.make_bzrdir(self.format_name)
828
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
829
    def test_stack_checks_rich_root_compatibility(self):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
830
        # early versions of the packing code relied on pack internals to
831
        # stack, but the current version should be able to stack on any
832
        # format.
833
        #
834
        # TODO: Possibly this should be run per-repository-format and raise
835
        # TestNotApplicable on formats that don't support stacking. -- mbp
836
        # 20080729
837
        repo = self.make_repository('repo', format=self.get_format())
838
        if repo.supports_rich_root():
839
            # can only stack on repositories that have compatible internal
840
            # metadata
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
841
            if getattr(repo._format, 'supports_tree_reference', False):
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
842
                matching_format_name = 'pack-0.92-subtree'
843
            else:
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
844
                if repo._format.supports_chks:
4597.1.6 by John Arbash Meinel
Add a test that inventory texts are preserved during pack.
845
                    matching_format_name = '2a'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
846
                else:
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
847
                    matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
848
            mismatching_format_name = 'pack-0.92'
849
        else:
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)
850
            # We don't have a non-rich-root CHK format.
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
851
            if repo._format.supports_chks:
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)
852
                raise AssertionError("no non-rich-root CHK formats known")
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
853
            else:
854
                matching_format_name = 'pack-0.92'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
855
            mismatching_format_name = 'pack-0.92-subtree'
856
        base = self.make_repository('base', format=matching_format_name)
857
        repo.add_fallback_repository(base)
858
        # you can't stack on something with incompatible data
859
        bad_repo = self.make_repository('mismatch',
860
            format=mismatching_format_name)
861
        e = self.assertRaises(errors.IncompatibleRepositories,
862
            repo.add_fallback_repository, bad_repo)
863
        self.assertContainsRe(str(e),
864
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
865
            r'.*Repository.*/repo/.*\n'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
866
            r'different rich-root support')
867
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
868
    def test_stack_checks_serializers_compatibility(self):
869
        repo = self.make_repository('repo', format=self.get_format())
870
        if getattr(repo._format, 'supports_tree_reference', False):
871
            # can only stack on repositories that have compatible internal
872
            # metadata
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
873
            matching_format_name = 'pack-0.92-subtree'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
874
            mismatching_format_name = 'rich-root-pack'
875
        else:
876
            if repo.supports_rich_root():
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
877
                if repo._format.supports_chks:
4597.1.6 by John Arbash Meinel
Add a test that inventory texts are preserved during pack.
878
                    matching_format_name = '2a'
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
879
                else:
880
                    matching_format_name = 'rich-root-pack'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
881
                mismatching_format_name = 'pack-0.92-subtree'
882
            else:
883
                raise TestNotApplicable('No formats use non-v5 serializer'
884
                    ' without having rich-root also set')
885
        base = self.make_repository('base', format=matching_format_name)
886
        repo.add_fallback_repository(base)
887
        # you can't stack on something with incompatible data
888
        bad_repo = self.make_repository('mismatch',
889
            format=mismatching_format_name)
890
        e = self.assertRaises(errors.IncompatibleRepositories,
891
            repo.add_fallback_repository, bad_repo)
892
        self.assertContainsRe(str(e),
893
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
894
            r'.*Repository.*/repo/.*\n'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
895
            r'different serializers')
896
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
897
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
898
        base = self.make_branch_and_tree('base', format=self.get_format())
899
        base.commit('foo')
900
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
901
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
4595.4.4 by Robert Collins
Disable committing directly to stacked branches from lightweight checkouts.
902
        local_tree = referencing.branch.create_checkout('local')
903
        local_tree.commit('bar')
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
904
        new_instance = referencing.bzrdir.open_repository()
905
        new_instance.lock_read()
906
        self.addCleanup(new_instance.unlock)
907
        new_instance._pack_collection.ensure_loaded()
908
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
909
910
    def test_autopack_only_considers_main_repo_packs(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)
911
        format = self.get_format()
912
        base = self.make_branch_and_tree('base', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
913
        base.commit('foo')
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)
914
        tree = self.make_branch_and_tree('repo', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
915
        tree.branch.repository.add_fallback_repository(base.branch.repository)
916
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
917
        # This test could be a little cheaper by replacing the packs
918
        # attribute on the repository to allow a different pack distribution
919
        # and max packs policy - so we are checking the policy is honoured
920
        # in the test. But for now 11 commits is not a big deal in a single
921
        # test.
4595.4.4 by Robert Collins
Disable committing directly to stacked branches from lightweight checkouts.
922
        local_tree = tree.branch.create_checkout('local')
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
923
        for x in range(9):
4595.4.4 by Robert Collins
Disable committing directly to stacked branches from lightweight checkouts.
924
            local_tree.commit('commit %s' % x)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
925
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
926
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
927
        self.assertEqual(9, len(list(index.iter_all_entries())))
928
        # committing one more should coalesce to 1 of 10.
4595.4.4 by Robert Collins
Disable committing directly to stacked branches from lightweight checkouts.
929
        local_tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
930
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
931
        self.assertEqual(1, len(list(index.iter_all_entries())))
932
        # packing should not damage data
933
        tree = tree.bzrdir.open_workingtree()
934
        check_result = tree.branch.repository.check(
935
            [tree.branch.last_revision()])
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)
936
        nb_files = 5 # .pack, .rix, .iix, .tix, .six
937
        if tree.branch.repository._format.supports_chks:
938
            nb_files += 1 # .cix
939
        # We should have 10 x nb_files files in the obsolete_packs directory.
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
940
        obsolete_files = list(trans.list_dir('obsolete_packs'))
941
        self.assertFalse('foo' in obsolete_files)
942
        self.assertFalse('bar' in obsolete_files)
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)
943
        self.assertEqual(10 * nb_files, len(obsolete_files))
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
944
        # XXX: Todo check packs obsoleted correctly - old packs and indices
945
        # in the obsolete_packs directory.
946
        large_pack_name = list(index.iter_all_entries())[0][1][0]
947
        # finally, committing again should not touch the large pack.
4595.4.4 by Robert Collins
Disable committing directly to stacked branches from lightweight checkouts.
948
        local_tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
949
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
950
        self.assertEqual(2, len(list(index.iter_all_entries())))
951
        pack_names = [node[1][0] for node in index.iter_all_entries()]
952
        self.assertTrue(large_pack_name in pack_names)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
953
954
4343.3.33 by John Arbash Meinel
Clear KeyDependencies on abort/suspend/commit_write_group.
955
class TestKeyDependencies(TestCaseWithTransport):
956
957
    def get_format(self):
958
        return bzrdir.format_registry.make_bzrdir(self.format_name)
959
960
    def create_source_and_target(self):
961
        builder = self.make_branch_builder('source', format=self.get_format())
962
        builder.start_series()
963
        builder.build_snapshot('A-id', None, [
964
            ('add', ('', 'root-id', 'directory', None))])
965
        builder.build_snapshot('B-id', ['A-id', 'ghost-id'], [])
966
        builder.finish_series()
967
        repo = self.make_repository('target')
968
        b = builder.get_branch()
969
        b.lock_read()
970
        self.addCleanup(b.unlock)
971
        repo.lock_write()
972
        self.addCleanup(repo.unlock)
973
        return b.repository, repo
974
975
    def test_key_dependencies_cleared_on_abort(self):
976
        source_repo, target_repo = self.create_source_and_target()
977
        target_repo.start_write_group()
978
        try:
979
            stream = source_repo.revisions.get_record_stream([('B-id',)],
980
                                                             'unordered', True)
981
            target_repo.revisions.insert_record_stream(stream)
982
            key_refs = target_repo.revisions._index._key_dependencies
983
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
984
        finally:
985
            target_repo.abort_write_group()
986
        self.assertEqual([], sorted(key_refs.get_referrers()))
987
988
    def test_key_dependencies_cleared_on_suspend(self):
989
        source_repo, target_repo = self.create_source_and_target()
990
        target_repo.start_write_group()
991
        try:
992
            stream = source_repo.revisions.get_record_stream([('B-id',)],
993
                                                             'unordered', True)
994
            target_repo.revisions.insert_record_stream(stream)
995
            key_refs = target_repo.revisions._index._key_dependencies
996
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
997
        finally:
998
            target_repo.suspend_write_group()
999
        self.assertEqual([], sorted(key_refs.get_referrers()))
1000
1001
    def test_key_dependencies_cleared_on_commit(self):
1002
        source_repo, target_repo = self.create_source_and_target()
1003
        target_repo.start_write_group()
1004
        try:
1005
            stream = source_repo.revisions.get_record_stream([('B-id',)],
1006
                                                             'unordered', True)
1007
            target_repo.revisions.insert_record_stream(stream)
1008
            key_refs = target_repo.revisions._index._key_dependencies
1009
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
1010
        finally:
1011
            target_repo.commit_write_group()
1012
        self.assertEqual([], sorted(key_refs.get_referrers()))
1013
1014
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1015
class TestSmartServerAutopack(TestCaseWithTransport):
1016
1017
    def setUp(self):
1018
        super(TestSmartServerAutopack, self).setUp()
1019
        # Create a smart server that publishes whatever the backing VFS server
1020
        # does.
1021
        self.smart_server = server.SmartTCPServer_for_testing()
4659.1.2 by Robert Collins
Refactor creation and shutdown of test servers to use a common helper,
1022
        self.start_server(self.smart_server, self.get_server())
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1023
        # Log all HPSS calls into self.hpss_calls.
1024
        client._SmartClient.hooks.install_named_hook(
1025
            'call', self.capture_hpss_call, None)
1026
        self.hpss_calls = []
1027
1028
    def capture_hpss_call(self, params):
1029
        self.hpss_calls.append(params.method)
1030
1031
    def get_format(self):
1032
        return bzrdir.format_registry.make_bzrdir(self.format_name)
1033
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
1034
    def test_autopack_or_streaming_rpc_is_used_when_using_hpss(self):
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1035
        # Make local and remote repos
3735.2.98 by John Arbash Meinel
Merge bzr.dev 4032. Resolve the new streaming fetch.
1036
        format = self.get_format()
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)
1037
        tree = self.make_branch_and_tree('local', format=format)
1038
        self.make_branch_and_tree('remote', format=format)
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1039
        remote_branch_url = self.smart_server.get_url() + 'remote'
1040
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
1041
        # Make 9 local revisions, and push them one at a time to the remote
1042
        # repo to produce 9 pack files.
1043
        for x in range(9):
1044
            tree.commit('commit %s' % x)
1045
            tree.branch.push(remote_branch)
1046
        # Make one more push to trigger an autopack
1047
        self.hpss_calls = []
1048
        tree.commit('commit triggering pack')
1049
        tree.branch.push(remote_branch)
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
1050
        autopack_calls = len([call for call in self.hpss_calls if call ==
1051
            'PackRepository.autopack'])
4476.3.66 by Andrew Bennetts
Fix trivial test failure by making the test recognise the new insert_stream_1.18 verb.
1052
        streaming_calls = len([call for call in self.hpss_calls if call in
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
1053
            ('Repository.insert_stream', 'Repository.insert_stream_1.19')])
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
1054
        if autopack_calls:
1055
            # Non streaming server
1056
            self.assertEqual(1, autopack_calls)
1057
            self.assertEqual(0, streaming_calls)
1058
        else:
1059
            # Streaming was used, which autopacks on the remote end.
1060
            self.assertEqual(0, autopack_calls)
1061
            # NB: The 2 calls are because of the sanity check that the server
1062
            # supports the verb (see remote.py:RemoteSink.insert_stream for
1063
            # details).
1064
            self.assertEqual(2, streaming_calls)
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1065
1066
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
1067
def load_tests(basic_tests, module, loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
1068
    # these give the bzrdir canned format name, and the repository on-disk
1069
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1070
    scenarios_params = [
1071
         dict(format_name='pack-0.92',
1072
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1073
              format_supports_external_lookups=False,
1074
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1075
         dict(format_name='pack-0.92-subtree',
1076
              format_string="Bazaar pack repository format 1 "
1077
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1078
              format_supports_external_lookups=False,
1079
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1080
         dict(format_name='1.6',
1081
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1082
              format_supports_external_lookups=True,
1083
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1084
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1085
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1086
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1087
              format_supports_external_lookups=True,
1088
              index_class=GraphIndex),
3805.3.1 by John Arbash Meinel
Add repository 1.9 format, and update the documentation.
1089
         dict(format_name='1.9',
1090
              format_string="Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n",
1091
              format_supports_external_lookups=True,
1092
              index_class=BTreeGraphIndex),
1093
         dict(format_name='1.9-rich-root',
1094
              format_string="Bazaar RepositoryFormatKnitPack6RichRoot "
1095
                  "(bzr 1.9)\n",
1096
              format_supports_external_lookups=True,
1097
              index_class=BTreeGraphIndex),
4597.1.6 by John Arbash Meinel
Add a test that inventory texts are preserved during pack.
1098
         dict(format_name='2a',
1099
              format_string="Bazaar repository format 2a "
1100
                "(needs bzr 1.16 or later)\n",
4343.3.8 by John Arbash Meinel
Some cleanup passes.
1101
              format_supports_external_lookups=True,
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1102
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1103
         ]
1104
    # name of the scenario is the format name
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
1105
    scenarios = [(s['format_name'], s) for s in scenarios_params]
1106
    return tests.multiply_tests(basic_tests, scenarios, loader.suiteClass())