~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: John Arbash Meinel
  • Date: 2010-02-16 16:08:40 UTC
  • mfrom: (4797.2.15 2.1)
  • mto: (4797.2.16 2.1)
  • mto: This revision was merged to the branch mainline in revision 5037.
  • Revision ID: john@arbash-meinel.com-20100216160840-xwbpuu0v89gq8lej
Tags: bzr-2.1.0
bring in the latest 2.1 changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
 
26
from StringIO import StringIO
 
27
import sys
26
28
 
27
29
import bzrlib
28
 
from bzrlib.errors import (
29
 
    UnknownFormatError,
30
 
    UnsupportedFormatError,
31
 
    )
 
30
from bzrlib.errors import (NotBranchError,
 
31
                           NoSuchFile,
 
32
                           UnknownFormatError,
 
33
                           UnsupportedFormatError,
 
34
                           )
32
35
from bzrlib import (
33
 
    btree_index,
34
36
    graph,
35
 
    symbol_versioning,
36
37
    tests,
37
 
    transport,
38
38
    )
 
39
from bzrlib.branchbuilder import BranchBuilder
39
40
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
40
 
from bzrlib.index import GraphIndex
 
41
from bzrlib.index import GraphIndex, InMemoryGraphIndex
41
42
from bzrlib.repository import RepositoryFormat
 
43
from bzrlib.smart import server
42
44
from bzrlib.tests import (
43
45
    TestCase,
44
46
    TestCaseWithTransport,
45
 
    )
 
47
    TestSkipped,
 
48
    test_knit,
 
49
    )
 
50
from bzrlib.transport import (
 
51
    fakenfs,
 
52
    get_transport,
 
53
    )
 
54
from bzrlib.transport.memory import MemoryServer
46
55
from bzrlib import (
 
56
    bencode,
47
57
    bzrdir,
48
58
    errors,
49
59
    inventory,
50
60
    osutils,
 
61
    progress,
51
62
    repository,
52
63
    revision as _mod_revision,
 
64
    symbol_versioning,
53
65
    upgrade,
54
66
    versionedfile,
55
 
    vf_repository,
56
67
    workingtree,
57
68
    )
58
69
from bzrlib.repofmt import (
59
70
    groupcompress_repo,
60
71
    knitrepo,
61
 
    knitpack_repo,
62
72
    pack_repo,
 
73
    weaverepo,
63
74
    )
64
75
 
65
76
 
68
79
    def test_get_set_default_format(self):
69
80
        old_default = bzrdir.format_registry.get('default')
70
81
        private_default = old_default().repository_format.__class__
71
 
        old_format = repository.format_registry.get_default()
 
82
        old_format = repository.RepositoryFormat.get_default_format()
72
83
        self.assertTrue(isinstance(old_format, private_default))
73
84
        def make_sample_bzrdir():
74
85
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
88
99
            bzrdir.format_registry.remove('default')
89
100
            bzrdir.format_registry.remove('sample')
90
101
            bzrdir.format_registry.register('default', old_default, '')
91
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
102
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
92
103
                              old_format.__class__)
93
104
 
94
105
 
116
127
        return "opened repository."
117
128
 
118
129
 
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
130
class TestRepositoryFormat(TestCaseWithTransport):
129
131
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
130
132
 
136
138
        def check_format(format, url):
137
139
            dir = format._matchingbzrdir.initialize(url)
138
140
            format.initialize(dir)
139
 
            t = transport.get_transport(url)
 
141
            t = get_transport(url)
140
142
            found_format = repository.RepositoryFormat.find_format(dir)
141
 
            self.assertIsInstance(found_format, format.__class__)
142
 
        check_format(repository.format_registry.get_default(), "bar")
 
143
            self.failUnless(isinstance(found_format, format.__class__))
 
144
        check_format(weaverepo.RepositoryFormat7(), "bar")
143
145
 
144
146
    def test_find_format_no_repository(self):
145
147
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
155
157
                          dir)
156
158
 
157
159
    def test_register_unregister_format(self):
158
 
        # Test deprecated format registration functions
159
160
        format = SampleRepositoryFormat()
160
161
        # make a control dir
161
162
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
163
        # make a repo
163
164
        format.initialize(dir)
164
165
        # register a format for it.
165
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
166
 
            repository.RepositoryFormat.register_format, format)
 
166
        repository.RepositoryFormat.register_format(format)
167
167
        # which repository.Open will refuse (not supported)
168
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
169
 
            self.get_url())
 
168
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
170
169
        # but open(unsupported) will work
171
170
        self.assertEqual(format.open(dir), "opened repository.")
172
171
        # 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)
 
172
        repository.RepositoryFormat.unregister_format(format)
 
173
 
 
174
 
 
175
class TestFormat6(TestCaseWithTransport):
 
176
 
 
177
    def test_attribute__fetch_order(self):
 
178
        """Weaves need topological data insertion."""
 
179
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
180
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
181
        self.assertEqual('topological', repo._format._fetch_order)
 
182
 
 
183
    def test_attribute__fetch_uses_deltas(self):
 
184
        """Weaves do not reuse deltas."""
 
185
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
186
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
187
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
188
 
 
189
    def test_attribute__fetch_reconcile(self):
 
190
        """Weave repositories need a reconcile after fetch."""
 
191
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
192
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
193
        self.assertEqual(True, repo._format._fetch_reconcile)
 
194
 
 
195
    def test_no_ancestry_weave(self):
 
196
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
197
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
198
        # We no longer need to create the ancestry.weave file
 
199
        # since it is *never* used.
 
200
        self.assertRaises(NoSuchFile,
 
201
                          control.transport.get,
 
202
                          'ancestry.weave')
 
203
 
 
204
    def test_supports_external_lookups(self):
 
205
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
206
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
207
        self.assertFalse(repo._format.supports_external_lookups)
 
208
 
 
209
 
 
210
class TestFormat7(TestCaseWithTransport):
 
211
 
 
212
    def test_attribute__fetch_order(self):
 
213
        """Weaves need topological data insertion."""
 
214
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
215
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
216
        self.assertEqual('topological', repo._format._fetch_order)
 
217
 
 
218
    def test_attribute__fetch_uses_deltas(self):
 
219
        """Weaves do not reuse deltas."""
 
220
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
221
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
222
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
223
 
 
224
    def test_attribute__fetch_reconcile(self):
 
225
        """Weave repositories need a reconcile after fetch."""
 
226
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
227
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
228
        self.assertEqual(True, repo._format._fetch_reconcile)
 
229
 
 
230
    def test_disk_layout(self):
 
231
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
232
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
233
        # in case of side effects of locking.
 
234
        repo.lock_write()
 
235
        repo.unlock()
 
236
        # we want:
 
237
        # format 'Bazaar-NG Repository format 7'
 
238
        # lock ''
 
239
        # inventory.weave == empty_weave
 
240
        # empty revision-store directory
 
241
        # empty weaves directory
 
242
        t = control.get_repository_transport(None)
 
243
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
244
                             t.get('format').read())
 
245
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
246
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
247
        self.assertEqualDiff('# bzr weave file v5\n'
 
248
                             'w\n'
 
249
                             'W\n',
 
250
                             t.get('inventory.weave').read())
 
251
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
252
        # disk.
 
253
        control.create_branch()
 
254
        tree = control.create_workingtree()
 
255
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
256
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
257
        try:
 
258
            tree.commit('first post', rev_id='first')
 
259
        except errors.IllegalPath:
 
260
            if sys.platform != 'win32':
 
261
                raise
 
262
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
 
263
                              ' in repo format 7')
 
264
            return
 
265
        self.assertEqualDiff(
 
266
            '# bzr weave file v5\n'
 
267
            'i\n'
 
268
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
269
            'n first\n'
 
270
            '\n'
 
271
            'w\n'
 
272
            '{ 0\n'
 
273
            '. content\n'
 
274
            '}\n'
 
275
            'W\n',
 
276
            t.get('weaves/74/Foo%3ABar.weave').read())
 
277
 
 
278
    def test_shared_disk_layout(self):
 
279
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
280
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
281
        # we want:
 
282
        # format 'Bazaar-NG Repository format 7'
 
283
        # inventory.weave == empty_weave
 
284
        # empty revision-store directory
 
285
        # empty weaves directory
 
286
        # a 'shared-storage' marker file.
 
287
        # lock is not present when unlocked
 
288
        t = control.get_repository_transport(None)
 
289
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
290
                             t.get('format').read())
 
291
        self.assertEqualDiff('', t.get('shared-storage').read())
 
292
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
293
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
294
        self.assertEqualDiff('# bzr weave file v5\n'
 
295
                             'w\n'
 
296
                             'W\n',
 
297
                             t.get('inventory.weave').read())
 
298
        self.assertFalse(t.has('branch-lock'))
 
299
 
 
300
    def test_creates_lockdir(self):
 
301
        """Make sure it appears to be controlled by a LockDir existence"""
 
302
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
303
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
304
        t = control.get_repository_transport(None)
 
305
        # TODO: Should check there is a 'lock' toplevel directory,
 
306
        # regardless of contents
 
307
        self.assertFalse(t.has('lock/held/info'))
 
308
        repo.lock_write()
 
309
        try:
 
310
            self.assertTrue(t.has('lock/held/info'))
 
311
        finally:
 
312
            # unlock so we don't get a warning about failing to do so
 
313
            repo.unlock()
 
314
 
 
315
    def test_uses_lockdir(self):
 
316
        """repo format 7 actually locks on lockdir"""
 
317
        base_url = self.get_url()
 
318
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
319
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
320
        t = control.get_repository_transport(None)
 
321
        repo.lock_write()
 
322
        repo.unlock()
 
323
        del repo
 
324
        # make sure the same lock is created by opening it
 
325
        repo = repository.Repository.open(base_url)
 
326
        repo.lock_write()
 
327
        self.assertTrue(t.has('lock/held/info'))
 
328
        repo.unlock()
 
329
        self.assertFalse(t.has('lock/held/info'))
 
330
 
 
331
    def test_shared_no_tree_disk_layout(self):
 
332
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
333
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
334
        repo.set_make_working_trees(False)
 
335
        # we want:
 
336
        # format 'Bazaar-NG Repository format 7'
 
337
        # lock ''
 
338
        # inventory.weave == empty_weave
 
339
        # empty revision-store directory
 
340
        # empty weaves directory
 
341
        # a 'shared-storage' marker file.
 
342
        t = control.get_repository_transport(None)
 
343
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
344
                             t.get('format').read())
 
345
        ## self.assertEqualDiff('', t.get('lock').read())
 
346
        self.assertEqualDiff('', t.get('shared-storage').read())
 
347
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
348
        repo.set_make_working_trees(True)
 
349
        self.assertFalse(t.has('no-working-trees'))
 
350
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
351
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
352
        self.assertEqualDiff('# bzr weave file v5\n'
 
353
                             'w\n'
 
354
                             'W\n',
 
355
                             t.get('inventory.weave').read())
 
356
 
 
357
    def test_supports_external_lookups(self):
 
358
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
359
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
360
        self.assertFalse(repo._format.supports_external_lookups)
209
361
 
210
362
 
211
363
class TestFormatKnit1(TestCaseWithTransport):
313
465
        repo = self.make_repository('.',
314
466
                format=bzrdir.format_registry.get('knit')())
315
467
        inv_xml = '<inventory format="5">\n</inventory>\n'
316
 
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
468
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
317
469
        self.assertEqual('test-rev-id', inv.root.revision)
318
470
 
319
471
    def test_deserialise_uses_global_revision_id(self):
325
477
        # Arguably, the deserialise_inventory should detect a mismatch, and
326
478
        # raise an error, rather than silently using one revision_id over the
327
479
        # other.
328
 
        self.assertRaises(AssertionError, repo._deserialise_inventory,
 
480
        self.assertRaises(AssertionError, repo.deserialise_inventory,
329
481
            'test-rev-id', inv_xml)
330
 
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
482
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
331
483
        self.assertEqual('other-rev-id', inv.root.revision)
332
484
 
333
485
    def test_supports_external_lookups(self):
379
531
        # classes do not barf inappropriately when a surprising repository type
380
532
        # is handed to them.
381
533
        dummy_a = DummyRepository()
382
 
        dummy_a._format = RepositoryFormat()
383
 
        dummy_a._format.supports_full_versioned_files = True
384
534
        dummy_b = DummyRepository()
385
 
        dummy_b._format = RepositoryFormat()
386
 
        dummy_b._format.supports_full_versioned_files = True
387
535
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
388
536
 
389
537
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
393
541
        no actual sane default in the presence of incompatible data models.
394
542
        """
395
543
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
396
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
544
        self.assertEqual(repository.InterSameDataRepository,
397
545
                         inter_repo.__class__)
398
546
        self.assertEqual(repo_a, inter_repo.source)
399
547
        self.assertEqual(repo_b, inter_repo.target)
413
561
        dummy_a._serializer = repo._serializer
414
562
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
415
563
        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
564
        dummy_b._serializer = repo._serializer
418
565
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
419
566
        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
567
        repository.InterRepository.register_optimiser(InterDummy)
422
568
        try:
423
569
            # we should get the default for something InterDummy returns False
436
582
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
437
583
 
438
584
 
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"
 
585
class TestInterWeaveRepo(TestCaseWithTransport):
 
586
 
 
587
    def test_is_compatible_and_registered(self):
 
588
        # InterWeaveRepo is compatible when either side
 
589
        # is a format 5/6/7 branch
 
590
        from bzrlib.repofmt import knitrepo, weaverepo
 
591
        formats = [weaverepo.RepositoryFormat5(),
 
592
                   weaverepo.RepositoryFormat6(),
 
593
                   weaverepo.RepositoryFormat7()]
 
594
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
595
                                knitrepo.RepositoryFormatKnit1(),
 
596
                                ]
 
597
        repo_a = self.make_repository('a')
 
598
        repo_b = self.make_repository('b')
 
599
        is_compatible = repository.InterWeaveRepo.is_compatible
 
600
        for source in incompatible_formats:
 
601
            # force incompatible left then right
 
602
            repo_a._format = source
 
603
            repo_b._format = formats[0]
 
604
            self.assertFalse(is_compatible(repo_a, repo_b))
 
605
            self.assertFalse(is_compatible(repo_b, repo_a))
 
606
        for source in formats:
 
607
            repo_a._format = source
 
608
            for target in formats:
 
609
                repo_b._format = target
 
610
                self.assertTrue(is_compatible(repo_a, repo_b))
 
611
        self.assertEqual(repository.InterWeaveRepo,
 
612
                         repository.InterRepository.get(repo_a,
 
613
                                                        repo_b).__class__)
449
614
 
450
615
 
451
616
class TestRepositoryConverter(TestCaseWithTransport):
452
617
 
453
618
    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()
 
619
        t = get_transport(self.get_url('.'))
463
620
        t.mkdir('repository')
464
621
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
465
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
622
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
623
        target_format = knitrepo.RepositoryFormatKnit1()
466
624
        converter = repository.CopyConverter(target_format)
467
625
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
468
626
        try:
473
631
        self.assertTrue(isinstance(target_format, repo._format.__class__))
474
632
 
475
633
 
 
634
class TestMisc(TestCase):
 
635
 
 
636
    def test_unescape_xml(self):
 
637
        """We get some kind of error when malformed entities are passed"""
 
638
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
639
 
 
640
 
476
641
class TestRepositoryFormatKnit3(TestCaseWithTransport):
477
642
 
478
643
    def test_attribute__fetch_order(self):
527
692
 
528
693
class Test2a(tests.TestCaseWithMemoryTransport):
529
694
 
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
695
    def test_fetch_combines_groups(self):
546
696
        builder = self.make_branch_builder('source', format='2a')
547
697
        builder.start_series()
673
823
        target = self.make_repository('target', format='rich-root-pack')
674
824
        stream = source._get_source(target._format)
675
825
        # We don't want the child GroupCHKStreamSource
676
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
826
        self.assertIs(type(stream), repository.StreamSource)
677
827
 
678
828
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
679
829
        source_builder = self.make_branch_builder('source',
755
905
        source = self.make_repository('source', format='pack-0.92')
756
906
        target = self.make_repository('target', format='pack-0.92')
757
907
        stream_source = source._get_source(target._format)
758
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
908
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
759
909
 
760
910
    def test_source_to_exact_pack_rich_root_pack(self):
761
911
        source = self.make_repository('source', format='rich-root-pack')
762
912
        target = self.make_repository('target', format='rich-root-pack')
763
913
        stream_source = source._get_source(target._format)
764
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
914
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
765
915
 
766
916
    def test_source_to_exact_pack_19(self):
767
917
        source = self.make_repository('source', format='1.9')
768
918
        target = self.make_repository('target', format='1.9')
769
919
        stream_source = source._get_source(target._format)
770
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
920
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
771
921
 
772
922
    def test_source_to_exact_pack_19_rich_root(self):
773
923
        source = self.make_repository('source', format='1.9-rich-root')
774
924
        target = self.make_repository('target', format='1.9-rich-root')
775
925
        stream_source = source._get_source(target._format)
776
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
926
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
777
927
 
778
928
    def test_source_to_remote_exact_pack_19(self):
779
929
        trans = self.make_smart_server('target')
782
932
        target = self.make_repository('target', format='1.9')
783
933
        target = repository.Repository.open(trans.base)
784
934
        stream_source = source._get_source(target._format)
785
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
935
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
786
936
 
787
937
    def test_stream_source_to_non_exact(self):
788
938
        source = self.make_repository('source', format='pack-0.92')
789
939
        target = self.make_repository('target', format='1.9')
790
940
        stream = source._get_source(target._format)
791
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
941
        self.assertIs(type(stream), repository.StreamSource)
792
942
 
793
943
    def test_stream_source_to_non_exact_rich_root(self):
794
944
        source = self.make_repository('source', format='1.9')
795
945
        target = self.make_repository('target', format='1.9-rich-root')
796
946
        stream = source._get_source(target._format)
797
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
947
        self.assertIs(type(stream), repository.StreamSource)
798
948
 
799
949
    def test_source_to_remote_non_exact_pack_19(self):
800
950
        trans = self.make_smart_server('target')
803
953
        target = self.make_repository('target', format='1.6')
804
954
        target = repository.Repository.open(trans.base)
805
955
        stream_source = source._get_source(target._format)
806
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
956
        self.assertIs(type(stream_source), repository.StreamSource)
807
957
 
808
958
    def test_stream_source_to_knit(self):
809
959
        source = self.make_repository('source', format='pack-0.92')
810
960
        target = self.make_repository('target', format='dirstate')
811
961
        stream = source._get_source(target._format)
812
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
962
        self.assertIs(type(stream), repository.StreamSource)
813
963
 
814
964
 
815
965
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
817
967
 
818
968
    def setUp(self):
819
969
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
820
 
        self.builder = self.make_branch_builder('source')
 
970
        self.builder = self.make_branch_builder('source',
 
971
            format='development6-rich-root')
821
972
        self.builder.start_series()
822
973
        self.builder.build_snapshot('initial', None,
823
974
            [('add', ('', 'tree-root', 'directory', None))])
1451
1602
        # Because of how they were built, they correspond to
1452
1603
        # ['D', 'C', 'B', 'A']
1453
1604
        packs = b.repository._pack_collection.packs
1454
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1605
        packer = pack_repo.Packer(b.repository._pack_collection,
1455
1606
                                  packs, 'testing',
1456
1607
                                  revision_ids=['B', 'C'])
1457
1608
        # Now, when we are copying the B & C revisions, their pack files should
1471
1622
        return repo._pack_collection
1472
1623
 
1473
1624
    def test_open_pack_will_optimise(self):
1474
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1625
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1475
1626
                                            [], '.test')
1476
1627
        new_pack = packer.open_pack()
1477
1628
        self.addCleanup(new_pack.abort) # ensure cleanup
1482
1633
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1483
1634
 
1484
1635
 
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
1636
class TestCrossFormatPacks(TestCaseWithTransport):
1586
1637
 
1587
1638
    def log_pack(self, hint=None):
1602
1653
        self.addCleanup(target.unlock)
1603
1654
        source = source_tree.branch.repository._get_source(target._format)
1604
1655
        self.orig_pack = target.pack
1605
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1656
        target.pack = self.log_pack
1606
1657
        search = target.search_missing_revision_ids(
1607
 
            source_tree.branch.repository, revision_ids=[tip])
 
1658
            source_tree.branch.repository, tip)
1608
1659
        stream = source.get_stream(search)
1609
1660
        from_format = source_tree.branch.repository._format
1610
1661
        sink = target._get_sink()
1626
1677
        self.addCleanup(target.unlock)
1627
1678
        source = source_tree.branch.repository
1628
1679
        self.orig_pack = target.pack
1629
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1680
        target.pack = self.log_pack
1630
1681
        target.fetch(source)
1631
1682
        if expect_pack_called:
1632
1683
            self.assertLength(1, self.calls)