/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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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,
31
    progress,
32
    repository,
33
    revision as _mod_revision,
34
    symbol_versioning,
35
    tests,
36
    ui,
37
    upgrade,
38
    workingtree,
39
    )
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
40
from bzrlib.smart import (
41
    client,
42
    server,
43
    )
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
44
from bzrlib.tests import (
45
    TestCase,
46
    TestCaseWithTransport,
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
47
    TestNotApplicable,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
48
    TestSkipped,
49
    )
50
from bzrlib.transport import (
51
    fakenfs,
52
    get_transport,
53
    )
54
55
56
class TestPackRepository(TestCaseWithTransport):
57
    """Tests to be repeated across all pack-based formats.
58
59
    The following are populated from the test scenario:
60
61
    :ivar format_name: Registered name fo the format to test.
62
    :ivar format_string: On-disk format marker.
63
    :ivar format_supports_external_lookups: Boolean.
64
    """
65
66
    def get_format(self):
67
        return bzrdir.format_registry.make_bzrdir(self.format_name)
68
69
    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.
70
        """Packs do not need ordered data retrieval."""
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
71
        format = self.get_format()
72
        repo = self.make_repository('.', format=format)
3606.7.8 by John Arbash Meinel
Switch names to 'unordered' that I missed before.
73
        self.assertEqual('unordered', repo._fetch_order)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
74
75
    def test_attribute__fetch_uses_deltas(self):
76
        """Packs reuse deltas."""
77
        format = self.get_format()
78
        repo = self.make_repository('.', format=format)
79
        self.assertEqual(True, repo._fetch_uses_deltas)
80
81
    def test_disk_layout(self):
82
        format = self.get_format()
83
        repo = self.make_repository('.', format=format)
84
        # in case of side effects of locking.
85
        repo.lock_write()
86
        repo.unlock()
87
        t = repo.bzrdir.get_repository_transport(None)
88
        self.check_format(t)
89
        # XXX: no locks left when unlocked at the moment
90
        # self.assertEqualDiff('', t.get('lock').read())
91
        self.check_databases(t)
92
93
    def check_format(self, t):
94
        self.assertEqualDiff(
95
            self.format_string, # from scenario
96
            t.get('format').read())
97
98
    def assertHasNoKndx(self, t, knit_name):
99
        """Assert that knit_name has no index on t."""
100
        self.assertFalse(t.has(knit_name + '.kndx'))
101
102
    def assertHasNoKnit(self, t, knit_name):
103
        """Assert that knit_name exists on t."""
104
        # no default content
105
        self.assertFalse(t.has(knit_name + '.knit'))
106
107
    def check_databases(self, t):
108
        """check knit content for a repository."""
109
        # check conversion worked
110
        self.assertHasNoKndx(t, 'inventory')
111
        self.assertHasNoKnit(t, 'inventory')
112
        self.assertHasNoKndx(t, 'revisions')
113
        self.assertHasNoKnit(t, 'revisions')
114
        self.assertHasNoKndx(t, 'signatures')
115
        self.assertHasNoKnit(t, 'signatures')
116
        self.assertFalse(t.has('knits'))
117
        # revision-indexes file-container directory
118
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
119
            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
120
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
121
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
122
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
123
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
124
125
    def test_shared_disk_layout(self):
126
        format = self.get_format()
127
        repo = self.make_repository('.', shared=True, format=format)
128
        # we want:
129
        t = repo.bzrdir.get_repository_transport(None)
130
        self.check_format(t)
131
        # XXX: no locks left when unlocked at the moment
132
        # self.assertEqualDiff('', t.get('lock').read())
133
        # We should have a 'shared-storage' marker file.
134
        self.assertEqualDiff('', t.get('shared-storage').read())
135
        self.check_databases(t)
136
137
    def test_shared_no_tree_disk_layout(self):
138
        format = self.get_format()
139
        repo = self.make_repository('.', shared=True, format=format)
140
        repo.set_make_working_trees(False)
141
        # we want:
142
        t = repo.bzrdir.get_repository_transport(None)
143
        self.check_format(t)
144
        # XXX: no locks left when unlocked at the moment
145
        # self.assertEqualDiff('', t.get('lock').read())
146
        # We should have a 'shared-storage' marker file.
147
        self.assertEqualDiff('', t.get('shared-storage').read())
148
        # We should have a marker for the no-working-trees flag.
149
        self.assertEqualDiff('', t.get('no-working-trees').read())
150
        # The marker should go when we toggle the setting.
151
        repo.set_make_working_trees(True)
152
        self.assertFalse(t.has('no-working-trees'))
153
        self.check_databases(t)
154
155
    def test_adding_revision_creates_pack_indices(self):
156
        format = self.get_format()
157
        tree = self.make_branch_and_tree('.', format=format)
158
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
159
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
160
            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
161
        tree.commit('foobarbaz')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
162
        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
163
        index_nodes = list(index.iter_all_entries())
164
        self.assertEqual(1, len(index_nodes))
165
        node = index_nodes[0]
166
        name = node[1][0]
167
        # the pack sizes should be listed in the index
168
        pack_value = node[2]
169
        sizes = [int(digits) for digits in pack_value.split(' ')]
170
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
171
            stat = trans.stat('indices/%s%s' % (name, suffix))
172
            self.assertEqual(size, stat.st_size)
173
174
    def test_pulling_nothing_leads_to_no_new_names(self):
175
        format = self.get_format()
176
        tree1 = self.make_branch_and_tree('1', format=format)
177
        tree2 = self.make_branch_and_tree('2', format=format)
178
        tree1.branch.repository.fetch(tree2.branch.repository)
179
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
180
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
181
            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
182
183
    def test_commit_across_pack_shape_boundary_autopacks(self):
184
        format = self.get_format()
185
        tree = self.make_branch_and_tree('.', format=format)
186
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
187
        # This test could be a little cheaper by replacing the packs
188
        # attribute on the repository to allow a different pack distribution
189
        # and max packs policy - so we are checking the policy is honoured
190
        # in the test. But for now 11 commits is not a big deal in a single
191
        # test.
192
        for x in range(9):
193
            tree.commit('commit %s' % x)
194
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
195
        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
196
        self.assertEqual(9, len(list(index.iter_all_entries())))
197
        # insert some files in obsolete_packs which should be removed by pack.
198
        trans.put_bytes('obsolete_packs/foo', '123')
199
        trans.put_bytes('obsolete_packs/bar', '321')
200
        # committing one more should coalesce to 1 of 10.
201
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
202
        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
203
        self.assertEqual(1, len(list(index.iter_all_entries())))
204
        # packing should not damage data
205
        tree = tree.bzrdir.open_workingtree()
206
        check_result = tree.branch.repository.check(
207
            [tree.branch.last_revision()])
208
        # We should have 50 (10x5) files in the obsolete_packs directory.
209
        obsolete_files = list(trans.list_dir('obsolete_packs'))
210
        self.assertFalse('foo' in obsolete_files)
211
        self.assertFalse('bar' in obsolete_files)
212
        self.assertEqual(50, len(obsolete_files))
213
        # XXX: Todo check packs obsoleted correctly - old packs and indices
214
        # in the obsolete_packs directory.
215
        large_pack_name = list(index.iter_all_entries())[0][1][0]
216
        # finally, committing again should not touch the large pack.
217
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
218
        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
219
        self.assertEqual(2, len(list(index.iter_all_entries())))
220
        pack_names = [node[1][0] for node in index.iter_all_entries()]
221
        self.assertTrue(large_pack_name in pack_names)
222
223
    def test_fail_obsolete_deletion(self):
224
        # failing to delete obsolete packs is not fatal
225
        format = self.get_format()
226
        server = fakenfs.FakeNFSServer()
227
        server.setUp()
228
        self.addCleanup(server.tearDown)
229
        transport = get_transport(server.get_url())
230
        bzrdir = self.get_format().initialize_on_transport(transport)
231
        repo = bzrdir.create_repository()
232
        repo_transport = bzrdir.get_repository_transport(None)
233
        self.assertTrue(repo_transport.has('obsolete_packs'))
234
        # these files are in use by another client and typically can't be deleted
235
        repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
236
        repo._pack_collection._clear_obsolete_packs()
237
        self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
238
239
    def test_pack_after_two_commits_packs_everything(self):
240
        format = self.get_format()
241
        tree = self.make_branch_and_tree('.', format=format)
242
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
243
        tree.commit('start')
244
        tree.commit('more work')
245
        tree.branch.repository.pack()
246
        # there should be 1 pack:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
247
        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
248
        self.assertEqual(1, len(list(index.iter_all_entries())))
249
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
250
251
    def test_pack_layout(self):
252
        format = self.get_format()
253
        tree = self.make_branch_and_tree('.', format=format)
254
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
255
        tree.commit('start', rev_id='1')
256
        tree.commit('more work', rev_id='2')
257
        tree.branch.repository.pack()
258
        tree.lock_read()
259
        self.addCleanup(tree.unlock)
260
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
261
            tree.branch.repository._pack_collection.names()[0])
262
        # revision access tends to be tip->ancestor, so ordering that way on 
263
        # disk is a good idea.
264
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
265
            if key == ('1',):
266
                pos_1 = int(val[1:].split()[0])
267
            else:
268
                pos_2 = int(val[1:].split()[0])
269
        self.assertTrue(pos_2 < pos_1)
270
271
    def test_pack_repositories_support_multiple_write_locks(self):
272
        format = self.get_format()
273
        self.make_repository('.', shared=True, format=format)
274
        r1 = repository.Repository.open('.')
275
        r2 = repository.Repository.open('.')
276
        r1.lock_write()
277
        self.addCleanup(r1.unlock)
278
        r2.lock_write()
279
        r2.unlock()
280
281
    def _add_text(self, repo, fileid):
282
        """Add a text to the repository within a write group."""
283
        repo.texts.add_lines((fileid, 'samplerev+'+fileid), [], [])
284
285
    def test_concurrent_writers_merge_new_packs(self):
286
        format = self.get_format()
287
        self.make_repository('.', shared=True, format=format)
288
        r1 = repository.Repository.open('.')
289
        r2 = repository.Repository.open('.')
290
        r1.lock_write()
291
        try:
292
            # access enough data to load the names list
293
            list(r1.all_revision_ids())
294
            r2.lock_write()
295
            try:
296
                # access enough data to load the names list
297
                list(r2.all_revision_ids())
298
                r1.start_write_group()
299
                try:
300
                    r2.start_write_group()
301
                    try:
302
                        self._add_text(r1, 'fileidr1')
303
                        self._add_text(r2, 'fileidr2')
304
                    except:
305
                        r2.abort_write_group()
306
                        raise
307
                except:
308
                    r1.abort_write_group()
309
                    raise
310
                # both r1 and r2 have open write groups with data in them
311
                # created while the other's write group was open.
312
                # Commit both which requires a merge to the pack-names.
313
                try:
314
                    r1.commit_write_group()
315
                except:
316
                    r1.abort_write_group()
317
                    r2.abort_write_group()
318
                    raise
319
                r2.commit_write_group()
320
                # tell r1 to reload from disk
321
                r1._pack_collection.reset()
322
                # Now both repositories should know about both names
323
                r1._pack_collection.ensure_loaded()
324
                r2._pack_collection.ensure_loaded()
325
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
326
                self.assertEqual(2, len(r1._pack_collection.names()))
327
            finally:
328
                r2.unlock()
329
        finally:
330
            r1.unlock()
331
332
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
333
        format = self.get_format()
334
        self.make_repository('.', shared=True, format=format)
335
        r1 = repository.Repository.open('.')
336
        r2 = repository.Repository.open('.')
337
        # add a pack to drop
338
        r1.lock_write()
339
        try:
340
            r1.start_write_group()
341
            try:
342
                self._add_text(r1, 'fileidr1')
343
            except:
344
                r1.abort_write_group()
345
                raise
346
            else:
347
                r1.commit_write_group()
348
            r1._pack_collection.ensure_loaded()
349
            name_to_drop = r1._pack_collection.all_packs()[0].name
350
        finally:
351
            r1.unlock()
352
        r1.lock_write()
353
        try:
354
            # access enough data to load the names list
355
            list(r1.all_revision_ids())
356
            r2.lock_write()
357
            try:
358
                # access enough data to load the names list
359
                list(r2.all_revision_ids())
360
                r1._pack_collection.ensure_loaded()
361
                try:
362
                    r2.start_write_group()
363
                    try:
364
                        # in r1, drop the pack
365
                        r1._pack_collection._remove_pack_from_memory(
366
                            r1._pack_collection.get_pack_by_name(name_to_drop))
367
                        # in r2, add a pack
368
                        self._add_text(r2, 'fileidr2')
369
                    except:
370
                        r2.abort_write_group()
371
                        raise
372
                except:
373
                    r1._pack_collection.reset()
374
                    raise
375
                # r1 has a changed names list, and r2 an open write groups with
376
                # changes.
377
                # save r1, and then commit the r2 write group, which requires a
378
                # merge to the pack-names, which should not reinstate
379
                # name_to_drop
380
                try:
381
                    r1._pack_collection._save_pack_names()
382
                    r1._pack_collection.reset()
383
                except:
384
                    r2.abort_write_group()
385
                    raise
386
                try:
387
                    r2.commit_write_group()
388
                except:
389
                    r2.abort_write_group()
390
                    raise
391
                # Now both repositories should now about just one name.
392
                r1._pack_collection.ensure_loaded()
393
                r2._pack_collection.ensure_loaded()
394
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
395
                self.assertEqual(1, len(r1._pack_collection.names()))
396
                self.assertFalse(name_to_drop in r1._pack_collection.names())
397
            finally:
398
                r2.unlock()
399
        finally:
400
            r1.unlock()
401
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
402
    def test_concurrent_pack_triggers_reload(self):
403
        # create 2 packs, which we will then collapse
404
        tree = self.make_branch_and_tree('tree')
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
405
        tree.lock_write()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
406
        try:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
407
            rev1 = tree.commit('one')
408
            rev2 = tree.commit('two')
409
            r2 = repository.Repository.open('tree')
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
410
            r2.lock_read()
411
            try:
412
                # Now r2 has read the pack-names file, but will need to reload
413
                # it after r1 has repacked
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
414
                tree.branch.repository.pack()
415
                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.
416
            finally:
417
                r2.unlock()
418
        finally:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
419
            tree.unlock()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
420
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
421
    def test_lock_write_does_not_physically_lock(self):
422
        repo = self.make_repository('.', format=self.get_format())
423
        repo.lock_write()
424
        self.addCleanup(repo.unlock)
425
        self.assertFalse(repo.get_physical_lock_status())
426
427
    def prepare_for_break_lock(self):
428
        # Setup the global ui factory state so that a break-lock method call
429
        # will find usable input in the input stream.
430
        old_factory = ui.ui_factory
431
        def restoreFactory():
432
            ui.ui_factory = old_factory
433
        self.addCleanup(restoreFactory)
434
        ui.ui_factory = ui.SilentUIFactory()
435
        ui.ui_factory.stdin = StringIO("y\n")
436
437
    def test_break_lock_breaks_physical_lock(self):
438
        repo = self.make_repository('.', format=self.get_format())
439
        repo._pack_collection.lock_names()
3650.4.1 by Aaron Bentley
Fix test kipple in test_break_lock_breaks_physical_lock
440
        repo.control_files.leave_in_place()
441
        repo.unlock()
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
442
        repo2 = repository.Repository.open('.')
443
        self.assertTrue(repo.get_physical_lock_status())
444
        self.prepare_for_break_lock()
445
        repo2.break_lock()
446
        self.assertFalse(repo.get_physical_lock_status())
447
448
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
449
        repo = self.make_repository('.', format=self.get_format())
450
        repo._pack_collection.lock_names()
451
        self.assertTrue(repo.get_physical_lock_status())
452
        repo2 = repository.Repository.open('.')
453
        self.prepare_for_break_lock()
454
        repo2.break_lock()
455
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
456
457
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
458
        # we want two repositories at this point:
459
        # one with a revision that is a ghost in the other
460
        # repository.
461
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
462
        # 'references' is present in both repositories, and 'tip' is present
463
        # just in has_ghost.
464
        # has_ghost       missing_ghost
465
        #------------------------------
466
        # 'ghost'             -
467
        # 'references'    'references'
468
        # 'tip'               -
469
        # In this test we fetch 'tip' which should not fetch 'ghost'
470
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
471
        missing_ghost = self.make_repository('missing_ghost',
472
            format=self.get_format())
473
474
        def add_commit(repo, revision_id, parent_ids):
475
            repo.lock_write()
476
            repo.start_write_group()
477
            inv = inventory.Inventory(revision_id=revision_id)
478
            inv.root.revision = revision_id
479
            root_id = inv.root.file_id
480
            sha1 = repo.add_inventory(revision_id, inv, [])
481
            repo.texts.add_lines((root_id, revision_id), [], [])
482
            rev = _mod_revision.Revision(timestamp=0,
483
                                         timezone=None,
484
                                         committer="Foo Bar <foo@example.com>",
485
                                         message="Message",
486
                                         inventory_sha1=sha1,
487
                                         revision_id=revision_id)
488
            rev.parent_ids = parent_ids
489
            repo.add_revision(revision_id, rev)
490
            repo.commit_write_group()
491
            repo.unlock()
492
        add_commit(has_ghost, 'ghost', [])
493
        add_commit(has_ghost, 'references', ['ghost'])
494
        add_commit(missing_ghost, 'references', ['ghost'])
495
        add_commit(has_ghost, 'tip', ['references'])
496
        missing_ghost.fetch(has_ghost, 'tip')
497
        # missing ghost now has tip and not ghost.
498
        rev = missing_ghost.get_revision('tip')
499
        inv = missing_ghost.get_inventory('tip')
500
        self.assertRaises(errors.NoSuchRevision,
501
            missing_ghost.get_revision, 'ghost')
502
        self.assertRaises(errors.NoSuchRevision,
503
            missing_ghost.get_inventory, 'ghost')
504
505
    def test_supports_external_lookups(self):
506
        repo = self.make_repository('.', format=self.get_format())
507
        self.assertEqual(self.format_supports_external_lookups,
508
            repo._format.supports_external_lookups)
509
510
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
511
class TestPackRepositoryStacking(TestCaseWithTransport):
512
513
    """Tests for stacking pack repositories"""
514
515
    def setUp(self):
516
        if not self.format_supports_external_lookups:
517
            raise TestNotApplicable("%r doesn't support stacking" 
518
                % (self.format_name,))
519
        super(TestPackRepositoryStacking, self).setUp()
520
521
    def get_format(self):
522
        return bzrdir.format_registry.make_bzrdir(self.format_name)
523
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
524
    def test_stack_checks_rich_root_compatibility(self):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
525
        # early versions of the packing code relied on pack internals to
526
        # stack, but the current version should be able to stack on any
527
        # format.
528
        #
529
        # TODO: Possibly this should be run per-repository-format and raise
530
        # TestNotApplicable on formats that don't support stacking. -- mbp
531
        # 20080729
532
        repo = self.make_repository('repo', format=self.get_format())
533
        if repo.supports_rich_root():
534
            # can only stack on repositories that have compatible internal
535
            # metadata
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
536
            if getattr(repo._format, 'supports_tree_reference', False):
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
537
                if repo._format.supports_chks:
538
                    matching_format_name = 'development3-subtree'
539
                else:
540
                    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.
541
            else:
542
                matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
543
            mismatching_format_name = 'pack-0.92'
544
        else:
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
545
            if repo._format.supports_chks:
546
                matching_format_name = 'development3'
547
            else:
548
                matching_format_name = 'pack-0.92'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
549
            mismatching_format_name = 'pack-0.92-subtree'
550
        base = self.make_repository('base', format=matching_format_name)
551
        repo.add_fallback_repository(base)
552
        # you can't stack on something with incompatible data
553
        bad_repo = self.make_repository('mismatch',
554
            format=mismatching_format_name)
555
        e = self.assertRaises(errors.IncompatibleRepositories,
556
            repo.add_fallback_repository, bad_repo)
557
        self.assertContainsRe(str(e),
558
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
559
            r'.*Repository.*/repo/.*\n'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
560
            r'different rich-root support')
561
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
562
    def test_stack_checks_serializers_compatibility(self):
563
        repo = self.make_repository('repo', format=self.get_format())
564
        if getattr(repo._format, 'supports_tree_reference', False):
565
            # can only stack on repositories that have compatible internal
566
            # metadata
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
567
            if repo._format.supports_chks:
568
                matching_format_name = 'development3-subtree'
569
            else:
570
                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.
571
            mismatching_format_name = 'rich-root-pack'
572
        else:
573
            if repo.supports_rich_root():
574
                matching_format_name = 'rich-root-pack'
575
                mismatching_format_name = 'pack-0.92-subtree'
576
            else:
577
                raise TestNotApplicable('No formats use non-v5 serializer'
578
                    ' without having rich-root also set')
579
        base = self.make_repository('base', format=matching_format_name)
580
        repo.add_fallback_repository(base)
581
        # you can't stack on something with incompatible data
582
        bad_repo = self.make_repository('mismatch',
583
            format=mismatching_format_name)
584
        e = self.assertRaises(errors.IncompatibleRepositories,
585
            repo.add_fallback_repository, bad_repo)
586
        self.assertContainsRe(str(e),
587
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
588
            r'.*Repository.*/repo/.*\n'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
589
            r'different serializers')
590
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
591
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
592
        base = self.make_branch_and_tree('base', format=self.get_format())
593
        base.commit('foo')
594
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
595
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
596
        referencing.commit('bar')
597
        new_instance = referencing.bzrdir.open_repository()
598
        new_instance.lock_read()
599
        self.addCleanup(new_instance.unlock)
600
        new_instance._pack_collection.ensure_loaded()
601
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
602
603
    def test_autopack_only_considers_main_repo_packs(self):
604
        base = self.make_branch_and_tree('base', format=self.get_format())
605
        base.commit('foo')
606
        tree = self.make_branch_and_tree('repo', format=self.get_format())
607
        tree.branch.repository.add_fallback_repository(base.branch.repository)
608
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
609
        # This test could be a little cheaper by replacing the packs
610
        # attribute on the repository to allow a different pack distribution
611
        # and max packs policy - so we are checking the policy is honoured
612
        # in the test. But for now 11 commits is not a big deal in a single
613
        # test.
614
        for x in range(9):
615
            tree.commit('commit %s' % x)
616
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
617
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
618
        self.assertEqual(9, len(list(index.iter_all_entries())))
619
        # committing one more should coalesce to 1 of 10.
620
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
621
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
622
        self.assertEqual(1, len(list(index.iter_all_entries())))
623
        # packing should not damage data
624
        tree = tree.bzrdir.open_workingtree()
625
        check_result = tree.branch.repository.check(
626
            [tree.branch.last_revision()])
627
        # We should have 50 (10x5) files in the obsolete_packs directory.
628
        obsolete_files = list(trans.list_dir('obsolete_packs'))
629
        self.assertFalse('foo' in obsolete_files)
630
        self.assertFalse('bar' in obsolete_files)
631
        self.assertEqual(50, len(obsolete_files))
632
        # XXX: Todo check packs obsoleted correctly - old packs and indices
633
        # in the obsolete_packs directory.
634
        large_pack_name = list(index.iter_all_entries())[0][1][0]
635
        # finally, committing again should not touch the large pack.
636
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
637
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
638
        self.assertEqual(2, len(list(index.iter_all_entries())))
639
        pack_names = [node[1][0] for node in index.iter_all_entries()]
640
        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
641
642
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
643
class TestSmartServerAutopack(TestCaseWithTransport):
644
645
    def setUp(self):
646
        super(TestSmartServerAutopack, self).setUp()
647
        # Create a smart server that publishes whatever the backing VFS server
648
        # does.
649
        self.smart_server = server.SmartTCPServer_for_testing()
650
        self.smart_server.setUp(self.get_server())
651
        self.addCleanup(self.smart_server.tearDown)
652
        # Log all HPSS calls into self.hpss_calls.
653
        client._SmartClient.hooks.install_named_hook(
654
            'call', self.capture_hpss_call, None)
655
        self.hpss_calls = []
656
657
    def capture_hpss_call(self, params):
658
        self.hpss_calls.append(params.method)
659
660
    def get_format(self):
661
        return bzrdir.format_registry.make_bzrdir(self.format_name)
662
663
    def test_autopack_rpc_is_used_when_using_hpss(self):
664
        # Make local and remote repos
665
        tree = self.make_branch_and_tree('local', format=self.get_format())
666
        self.make_branch_and_tree('remote', format=self.get_format())
667
        remote_branch_url = self.smart_server.get_url() + 'remote'
668
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
669
        # Make 9 local revisions, and push them one at a time to the remote
670
        # repo to produce 9 pack files.
671
        for x in range(9):
672
            tree.commit('commit %s' % x)
673
            tree.branch.push(remote_branch)
674
        # Make one more push to trigger an autopack
675
        self.hpss_calls = []
676
        tree.commit('commit triggering pack')
677
        tree.branch.push(remote_branch)
678
        self.assertTrue('PackRepository.autopack' in self.hpss_calls)
679
680
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
681
def load_tests(basic_tests, module, test_loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
682
    # these give the bzrdir canned format name, and the repository on-disk
683
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
684
    scenarios_params = [
685
         dict(format_name='pack-0.92',
686
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
687
              format_supports_external_lookups=False,
688
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
689
         dict(format_name='pack-0.92-subtree',
690
              format_string="Bazaar pack repository format 1 "
691
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
692
              format_supports_external_lookups=False,
693
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
694
         dict(format_name='1.6',
695
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
696
              format_supports_external_lookups=True,
697
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
698
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
699
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
700
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
701
              format_supports_external_lookups=True,
702
              index_class=GraphIndex),
3805.3.1 by John Arbash Meinel
Add repository 1.9 format, and update the documentation.
703
         dict(format_name='1.9',
704
              format_string="Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n",
705
              format_supports_external_lookups=True,
706
              index_class=BTreeGraphIndex),
707
         dict(format_name='1.9-rich-root',
708
              format_string="Bazaar RepositoryFormatKnitPack6RichRoot "
709
                  "(bzr 1.9)\n",
710
              format_supports_external_lookups=True,
711
              index_class=BTreeGraphIndex),
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
712
         dict(format_name='development2',
713
              format_string="Bazaar development format 2 "
714
                  "(needs bzr.dev from before 1.8)\n",
715
              format_supports_external_lookups=True,
716
              index_class=BTreeGraphIndex),
717
         dict(format_name='development2-subtree',
718
              format_string="Bazaar development format 2 "
719
                  "with subtree support (needs bzr.dev from before 1.8)\n",
720
              format_supports_external_lookups=True,
721
              index_class=BTreeGraphIndex),
3735.2.3 by Robert Collins
Create a format which passes the basic smoke test for CHK availability.
722
         dict(format_name='development3',
723
              format_string="Bazaar development format 3 "
724
                  "(needs bzr.dev from before 1.8)\n",
725
              format_supports_external_lookups=True,
726
              index_class=BTreeGraphIndex),
727
         dict(format_name='development3-subtree',
728
              format_string="Bazaar development format 3 "
729
                  "with subtree support (needs bzr.dev from before 1.8)\n",
730
              format_supports_external_lookups=True,
731
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
732
         ]
733
    adapter = tests.TestScenarioApplier()
734
    # name of the scenario is the format name
735
    adapter.scenarios = [(s['format_name'], s) for s in scenarios_params]
736
    suite = tests.TestSuite()
737
    tests.adapt_tests(basic_tests, adapter, suite)
738
    return suite