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