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