/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
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
409
                # it after 'tree' has packed.
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
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
417
    def test_concurrent_pack_during_get_record_reloads(self):
418
        tree = self.make_branch_and_tree('tree')
419
        tree.lock_write()
420
        try:
421
            rev1 = tree.commit('one')
422
            rev2 = tree.commit('two')
423
            r2 = repository.Repository.open('tree')
424
            r2.lock_read()
425
            try:
426
                # At this point, we will start grabbing a record stream, and
427
                # trigger a repack mid-way
428
                packed = False
429
                result = {}
430
                keys = [(rev1,), (rev2,)]
431
                record_stream = r2.revisions.get_record_stream(keys,
432
                                    'unordered', False)
433
                for record in record_stream:
434
                    result[record.key] = record
435
                    if not packed:
436
                        tree.branch.repository.pack()
437
                        packed = True
438
                # The first record will be found in the original location, but
439
                # after the pack, we have to reload to find the next record
440
                self.assertEqual(sorted([rev1, rev2]), sorted(result.keys()))
441
            finally:
442
                r2.unlock()
443
        finally:
444
            tree.unlock()
445
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
446
    def test_lock_write_does_not_physically_lock(self):
447
        repo = self.make_repository('.', format=self.get_format())
448
        repo.lock_write()
449
        self.addCleanup(repo.unlock)
450
        self.assertFalse(repo.get_physical_lock_status())
451
452
    def prepare_for_break_lock(self):
453
        # Setup the global ui factory state so that a break-lock method call
454
        # will find usable input in the input stream.
455
        old_factory = ui.ui_factory
456
        def restoreFactory():
457
            ui.ui_factory = old_factory
458
        self.addCleanup(restoreFactory)
459
        ui.ui_factory = ui.SilentUIFactory()
460
        ui.ui_factory.stdin = StringIO("y\n")
461
462
    def test_break_lock_breaks_physical_lock(self):
463
        repo = self.make_repository('.', format=self.get_format())
464
        repo._pack_collection.lock_names()
3650.4.1 by Aaron Bentley
Fix test kipple in test_break_lock_breaks_physical_lock
465
        repo.control_files.leave_in_place()
466
        repo.unlock()
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
467
        repo2 = repository.Repository.open('.')
468
        self.assertTrue(repo.get_physical_lock_status())
469
        self.prepare_for_break_lock()
470
        repo2.break_lock()
471
        self.assertFalse(repo.get_physical_lock_status())
472
473
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
474
        repo = self.make_repository('.', format=self.get_format())
475
        repo._pack_collection.lock_names()
476
        self.assertTrue(repo.get_physical_lock_status())
477
        repo2 = repository.Repository.open('.')
478
        self.prepare_for_break_lock()
479
        repo2.break_lock()
480
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
481
482
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
483
        # we want two repositories at this point:
484
        # one with a revision that is a ghost in the other
485
        # repository.
486
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
487
        # 'references' is present in both repositories, and 'tip' is present
488
        # just in has_ghost.
489
        # has_ghost       missing_ghost
490
        #------------------------------
491
        # 'ghost'             -
492
        # 'references'    'references'
493
        # 'tip'               -
494
        # In this test we fetch 'tip' which should not fetch 'ghost'
495
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
496
        missing_ghost = self.make_repository('missing_ghost',
497
            format=self.get_format())
498
499
        def add_commit(repo, revision_id, parent_ids):
500
            repo.lock_write()
501
            repo.start_write_group()
502
            inv = inventory.Inventory(revision_id=revision_id)
503
            inv.root.revision = revision_id
504
            root_id = inv.root.file_id
505
            sha1 = repo.add_inventory(revision_id, inv, [])
506
            repo.texts.add_lines((root_id, revision_id), [], [])
507
            rev = _mod_revision.Revision(timestamp=0,
508
                                         timezone=None,
509
                                         committer="Foo Bar <foo@example.com>",
510
                                         message="Message",
511
                                         inventory_sha1=sha1,
512
                                         revision_id=revision_id)
513
            rev.parent_ids = parent_ids
514
            repo.add_revision(revision_id, rev)
515
            repo.commit_write_group()
516
            repo.unlock()
517
        add_commit(has_ghost, 'ghost', [])
518
        add_commit(has_ghost, 'references', ['ghost'])
519
        add_commit(missing_ghost, 'references', ['ghost'])
520
        add_commit(has_ghost, 'tip', ['references'])
521
        missing_ghost.fetch(has_ghost, 'tip')
522
        # missing ghost now has tip and not ghost.
523
        rev = missing_ghost.get_revision('tip')
524
        inv = missing_ghost.get_inventory('tip')
525
        self.assertRaises(errors.NoSuchRevision,
526
            missing_ghost.get_revision, 'ghost')
527
        self.assertRaises(errors.NoSuchRevision,
528
            missing_ghost.get_inventory, 'ghost')
529
530
    def test_supports_external_lookups(self):
531
        repo = self.make_repository('.', format=self.get_format())
532
        self.assertEqual(self.format_supports_external_lookups,
533
            repo._format.supports_external_lookups)
534
535
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
536
class TestPackRepositoryStacking(TestCaseWithTransport):
537
538
    """Tests for stacking pack repositories"""
539
540
    def setUp(self):
541
        if not self.format_supports_external_lookups:
542
            raise TestNotApplicable("%r doesn't support stacking" 
543
                % (self.format_name,))
544
        super(TestPackRepositoryStacking, self).setUp()
545
546
    def get_format(self):
547
        return bzrdir.format_registry.make_bzrdir(self.format_name)
548
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
549
    def test_stack_checks_rich_root_compatibility(self):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
550
        # early versions of the packing code relied on pack internals to
551
        # stack, but the current version should be able to stack on any
552
        # format.
553
        #
554
        # TODO: Possibly this should be run per-repository-format and raise
555
        # TestNotApplicable on formats that don't support stacking. -- mbp
556
        # 20080729
557
        repo = self.make_repository('repo', format=self.get_format())
558
        if repo.supports_rich_root():
559
            # can only stack on repositories that have compatible internal
560
            # metadata
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
561
            if getattr(repo._format, 'supports_tree_reference', False):
562
                matching_format_name = 'pack-0.92-subtree'
563
            else:
564
                matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
565
            mismatching_format_name = 'pack-0.92'
566
        else:
567
            matching_format_name = 'pack-0.92'
568
            mismatching_format_name = 'pack-0.92-subtree'
569
        base = self.make_repository('base', format=matching_format_name)
570
        repo.add_fallback_repository(base)
571
        # you can't stack on something with incompatible data
572
        bad_repo = self.make_repository('mismatch',
573
            format=mismatching_format_name)
574
        e = self.assertRaises(errors.IncompatibleRepositories,
575
            repo.add_fallback_repository, bad_repo)
576
        self.assertContainsRe(str(e),
577
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
578
            r'KnitPackRepository.*/repo/.*\n'
579
            r'different rich-root support')
580
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
581
    def test_stack_checks_serializers_compatibility(self):
582
        repo = self.make_repository('repo', format=self.get_format())
583
        if getattr(repo._format, 'supports_tree_reference', False):
584
            # can only stack on repositories that have compatible internal
585
            # metadata
586
            matching_format_name = 'pack-0.92-subtree'
587
            mismatching_format_name = 'rich-root-pack'
588
        else:
589
            if repo.supports_rich_root():
590
                matching_format_name = 'rich-root-pack'
591
                mismatching_format_name = 'pack-0.92-subtree'
592
            else:
593
                raise TestNotApplicable('No formats use non-v5 serializer'
594
                    ' without having rich-root also set')
595
        base = self.make_repository('base', format=matching_format_name)
596
        repo.add_fallback_repository(base)
597
        # you can't stack on something with incompatible data
598
        bad_repo = self.make_repository('mismatch',
599
            format=mismatching_format_name)
600
        e = self.assertRaises(errors.IncompatibleRepositories,
601
            repo.add_fallback_repository, bad_repo)
602
        self.assertContainsRe(str(e),
603
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
604
            r'KnitPackRepository.*/repo/.*\n'
605
            r'different serializers')
606
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
607
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
608
        base = self.make_branch_and_tree('base', format=self.get_format())
609
        base.commit('foo')
610
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
611
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
612
        referencing.commit('bar')
613
        new_instance = referencing.bzrdir.open_repository()
614
        new_instance.lock_read()
615
        self.addCleanup(new_instance.unlock)
616
        new_instance._pack_collection.ensure_loaded()
617
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
618
619
    def test_autopack_only_considers_main_repo_packs(self):
620
        base = self.make_branch_and_tree('base', format=self.get_format())
621
        base.commit('foo')
622
        tree = self.make_branch_and_tree('repo', format=self.get_format())
623
        tree.branch.repository.add_fallback_repository(base.branch.repository)
624
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
625
        # This test could be a little cheaper by replacing the packs
626
        # attribute on the repository to allow a different pack distribution
627
        # and max packs policy - so we are checking the policy is honoured
628
        # in the test. But for now 11 commits is not a big deal in a single
629
        # test.
630
        for x in range(9):
631
            tree.commit('commit %s' % x)
632
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
633
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
634
        self.assertEqual(9, len(list(index.iter_all_entries())))
635
        # committing one more should coalesce to 1 of 10.
636
        tree.commit('commit 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(1, len(list(index.iter_all_entries())))
639
        # packing should not damage data
640
        tree = tree.bzrdir.open_workingtree()
641
        check_result = tree.branch.repository.check(
642
            [tree.branch.last_revision()])
643
        # We should have 50 (10x5) files in the obsolete_packs directory.
644
        obsolete_files = list(trans.list_dir('obsolete_packs'))
645
        self.assertFalse('foo' in obsolete_files)
646
        self.assertFalse('bar' in obsolete_files)
647
        self.assertEqual(50, len(obsolete_files))
648
        # XXX: Todo check packs obsoleted correctly - old packs and indices
649
        # in the obsolete_packs directory.
650
        large_pack_name = list(index.iter_all_entries())[0][1][0]
651
        # finally, committing again should not touch the large pack.
652
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
653
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
654
        self.assertEqual(2, len(list(index.iter_all_entries())))
655
        pack_names = [node[1][0] for node in index.iter_all_entries()]
656
        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
657
658
659
def load_tests(basic_tests, module, test_loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
660
    # these give the bzrdir canned format name, and the repository on-disk
661
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
662
    scenarios_params = [
663
         dict(format_name='pack-0.92',
664
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
665
              format_supports_external_lookups=False,
666
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
667
         dict(format_name='pack-0.92-subtree',
668
              format_string="Bazaar pack repository format 1 "
669
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
670
              format_supports_external_lookups=False,
671
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
672
         dict(format_name='1.6',
673
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
674
              format_supports_external_lookups=True,
675
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
676
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
677
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
678
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
679
              format_supports_external_lookups=True,
680
              index_class=GraphIndex),
681
         dict(format_name='development2',
682
              format_string="Bazaar development format 2 "
683
                  "(needs bzr.dev from before 1.8)\n",
684
              format_supports_external_lookups=True,
685
              index_class=BTreeGraphIndex),
686
         dict(format_name='development2-subtree',
687
              format_string="Bazaar development format 2 "
688
                  "with subtree support (needs bzr.dev from before 1.8)\n",
689
              format_supports_external_lookups=True,
690
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
691
         ]
692
    adapter = tests.TestScenarioApplier()
693
    # name of the scenario is the format name
694
    adapter.scenarios = [(s['format_name'], s) for s in scenarios_params]
695
    suite = tests.TestSuite()
696
    tests.adapt_tests(basic_tests, adapter, suite)
697
    return suite