~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Benoît Pierre
  • Date: 2009-11-02 22:24:29 UTC
  • mto: (4634.96.1 integration-2.0)
  • mto: This revision was merged to the branch mainline in revision 4798.
  • Revision ID: benoit.pierre@gmail.com-20091102222429-xqdyo6n8odh3xbbd
Small fix for handling of short option names in shellcomplete_on_options.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 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
from StringIO import StringIO
26
27
 
27
28
import bzrlib
28
 
from bzrlib.errors import (
29
 
    UnknownFormatError,
30
 
    UnsupportedFormatError,
31
 
    )
 
29
from bzrlib.errors import (NotBranchError,
 
30
                           NoSuchFile,
 
31
                           UnknownFormatError,
 
32
                           UnsupportedFormatError,
 
33
                           )
32
34
from bzrlib import (
33
 
    btree_index,
34
35
    graph,
35
 
    symbol_versioning,
36
36
    tests,
37
 
    transport,
38
37
    )
 
38
from bzrlib.branchbuilder import BranchBuilder
39
39
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
40
 
from bzrlib.index import GraphIndex
 
40
from bzrlib.index import GraphIndex, InMemoryGraphIndex
41
41
from bzrlib.repository import RepositoryFormat
 
42
from bzrlib.smart import server
42
43
from bzrlib.tests import (
43
44
    TestCase,
44
45
    TestCaseWithTransport,
45
 
    )
 
46
    TestSkipped,
 
47
    test_knit,
 
48
    )
 
49
from bzrlib.transport import (
 
50
    fakenfs,
 
51
    get_transport,
 
52
    )
 
53
from bzrlib.transport.memory import MemoryServer
46
54
from bzrlib import (
 
55
    bencode,
47
56
    bzrdir,
48
57
    errors,
49
58
    inventory,
50
59
    osutils,
 
60
    progress,
51
61
    repository,
52
62
    revision as _mod_revision,
 
63
    symbol_versioning,
53
64
    upgrade,
54
 
    versionedfile,
55
 
    vf_repository,
56
65
    workingtree,
57
66
    )
58
67
from bzrlib.repofmt import (
59
68
    groupcompress_repo,
60
69
    knitrepo,
61
 
    knitpack_repo,
62
70
    pack_repo,
 
71
    weaverepo,
63
72
    )
64
73
 
65
74
 
68
77
    def test_get_set_default_format(self):
69
78
        old_default = bzrdir.format_registry.get('default')
70
79
        private_default = old_default().repository_format.__class__
71
 
        old_format = repository.format_registry.get_default()
 
80
        old_format = repository.RepositoryFormat.get_default_format()
72
81
        self.assertTrue(isinstance(old_format, private_default))
73
82
        def make_sample_bzrdir():
74
83
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
88
97
            bzrdir.format_registry.remove('default')
89
98
            bzrdir.format_registry.remove('sample')
90
99
            bzrdir.format_registry.register('default', old_default, '')
91
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
100
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
92
101
                              old_format.__class__)
93
102
 
94
103
 
116
125
        return "opened repository."
117
126
 
118
127
 
119
 
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
120
 
    """A sample format that can not be used in a metadir
121
 
 
122
 
    """
123
 
 
124
 
    def get_format_string(self):
125
 
        raise NotImplementedError
126
 
 
127
 
 
128
128
class TestRepositoryFormat(TestCaseWithTransport):
129
129
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
130
130
 
136
136
        def check_format(format, url):
137
137
            dir = format._matchingbzrdir.initialize(url)
138
138
            format.initialize(dir)
139
 
            t = transport.get_transport(url)
 
139
            t = get_transport(url)
140
140
            found_format = repository.RepositoryFormat.find_format(dir)
141
 
            self.assertIsInstance(found_format, format.__class__)
142
 
        check_format(repository.format_registry.get_default(), "bar")
 
141
            self.failUnless(isinstance(found_format, format.__class__))
 
142
        check_format(weaverepo.RepositoryFormat7(), "bar")
143
143
 
144
144
    def test_find_format_no_repository(self):
145
145
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
155
155
                          dir)
156
156
 
157
157
    def test_register_unregister_format(self):
158
 
        # Test deprecated format registration functions
159
158
        format = SampleRepositoryFormat()
160
159
        # make a control dir
161
160
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
161
        # make a repo
163
162
        format.initialize(dir)
164
163
        # register a format for it.
165
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
166
 
            repository.RepositoryFormat.register_format, format)
 
164
        repository.RepositoryFormat.register_format(format)
167
165
        # which repository.Open will refuse (not supported)
168
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
169
 
            self.get_url())
 
166
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
170
167
        # but open(unsupported) will work
171
168
        self.assertEqual(format.open(dir), "opened repository.")
172
169
        # unregister the format
173
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
174
 
            repository.RepositoryFormat.unregister_format, format)
175
 
 
176
 
 
177
 
class TestRepositoryFormatRegistry(TestCase):
178
 
 
179
 
    def setUp(self):
180
 
        super(TestRepositoryFormatRegistry, self).setUp()
181
 
        self.registry = repository.RepositoryFormatRegistry()
182
 
 
183
 
    def test_register_unregister_format(self):
184
 
        format = SampleRepositoryFormat()
185
 
        self.registry.register(format)
186
 
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
187
 
        self.registry.remove(format)
188
 
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
189
 
 
190
 
    def test_get_all(self):
191
 
        format = SampleRepositoryFormat()
192
 
        self.assertEquals([], self.registry._get_all())
193
 
        self.registry.register(format)
194
 
        self.assertEquals([format], self.registry._get_all())
195
 
 
196
 
    def test_register_extra(self):
197
 
        format = SampleExtraRepositoryFormat()
198
 
        self.assertEquals([], self.registry._get_all())
199
 
        self.registry.register_extra(format)
200
 
        self.assertEquals([format], self.registry._get_all())
201
 
 
202
 
    def test_register_extra_lazy(self):
203
 
        self.assertEquals([], self.registry._get_all())
204
 
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
205
 
            "SampleExtraRepositoryFormat")
206
 
        formats = self.registry._get_all()
207
 
        self.assertEquals(1, len(formats))
208
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
170
        repository.RepositoryFormat.unregister_format(format)
 
171
 
 
172
 
 
173
class TestFormat6(TestCaseWithTransport):
 
174
 
 
175
    def test_attribute__fetch_order(self):
 
176
        """Weaves need topological data insertion."""
 
177
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
178
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
179
        self.assertEqual('topological', repo._format._fetch_order)
 
180
 
 
181
    def test_attribute__fetch_uses_deltas(self):
 
182
        """Weaves do not reuse deltas."""
 
183
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
184
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
185
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
186
 
 
187
    def test_attribute__fetch_reconcile(self):
 
188
        """Weave repositories need a reconcile after fetch."""
 
189
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
190
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
191
        self.assertEqual(True, repo._format._fetch_reconcile)
 
192
 
 
193
    def test_no_ancestry_weave(self):
 
194
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
195
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
196
        # We no longer need to create the ancestry.weave file
 
197
        # since it is *never* used.
 
198
        self.assertRaises(NoSuchFile,
 
199
                          control.transport.get,
 
200
                          'ancestry.weave')
 
201
 
 
202
    def test_supports_external_lookups(self):
 
203
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
204
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
205
        self.assertFalse(repo._format.supports_external_lookups)
 
206
 
 
207
 
 
208
class TestFormat7(TestCaseWithTransport):
 
209
 
 
210
    def test_attribute__fetch_order(self):
 
211
        """Weaves need topological data insertion."""
 
212
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
213
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
214
        self.assertEqual('topological', repo._format._fetch_order)
 
215
 
 
216
    def test_attribute__fetch_uses_deltas(self):
 
217
        """Weaves do not reuse deltas."""
 
218
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
219
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
220
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
221
 
 
222
    def test_attribute__fetch_reconcile(self):
 
223
        """Weave repositories need a reconcile after fetch."""
 
224
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
225
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
226
        self.assertEqual(True, repo._format._fetch_reconcile)
 
227
 
 
228
    def test_disk_layout(self):
 
229
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
230
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
231
        # in case of side effects of locking.
 
232
        repo.lock_write()
 
233
        repo.unlock()
 
234
        # we want:
 
235
        # format 'Bazaar-NG Repository format 7'
 
236
        # lock ''
 
237
        # inventory.weave == empty_weave
 
238
        # empty revision-store directory
 
239
        # empty weaves directory
 
240
        t = control.get_repository_transport(None)
 
241
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
242
                             t.get('format').read())
 
243
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
244
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
245
        self.assertEqualDiff('# bzr weave file v5\n'
 
246
                             'w\n'
 
247
                             'W\n',
 
248
                             t.get('inventory.weave').read())
 
249
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
250
        # disk.
 
251
        control.create_branch()
 
252
        tree = control.create_workingtree()
 
253
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
254
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
255
        tree.commit('first post', rev_id='first')
 
256
        self.assertEqualDiff(
 
257
            '# bzr weave file v5\n'
 
258
            'i\n'
 
259
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
260
            'n first\n'
 
261
            '\n'
 
262
            'w\n'
 
263
            '{ 0\n'
 
264
            '. content\n'
 
265
            '}\n'
 
266
            'W\n',
 
267
            t.get('weaves/74/Foo%3ABar.weave').read())
 
268
 
 
269
    def test_shared_disk_layout(self):
 
270
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
271
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
272
        # we want:
 
273
        # format 'Bazaar-NG Repository format 7'
 
274
        # inventory.weave == empty_weave
 
275
        # empty revision-store directory
 
276
        # empty weaves directory
 
277
        # a 'shared-storage' marker file.
 
278
        # lock is not present when unlocked
 
279
        t = control.get_repository_transport(None)
 
280
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
281
                             t.get('format').read())
 
282
        self.assertEqualDiff('', t.get('shared-storage').read())
 
283
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
284
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
285
        self.assertEqualDiff('# bzr weave file v5\n'
 
286
                             'w\n'
 
287
                             'W\n',
 
288
                             t.get('inventory.weave').read())
 
289
        self.assertFalse(t.has('branch-lock'))
 
290
 
 
291
    def test_creates_lockdir(self):
 
292
        """Make sure it appears to be controlled by a LockDir existence"""
 
293
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
294
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
295
        t = control.get_repository_transport(None)
 
296
        # TODO: Should check there is a 'lock' toplevel directory,
 
297
        # regardless of contents
 
298
        self.assertFalse(t.has('lock/held/info'))
 
299
        repo.lock_write()
 
300
        try:
 
301
            self.assertTrue(t.has('lock/held/info'))
 
302
        finally:
 
303
            # unlock so we don't get a warning about failing to do so
 
304
            repo.unlock()
 
305
 
 
306
    def test_uses_lockdir(self):
 
307
        """repo format 7 actually locks on lockdir"""
 
308
        base_url = self.get_url()
 
309
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
310
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
311
        t = control.get_repository_transport(None)
 
312
        repo.lock_write()
 
313
        repo.unlock()
 
314
        del repo
 
315
        # make sure the same lock is created by opening it
 
316
        repo = repository.Repository.open(base_url)
 
317
        repo.lock_write()
 
318
        self.assertTrue(t.has('lock/held/info'))
 
319
        repo.unlock()
 
320
        self.assertFalse(t.has('lock/held/info'))
 
321
 
 
322
    def test_shared_no_tree_disk_layout(self):
 
323
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
324
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
325
        repo.set_make_working_trees(False)
 
326
        # we want:
 
327
        # format 'Bazaar-NG Repository format 7'
 
328
        # lock ''
 
329
        # inventory.weave == empty_weave
 
330
        # empty revision-store directory
 
331
        # empty weaves directory
 
332
        # a 'shared-storage' marker file.
 
333
        t = control.get_repository_transport(None)
 
334
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
335
                             t.get('format').read())
 
336
        ## self.assertEqualDiff('', t.get('lock').read())
 
337
        self.assertEqualDiff('', t.get('shared-storage').read())
 
338
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
339
        repo.set_make_working_trees(True)
 
340
        self.assertFalse(t.has('no-working-trees'))
 
341
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
342
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
343
        self.assertEqualDiff('# bzr weave file v5\n'
 
344
                             'w\n'
 
345
                             'W\n',
 
346
                             t.get('inventory.weave').read())
 
347
 
 
348
    def test_supports_external_lookups(self):
 
349
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
350
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
351
        self.assertFalse(repo._format.supports_external_lookups)
209
352
 
210
353
 
211
354
class TestFormatKnit1(TestCaseWithTransport):
313
456
        repo = self.make_repository('.',
314
457
                format=bzrdir.format_registry.get('knit')())
315
458
        inv_xml = '<inventory format="5">\n</inventory>\n'
316
 
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
459
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
317
460
        self.assertEqual('test-rev-id', inv.root.revision)
318
461
 
319
462
    def test_deserialise_uses_global_revision_id(self):
325
468
        # Arguably, the deserialise_inventory should detect a mismatch, and
326
469
        # raise an error, rather than silently using one revision_id over the
327
470
        # other.
328
 
        self.assertRaises(AssertionError, repo._deserialise_inventory,
 
471
        self.assertRaises(AssertionError, repo.deserialise_inventory,
329
472
            'test-rev-id', inv_xml)
330
 
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
473
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
331
474
        self.assertEqual('other-rev-id', inv.root.revision)
332
475
 
333
476
    def test_supports_external_lookups(self):
379
522
        # classes do not barf inappropriately when a surprising repository type
380
523
        # is handed to them.
381
524
        dummy_a = DummyRepository()
382
 
        dummy_a._format = RepositoryFormat()
383
 
        dummy_a._format.supports_full_versioned_files = True
384
525
        dummy_b = DummyRepository()
385
 
        dummy_b._format = RepositoryFormat()
386
 
        dummy_b._format.supports_full_versioned_files = True
387
526
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
388
527
 
389
528
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
393
532
        no actual sane default in the presence of incompatible data models.
394
533
        """
395
534
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
396
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
535
        self.assertEqual(repository.InterSameDataRepository,
397
536
                         inter_repo.__class__)
398
537
        self.assertEqual(repo_a, inter_repo.source)
399
538
        self.assertEqual(repo_b, inter_repo.target)
413
552
        dummy_a._serializer = repo._serializer
414
553
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
415
554
        dummy_a._format.rich_root_data = repo._format.rich_root_data
416
 
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
417
555
        dummy_b._serializer = repo._serializer
418
556
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
419
557
        dummy_b._format.rich_root_data = repo._format.rich_root_data
420
 
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
421
558
        repository.InterRepository.register_optimiser(InterDummy)
422
559
        try:
423
560
            # we should get the default for something InterDummy returns False
436
573
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
437
574
 
438
575
 
439
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
440
 
 
441
 
    def get_format_string(self):
442
 
        return "Test Format 1"
443
 
 
444
 
 
445
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
446
 
 
447
 
    def get_format_string(self):
448
 
        return "Test Format 2"
 
576
class TestInterWeaveRepo(TestCaseWithTransport):
 
577
 
 
578
    def test_is_compatible_and_registered(self):
 
579
        # InterWeaveRepo is compatible when either side
 
580
        # is a format 5/6/7 branch
 
581
        from bzrlib.repofmt import knitrepo, weaverepo
 
582
        formats = [weaverepo.RepositoryFormat5(),
 
583
                   weaverepo.RepositoryFormat6(),
 
584
                   weaverepo.RepositoryFormat7()]
 
585
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
586
                                knitrepo.RepositoryFormatKnit1(),
 
587
                                ]
 
588
        repo_a = self.make_repository('a')
 
589
        repo_b = self.make_repository('b')
 
590
        is_compatible = repository.InterWeaveRepo.is_compatible
 
591
        for source in incompatible_formats:
 
592
            # force incompatible left then right
 
593
            repo_a._format = source
 
594
            repo_b._format = formats[0]
 
595
            self.assertFalse(is_compatible(repo_a, repo_b))
 
596
            self.assertFalse(is_compatible(repo_b, repo_a))
 
597
        for source in formats:
 
598
            repo_a._format = source
 
599
            for target in formats:
 
600
                repo_b._format = target
 
601
                self.assertTrue(is_compatible(repo_a, repo_b))
 
602
        self.assertEqual(repository.InterWeaveRepo,
 
603
                         repository.InterRepository.get(repo_a,
 
604
                                                        repo_b).__class__)
449
605
 
450
606
 
451
607
class TestRepositoryConverter(TestCaseWithTransport):
452
608
 
453
609
    def test_convert_empty(self):
454
 
        source_format = TestRepositoryFormat1()
455
 
        target_format = TestRepositoryFormat2()
456
 
        repository.format_registry.register(source_format)
457
 
        self.addCleanup(repository.format_registry.remove,
458
 
            source_format)
459
 
        repository.format_registry.register(target_format)
460
 
        self.addCleanup(repository.format_registry.remove,
461
 
            target_format)
462
 
        t = self.get_transport()
 
610
        t = get_transport(self.get_url('.'))
463
611
        t.mkdir('repository')
464
612
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
465
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
613
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
614
        target_format = knitrepo.RepositoryFormatKnit1()
466
615
        converter = repository.CopyConverter(target_format)
467
616
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
468
617
        try:
473
622
        self.assertTrue(isinstance(target_format, repo._format.__class__))
474
623
 
475
624
 
 
625
class TestMisc(TestCase):
 
626
 
 
627
    def test_unescape_xml(self):
 
628
        """We get some kind of error when malformed entities are passed"""
 
629
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
630
 
 
631
 
476
632
class TestRepositoryFormatKnit3(TestCaseWithTransport):
477
633
 
478
634
    def test_attribute__fetch_order(self):
525
681
        self.assertFalse(repo._format.supports_external_lookups)
526
682
 
527
683
 
528
 
class Test2a(tests.TestCaseWithMemoryTransport):
529
 
 
530
 
    def test_chk_bytes_uses_custom_btree_parser(self):
531
 
        mt = self.make_branch_and_memory_tree('test', format='2a')
532
 
        mt.lock_write()
533
 
        self.addCleanup(mt.unlock)
534
 
        mt.add([''], ['root-id'])
535
 
        mt.commit('first')
536
 
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
537
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
538
 
        # It should also work if we re-open the repo
539
 
        repo = mt.branch.repository.bzrdir.open_repository()
540
 
        repo.lock_read()
541
 
        self.addCleanup(repo.unlock)
542
 
        index = repo.chk_bytes._index._graph_index._indices[0]
543
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
544
 
 
545
 
    def test_fetch_combines_groups(self):
546
 
        builder = self.make_branch_builder('source', format='2a')
547
 
        builder.start_series()
548
 
        builder.build_snapshot('1', None, [
549
 
            ('add', ('', 'root-id', 'directory', '')),
550
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
551
 
        builder.build_snapshot('2', ['1'], [
552
 
            ('modify', ('file-id', 'content-2\n'))])
553
 
        builder.finish_series()
554
 
        source = builder.get_branch()
555
 
        target = self.make_repository('target', format='2a')
556
 
        target.fetch(source.repository)
557
 
        target.lock_read()
558
 
        self.addCleanup(target.unlock)
559
 
        details = target.texts._index.get_build_details(
560
 
            [('file-id', '1',), ('file-id', '2',)])
561
 
        file_1_details = details[('file-id', '1')]
562
 
        file_2_details = details[('file-id', '2')]
563
 
        # The index, and what to read off disk, should be the same for both
564
 
        # versions of the file.
565
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
566
 
 
567
 
    def test_fetch_combines_groups(self):
568
 
        builder = self.make_branch_builder('source', format='2a')
569
 
        builder.start_series()
570
 
        builder.build_snapshot('1', None, [
571
 
            ('add', ('', 'root-id', 'directory', '')),
572
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
573
 
        builder.build_snapshot('2', ['1'], [
574
 
            ('modify', ('file-id', 'content-2\n'))])
575
 
        builder.finish_series()
576
 
        source = builder.get_branch()
577
 
        target = self.make_repository('target', format='2a')
578
 
        target.fetch(source.repository)
579
 
        target.lock_read()
580
 
        self.addCleanup(target.unlock)
581
 
        details = target.texts._index.get_build_details(
582
 
            [('file-id', '1',), ('file-id', '2',)])
583
 
        file_1_details = details[('file-id', '1')]
584
 
        file_2_details = details[('file-id', '2')]
585
 
        # The index, and what to read off disk, should be the same for both
586
 
        # versions of the file.
587
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
684
class Test2a(TestCaseWithTransport):
588
685
 
589
686
    def test_fetch_combines_groups(self):
590
687
        builder = self.make_branch_builder('source', format='2a')
613
710
        self.assertTrue(repo._format.pack_compresses)
614
711
 
615
712
    def test_inventories_use_chk_map_with_parent_base_dict(self):
616
 
        tree = self.make_branch_and_memory_tree('repo', format="2a")
617
 
        tree.lock_write()
618
 
        tree.add([''], ['TREE_ROOT'])
 
713
        tree = self.make_branch_and_tree('repo', format="2a")
619
714
        revid = tree.commit("foo")
620
 
        tree.unlock()
621
715
        tree.lock_read()
622
716
        self.addCleanup(tree.unlock)
623
717
        inv = tree.branch.repository.get_inventory(revid)
632
726
        # at 20 unchanged commits, chk pages are packed that are split into
633
727
        # two groups such that the new pack being made doesn't have all its
634
728
        # pages in the source packs (though they are in the repository).
635
 
        # Use a memory backed repository, we don't need to hit disk for this
636
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
637
 
        tree.lock_write()
638
 
        self.addCleanup(tree.unlock)
639
 
        tree.add([''], ['TREE_ROOT'])
 
729
        tree = self.make_branch_and_tree('tree', format='2a')
640
730
        for pos in range(20):
641
731
            tree.commit(str(pos))
642
732
 
643
733
    def test_pack_with_hint(self):
644
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
645
 
        tree.lock_write()
646
 
        self.addCleanup(tree.unlock)
647
 
        tree.add([''], ['TREE_ROOT'])
 
734
        tree = self.make_branch_and_tree('tree', format='2a')
648
735
        # 1 commit to leave untouched
649
736
        tree.commit('1')
650
737
        to_keep = tree.branch.repository._pack_collection.names()
673
760
        target = self.make_repository('target', format='rich-root-pack')
674
761
        stream = source._get_source(target._format)
675
762
        # We don't want the child GroupCHKStreamSource
676
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
763
        self.assertIs(type(stream), repository.StreamSource)
677
764
 
678
765
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
679
766
        source_builder = self.make_branch_builder('source',
755
842
        source = self.make_repository('source', format='pack-0.92')
756
843
        target = self.make_repository('target', format='pack-0.92')
757
844
        stream_source = source._get_source(target._format)
758
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
845
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
759
846
 
760
847
    def test_source_to_exact_pack_rich_root_pack(self):
761
848
        source = self.make_repository('source', format='rich-root-pack')
762
849
        target = self.make_repository('target', format='rich-root-pack')
763
850
        stream_source = source._get_source(target._format)
764
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
851
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
765
852
 
766
853
    def test_source_to_exact_pack_19(self):
767
854
        source = self.make_repository('source', format='1.9')
768
855
        target = self.make_repository('target', format='1.9')
769
856
        stream_source = source._get_source(target._format)
770
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
857
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
771
858
 
772
859
    def test_source_to_exact_pack_19_rich_root(self):
773
860
        source = self.make_repository('source', format='1.9-rich-root')
774
861
        target = self.make_repository('target', format='1.9-rich-root')
775
862
        stream_source = source._get_source(target._format)
776
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
863
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
777
864
 
778
865
    def test_source_to_remote_exact_pack_19(self):
779
866
        trans = self.make_smart_server('target')
782
869
        target = self.make_repository('target', format='1.9')
783
870
        target = repository.Repository.open(trans.base)
784
871
        stream_source = source._get_source(target._format)
785
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
872
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
786
873
 
787
874
    def test_stream_source_to_non_exact(self):
788
875
        source = self.make_repository('source', format='pack-0.92')
789
876
        target = self.make_repository('target', format='1.9')
790
877
        stream = source._get_source(target._format)
791
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
878
        self.assertIs(type(stream), repository.StreamSource)
792
879
 
793
880
    def test_stream_source_to_non_exact_rich_root(self):
794
881
        source = self.make_repository('source', format='1.9')
795
882
        target = self.make_repository('target', format='1.9-rich-root')
796
883
        stream = source._get_source(target._format)
797
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
884
        self.assertIs(type(stream), repository.StreamSource)
798
885
 
799
886
    def test_source_to_remote_non_exact_pack_19(self):
800
887
        trans = self.make_smart_server('target')
803
890
        target = self.make_repository('target', format='1.6')
804
891
        target = repository.Repository.open(trans.base)
805
892
        stream_source = source._get_source(target._format)
806
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
893
        self.assertIs(type(stream_source), repository.StreamSource)
807
894
 
808
895
    def test_stream_source_to_knit(self):
809
896
        source = self.make_repository('source', format='pack-0.92')
810
897
        target = self.make_repository('target', format='dirstate')
811
898
        stream = source._get_source(target._format)
812
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
899
        self.assertIs(type(stream), repository.StreamSource)
813
900
 
814
901
 
815
902
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
817
904
 
818
905
    def setUp(self):
819
906
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
820
 
        self.builder = self.make_branch_builder('source')
 
907
        self.builder = self.make_branch_builder('source',
 
908
            format='development6-rich-root')
821
909
        self.builder.start_series()
822
910
        self.builder.build_snapshot('initial', None,
823
911
            [('add', ('', 'tree-root', 'directory', None))])
993
1081
        packs.ensure_loaded()
994
1082
        return tree, r, packs, [rev1, rev2, rev3]
995
1083
 
996
 
    def test__clear_obsolete_packs(self):
997
 
        packs = self.get_packs()
998
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
999
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1000
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1001
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1002
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1003
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1004
 
        res = packs._clear_obsolete_packs()
1005
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1006
 
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1007
 
 
1008
 
    def test__clear_obsolete_packs_preserve(self):
1009
 
        packs = self.get_packs()
1010
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1011
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1012
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1013
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1014
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1015
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1016
 
        res = packs._clear_obsolete_packs(preserve=set(['a-pack']))
1017
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1018
 
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1019
 
                         sorted(obsolete_pack_trans.list_dir('.')))
1020
 
 
1021
1084
    def test__max_pack_count(self):
1022
1085
        """The maximum pack count is a function of the number of revisions."""
1023
1086
        # no revisions - one pack, so that we can have a revision free repo
1043
1106
        # check some arbitrary big numbers
1044
1107
        self.assertEqual(25, packs._max_pack_count(112894))
1045
1108
 
1046
 
    def test_repr(self):
1047
 
        packs = self.get_packs()
1048
 
        self.assertContainsRe(repr(packs),
1049
 
            'RepositoryPackCollection(.*Repository(.*))')
1050
 
 
1051
 
    def test__obsolete_packs(self):
1052
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1053
 
        names = packs.names()
1054
 
        pack = packs.get_pack_by_name(names[0])
1055
 
        # Schedule this one for removal
1056
 
        packs._remove_pack_from_memory(pack)
1057
 
        # Simulate a concurrent update by renaming the .pack file and one of
1058
 
        # the indices
1059
 
        packs.transport.rename('packs/%s.pack' % (names[0],),
1060
 
                               'obsolete_packs/%s.pack' % (names[0],))
1061
 
        packs.transport.rename('indices/%s.iix' % (names[0],),
1062
 
                               'obsolete_packs/%s.iix' % (names[0],))
1063
 
        # Now trigger the obsoletion, and ensure that all the remaining files
1064
 
        # are still renamed
1065
 
        packs._obsolete_packs([pack])
1066
 
        self.assertEqual([n + '.pack' for n in names[1:]],
1067
 
                         sorted(packs._pack_transport.list_dir('.')))
1068
 
        # names[0] should not be present in the index anymore
1069
 
        self.assertEqual(names[1:],
1070
 
            sorted(set([osutils.splitext(n)[0] for n in
1071
 
                        packs._index_transport.list_dir('.')])))
1072
 
 
1073
1109
    def test_pack_distribution_zero(self):
1074
1110
        packs = self.get_packs()
1075
1111
        self.assertEqual([0], packs.pack_distribution(0))
1243
1279
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
1244
1280
        self.assertFalse(packs.reload_pack_names())
1245
1281
 
1246
 
    def test_reload_pack_names_preserves_pending(self):
1247
 
        # TODO: Update this to also test for pending-deleted names
1248
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1249
 
        # We will add one pack (via start_write_group + insert_record_stream),
1250
 
        # and remove another pack (via _remove_pack_from_memory)
1251
 
        orig_names = packs.names()
1252
 
        orig_at_load = packs._packs_at_load
1253
 
        to_remove_name = iter(orig_names).next()
1254
 
        r.start_write_group()
1255
 
        self.addCleanup(r.abort_write_group)
1256
 
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1257
 
            ('text', 'rev'), (), None, 'content\n')])
1258
 
        new_pack = packs._new_pack
1259
 
        self.assertTrue(new_pack.data_inserted())
1260
 
        new_pack.finish()
1261
 
        packs.allocate(new_pack)
1262
 
        packs._new_pack = None
1263
 
        removed_pack = packs.get_pack_by_name(to_remove_name)
1264
 
        packs._remove_pack_from_memory(removed_pack)
1265
 
        names = packs.names()
1266
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1267
 
        new_names = set([x[0][0] for x in new_nodes])
1268
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1269
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1270
 
        self.assertEqual(set([new_pack.name]), new_names)
1271
 
        self.assertEqual([to_remove_name],
1272
 
                         sorted([x[0][0] for x in deleted_nodes]))
1273
 
        packs.reload_pack_names()
1274
 
        reloaded_names = packs.names()
1275
 
        self.assertEqual(orig_at_load, packs._packs_at_load)
1276
 
        self.assertEqual(names, reloaded_names)
1277
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1278
 
        new_names = set([x[0][0] for x in new_nodes])
1279
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1280
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1281
 
        self.assertEqual(set([new_pack.name]), new_names)
1282
 
        self.assertEqual([to_remove_name],
1283
 
                         sorted([x[0][0] for x in deleted_nodes]))
1284
 
 
1285
 
    def test_autopack_obsoletes_new_pack(self):
1286
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1287
 
        packs._max_pack_count = lambda x: 1
1288
 
        packs.pack_distribution = lambda x: [10]
1289
 
        r.start_write_group()
1290
 
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1291
 
            ('bogus-rev',), (), None, 'bogus-content\n')])
1292
 
        # This should trigger an autopack, which will combine everything into a
1293
 
        # single pack file.
1294
 
        new_names = r.commit_write_group()
1295
 
        names = packs.names()
1296
 
        self.assertEqual(1, len(names))
1297
 
        self.assertEqual([names[0] + '.pack'],
1298
 
                         packs._pack_transport.list_dir('.'))
1299
 
 
1300
1282
    def test_autopack_reloads_and_stops(self):
1301
1283
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1302
1284
        # After we have determined what needs to be autopacked, trigger a
1314
1296
        self.assertEqual(tree.branch.repository._pack_collection.names(),
1315
1297
                         packs.names())
1316
1298
 
1317
 
    def test__save_pack_names(self):
1318
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1319
 
        names = packs.names()
1320
 
        pack = packs.get_pack_by_name(names[0])
1321
 
        packs._remove_pack_from_memory(pack)
1322
 
        packs._save_pack_names(obsolete_packs=[pack])
1323
 
        cur_packs = packs._pack_transport.list_dir('.')
1324
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1325
 
        # obsolete_packs will also have stuff like .rix and .iix present.
1326
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1327
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1328
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1329
 
 
1330
 
    def test__save_pack_names_already_obsoleted(self):
1331
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1332
 
        names = packs.names()
1333
 
        pack = packs.get_pack_by_name(names[0])
1334
 
        packs._remove_pack_from_memory(pack)
1335
 
        # We are going to simulate a concurrent autopack by manually obsoleting
1336
 
        # the pack directly.
1337
 
        packs._obsolete_packs([pack])
1338
 
        packs._save_pack_names(clear_obsolete_packs=True,
1339
 
                               obsolete_packs=[pack])
1340
 
        cur_packs = packs._pack_transport.list_dir('.')
1341
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1342
 
        # Note that while we set clear_obsolete_packs=True, it should not
1343
 
        # delete a pack file that we have also scheduled for obsoletion.
1344
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1345
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1346
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1347
 
 
1348
 
 
1349
1299
 
1350
1300
class TestPack(TestCaseWithTransport):
1351
1301
    """Tests for the Pack object."""
1415
1365
            index_class=BTreeGraphIndex,
1416
1366
            use_chk_index=False)
1417
1367
        pack = pack_repo.NewPack(collection)
1418
 
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
1419
1368
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1420
1369
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1421
1370
        self.assertIsInstance(pack._hash, type(osutils.md5()))
1451
1400
        # Because of how they were built, they correspond to
1452
1401
        # ['D', 'C', 'B', 'A']
1453
1402
        packs = b.repository._pack_collection.packs
1454
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1403
        packer = pack_repo.Packer(b.repository._pack_collection,
1455
1404
                                  packs, 'testing',
1456
1405
                                  revision_ids=['B', 'C'])
1457
1406
        # Now, when we are copying the B & C revisions, their pack files should
1471
1420
        return repo._pack_collection
1472
1421
 
1473
1422
    def test_open_pack_will_optimise(self):
1474
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1423
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1475
1424
                                            [], '.test')
1476
1425
        new_pack = packer.open_pack()
1477
 
        self.addCleanup(new_pack.abort) # ensure cleanup
1478
1426
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1479
1427
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1480
1428
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1482
1430
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1483
1431
 
1484
1432
 
1485
 
class TestGCCHKPacker(TestCaseWithTransport):
1486
 
 
1487
 
    def make_abc_branch(self):
1488
 
        builder = self.make_branch_builder('source')
1489
 
        builder.start_series()
1490
 
        builder.build_snapshot('A', None, [
1491
 
            ('add', ('', 'root-id', 'directory', None)),
1492
 
            ('add', ('file', 'file-id', 'file', 'content\n')),
1493
 
            ])
1494
 
        builder.build_snapshot('B', ['A'], [
1495
 
            ('add', ('dir', 'dir-id', 'directory', None))])
1496
 
        builder.build_snapshot('C', ['B'], [
1497
 
            ('modify', ('file-id', 'new content\n'))])
1498
 
        builder.finish_series()
1499
 
        return builder.get_branch()
1500
 
 
1501
 
    def make_branch_with_disjoint_inventory_and_revision(self):
1502
 
        """a repo with separate packs for a revisions Revision and Inventory.
1503
 
 
1504
 
        There will be one pack file that holds the Revision content, and one
1505
 
        for the Inventory content.
1506
 
 
1507
 
        :return: (repository,
1508
 
                  pack_name_with_rev_A_Revision,
1509
 
                  pack_name_with_rev_A_Inventory,
1510
 
                  pack_name_with_rev_C_content)
1511
 
        """
1512
 
        b_source = self.make_abc_branch()
1513
 
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1514
 
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1515
 
        b_stacked.lock_write()
1516
 
        self.addCleanup(b_stacked.unlock)
1517
 
        b_stacked.fetch(b_source, 'B')
1518
 
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1519
 
        # fill in the A rev.
1520
 
        repo_not_stacked = b_stacked.bzrdir.open_repository()
1521
 
        repo_not_stacked.lock_write()
1522
 
        self.addCleanup(repo_not_stacked.unlock)
1523
 
        # Now we should have a pack file with A's inventory, but not its
1524
 
        # Revision
1525
 
        self.assertEqual([('A',), ('B',)],
1526
 
                         sorted(repo_not_stacked.inventories.keys()))
1527
 
        self.assertEqual([('B',)],
1528
 
                         sorted(repo_not_stacked.revisions.keys()))
1529
 
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1530
 
        # We have a couple names here, figure out which has A's inventory
1531
 
        for name in stacked_pack_names:
1532
 
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1533
 
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1534
 
            if ('A',) in keys:
1535
 
                inv_a_pack_name = name
1536
 
                break
1537
 
        else:
1538
 
            self.fail('Could not find pack containing A\'s inventory')
1539
 
        repo_not_stacked.fetch(b_source.repository, 'A')
1540
 
        self.assertEqual([('A',), ('B',)],
1541
 
                         sorted(repo_not_stacked.revisions.keys()))
1542
 
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1543
 
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1544
 
        self.assertEqual(1, len(rev_a_pack_names))
1545
 
        rev_a_pack_name = list(rev_a_pack_names)[0]
1546
 
        # Now fetch 'C', so we have a couple pack files to join
1547
 
        repo_not_stacked.fetch(b_source.repository, 'C')
1548
 
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1549
 
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1550
 
        self.assertEqual(1, len(rev_c_pack_names))
1551
 
        rev_c_pack_name = list(rev_c_pack_names)[0]
1552
 
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1553
 
                rev_c_pack_name)
1554
 
 
1555
 
    def test_pack_with_distant_inventories(self):
1556
 
        # See https://bugs.launchpad.net/bzr/+bug/437003
1557
 
        # When repacking, it is possible to have an inventory in a different
1558
 
        # pack file than the associated revision. An autopack can then come
1559
 
        # along, and miss that inventory, and complain.
1560
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1561
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1562
 
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1563
 
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1564
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1565
 
                    [a_pack, c_pack], '.test-pack')
1566
 
        # This would raise ValueError in bug #437003, but should not raise an
1567
 
        # error once fixed.
1568
 
        packer.pack()
1569
 
 
1570
 
    def test_pack_with_missing_inventory(self):
1571
 
        # Similar to test_pack_with_missing_inventory, but this time, we force
1572
 
        # the A inventory to actually be gone from the repository.
1573
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1574
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1575
 
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1576
 
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1577
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1578
 
            repo._pack_collection.all_packs(), '.test-pack')
1579
 
        e = self.assertRaises(ValueError, packer.pack)
1580
 
        packer.new_pack.abort()
1581
 
        self.assertContainsRe(str(e),
1582
 
            r"We are missing inventories for revisions: .*'A'")
1583
 
 
1584
 
 
1585
1433
class TestCrossFormatPacks(TestCaseWithTransport):
1586
1434
 
1587
1435
    def log_pack(self, hint=None):
1602
1450
        self.addCleanup(target.unlock)
1603
1451
        source = source_tree.branch.repository._get_source(target._format)
1604
1452
        self.orig_pack = target.pack
1605
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1453
        target.pack = self.log_pack
1606
1454
        search = target.search_missing_revision_ids(
1607
 
            source_tree.branch.repository, revision_ids=[tip])
 
1455
            source_tree.branch.repository, tip)
1608
1456
        stream = source.get_stream(search)
1609
1457
        from_format = source_tree.branch.repository._format
1610
1458
        sink = target._get_sink()
1626
1474
        self.addCleanup(target.unlock)
1627
1475
        source = source_tree.branch.repository
1628
1476
        self.orig_pack = target.pack
1629
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1477
        target.pack = self.log_pack
1630
1478
        target.fetch(source)
1631
1479
        if expect_pack_called:
1632
1480
            self.assertLength(1, self.calls)