/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):
537
                matching_format_name = 'pack-0.92-subtree'
538
            else:
539
                matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
540
            mismatching_format_name = 'pack-0.92'
541
        else:
542
            matching_format_name = 'pack-0.92'
543
            mismatching_format_name = 'pack-0.92-subtree'
544
        base = self.make_repository('base', format=matching_format_name)
545
        repo.add_fallback_repository(base)
546
        # you can't stack on something with incompatible data
547
        bad_repo = self.make_repository('mismatch',
548
            format=mismatching_format_name)
549
        e = self.assertRaises(errors.IncompatibleRepositories,
550
            repo.add_fallback_repository, bad_repo)
551
        self.assertContainsRe(str(e),
552
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
553
            r'KnitPackRepository.*/repo/.*\n'
554
            r'different rich-root support')
555
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
556
    def test_stack_checks_serializers_compatibility(self):
557
        repo = self.make_repository('repo', format=self.get_format())
558
        if getattr(repo._format, 'supports_tree_reference', False):
559
            # can only stack on repositories that have compatible internal
560
            # metadata
561
            matching_format_name = 'pack-0.92-subtree'
562
            mismatching_format_name = 'rich-root-pack'
563
        else:
564
            if repo.supports_rich_root():
565
                matching_format_name = 'rich-root-pack'
566
                mismatching_format_name = 'pack-0.92-subtree'
567
            else:
568
                raise TestNotApplicable('No formats use non-v5 serializer'
569
                    ' without having rich-root also set')
570
        base = self.make_repository('base', format=matching_format_name)
571
        repo.add_fallback_repository(base)
572
        # you can't stack on something with incompatible data
573
        bad_repo = self.make_repository('mismatch',
574
            format=mismatching_format_name)
575
        e = self.assertRaises(errors.IncompatibleRepositories,
576
            repo.add_fallback_repository, bad_repo)
577
        self.assertContainsRe(str(e),
578
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
579
            r'KnitPackRepository.*/repo/.*\n'
580
            r'different serializers')
581
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
582
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
583
        base = self.make_branch_and_tree('base', format=self.get_format())
584
        base.commit('foo')
585
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
586
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
587
        referencing.commit('bar')
588
        new_instance = referencing.bzrdir.open_repository()
589
        new_instance.lock_read()
590
        self.addCleanup(new_instance.unlock)
591
        new_instance._pack_collection.ensure_loaded()
592
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
593
594
    def test_autopack_only_considers_main_repo_packs(self):
595
        base = self.make_branch_and_tree('base', format=self.get_format())
596
        base.commit('foo')
597
        tree = self.make_branch_and_tree('repo', format=self.get_format())
598
        tree.branch.repository.add_fallback_repository(base.branch.repository)
599
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
600
        # This test could be a little cheaper by replacing the packs
601
        # attribute on the repository to allow a different pack distribution
602
        # and max packs policy - so we are checking the policy is honoured
603
        # in the test. But for now 11 commits is not a big deal in a single
604
        # test.
605
        for x in range(9):
606
            tree.commit('commit %s' % x)
607
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
608
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
609
        self.assertEqual(9, len(list(index.iter_all_entries())))
610
        # committing one more should coalesce to 1 of 10.
611
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
612
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
613
        self.assertEqual(1, len(list(index.iter_all_entries())))
614
        # packing should not damage data
615
        tree = tree.bzrdir.open_workingtree()
616
        check_result = tree.branch.repository.check(
617
            [tree.branch.last_revision()])
618
        # We should have 50 (10x5) files in the obsolete_packs directory.
619
        obsolete_files = list(trans.list_dir('obsolete_packs'))
620
        self.assertFalse('foo' in obsolete_files)
621
        self.assertFalse('bar' in obsolete_files)
622
        self.assertEqual(50, len(obsolete_files))
623
        # XXX: Todo check packs obsoleted correctly - old packs and indices
624
        # in the obsolete_packs directory.
625
        large_pack_name = list(index.iter_all_entries())[0][1][0]
626
        # finally, committing again should not touch the large pack.
627
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
628
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
629
        self.assertEqual(2, len(list(index.iter_all_entries())))
630
        pack_names = [node[1][0] for node in index.iter_all_entries()]
631
        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
632
633
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
634
class TestSmartServerAutopack(TestCaseWithTransport):
635
636
    def setUp(self):
637
        super(TestSmartServerAutopack, self).setUp()
638
        # Create a smart server that publishes whatever the backing VFS server
639
        # does.
640
        self.smart_server = server.SmartTCPServer_for_testing()
641
        self.smart_server.setUp(self.get_server())
642
        self.addCleanup(self.smart_server.tearDown)
643
        # Log all HPSS calls into self.hpss_calls.
644
        client._SmartClient.hooks.install_named_hook(
645
            'call', self.capture_hpss_call, None)
646
        self.hpss_calls = []
647
648
    def capture_hpss_call(self, params):
649
        self.hpss_calls.append(params.method)
650
651
    def get_format(self):
652
        return bzrdir.format_registry.make_bzrdir(self.format_name)
653
654
    def test_autopack_rpc_is_used_when_using_hpss(self):
655
        # Make local and remote repos
656
        tree = self.make_branch_and_tree('local', format=self.get_format())
657
        self.make_branch_and_tree('remote', format=self.get_format())
658
        remote_branch_url = self.smart_server.get_url() + 'remote'
659
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
660
        # Make 9 local revisions, and push them one at a time to the remote
661
        # repo to produce 9 pack files.
662
        for x in range(9):
663
            tree.commit('commit %s' % x)
664
            tree.branch.push(remote_branch)
665
        # Make one more push to trigger an autopack
666
        self.hpss_calls = []
667
        tree.commit('commit triggering pack')
668
        tree.branch.push(remote_branch)
669
        self.assertTrue('PackRepository.autopack' in self.hpss_calls)
670
671
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
672
def load_tests(basic_tests, module, test_loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
673
    # these give the bzrdir canned format name, and the repository on-disk
674
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
675
    scenarios_params = [
676
         dict(format_name='pack-0.92',
677
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
678
              format_supports_external_lookups=False,
679
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
680
         dict(format_name='pack-0.92-subtree',
681
              format_string="Bazaar pack repository format 1 "
682
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
683
              format_supports_external_lookups=False,
684
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
685
         dict(format_name='1.6',
686
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
687
              format_supports_external_lookups=True,
688
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
689
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
690
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
691
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
692
              format_supports_external_lookups=True,
693
              index_class=GraphIndex),
3805.3.1 by John Arbash Meinel
Add repository 1.9 format, and update the documentation.
694
         dict(format_name='1.9',
695
              format_string="Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n",
696
              format_supports_external_lookups=True,
697
              index_class=BTreeGraphIndex),
698
         dict(format_name='1.9-rich-root',
699
              format_string="Bazaar RepositoryFormatKnitPack6RichRoot "
700
                  "(bzr 1.9)\n",
701
              format_supports_external_lookups=True,
702
              index_class=BTreeGraphIndex),
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
703
         dict(format_name='development2',
704
              format_string="Bazaar development format 2 "
705
                  "(needs bzr.dev from before 1.8)\n",
706
              format_supports_external_lookups=True,
707
              index_class=BTreeGraphIndex),
708
         dict(format_name='development2-subtree',
709
              format_string="Bazaar development format 2 "
710
                  "with subtree support (needs bzr.dev from before 1.8)\n",
711
              format_supports_external_lookups=True,
712
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
713
         ]
714
    adapter = tests.TestScenarioApplier()
715
    # name of the scenario is the format name
716
    adapter.scenarios = [(s['format_name'], s) for s in scenarios_params]
717
    suite = tests.TestSuite()
718
    tests.adapt_tests(basic_tests, adapter, suite)
719
    return suite