~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: John Ferlito
  • Date: 2009-09-02 04:31:45 UTC
  • mto: (4665.7.1 serve-init)
  • mto: This revision was merged to the branch mainline in revision 4913.
  • Revision ID: johnf@inodes.org-20090902043145-gxdsfw03ilcwbyn5
Add a debian init script for bzr --serve

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
65
    workingtree,
56
66
    )
57
67
from bzrlib.repofmt import (
58
68
    groupcompress_repo,
59
69
    knitrepo,
60
 
    knitpack_repo,
61
70
    pack_repo,
 
71
    weaverepo,
62
72
    )
63
73
 
64
74
 
67
77
    def test_get_set_default_format(self):
68
78
        old_default = bzrdir.format_registry.get('default')
69
79
        private_default = old_default().repository_format.__class__
70
 
        old_format = repository.format_registry.get_default()
 
80
        old_format = repository.RepositoryFormat.get_default_format()
71
81
        self.assertTrue(isinstance(old_format, private_default))
72
82
        def make_sample_bzrdir():
73
83
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
87
97
            bzrdir.format_registry.remove('default')
88
98
            bzrdir.format_registry.remove('sample')
89
99
            bzrdir.format_registry.register('default', old_default, '')
90
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
100
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
91
101
                              old_format.__class__)
92
102
 
93
103
 
115
125
        return "opened repository."
116
126
 
117
127
 
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
128
class TestRepositoryFormat(TestCaseWithTransport):
128
129
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
129
130
 
135
136
        def check_format(format, url):
136
137
            dir = format._matchingbzrdir.initialize(url)
137
138
            format.initialize(dir)
138
 
            t = transport.get_transport(url)
 
139
            t = get_transport(url)
139
140
            found_format = repository.RepositoryFormat.find_format(dir)
140
 
            self.assertIsInstance(found_format, format.__class__)
141
 
        check_format(repository.format_registry.get_default(), "bar")
 
141
            self.failUnless(isinstance(found_format, format.__class__))
 
142
        check_format(weaverepo.RepositoryFormat7(), "bar")
142
143
 
143
144
    def test_find_format_no_repository(self):
144
145
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
154
155
                          dir)
155
156
 
156
157
    def test_register_unregister_format(self):
157
 
        # Test deprecated format registration functions
158
158
        format = SampleRepositoryFormat()
159
159
        # make a control dir
160
160
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
161
161
        # make a repo
162
162
        format.initialize(dir)
163
163
        # register a format for it.
164
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
165
 
            repository.RepositoryFormat.register_format, format)
 
164
        repository.RepositoryFormat.register_format(format)
166
165
        # which repository.Open will refuse (not supported)
167
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
168
 
            self.get_url())
 
166
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
169
167
        # but open(unsupported) will work
170
168
        self.assertEqual(format.open(dir), "opened repository.")
171
169
        # 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)
 
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)
208
352
 
209
353
 
210
354
class TestFormatKnit1(TestCaseWithTransport):
312
456
        repo = self.make_repository('.',
313
457
                format=bzrdir.format_registry.get('knit')())
314
458
        inv_xml = '<inventory format="5">\n</inventory>\n'
315
 
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
459
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
316
460
        self.assertEqual('test-rev-id', inv.root.revision)
317
461
 
318
462
    def test_deserialise_uses_global_revision_id(self):
324
468
        # Arguably, the deserialise_inventory should detect a mismatch, and
325
469
        # raise an error, rather than silently using one revision_id over the
326
470
        # other.
327
 
        self.assertRaises(AssertionError, repo._deserialise_inventory,
 
471
        self.assertRaises(AssertionError, repo.deserialise_inventory,
328
472
            'test-rev-id', inv_xml)
329
 
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
473
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
330
474
        self.assertEqual('other-rev-id', inv.root.revision)
331
475
 
332
476
    def test_supports_external_lookups(self):
429
573
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
430
574
 
431
575
 
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"
 
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__)
442
605
 
443
606
 
444
607
class TestRepositoryConverter(TestCaseWithTransport):
445
608
 
446
609
    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
 
        t = self.get_transport()
 
610
        t = get_transport(self.get_url('.'))
456
611
        t.mkdir('repository')
457
612
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
458
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
613
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
614
        target_format = knitrepo.RepositoryFormatKnit1()
459
615
        converter = repository.CopyConverter(target_format)
460
616
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
461
617
        try:
466
622
        self.assertTrue(isinstance(target_format, repo._format.__class__))
467
623
 
468
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
 
469
632
class TestRepositoryFormatKnit3(TestCaseWithTransport):
470
633
 
471
634
    def test_attribute__fetch_order(self):
518
681
        self.assertFalse(repo._format.supports_external_lookups)
519
682
 
520
683
 
521
 
class Test2a(tests.TestCaseWithMemoryTransport):
522
 
 
523
 
    def test_chk_bytes_uses_custom_btree_parser(self):
524
 
        mt = self.make_branch_and_memory_tree('test', format='2a')
525
 
        mt.lock_write()
526
 
        self.addCleanup(mt.unlock)
527
 
        mt.add([''], ['root-id'])
528
 
        mt.commit('first')
529
 
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
530
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
531
 
        # It should also work if we re-open the repo
532
 
        repo = mt.branch.repository.bzrdir.open_repository()
533
 
        repo.lock_read()
534
 
        self.addCleanup(repo.unlock)
535
 
        index = repo.chk_bytes._index._graph_index._indices[0]
536
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
537
 
 
538
 
    def test_fetch_combines_groups(self):
539
 
        builder = self.make_branch_builder('source', format='2a')
540
 
        builder.start_series()
541
 
        builder.build_snapshot('1', None, [
542
 
            ('add', ('', 'root-id', 'directory', '')),
543
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
544
 
        builder.build_snapshot('2', ['1'], [
545
 
            ('modify', ('file-id', 'content-2\n'))])
546
 
        builder.finish_series()
547
 
        source = builder.get_branch()
548
 
        target = self.make_repository('target', format='2a')
549
 
        target.fetch(source.repository)
550
 
        target.lock_read()
551
 
        self.addCleanup(target.unlock)
552
 
        details = target.texts._index.get_build_details(
553
 
            [('file-id', '1',), ('file-id', '2',)])
554
 
        file_1_details = details[('file-id', '1')]
555
 
        file_2_details = details[('file-id', '2')]
556
 
        # The index, and what to read off disk, should be the same for both
557
 
        # versions of the file.
558
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
559
 
 
560
 
    def test_fetch_combines_groups(self):
561
 
        builder = self.make_branch_builder('source', format='2a')
562
 
        builder.start_series()
563
 
        builder.build_snapshot('1', None, [
564
 
            ('add', ('', 'root-id', 'directory', '')),
565
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
566
 
        builder.build_snapshot('2', ['1'], [
567
 
            ('modify', ('file-id', 'content-2\n'))])
568
 
        builder.finish_series()
569
 
        source = builder.get_branch()
570
 
        target = self.make_repository('target', format='2a')
571
 
        target.fetch(source.repository)
572
 
        target.lock_read()
573
 
        self.addCleanup(target.unlock)
574
 
        details = target.texts._index.get_build_details(
575
 
            [('file-id', '1',), ('file-id', '2',)])
576
 
        file_1_details = details[('file-id', '1')]
577
 
        file_2_details = details[('file-id', '2')]
578
 
        # The index, and what to read off disk, should be the same for both
579
 
        # versions of the file.
580
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
581
 
 
582
 
    def test_fetch_combines_groups(self):
583
 
        builder = self.make_branch_builder('source', format='2a')
584
 
        builder.start_series()
585
 
        builder.build_snapshot('1', None, [
586
 
            ('add', ('', 'root-id', 'directory', '')),
587
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
588
 
        builder.build_snapshot('2', ['1'], [
589
 
            ('modify', ('file-id', 'content-2\n'))])
590
 
        builder.finish_series()
591
 
        source = builder.get_branch()
592
 
        target = self.make_repository('target', format='2a')
593
 
        target.fetch(source.repository)
594
 
        target.lock_read()
595
 
        self.addCleanup(target.unlock)
596
 
        details = target.texts._index.get_build_details(
597
 
            [('file-id', '1',), ('file-id', '2',)])
598
 
        file_1_details = details[('file-id', '1')]
599
 
        file_2_details = details[('file-id', '2')]
600
 
        # The index, and what to read off disk, should be the same for both
601
 
        # versions of the file.
602
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
684
class Test2a(TestCaseWithTransport):
603
685
 
604
686
    def test_format_pack_compresses_True(self):
605
687
        repo = self.make_repository('repo', format='2a')
606
688
        self.assertTrue(repo._format.pack_compresses)
607
689
 
608
690
    def test_inventories_use_chk_map_with_parent_base_dict(self):
609
 
        tree = self.make_branch_and_memory_tree('repo', format="2a")
610
 
        tree.lock_write()
611
 
        tree.add([''], ['TREE_ROOT'])
 
691
        tree = self.make_branch_and_tree('repo', format="2a")
612
692
        revid = tree.commit("foo")
613
 
        tree.unlock()
614
693
        tree.lock_read()
615
694
        self.addCleanup(tree.unlock)
616
695
        inv = tree.branch.repository.get_inventory(revid)
625
704
        # at 20 unchanged commits, chk pages are packed that are split into
626
705
        # two groups such that the new pack being made doesn't have all its
627
706
        # pages in the source packs (though they are in the repository).
628
 
        # Use a memory backed repository, we don't need to hit disk for this
629
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
630
 
        tree.lock_write()
631
 
        self.addCleanup(tree.unlock)
632
 
        tree.add([''], ['TREE_ROOT'])
 
707
        tree = self.make_branch_and_tree('tree', format='2a')
633
708
        for pos in range(20):
634
709
            tree.commit(str(pos))
635
710
 
636
711
    def test_pack_with_hint(self):
637
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
638
 
        tree.lock_write()
639
 
        self.addCleanup(tree.unlock)
640
 
        tree.add([''], ['TREE_ROOT'])
 
712
        tree = self.make_branch_and_tree('tree', format='2a')
641
713
        # 1 commit to leave untouched
642
714
        tree.commit('1')
643
715
        to_keep = tree.branch.repository._pack_collection.names()
748
820
        source = self.make_repository('source', format='pack-0.92')
749
821
        target = self.make_repository('target', format='pack-0.92')
750
822
        stream_source = source._get_source(target._format)
751
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
823
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
752
824
 
753
825
    def test_source_to_exact_pack_rich_root_pack(self):
754
826
        source = self.make_repository('source', format='rich-root-pack')
755
827
        target = self.make_repository('target', format='rich-root-pack')
756
828
        stream_source = source._get_source(target._format)
757
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
829
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
758
830
 
759
831
    def test_source_to_exact_pack_19(self):
760
832
        source = self.make_repository('source', format='1.9')
761
833
        target = self.make_repository('target', format='1.9')
762
834
        stream_source = source._get_source(target._format)
763
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
835
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
764
836
 
765
837
    def test_source_to_exact_pack_19_rich_root(self):
766
838
        source = self.make_repository('source', format='1.9-rich-root')
767
839
        target = self.make_repository('target', format='1.9-rich-root')
768
840
        stream_source = source._get_source(target._format)
769
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
841
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
770
842
 
771
843
    def test_source_to_remote_exact_pack_19(self):
772
844
        trans = self.make_smart_server('target')
775
847
        target = self.make_repository('target', format='1.9')
776
848
        target = repository.Repository.open(trans.base)
777
849
        stream_source = source._get_source(target._format)
778
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
850
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
779
851
 
780
852
    def test_stream_source_to_non_exact(self):
781
853
        source = self.make_repository('source', format='pack-0.92')
810
882
 
811
883
    def setUp(self):
812
884
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
813
 
        self.builder = self.make_branch_builder('source')
 
885
        self.builder = self.make_branch_builder('source',
 
886
            format='development6-rich-root')
814
887
        self.builder.start_series()
815
888
        self.builder.build_snapshot('initial', None,
816
889
            [('add', ('', 'tree-root', 'directory', None))])
881
954
            inv = inventory.Inventory(revision_id='rev1a')
882
955
            inv.root.revision = 'rev1a'
883
956
            self.add_file(repo, inv, 'file1', 'rev1a', [])
884
 
            repo.texts.add_lines((inv.root.file_id, 'rev1a'), [], [])
885
957
            repo.add_inventory('rev1a', inv, [])
886
958
            revision = _mod_revision.Revision('rev1a',
887
959
                committer='jrandom@example.com', timestamp=0,
922
994
    def add_revision(self, repo, revision_id, inv, parent_ids):
923
995
        inv.revision_id = revision_id
924
996
        inv.root.revision = revision_id
925
 
        repo.texts.add_lines((inv.root.file_id, revision_id), [], [])
926
997
        repo.add_inventory(revision_id, inv, parent_ids)
927
998
        revision = _mod_revision.Revision(revision_id,
928
999
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
986
1057
        packs.ensure_loaded()
987
1058
        return tree, r, packs, [rev1, rev2, rev3]
988
1059
 
989
 
    def test__clear_obsolete_packs(self):
990
 
        packs = self.get_packs()
991
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
992
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
993
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
994
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
995
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
996
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
997
 
        res = packs._clear_obsolete_packs()
998
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
999
 
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1000
 
 
1001
 
    def test__clear_obsolete_packs_preserve(self):
1002
 
        packs = self.get_packs()
1003
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1004
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1005
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1006
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1007
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1008
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1009
 
        res = packs._clear_obsolete_packs(preserve=set(['a-pack']))
1010
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1011
 
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1012
 
                         sorted(obsolete_pack_trans.list_dir('.')))
1013
 
 
1014
1060
    def test__max_pack_count(self):
1015
1061
        """The maximum pack count is a function of the number of revisions."""
1016
1062
        # no revisions - one pack, so that we can have a revision free repo
1036
1082
        # check some arbitrary big numbers
1037
1083
        self.assertEqual(25, packs._max_pack_count(112894))
1038
1084
 
1039
 
    def test_repr(self):
1040
 
        packs = self.get_packs()
1041
 
        self.assertContainsRe(repr(packs),
1042
 
            'RepositoryPackCollection(.*Repository(.*))')
1043
 
 
1044
 
    def test__obsolete_packs(self):
1045
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1046
 
        names = packs.names()
1047
 
        pack = packs.get_pack_by_name(names[0])
1048
 
        # Schedule this one for removal
1049
 
        packs._remove_pack_from_memory(pack)
1050
 
        # Simulate a concurrent update by renaming the .pack file and one of
1051
 
        # the indices
1052
 
        packs.transport.rename('packs/%s.pack' % (names[0],),
1053
 
                               'obsolete_packs/%s.pack' % (names[0],))
1054
 
        packs.transport.rename('indices/%s.iix' % (names[0],),
1055
 
                               'obsolete_packs/%s.iix' % (names[0],))
1056
 
        # Now trigger the obsoletion, and ensure that all the remaining files
1057
 
        # are still renamed
1058
 
        packs._obsolete_packs([pack])
1059
 
        self.assertEqual([n + '.pack' for n in names[1:]],
1060
 
                         sorted(packs._pack_transport.list_dir('.')))
1061
 
        # names[0] should not be present in the index anymore
1062
 
        self.assertEqual(names[1:],
1063
 
            sorted(set([osutils.splitext(n)[0] for n in
1064
 
                        packs._index_transport.list_dir('.')])))
1065
 
 
1066
1085
    def test_pack_distribution_zero(self):
1067
1086
        packs = self.get_packs()
1068
1087
        self.assertEqual([0], packs.pack_distribution(0))
1236
1255
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
1237
1256
        self.assertFalse(packs.reload_pack_names())
1238
1257
 
1239
 
    def test_reload_pack_names_preserves_pending(self):
1240
 
        # TODO: Update this to also test for pending-deleted names
1241
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1242
 
        # We will add one pack (via start_write_group + insert_record_stream),
1243
 
        # and remove another pack (via _remove_pack_from_memory)
1244
 
        orig_names = packs.names()
1245
 
        orig_at_load = packs._packs_at_load
1246
 
        to_remove_name = iter(orig_names).next()
1247
 
        r.start_write_group()
1248
 
        self.addCleanup(r.abort_write_group)
1249
 
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1250
 
            ('text', 'rev'), (), None, 'content\n')])
1251
 
        new_pack = packs._new_pack
1252
 
        self.assertTrue(new_pack.data_inserted())
1253
 
        new_pack.finish()
1254
 
        packs.allocate(new_pack)
1255
 
        packs._new_pack = None
1256
 
        removed_pack = packs.get_pack_by_name(to_remove_name)
1257
 
        packs._remove_pack_from_memory(removed_pack)
1258
 
        names = packs.names()
1259
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1260
 
        new_names = set([x[0][0] for x in new_nodes])
1261
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1262
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1263
 
        self.assertEqual(set([new_pack.name]), new_names)
1264
 
        self.assertEqual([to_remove_name],
1265
 
                         sorted([x[0][0] for x in deleted_nodes]))
1266
 
        packs.reload_pack_names()
1267
 
        reloaded_names = packs.names()
1268
 
        self.assertEqual(orig_at_load, packs._packs_at_load)
1269
 
        self.assertEqual(names, reloaded_names)
1270
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1271
 
        new_names = set([x[0][0] for x in new_nodes])
1272
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1273
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1274
 
        self.assertEqual(set([new_pack.name]), new_names)
1275
 
        self.assertEqual([to_remove_name],
1276
 
                         sorted([x[0][0] for x in deleted_nodes]))
1277
 
 
1278
 
    def test_autopack_obsoletes_new_pack(self):
1279
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1280
 
        packs._max_pack_count = lambda x: 1
1281
 
        packs.pack_distribution = lambda x: [10]
1282
 
        r.start_write_group()
1283
 
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1284
 
            ('bogus-rev',), (), None, 'bogus-content\n')])
1285
 
        # This should trigger an autopack, which will combine everything into a
1286
 
        # single pack file.
1287
 
        new_names = r.commit_write_group()
1288
 
        names = packs.names()
1289
 
        self.assertEqual(1, len(names))
1290
 
        self.assertEqual([names[0] + '.pack'],
1291
 
                         packs._pack_transport.list_dir('.'))
1292
 
 
1293
1258
    def test_autopack_reloads_and_stops(self):
1294
1259
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1295
1260
        # After we have determined what needs to be autopacked, trigger a
1307
1272
        self.assertEqual(tree.branch.repository._pack_collection.names(),
1308
1273
                         packs.names())
1309
1274
 
1310
 
    def test__save_pack_names(self):
1311
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1312
 
        names = packs.names()
1313
 
        pack = packs.get_pack_by_name(names[0])
1314
 
        packs._remove_pack_from_memory(pack)
1315
 
        packs._save_pack_names(obsolete_packs=[pack])
1316
 
        cur_packs = packs._pack_transport.list_dir('.')
1317
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1318
 
        # obsolete_packs will also have stuff like .rix and .iix present.
1319
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1320
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1321
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1322
 
 
1323
 
    def test__save_pack_names_already_obsoleted(self):
1324
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1325
 
        names = packs.names()
1326
 
        pack = packs.get_pack_by_name(names[0])
1327
 
        packs._remove_pack_from_memory(pack)
1328
 
        # We are going to simulate a concurrent autopack by manually obsoleting
1329
 
        # the pack directly.
1330
 
        packs._obsolete_packs([pack])
1331
 
        packs._save_pack_names(clear_obsolete_packs=True,
1332
 
                               obsolete_packs=[pack])
1333
 
        cur_packs = packs._pack_transport.list_dir('.')
1334
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1335
 
        # Note that while we set clear_obsolete_packs=True, it should not
1336
 
        # delete a pack file that we have also scheduled for obsoletion.
1337
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1338
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1339
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1340
 
 
1341
 
 
1342
1275
 
1343
1276
class TestPack(TestCaseWithTransport):
1344
1277
    """Tests for the Pack object."""
1408
1341
            index_class=BTreeGraphIndex,
1409
1342
            use_chk_index=False)
1410
1343
        pack = pack_repo.NewPack(collection)
1411
 
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
1412
1344
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1413
1345
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1414
1346
        self.assertIsInstance(pack._hash, type(osutils.md5()))
1444
1376
        # Because of how they were built, they correspond to
1445
1377
        # ['D', 'C', 'B', 'A']
1446
1378
        packs = b.repository._pack_collection.packs
1447
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1379
        packer = pack_repo.Packer(b.repository._pack_collection,
1448
1380
                                  packs, 'testing',
1449
1381
                                  revision_ids=['B', 'C'])
1450
1382
        # Now, when we are copying the B & C revisions, their pack files should
1464
1396
        return repo._pack_collection
1465
1397
 
1466
1398
    def test_open_pack_will_optimise(self):
1467
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1399
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1468
1400
                                            [], '.test')
1469
1401
        new_pack = packer.open_pack()
1470
 
        self.addCleanup(new_pack.abort) # ensure cleanup
1471
1402
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1472
1403
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1473
1404
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1475
1406
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1476
1407
 
1477
1408
 
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
1409
class TestCrossFormatPacks(TestCaseWithTransport):
1579
1410
 
1580
1411
    def log_pack(self, hint=None):
1597
1428
        self.orig_pack = target.pack
1598
1429
        target.pack = self.log_pack
1599
1430
        search = target.search_missing_revision_ids(
1600
 
            source_tree.branch.repository, revision_ids=[tip])
 
1431
            source_tree.branch.repository, tip)
1601
1432
        stream = source.get_stream(search)
1602
1433
        from_format = source_tree.branch.repository._format
1603
1434
        sink = target._get_sink()