/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
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
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
    )
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)
45
from bzrlib.repofmt.groupcompress_repo import RepositoryFormatCHK1
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)
4265.1.4 by John Arbash Meinel
Special case the CHK1 format to allow it to not fetch using deltas.
87
        if isinstance(format.repository_format, RepositoryFormatCHK1):
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
241
    def test_fail_obsolete_deletion(self):
242
        # failing to delete obsolete packs is not fatal
243
        format = self.get_format()
244
        server = fakenfs.FakeNFSServer()
245
        server.setUp()
246
        self.addCleanup(server.tearDown)
247
        transport = get_transport(server.get_url())
248
        bzrdir = self.get_format().initialize_on_transport(transport)
249
        repo = bzrdir.create_repository()
250
        repo_transport = bzrdir.get_repository_transport(None)
251
        self.assertTrue(repo_transport.has('obsolete_packs'))
252
        # these files are in use by another client and typically can't be deleted
253
        repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
254
        repo._pack_collection._clear_obsolete_packs()
255
        self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
256
257
    def test_pack_after_two_commits_packs_everything(self):
258
        format = self.get_format()
259
        tree = self.make_branch_and_tree('.', format=format)
260
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
261
        tree.commit('start')
262
        tree.commit('more work')
263
        tree.branch.repository.pack()
264
        # there should be 1 pack:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
265
        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
266
        self.assertEqual(1, len(list(index.iter_all_entries())))
267
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
268
269
    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)
270
        # Test that the ordering of revisions in pack repositories is
271
        # tip->ancestor
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
272
        format = self.get_format()
273
        tree = self.make_branch_and_tree('.', format=format)
274
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
275
        tree.commit('start', rev_id='1')
276
        tree.commit('more work', rev_id='2')
277
        tree.branch.repository.pack()
278
        tree.lock_read()
279
        self.addCleanup(tree.unlock)
280
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
281
            tree.branch.repository._pack_collection.names()[0])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
282
        # 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
283
        # disk is a good idea.
284
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
285
            if type(format.repository_format) is RepositoryFormatCHK1:
286
                # group_start, group_len, internal_start, internal_len
287
                pos = map(int, val.split())
288
            else:
289
                # eol_flag, start, len
290
                pos = int(val[1:].split()[0])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
291
            if key == ('1',):
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
292
                pos_1 = pos
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
293
            else:
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
294
                pos_2 = pos
295
        self.assertTrue(pos_2 < pos_1, 'rev 1 came before rev 2 %s > %s'
296
                                       % (pos_1, pos_2))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
297
298
    def test_pack_repositories_support_multiple_write_locks(self):
299
        format = self.get_format()
300
        self.make_repository('.', shared=True, format=format)
301
        r1 = repository.Repository.open('.')
302
        r2 = repository.Repository.open('.')
303
        r1.lock_write()
304
        self.addCleanup(r1.unlock)
305
        r2.lock_write()
306
        r2.unlock()
307
308
    def _add_text(self, repo, fileid):
309
        """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)
310
        repo.texts.add_lines((fileid, 'samplerev+'+fileid), [],
311
            ['smaplerev+'+fileid])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
312
313
    def test_concurrent_writers_merge_new_packs(self):
314
        format = self.get_format()
315
        self.make_repository('.', shared=True, format=format)
316
        r1 = repository.Repository.open('.')
317
        r2 = repository.Repository.open('.')
318
        r1.lock_write()
319
        try:
320
            # access enough data to load the names list
321
            list(r1.all_revision_ids())
322
            r2.lock_write()
323
            try:
324
                # access enough data to load the names list
325
                list(r2.all_revision_ids())
326
                r1.start_write_group()
327
                try:
328
                    r2.start_write_group()
329
                    try:
330
                        self._add_text(r1, 'fileidr1')
331
                        self._add_text(r2, 'fileidr2')
332
                    except:
333
                        r2.abort_write_group()
334
                        raise
335
                except:
336
                    r1.abort_write_group()
337
                    raise
338
                # both r1 and r2 have open write groups with data in them
339
                # created while the other's write group was open.
340
                # Commit both which requires a merge to the pack-names.
341
                try:
342
                    r1.commit_write_group()
343
                except:
344
                    r1.abort_write_group()
345
                    r2.abort_write_group()
346
                    raise
347
                r2.commit_write_group()
348
                # tell r1 to reload from disk
349
                r1._pack_collection.reset()
350
                # Now both repositories should know about both names
351
                r1._pack_collection.ensure_loaded()
352
                r2._pack_collection.ensure_loaded()
353
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
354
                self.assertEqual(2, len(r1._pack_collection.names()))
355
            finally:
356
                r2.unlock()
357
        finally:
358
            r1.unlock()
359
360
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
361
        format = self.get_format()
362
        self.make_repository('.', shared=True, format=format)
363
        r1 = repository.Repository.open('.')
364
        r2 = repository.Repository.open('.')
365
        # add a pack to drop
366
        r1.lock_write()
367
        try:
368
            r1.start_write_group()
369
            try:
370
                self._add_text(r1, 'fileidr1')
371
            except:
372
                r1.abort_write_group()
373
                raise
374
            else:
375
                r1.commit_write_group()
376
            r1._pack_collection.ensure_loaded()
377
            name_to_drop = r1._pack_collection.all_packs()[0].name
378
        finally:
379
            r1.unlock()
380
        r1.lock_write()
381
        try:
382
            # access enough data to load the names list
383
            list(r1.all_revision_ids())
384
            r2.lock_write()
385
            try:
386
                # access enough data to load the names list
387
                list(r2.all_revision_ids())
388
                r1._pack_collection.ensure_loaded()
389
                try:
390
                    r2.start_write_group()
391
                    try:
392
                        # in r1, drop the pack
393
                        r1._pack_collection._remove_pack_from_memory(
394
                            r1._pack_collection.get_pack_by_name(name_to_drop))
395
                        # in r2, add a pack
396
                        self._add_text(r2, 'fileidr2')
397
                    except:
398
                        r2.abort_write_group()
399
                        raise
400
                except:
401
                    r1._pack_collection.reset()
402
                    raise
403
                # r1 has a changed names list, and r2 an open write groups with
404
                # changes.
405
                # save r1, and then commit the r2 write group, which requires a
406
                # merge to the pack-names, which should not reinstate
407
                # name_to_drop
408
                try:
409
                    r1._pack_collection._save_pack_names()
410
                    r1._pack_collection.reset()
411
                except:
412
                    r2.abort_write_group()
413
                    raise
414
                try:
415
                    r2.commit_write_group()
416
                except:
417
                    r2.abort_write_group()
418
                    raise
419
                # Now both repositories should now about just one name.
420
                r1._pack_collection.ensure_loaded()
421
                r2._pack_collection.ensure_loaded()
422
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
423
                self.assertEqual(1, len(r1._pack_collection.names()))
424
                self.assertFalse(name_to_drop in r1._pack_collection.names())
425
            finally:
426
                r2.unlock()
427
        finally:
428
            r1.unlock()
429
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
430
    def test_concurrent_pack_triggers_reload(self):
431
        # create 2 packs, which we will then collapse
432
        tree = self.make_branch_and_tree('tree')
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
433
        tree.lock_write()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
434
        try:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
435
            rev1 = tree.commit('one')
436
            rev2 = tree.commit('two')
437
            r2 = repository.Repository.open('tree')
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
438
            r2.lock_read()
439
            try:
440
                # Now r2 has read the pack-names file, but will need to reload
441
                # it after r1 has repacked
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
442
                tree.branch.repository.pack()
443
                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.
444
            finally:
445
                r2.unlock()
446
        finally:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
447
            tree.unlock()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
448
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
449
    def test_concurrent_pack_during_get_record_reloads(self):
450
        tree = self.make_branch_and_tree('tree')
451
        tree.lock_write()
452
        try:
453
            rev1 = tree.commit('one')
454
            rev2 = tree.commit('two')
3789.2.14 by John Arbash Meinel
Update AggregateIndex to pass the reload_func into _DirectPackAccess
455
            keys = [(rev1,), (rev2,)]
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
456
            r2 = repository.Repository.open('tree')
457
            r2.lock_read()
458
            try:
459
                # At this point, we will start grabbing a record stream, and
460
                # trigger a repack mid-way
461
                packed = False
462
                result = {}
463
                record_stream = r2.revisions.get_record_stream(keys,
464
                                    'unordered', False)
465
                for record in record_stream:
466
                    result[record.key] = record
467
                    if not packed:
468
                        tree.branch.repository.pack()
469
                        packed = True
470
                # The first record will be found in the original location, but
471
                # 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
472
                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.
473
            finally:
474
                r2.unlock()
475
        finally:
476
            tree.unlock()
477
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
478
    def test_lock_write_does_not_physically_lock(self):
479
        repo = self.make_repository('.', format=self.get_format())
480
        repo.lock_write()
481
        self.addCleanup(repo.unlock)
482
        self.assertFalse(repo.get_physical_lock_status())
483
484
    def prepare_for_break_lock(self):
485
        # Setup the global ui factory state so that a break-lock method call
486
        # will find usable input in the input stream.
487
        old_factory = ui.ui_factory
488
        def restoreFactory():
489
            ui.ui_factory = old_factory
490
        self.addCleanup(restoreFactory)
491
        ui.ui_factory = ui.SilentUIFactory()
492
        ui.ui_factory.stdin = StringIO("y\n")
493
494
    def test_break_lock_breaks_physical_lock(self):
495
        repo = self.make_repository('.', format=self.get_format())
496
        repo._pack_collection.lock_names()
3650.4.1 by Aaron Bentley
Fix test kipple in test_break_lock_breaks_physical_lock
497
        repo.control_files.leave_in_place()
498
        repo.unlock()
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
499
        repo2 = repository.Repository.open('.')
500
        self.assertTrue(repo.get_physical_lock_status())
501
        self.prepare_for_break_lock()
502
        repo2.break_lock()
503
        self.assertFalse(repo.get_physical_lock_status())
504
505
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
506
        repo = self.make_repository('.', format=self.get_format())
507
        repo._pack_collection.lock_names()
508
        self.assertTrue(repo.get_physical_lock_status())
509
        repo2 = repository.Repository.open('.')
510
        self.prepare_for_break_lock()
511
        repo2.break_lock()
512
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
513
514
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
515
        # we want two repositories at this point:
516
        # one with a revision that is a ghost in the other
517
        # repository.
518
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
519
        # 'references' is present in both repositories, and 'tip' is present
520
        # just in has_ghost.
521
        # has_ghost       missing_ghost
522
        #------------------------------
523
        # 'ghost'             -
524
        # 'references'    'references'
525
        # 'tip'               -
526
        # In this test we fetch 'tip' which should not fetch 'ghost'
527
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
528
        missing_ghost = self.make_repository('missing_ghost',
529
            format=self.get_format())
530
531
        def add_commit(repo, revision_id, parent_ids):
532
            repo.lock_write()
533
            repo.start_write_group()
534
            inv = inventory.Inventory(revision_id=revision_id)
535
            inv.root.revision = revision_id
536
            root_id = inv.root.file_id
537
            sha1 = repo.add_inventory(revision_id, inv, [])
538
            repo.texts.add_lines((root_id, revision_id), [], [])
539
            rev = _mod_revision.Revision(timestamp=0,
540
                                         timezone=None,
541
                                         committer="Foo Bar <foo@example.com>",
542
                                         message="Message",
543
                                         inventory_sha1=sha1,
544
                                         revision_id=revision_id)
545
            rev.parent_ids = parent_ids
546
            repo.add_revision(revision_id, rev)
547
            repo.commit_write_group()
548
            repo.unlock()
549
        add_commit(has_ghost, 'ghost', [])
550
        add_commit(has_ghost, 'references', ['ghost'])
551
        add_commit(missing_ghost, 'references', ['ghost'])
552
        add_commit(has_ghost, 'tip', ['references'])
553
        missing_ghost.fetch(has_ghost, 'tip')
554
        # missing ghost now has tip and not ghost.
555
        rev = missing_ghost.get_revision('tip')
556
        inv = missing_ghost.get_inventory('tip')
557
        self.assertRaises(errors.NoSuchRevision,
558
            missing_ghost.get_revision, 'ghost')
559
        self.assertRaises(errors.NoSuchRevision,
560
            missing_ghost.get_inventory, 'ghost')
561
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.
562
    def make_write_ready_repo(self):
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
563
        format = self.get_format()
564
        if isinstance(format.repository_format, RepositoryFormatCHK1):
565
            raise TestNotApplicable("No missing compression parents")
566
        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.
567
        repo.lock_write()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
568
        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.
569
        repo.start_write_group()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
570
        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.
571
        return repo
572
573
    def test_missing_inventories_compression_parent_prevents_commit(self):
574
        repo = self.make_write_ready_repo()
575
        key = ('junk',)
576
        repo.inventories._index._missing_compression_parents.add(key)
577
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
578
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
579
580
    def test_missing_revisions_compression_parent_prevents_commit(self):
581
        repo = self.make_write_ready_repo()
582
        key = ('junk',)
583
        repo.revisions._index._missing_compression_parents.add(key)
584
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
585
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
586
587
    def test_missing_signatures_compression_parent_prevents_commit(self):
588
        repo = self.make_write_ready_repo()
589
        key = ('junk',)
590
        repo.signatures._index._missing_compression_parents.add(key)
591
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
592
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
593
594
    def test_missing_text_compression_parent_prevents_commit(self):
595
        repo = self.make_write_ready_repo()
596
        key = ('some', 'junk')
597
        repo.texts._index._missing_compression_parents.add(key)
598
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
599
        e = self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
600
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
601
    def test_supports_external_lookups(self):
602
        repo = self.make_repository('.', format=self.get_format())
603
        self.assertEqual(self.format_supports_external_lookups,
604
            repo._format.supports_external_lookups)
605
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
606
    def test_abort_write_group_does_not_raise_when_suppressed(self):
607
        """Similar to per_repository.test_write_group's test of the same name.
608
609
        Also requires that the exception is logged.
610
        """
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.
611
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
612
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
613
        token = repo.lock_write()
614
        self.addCleanup(repo.unlock)
615
        repo.start_write_group()
616
        # Damage the repository on the filesystem
617
        self.get_transport('').rename('repo', 'foo')
618
        # abort_write_group will not raise an error
619
        self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
620
        # But it does log an error
621
        log_file = self._get_log(keep_log_file=True)
622
        self.assertContainsRe(log_file, 'abort_write_group failed')
623
        self.assertContainsRe(log_file, r'INFO  bzr: ERROR \(ignored\):')
624
        if token is not None:
625
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
626
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
627
    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.
628
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
629
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
630
        token = repo.lock_write()
631
        self.addCleanup(repo.unlock)
632
        repo.start_write_group()
633
        # Damage the repository on the filesystem
634
        self.get_transport('').rename('repo', 'foo')
635
        # abort_write_group will not raise an error
636
        self.assertRaises(Exception, repo.abort_write_group)
637
        if token is not None:
638
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
639
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
640
    def test_suspend_write_group(self):
641
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
642
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
643
        token = repo.lock_write()
644
        self.addCleanup(repo.unlock)
645
        repo.start_write_group()
646
        repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
647
        wg_tokens = repo.suspend_write_group()
648
        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.
649
        expected_names = [wg_tokens[0] + ext for ext in
650
                            ('.rix', '.iix', '.tix', '.six')]
651
        if repo.chk_bytes is not None:
652
            expected_names.append(wg_tokens[0] + '.cix')
653
        expected_names.append(expected_pack_name)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
654
        upload_transport = repo._pack_collection._upload_transport
655
        limbo_files = upload_transport.list_dir('')
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
656
        self.assertEqual(sorted(expected_names), sorted(limbo_files))
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
657
        md5 = osutils.md5(upload_transport.get_bytes(expected_pack_name))
658
        self.assertEqual(wg_tokens[0], md5.hexdigest())
659
4343.3.8 by John Arbash Meinel
Some cleanup passes.
660
    def test_resume_chk_bytes(self):
661
        self.vfs_transport_factory = memory.MemoryServer
662
        repo = self.make_repository('repo', format=self.get_format())
663
        if repo.chk_bytes is None:
664
            raise TestNotApplicable('no chk_bytes for this repository')
665
        token = repo.lock_write()
666
        self.addCleanup(repo.unlock)
667
        repo.start_write_group()
668
        text = 'a bit of text\n'
669
        key = ('sha1:' + osutils.sha_string(text),)
670
        repo.chk_bytes.add_lines(key, (), [text])
671
        wg_tokens = repo.suspend_write_group()
672
        same_repo = repo.bzrdir.open_repository()
673
        same_repo.lock_write()
674
        self.addCleanup(same_repo.unlock)
675
        same_repo.resume_write_group(wg_tokens)
676
        self.assertEqual([key], list(same_repo.chk_bytes.keys()))
677
        self.assertEqual(
678
            text, same_repo.chk_bytes.get_record_stream([key],
679
                'unordered', True).next().get_bytes_as('fulltext'))
680
        same_repo.abort_write_group()
681
        self.assertEqual([], list(same_repo.chk_bytes.keys()))
682
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
683
    def test_resume_write_group_then_abort(self):
684
        # Create a repo, start a write group, insert some data, suspend.
685
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
686
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
687
        token = repo.lock_write()
688
        self.addCleanup(repo.unlock)
689
        repo.start_write_group()
690
        text_key = ('file-id', 'revid')
691
        repo.texts.add_lines(text_key, (), ['lines'])
692
        wg_tokens = repo.suspend_write_group()
693
        # Get a fresh repository object for the repo on the filesystem.
694
        same_repo = repo.bzrdir.open_repository()
695
        # Resume
696
        same_repo.lock_write()
697
        self.addCleanup(same_repo.unlock)
698
        same_repo.resume_write_group(wg_tokens)
699
        same_repo.abort_write_group()
700
        self.assertEqual(
701
            [], same_repo._pack_collection._upload_transport.list_dir(''))
702
        self.assertEqual(
703
            [], same_repo._pack_collection._pack_transport.list_dir(''))
704
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
705
    def test_commit_resumed_write_group(self):
706
        self.vfs_transport_factory = memory.MemoryServer
707
        repo = self.make_repository('repo', format=self.get_format())
708
        token = repo.lock_write()
709
        self.addCleanup(repo.unlock)
710
        repo.start_write_group()
711
        text_key = ('file-id', 'revid')
712
        repo.texts.add_lines(text_key, (), ['lines'])
713
        wg_tokens = repo.suspend_write_group()
714
        # Get a fresh repository object for the repo on the filesystem.
715
        same_repo = repo.bzrdir.open_repository()
716
        # Resume
717
        same_repo.lock_write()
718
        self.addCleanup(same_repo.unlock)
719
        same_repo.resume_write_group(wg_tokens)
720
        same_repo.commit_write_group()
721
        expected_pack_name = wg_tokens[0] + '.pack'
722
        expected_names = [wg_tokens[0] + ext for ext in
723
                            ('.rix', '.iix', '.tix', '.six')]
724
        if repo.chk_bytes is not None:
725
            expected_names.append(wg_tokens[0] + '.cix')
726
        self.assertEqual(
727
            [], same_repo._pack_collection._upload_transport.list_dir(''))
728
        index_names = repo._pack_collection._index_transport.list_dir('')
729
        self.assertEqual(sorted(expected_names), sorted(index_names))
730
        pack_names = repo._pack_collection._pack_transport.list_dir('')
731
        self.assertEqual([expected_pack_name], pack_names)
732
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.
733
    def test_resume_malformed_token(self):
734
        self.vfs_transport_factory = memory.MemoryServer
735
        # 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.
736
        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.
737
        token = repo.lock_write()
738
        self.addCleanup(repo.unlock)
739
        repo.start_write_group()
740
        text_key = ('file-id', 'revid')
741
        repo.texts.add_lines(text_key, (), ['lines'])
742
        wg_tokens = repo.suspend_write_group()
743
        # Make a new repository
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
744
        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.
745
        token = new_repo.lock_write()
746
        self.addCleanup(new_repo.unlock)
747
        hacked_wg_token = (
748
            '../../../../repo/.bzr/repository/upload/' + wg_tokens[0])
749
        self.assertRaises(
4002.1.7 by Andrew Bennetts
Rename UnresumableWriteGroups to UnresumableWriteGroup.
750
            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.
751
            new_repo.resume_write_group, [hacked_wg_token])
752
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
753
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
754
class TestPackRepositoryStacking(TestCaseWithTransport):
755
756
    """Tests for stacking pack repositories"""
757
758
    def setUp(self):
759
        if not self.format_supports_external_lookups:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
760
            raise TestNotApplicable("%r doesn't support stacking"
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
761
                % (self.format_name,))
762
        super(TestPackRepositoryStacking, self).setUp()
763
764
    def get_format(self):
765
        return bzrdir.format_registry.make_bzrdir(self.format_name)
766
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
767
    def test_stack_checks_rich_root_compatibility(self):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
768
        # early versions of the packing code relied on pack internals to
769
        # stack, but the current version should be able to stack on any
770
        # format.
771
        #
772
        # TODO: Possibly this should be run per-repository-format and raise
773
        # TestNotApplicable on formats that don't support stacking. -- mbp
774
        # 20080729
775
        repo = self.make_repository('repo', format=self.get_format())
776
        if repo.supports_rich_root():
777
            # can only stack on repositories that have compatible internal
778
            # metadata
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
779
            if getattr(repo._format, 'supports_tree_reference', False):
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
780
                matching_format_name = 'pack-0.92-subtree'
781
            else:
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
782
                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)
783
                    matching_format_name = 'development6-rich-root'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
784
                else:
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
785
                    matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
786
            mismatching_format_name = 'pack-0.92'
787
        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)
788
            # 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.
789
            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)
790
                raise AssertionError("no non-rich-root CHK formats known")
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
791
            else:
792
                matching_format_name = 'pack-0.92'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
793
            mismatching_format_name = 'pack-0.92-subtree'
794
        base = self.make_repository('base', format=matching_format_name)
795
        repo.add_fallback_repository(base)
796
        # you can't stack on something with incompatible data
797
        bad_repo = self.make_repository('mismatch',
798
            format=mismatching_format_name)
799
        e = self.assertRaises(errors.IncompatibleRepositories,
800
            repo.add_fallback_repository, bad_repo)
801
        self.assertContainsRe(str(e),
802
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
803
            r'.*Repository.*/repo/.*\n'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
804
            r'different rich-root support')
805
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
806
    def test_stack_checks_serializers_compatibility(self):
807
        repo = self.make_repository('repo', format=self.get_format())
808
        if getattr(repo._format, 'supports_tree_reference', False):
809
            # can only stack on repositories that have compatible internal
810
            # metadata
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
811
            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.
812
            mismatching_format_name = 'rich-root-pack'
813
        else:
814
            if repo.supports_rich_root():
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
815
                if repo._format.supports_chks:
816
                    matching_format_name = 'development6-rich-root'
817
                else:
818
                    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.
819
                mismatching_format_name = 'pack-0.92-subtree'
820
            else:
821
                raise TestNotApplicable('No formats use non-v5 serializer'
822
                    ' without having rich-root also set')
823
        base = self.make_repository('base', format=matching_format_name)
824
        repo.add_fallback_repository(base)
825
        # you can't stack on something with incompatible data
826
        bad_repo = self.make_repository('mismatch',
827
            format=mismatching_format_name)
828
        e = self.assertRaises(errors.IncompatibleRepositories,
829
            repo.add_fallback_repository, bad_repo)
830
        self.assertContainsRe(str(e),
831
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
832
            r'.*Repository.*/repo/.*\n'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
833
            r'different serializers')
834
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
835
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
836
        base = self.make_branch_and_tree('base', format=self.get_format())
837
        base.commit('foo')
838
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
839
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
840
        referencing.commit('bar')
841
        new_instance = referencing.bzrdir.open_repository()
842
        new_instance.lock_read()
843
        self.addCleanup(new_instance.unlock)
844
        new_instance._pack_collection.ensure_loaded()
845
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
846
847
    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)
848
        format = self.get_format()
849
        base = self.make_branch_and_tree('base', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
850
        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)
851
        tree = self.make_branch_and_tree('repo', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
852
        tree.branch.repository.add_fallback_repository(base.branch.repository)
853
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
854
        # This test could be a little cheaper by replacing the packs
855
        # attribute on the repository to allow a different pack distribution
856
        # and max packs policy - so we are checking the policy is honoured
857
        # in the test. But for now 11 commits is not a big deal in a single
858
        # test.
859
        for x in range(9):
860
            tree.commit('commit %s' % x)
861
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
862
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
863
        self.assertEqual(9, len(list(index.iter_all_entries())))
864
        # committing one more should coalesce to 1 of 10.
865
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
866
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
867
        self.assertEqual(1, len(list(index.iter_all_entries())))
868
        # packing should not damage data
869
        tree = tree.bzrdir.open_workingtree()
870
        check_result = tree.branch.repository.check(
871
            [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)
872
        nb_files = 5 # .pack, .rix, .iix, .tix, .six
873
        if tree.branch.repository._format.supports_chks:
874
            nb_files += 1 # .cix
875
        # 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
876
        obsolete_files = list(trans.list_dir('obsolete_packs'))
877
        self.assertFalse('foo' in obsolete_files)
878
        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)
879
        self.assertEqual(10 * nb_files, len(obsolete_files))
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
880
        # XXX: Todo check packs obsoleted correctly - old packs and indices
881
        # in the obsolete_packs directory.
882
        large_pack_name = list(index.iter_all_entries())[0][1][0]
883
        # finally, committing again should not touch the large pack.
884
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
885
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
886
        self.assertEqual(2, len(list(index.iter_all_entries())))
887
        pack_names = [node[1][0] for node in index.iter_all_entries()]
888
        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
889
890
4343.3.33 by John Arbash Meinel
Clear KeyDependencies on abort/suspend/commit_write_group.
891
class TestKeyDependencies(TestCaseWithTransport):
892
893
    def get_format(self):
894
        return bzrdir.format_registry.make_bzrdir(self.format_name)
895
896
    def create_source_and_target(self):
897
        builder = self.make_branch_builder('source', format=self.get_format())
898
        builder.start_series()
899
        builder.build_snapshot('A-id', None, [
900
            ('add', ('', 'root-id', 'directory', None))])
901
        builder.build_snapshot('B-id', ['A-id', 'ghost-id'], [])
902
        builder.finish_series()
903
        repo = self.make_repository('target')
904
        b = builder.get_branch()
905
        b.lock_read()
906
        self.addCleanup(b.unlock)
907
        repo.lock_write()
908
        self.addCleanup(repo.unlock)
909
        return b.repository, repo
910
911
    def test_key_dependencies_cleared_on_abort(self):
912
        source_repo, target_repo = self.create_source_and_target()
913
        target_repo.start_write_group()
914
        try:
915
            stream = source_repo.revisions.get_record_stream([('B-id',)],
916
                                                             'unordered', True)
917
            target_repo.revisions.insert_record_stream(stream)
918
            key_refs = target_repo.revisions._index._key_dependencies
919
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
920
        finally:
921
            target_repo.abort_write_group()
922
        self.assertEqual([], sorted(key_refs.get_referrers()))
923
924
    def test_key_dependencies_cleared_on_suspend(self):
925
        source_repo, target_repo = self.create_source_and_target()
926
        target_repo.start_write_group()
927
        try:
928
            stream = source_repo.revisions.get_record_stream([('B-id',)],
929
                                                             'unordered', True)
930
            target_repo.revisions.insert_record_stream(stream)
931
            key_refs = target_repo.revisions._index._key_dependencies
932
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
933
        finally:
934
            target_repo.suspend_write_group()
935
        self.assertEqual([], sorted(key_refs.get_referrers()))
936
937
    def test_key_dependencies_cleared_on_commit(self):
938
        source_repo, target_repo = self.create_source_and_target()
939
        target_repo.start_write_group()
940
        try:
941
            stream = source_repo.revisions.get_record_stream([('B-id',)],
942
                                                             'unordered', True)
943
            target_repo.revisions.insert_record_stream(stream)
944
            key_refs = target_repo.revisions._index._key_dependencies
945
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
946
        finally:
947
            target_repo.commit_write_group()
948
        self.assertEqual([], sorted(key_refs.get_referrers()))
949
950
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
951
class TestSmartServerAutopack(TestCaseWithTransport):
952
953
    def setUp(self):
954
        super(TestSmartServerAutopack, self).setUp()
955
        # Create a smart server that publishes whatever the backing VFS server
956
        # does.
957
        self.smart_server = server.SmartTCPServer_for_testing()
958
        self.smart_server.setUp(self.get_server())
959
        self.addCleanup(self.smart_server.tearDown)
960
        # Log all HPSS calls into self.hpss_calls.
961
        client._SmartClient.hooks.install_named_hook(
962
            'call', self.capture_hpss_call, None)
963
        self.hpss_calls = []
964
965
    def capture_hpss_call(self, params):
966
        self.hpss_calls.append(params.method)
967
968
    def get_format(self):
969
        return bzrdir.format_registry.make_bzrdir(self.format_name)
970
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
971
    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.
972
        # Make local and remote repos
3735.2.98 by John Arbash Meinel
Merge bzr.dev 4032. Resolve the new streaming fetch.
973
        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)
974
        tree = self.make_branch_and_tree('local', format=format)
975
        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.
976
        remote_branch_url = self.smart_server.get_url() + 'remote'
977
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
978
        # Make 9 local revisions, and push them one at a time to the remote
979
        # repo to produce 9 pack files.
980
        for x in range(9):
981
            tree.commit('commit %s' % x)
982
            tree.branch.push(remote_branch)
983
        # Make one more push to trigger an autopack
984
        self.hpss_calls = []
985
        tree.commit('commit triggering pack')
986
        tree.branch.push(remote_branch)
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
987
        autopack_calls = len([call for call in self.hpss_calls if call ==
988
            'PackRepository.autopack'])
989
        streaming_calls = len([call for call in self.hpss_calls if call ==
990
            'Repository.insert_stream'])
991
        if autopack_calls:
992
            # Non streaming server
993
            self.assertEqual(1, autopack_calls)
994
            self.assertEqual(0, streaming_calls)
995
        else:
996
            # Streaming was used, which autopacks on the remote end.
997
            self.assertEqual(0, autopack_calls)
998
            # NB: The 2 calls are because of the sanity check that the server
999
            # supports the verb (see remote.py:RemoteSink.insert_stream for
1000
            # details).
1001
            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.
1002
1003
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
1004
def load_tests(basic_tests, module, loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
1005
    # these give the bzrdir canned format name, and the repository on-disk
1006
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1007
    scenarios_params = [
1008
         dict(format_name='pack-0.92',
1009
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1010
              format_supports_external_lookups=False,
1011
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1012
         dict(format_name='pack-0.92-subtree',
1013
              format_string="Bazaar pack repository format 1 "
1014
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1015
              format_supports_external_lookups=False,
1016
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1017
         dict(format_name='1.6',
1018
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1019
              format_supports_external_lookups=True,
1020
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1021
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1022
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1023
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1024
              format_supports_external_lookups=True,
1025
              index_class=GraphIndex),
3805.3.1 by John Arbash Meinel
Add repository 1.9 format, and update the documentation.
1026
         dict(format_name='1.9',
1027
              format_string="Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n",
1028
              format_supports_external_lookups=True,
1029
              index_class=BTreeGraphIndex),
1030
         dict(format_name='1.9-rich-root',
1031
              format_string="Bazaar RepositoryFormatKnitPack6RichRoot "
1032
                  "(bzr 1.9)\n",
1033
              format_supports_external_lookups=True,
1034
              index_class=BTreeGraphIndex),
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)
1035
         dict(format_name='development6-rich-root',
1036
              format_string='Bazaar development format - group compression '
1037
                  'and chk inventory (needs bzr.dev from 1.14)\n',
4343.3.8 by John Arbash Meinel
Some cleanup passes.
1038
              format_supports_external_lookups=True,
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1039
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1040
         ]
1041
    # 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.
1042
    scenarios = [(s['format_name'], s) for s in scenarios_params]
1043
    return tests.multiply_tests(basic_tests, scenarios, loader.suiteClass())