~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Martin Pool
  • Date: 2011-02-07 01:39:42 UTC
  • mto: This revision was merged to the branch mainline in revision 5650.
  • Revision ID: mbp@canonical.com-20110207013942-roj88kez6jir13tr
Add brief user documentation of command line splitting

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
 
26
import sys
26
27
 
27
28
import bzrlib
28
 
from bzrlib.errors import (
29
 
    UnknownFormatError,
30
 
    UnsupportedFormatError,
31
 
    )
 
29
from bzrlib.errors import (NoSuchFile,
 
30
                           UnknownFormatError,
 
31
                           UnsupportedFormatError,
 
32
                           )
32
33
from bzrlib import (
33
34
    btree_index,
34
35
    graph,
35
 
    symbol_versioning,
36
36
    tests,
37
37
    transport,
38
38
    )
57
57
from bzrlib.repofmt import (
58
58
    groupcompress_repo,
59
59
    knitrepo,
60
 
    knitpack_repo,
61
60
    pack_repo,
 
61
    weaverepo,
62
62
    )
63
63
 
64
64
 
67
67
    def test_get_set_default_format(self):
68
68
        old_default = bzrdir.format_registry.get('default')
69
69
        private_default = old_default().repository_format.__class__
70
 
        old_format = repository.format_registry.get_default()
 
70
        old_format = repository.RepositoryFormat.get_default_format()
71
71
        self.assertTrue(isinstance(old_format, private_default))
72
72
        def make_sample_bzrdir():
73
73
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
87
87
            bzrdir.format_registry.remove('default')
88
88
            bzrdir.format_registry.remove('sample')
89
89
            bzrdir.format_registry.register('default', old_default, '')
90
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
90
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
91
91
                              old_format.__class__)
92
92
 
93
93
 
115
115
        return "opened repository."
116
116
 
117
117
 
118
 
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
119
 
    """A sample format that can not be used in a metadir
120
 
 
121
 
    """
122
 
 
123
 
    def get_format_string(self):
124
 
        raise NotImplementedError
125
 
 
126
 
 
127
118
class TestRepositoryFormat(TestCaseWithTransport):
128
119
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
129
120
 
137
128
            format.initialize(dir)
138
129
            t = transport.get_transport(url)
139
130
            found_format = repository.RepositoryFormat.find_format(dir)
140
 
            self.assertIsInstance(found_format, format.__class__)
141
 
        check_format(repository.format_registry.get_default(), "bar")
 
131
            self.failUnless(isinstance(found_format, format.__class__))
 
132
        check_format(weaverepo.RepositoryFormat7(), "bar")
142
133
 
143
134
    def test_find_format_no_repository(self):
144
135
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
154
145
                          dir)
155
146
 
156
147
    def test_register_unregister_format(self):
157
 
        # Test deprecated format registration functions
158
148
        format = SampleRepositoryFormat()
159
149
        # make a control dir
160
150
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
161
151
        # make a repo
162
152
        format.initialize(dir)
163
153
        # register a format for it.
164
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
165
 
            repository.RepositoryFormat.register_format, format)
 
154
        repository.RepositoryFormat.register_format(format)
166
155
        # which repository.Open will refuse (not supported)
167
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
168
 
            self.get_url())
 
156
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
169
157
        # but open(unsupported) will work
170
158
        self.assertEqual(format.open(dir), "opened repository.")
171
159
        # unregister the format
172
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
173
 
            repository.RepositoryFormat.unregister_format, format)
174
 
 
175
 
 
176
 
class TestRepositoryFormatRegistry(TestCase):
177
 
 
178
 
    def setUp(self):
179
 
        super(TestRepositoryFormatRegistry, self).setUp()
180
 
        self.registry = repository.RepositoryFormatRegistry()
181
 
 
182
 
    def test_register_unregister_format(self):
183
 
        format = SampleRepositoryFormat()
184
 
        self.registry.register(format)
185
 
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
186
 
        self.registry.remove(format)
187
 
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
188
 
 
189
 
    def test_get_all(self):
190
 
        format = SampleRepositoryFormat()
191
 
        self.assertEquals([], self.registry._get_all())
192
 
        self.registry.register(format)
193
 
        self.assertEquals([format], self.registry._get_all())
194
 
 
195
 
    def test_register_extra(self):
196
 
        format = SampleExtraRepositoryFormat()
197
 
        self.assertEquals([], self.registry._get_all())
198
 
        self.registry.register_extra(format)
199
 
        self.assertEquals([format], self.registry._get_all())
200
 
 
201
 
    def test_register_extra_lazy(self):
202
 
        self.assertEquals([], self.registry._get_all())
203
 
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
204
 
            "SampleExtraRepositoryFormat")
205
 
        formats = self.registry._get_all()
206
 
        self.assertEquals(1, len(formats))
207
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
160
        repository.RepositoryFormat.unregister_format(format)
 
161
 
 
162
 
 
163
class TestFormat6(TestCaseWithTransport):
 
164
 
 
165
    def test_attribute__fetch_order(self):
 
166
        """Weaves need topological data insertion."""
 
167
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
168
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
169
        self.assertEqual('topological', repo._format._fetch_order)
 
170
 
 
171
    def test_attribute__fetch_uses_deltas(self):
 
172
        """Weaves do not reuse deltas."""
 
173
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
174
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
175
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
176
 
 
177
    def test_attribute__fetch_reconcile(self):
 
178
        """Weave repositories need a reconcile after fetch."""
 
179
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
180
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
181
        self.assertEqual(True, repo._format._fetch_reconcile)
 
182
 
 
183
    def test_no_ancestry_weave(self):
 
184
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
185
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
186
        # We no longer need to create the ancestry.weave file
 
187
        # since it is *never* used.
 
188
        self.assertRaises(NoSuchFile,
 
189
                          control.transport.get,
 
190
                          'ancestry.weave')
 
191
 
 
192
    def test_supports_external_lookups(self):
 
193
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
194
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
195
        self.assertFalse(repo._format.supports_external_lookups)
 
196
 
 
197
 
 
198
class TestFormat7(TestCaseWithTransport):
 
199
 
 
200
    def test_attribute__fetch_order(self):
 
201
        """Weaves need topological data insertion."""
 
202
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
203
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
204
        self.assertEqual('topological', repo._format._fetch_order)
 
205
 
 
206
    def test_attribute__fetch_uses_deltas(self):
 
207
        """Weaves do not reuse deltas."""
 
208
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
209
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
210
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
211
 
 
212
    def test_attribute__fetch_reconcile(self):
 
213
        """Weave repositories need a reconcile after fetch."""
 
214
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
215
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
216
        self.assertEqual(True, repo._format._fetch_reconcile)
 
217
 
 
218
    def test_disk_layout(self):
 
219
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
220
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
221
        # in case of side effects of locking.
 
222
        repo.lock_write()
 
223
        repo.unlock()
 
224
        # we want:
 
225
        # format 'Bazaar-NG Repository format 7'
 
226
        # lock ''
 
227
        # inventory.weave == empty_weave
 
228
        # empty revision-store directory
 
229
        # empty weaves directory
 
230
        t = control.get_repository_transport(None)
 
231
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
232
                             t.get('format').read())
 
233
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
234
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
235
        self.assertEqualDiff('# bzr weave file v5\n'
 
236
                             'w\n'
 
237
                             'W\n',
 
238
                             t.get('inventory.weave').read())
 
239
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
240
        # disk.
 
241
        control.create_branch()
 
242
        tree = control.create_workingtree()
 
243
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
244
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
245
        try:
 
246
            tree.commit('first post', rev_id='first')
 
247
        except errors.IllegalPath:
 
248
            if sys.platform != 'win32':
 
249
                raise
 
250
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
 
251
                              ' in repo format 7')
 
252
            return
 
253
        self.assertEqualDiff(
 
254
            '# bzr weave file v5\n'
 
255
            'i\n'
 
256
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
257
            'n first\n'
 
258
            '\n'
 
259
            'w\n'
 
260
            '{ 0\n'
 
261
            '. content\n'
 
262
            '}\n'
 
263
            'W\n',
 
264
            t.get('weaves/74/Foo%3ABar.weave').read())
 
265
 
 
266
    def test_shared_disk_layout(self):
 
267
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
268
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
269
        # we want:
 
270
        # format 'Bazaar-NG Repository format 7'
 
271
        # inventory.weave == empty_weave
 
272
        # empty revision-store directory
 
273
        # empty weaves directory
 
274
        # a 'shared-storage' marker file.
 
275
        # lock is not present when unlocked
 
276
        t = control.get_repository_transport(None)
 
277
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
278
                             t.get('format').read())
 
279
        self.assertEqualDiff('', t.get('shared-storage').read())
 
280
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
281
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
282
        self.assertEqualDiff('# bzr weave file v5\n'
 
283
                             'w\n'
 
284
                             'W\n',
 
285
                             t.get('inventory.weave').read())
 
286
        self.assertFalse(t.has('branch-lock'))
 
287
 
 
288
    def test_creates_lockdir(self):
 
289
        """Make sure it appears to be controlled by a LockDir existence"""
 
290
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
291
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
292
        t = control.get_repository_transport(None)
 
293
        # TODO: Should check there is a 'lock' toplevel directory,
 
294
        # regardless of contents
 
295
        self.assertFalse(t.has('lock/held/info'))
 
296
        repo.lock_write()
 
297
        try:
 
298
            self.assertTrue(t.has('lock/held/info'))
 
299
        finally:
 
300
            # unlock so we don't get a warning about failing to do so
 
301
            repo.unlock()
 
302
 
 
303
    def test_uses_lockdir(self):
 
304
        """repo format 7 actually locks on lockdir"""
 
305
        base_url = self.get_url()
 
306
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
307
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
308
        t = control.get_repository_transport(None)
 
309
        repo.lock_write()
 
310
        repo.unlock()
 
311
        del repo
 
312
        # make sure the same lock is created by opening it
 
313
        repo = repository.Repository.open(base_url)
 
314
        repo.lock_write()
 
315
        self.assertTrue(t.has('lock/held/info'))
 
316
        repo.unlock()
 
317
        self.assertFalse(t.has('lock/held/info'))
 
318
 
 
319
    def test_shared_no_tree_disk_layout(self):
 
320
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
321
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
322
        repo.set_make_working_trees(False)
 
323
        # we want:
 
324
        # format 'Bazaar-NG Repository format 7'
 
325
        # lock ''
 
326
        # inventory.weave == empty_weave
 
327
        # empty revision-store directory
 
328
        # empty weaves directory
 
329
        # a 'shared-storage' marker file.
 
330
        t = control.get_repository_transport(None)
 
331
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
332
                             t.get('format').read())
 
333
        ## self.assertEqualDiff('', t.get('lock').read())
 
334
        self.assertEqualDiff('', t.get('shared-storage').read())
 
335
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
336
        repo.set_make_working_trees(True)
 
337
        self.assertFalse(t.has('no-working-trees'))
 
338
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
339
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
340
        self.assertEqualDiff('# bzr weave file v5\n'
 
341
                             'w\n'
 
342
                             'W\n',
 
343
                             t.get('inventory.weave').read())
 
344
 
 
345
    def test_supports_external_lookups(self):
 
346
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
347
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
348
        self.assertFalse(repo._format.supports_external_lookups)
208
349
 
209
350
 
210
351
class TestFormatKnit1(TestCaseWithTransport):
429
570
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
430
571
 
431
572
 
432
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
433
 
 
434
 
    def get_format_string(self):
435
 
        return "Test Format 1"
436
 
 
437
 
 
438
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
439
 
 
440
 
    def get_format_string(self):
441
 
        return "Test Format 2"
 
573
class TestInterWeaveRepo(TestCaseWithTransport):
 
574
 
 
575
    def test_is_compatible_and_registered(self):
 
576
        # InterWeaveRepo is compatible when either side
 
577
        # is a format 5/6/7 branch
 
578
        from bzrlib.repofmt import knitrepo, weaverepo
 
579
        formats = [weaverepo.RepositoryFormat5(),
 
580
                   weaverepo.RepositoryFormat6(),
 
581
                   weaverepo.RepositoryFormat7()]
 
582
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
583
                                knitrepo.RepositoryFormatKnit1(),
 
584
                                ]
 
585
        repo_a = self.make_repository('a')
 
586
        repo_b = self.make_repository('b')
 
587
        is_compatible = weaverepo.InterWeaveRepo.is_compatible
 
588
        for source in incompatible_formats:
 
589
            # force incompatible left then right
 
590
            repo_a._format = source
 
591
            repo_b._format = formats[0]
 
592
            self.assertFalse(is_compatible(repo_a, repo_b))
 
593
            self.assertFalse(is_compatible(repo_b, repo_a))
 
594
        for source in formats:
 
595
            repo_a._format = source
 
596
            for target in formats:
 
597
                repo_b._format = target
 
598
                self.assertTrue(is_compatible(repo_a, repo_b))
 
599
        self.assertEqual(weaverepo.InterWeaveRepo,
 
600
                         repository.InterRepository.get(repo_a,
 
601
                                                        repo_b).__class__)
442
602
 
443
603
 
444
604
class TestRepositoryConverter(TestCaseWithTransport):
445
605
 
446
606
    def test_convert_empty(self):
447
 
        source_format = TestRepositoryFormat1()
448
 
        target_format = TestRepositoryFormat2()
449
 
        repository.format_registry.register(source_format)
450
 
        self.addCleanup(repository.format_registry.remove,
451
 
            source_format)
452
 
        repository.format_registry.register(target_format)
453
 
        self.addCleanup(repository.format_registry.remove,
454
 
            target_format)
455
607
        t = self.get_transport()
456
608
        t.mkdir('repository')
457
609
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
458
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
610
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
611
        target_format = knitrepo.RepositoryFormatKnit1()
459
612
        converter = repository.CopyConverter(target_format)
460
613
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
461
614
        try:
466
619
        self.assertTrue(isinstance(target_format, repo._format.__class__))
467
620
 
468
621
 
 
622
class TestMisc(TestCase):
 
623
 
 
624
    def test_unescape_xml(self):
 
625
        """We get some kind of error when malformed entities are passed"""
 
626
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
627
 
 
628
 
469
629
class TestRepositoryFormatKnit3(TestCaseWithTransport):
470
630
 
471
631
    def test_attribute__fetch_order(self):
748
908
        source = self.make_repository('source', format='pack-0.92')
749
909
        target = self.make_repository('target', format='pack-0.92')
750
910
        stream_source = source._get_source(target._format)
751
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
911
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
752
912
 
753
913
    def test_source_to_exact_pack_rich_root_pack(self):
754
914
        source = self.make_repository('source', format='rich-root-pack')
755
915
        target = self.make_repository('target', format='rich-root-pack')
756
916
        stream_source = source._get_source(target._format)
757
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
917
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
758
918
 
759
919
    def test_source_to_exact_pack_19(self):
760
920
        source = self.make_repository('source', format='1.9')
761
921
        target = self.make_repository('target', format='1.9')
762
922
        stream_source = source._get_source(target._format)
763
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
923
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
764
924
 
765
925
    def test_source_to_exact_pack_19_rich_root(self):
766
926
        source = self.make_repository('source', format='1.9-rich-root')
767
927
        target = self.make_repository('target', format='1.9-rich-root')
768
928
        stream_source = source._get_source(target._format)
769
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
929
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
770
930
 
771
931
    def test_source_to_remote_exact_pack_19(self):
772
932
        trans = self.make_smart_server('target')
775
935
        target = self.make_repository('target', format='1.9')
776
936
        target = repository.Repository.open(trans.base)
777
937
        stream_source = source._get_source(target._format)
778
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
938
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
779
939
 
780
940
    def test_stream_source_to_non_exact(self):
781
941
        source = self.make_repository('source', format='pack-0.92')
1444
1604
        # Because of how they were built, they correspond to
1445
1605
        # ['D', 'C', 'B', 'A']
1446
1606
        packs = b.repository._pack_collection.packs
1447
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1607
        packer = pack_repo.Packer(b.repository._pack_collection,
1448
1608
                                  packs, 'testing',
1449
1609
                                  revision_ids=['B', 'C'])
1450
1610
        # Now, when we are copying the B & C revisions, their pack files should
1464
1624
        return repo._pack_collection
1465
1625
 
1466
1626
    def test_open_pack_will_optimise(self):
1467
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1627
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1468
1628
                                            [], '.test')
1469
1629
        new_pack = packer.open_pack()
1470
1630
        self.addCleanup(new_pack.abort) # ensure cleanup
1475
1635
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1476
1636
 
1477
1637
 
1478
 
class TestGCCHKPacker(TestCaseWithTransport):
1479
 
 
1480
 
    def make_abc_branch(self):
1481
 
        builder = self.make_branch_builder('source')
1482
 
        builder.start_series()
1483
 
        builder.build_snapshot('A', None, [
1484
 
            ('add', ('', 'root-id', 'directory', None)),
1485
 
            ('add', ('file', 'file-id', 'file', 'content\n')),
1486
 
            ])
1487
 
        builder.build_snapshot('B', ['A'], [
1488
 
            ('add', ('dir', 'dir-id', 'directory', None))])
1489
 
        builder.build_snapshot('C', ['B'], [
1490
 
            ('modify', ('file-id', 'new content\n'))])
1491
 
        builder.finish_series()
1492
 
        return builder.get_branch()
1493
 
 
1494
 
    def make_branch_with_disjoint_inventory_and_revision(self):
1495
 
        """a repo with separate packs for a revisions Revision and Inventory.
1496
 
 
1497
 
        There will be one pack file that holds the Revision content, and one
1498
 
        for the Inventory content.
1499
 
 
1500
 
        :return: (repository,
1501
 
                  pack_name_with_rev_A_Revision,
1502
 
                  pack_name_with_rev_A_Inventory,
1503
 
                  pack_name_with_rev_C_content)
1504
 
        """
1505
 
        b_source = self.make_abc_branch()
1506
 
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1507
 
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1508
 
        b_stacked.lock_write()
1509
 
        self.addCleanup(b_stacked.unlock)
1510
 
        b_stacked.fetch(b_source, 'B')
1511
 
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1512
 
        # fill in the A rev.
1513
 
        repo_not_stacked = b_stacked.bzrdir.open_repository()
1514
 
        repo_not_stacked.lock_write()
1515
 
        self.addCleanup(repo_not_stacked.unlock)
1516
 
        # Now we should have a pack file with A's inventory, but not its
1517
 
        # Revision
1518
 
        self.assertEqual([('A',), ('B',)],
1519
 
                         sorted(repo_not_stacked.inventories.keys()))
1520
 
        self.assertEqual([('B',)],
1521
 
                         sorted(repo_not_stacked.revisions.keys()))
1522
 
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1523
 
        # We have a couple names here, figure out which has A's inventory
1524
 
        for name in stacked_pack_names:
1525
 
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1526
 
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1527
 
            if ('A',) in keys:
1528
 
                inv_a_pack_name = name
1529
 
                break
1530
 
        else:
1531
 
            self.fail('Could not find pack containing A\'s inventory')
1532
 
        repo_not_stacked.fetch(b_source.repository, 'A')
1533
 
        self.assertEqual([('A',), ('B',)],
1534
 
                         sorted(repo_not_stacked.revisions.keys()))
1535
 
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1536
 
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1537
 
        self.assertEqual(1, len(rev_a_pack_names))
1538
 
        rev_a_pack_name = list(rev_a_pack_names)[0]
1539
 
        # Now fetch 'C', so we have a couple pack files to join
1540
 
        repo_not_stacked.fetch(b_source.repository, 'C')
1541
 
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1542
 
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1543
 
        self.assertEqual(1, len(rev_c_pack_names))
1544
 
        rev_c_pack_name = list(rev_c_pack_names)[0]
1545
 
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1546
 
                rev_c_pack_name)
1547
 
 
1548
 
    def test_pack_with_distant_inventories(self):
1549
 
        # See https://bugs.launchpad.net/bzr/+bug/437003
1550
 
        # When repacking, it is possible to have an inventory in a different
1551
 
        # pack file than the associated revision. An autopack can then come
1552
 
        # along, and miss that inventory, and complain.
1553
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1554
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1555
 
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1556
 
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1557
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1558
 
                    [a_pack, c_pack], '.test-pack')
1559
 
        # This would raise ValueError in bug #437003, but should not raise an
1560
 
        # error once fixed.
1561
 
        packer.pack()
1562
 
 
1563
 
    def test_pack_with_missing_inventory(self):
1564
 
        # Similar to test_pack_with_missing_inventory, but this time, we force
1565
 
        # the A inventory to actually be gone from the repository.
1566
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1567
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1568
 
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1569
 
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1570
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1571
 
            repo._pack_collection.all_packs(), '.test-pack')
1572
 
        e = self.assertRaises(ValueError, packer.pack)
1573
 
        packer.new_pack.abort()
1574
 
        self.assertContainsRe(str(e),
1575
 
            r"We are missing inventories for revisions: .*'A'")
1576
 
 
1577
 
 
1578
1638
class TestCrossFormatPacks(TestCaseWithTransport):
1579
1639
 
1580
1640
    def log_pack(self, hint=None):