~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-12-09 02:53:42 UTC
  • mfrom: (4873.2.3 2.1.0b4-win32-test-imports)
  • Revision ID: pqm@pqm.ubuntu.com-20091209025342-sidvxfcqdgxmuz59
(jam) Get the test suite running again on Windows, (bug #492561)

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
 
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
 
    versionedfile,
55
 
    vf_repository,
56
66
    workingtree,
57
67
    )
58
68
from bzrlib.repofmt import (
59
69
    groupcompress_repo,
60
70
    knitrepo,
61
 
    knitpack_repo,
62
71
    pack_repo,
 
72
    weaverepo,
63
73
    )
64
74
 
65
75
 
68
78
    def test_get_set_default_format(self):
69
79
        old_default = bzrdir.format_registry.get('default')
70
80
        private_default = old_default().repository_format.__class__
71
 
        old_format = repository.format_registry.get_default()
 
81
        old_format = repository.RepositoryFormat.get_default_format()
72
82
        self.assertTrue(isinstance(old_format, private_default))
73
83
        def make_sample_bzrdir():
74
84
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
88
98
            bzrdir.format_registry.remove('default')
89
99
            bzrdir.format_registry.remove('sample')
90
100
            bzrdir.format_registry.register('default', old_default, '')
91
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
101
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
92
102
                              old_format.__class__)
93
103
 
94
104
 
116
126
        return "opened repository."
117
127
 
118
128
 
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
129
class TestRepositoryFormat(TestCaseWithTransport):
129
130
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
130
131
 
136
137
        def check_format(format, url):
137
138
            dir = format._matchingbzrdir.initialize(url)
138
139
            format.initialize(dir)
139
 
            t = transport.get_transport_from_path(url)
 
140
            t = get_transport(url)
140
141
            found_format = repository.RepositoryFormat.find_format(dir)
141
 
            self.assertIsInstance(found_format, format.__class__)
142
 
        check_format(repository.format_registry.get_default(), "bar")
 
142
            self.failUnless(isinstance(found_format, format.__class__))
 
143
        check_format(weaverepo.RepositoryFormat7(), "bar")
143
144
 
144
145
    def test_find_format_no_repository(self):
145
146
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
155
156
                          dir)
156
157
 
157
158
    def test_register_unregister_format(self):
158
 
        # Test deprecated format registration functions
159
159
        format = SampleRepositoryFormat()
160
160
        # make a control dir
161
161
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
162
        # make a repo
163
163
        format.initialize(dir)
164
164
        # register a format for it.
165
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
166
 
            repository.RepositoryFormat.register_format, format)
 
165
        repository.RepositoryFormat.register_format(format)
167
166
        # which repository.Open will refuse (not supported)
168
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
169
 
            self.get_url())
 
167
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
170
168
        # but open(unsupported) will work
171
169
        self.assertEqual(format.open(dir), "opened repository.")
172
170
        # 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)
 
171
        repository.RepositoryFormat.unregister_format(format)
 
172
 
 
173
 
 
174
class TestFormat6(TestCaseWithTransport):
 
175
 
 
176
    def test_attribute__fetch_order(self):
 
177
        """Weaves need topological data insertion."""
 
178
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
179
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
180
        self.assertEqual('topological', repo._format._fetch_order)
 
181
 
 
182
    def test_attribute__fetch_uses_deltas(self):
 
183
        """Weaves do not reuse deltas."""
 
184
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
185
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
186
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
187
 
 
188
    def test_attribute__fetch_reconcile(self):
 
189
        """Weave repositories need a reconcile after fetch."""
 
190
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
191
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
192
        self.assertEqual(True, repo._format._fetch_reconcile)
 
193
 
 
194
    def test_no_ancestry_weave(self):
 
195
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
196
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
197
        # We no longer need to create the ancestry.weave file
 
198
        # since it is *never* used.
 
199
        self.assertRaises(NoSuchFile,
 
200
                          control.transport.get,
 
201
                          'ancestry.weave')
 
202
 
 
203
    def test_supports_external_lookups(self):
 
204
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
205
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
206
        self.assertFalse(repo._format.supports_external_lookups)
 
207
 
 
208
 
 
209
class TestFormat7(TestCaseWithTransport):
 
210
 
 
211
    def test_attribute__fetch_order(self):
 
212
        """Weaves need topological data insertion."""
 
213
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
214
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
215
        self.assertEqual('topological', repo._format._fetch_order)
 
216
 
 
217
    def test_attribute__fetch_uses_deltas(self):
 
218
        """Weaves do not reuse deltas."""
 
219
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
220
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
221
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
222
 
 
223
    def test_attribute__fetch_reconcile(self):
 
224
        """Weave repositories need a reconcile after fetch."""
 
225
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
226
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
227
        self.assertEqual(True, repo._format._fetch_reconcile)
 
228
 
 
229
    def test_disk_layout(self):
 
230
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
231
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
232
        # in case of side effects of locking.
 
233
        repo.lock_write()
 
234
        repo.unlock()
 
235
        # we want:
 
236
        # format 'Bazaar-NG Repository format 7'
 
237
        # lock ''
 
238
        # inventory.weave == empty_weave
 
239
        # empty revision-store directory
 
240
        # empty weaves directory
 
241
        t = control.get_repository_transport(None)
 
242
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
243
                             t.get('format').read())
 
244
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
245
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
246
        self.assertEqualDiff('# bzr weave file v5\n'
 
247
                             'w\n'
 
248
                             'W\n',
 
249
                             t.get('inventory.weave').read())
 
250
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
251
        # disk.
 
252
        control.create_branch()
 
253
        tree = control.create_workingtree()
 
254
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
255
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
256
        try:
 
257
            tree.commit('first post', rev_id='first')
 
258
        except errors.IllegalPath:
 
259
            if sys.platform != 'win32':
 
260
                raise
 
261
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
 
262
                              ' in repo format 7')
 
263
            return
 
264
        self.assertEqualDiff(
 
265
            '# bzr weave file v5\n'
 
266
            'i\n'
 
267
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
268
            'n first\n'
 
269
            '\n'
 
270
            'w\n'
 
271
            '{ 0\n'
 
272
            '. content\n'
 
273
            '}\n'
 
274
            'W\n',
 
275
            t.get('weaves/74/Foo%3ABar.weave').read())
 
276
 
 
277
    def test_shared_disk_layout(self):
 
278
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
279
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
280
        # we want:
 
281
        # format 'Bazaar-NG Repository format 7'
 
282
        # inventory.weave == empty_weave
 
283
        # empty revision-store directory
 
284
        # empty weaves directory
 
285
        # a 'shared-storage' marker file.
 
286
        # lock is not present when unlocked
 
287
        t = control.get_repository_transport(None)
 
288
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
289
                             t.get('format').read())
 
290
        self.assertEqualDiff('', t.get('shared-storage').read())
 
291
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
292
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
293
        self.assertEqualDiff('# bzr weave file v5\n'
 
294
                             'w\n'
 
295
                             'W\n',
 
296
                             t.get('inventory.weave').read())
 
297
        self.assertFalse(t.has('branch-lock'))
 
298
 
 
299
    def test_creates_lockdir(self):
 
300
        """Make sure it appears to be controlled by a LockDir existence"""
 
301
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
302
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
303
        t = control.get_repository_transport(None)
 
304
        # TODO: Should check there is a 'lock' toplevel directory,
 
305
        # regardless of contents
 
306
        self.assertFalse(t.has('lock/held/info'))
 
307
        repo.lock_write()
 
308
        try:
 
309
            self.assertTrue(t.has('lock/held/info'))
 
310
        finally:
 
311
            # unlock so we don't get a warning about failing to do so
 
312
            repo.unlock()
 
313
 
 
314
    def test_uses_lockdir(self):
 
315
        """repo format 7 actually locks on lockdir"""
 
316
        base_url = self.get_url()
 
317
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
318
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
319
        t = control.get_repository_transport(None)
 
320
        repo.lock_write()
 
321
        repo.unlock()
 
322
        del repo
 
323
        # make sure the same lock is created by opening it
 
324
        repo = repository.Repository.open(base_url)
 
325
        repo.lock_write()
 
326
        self.assertTrue(t.has('lock/held/info'))
 
327
        repo.unlock()
 
328
        self.assertFalse(t.has('lock/held/info'))
 
329
 
 
330
    def test_shared_no_tree_disk_layout(self):
 
331
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
332
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
333
        repo.set_make_working_trees(False)
 
334
        # we want:
 
335
        # format 'Bazaar-NG Repository format 7'
 
336
        # lock ''
 
337
        # inventory.weave == empty_weave
 
338
        # empty revision-store directory
 
339
        # empty weaves directory
 
340
        # a 'shared-storage' marker file.
 
341
        t = control.get_repository_transport(None)
 
342
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
343
                             t.get('format').read())
 
344
        ## self.assertEqualDiff('', t.get('lock').read())
 
345
        self.assertEqualDiff('', t.get('shared-storage').read())
 
346
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
347
        repo.set_make_working_trees(True)
 
348
        self.assertFalse(t.has('no-working-trees'))
 
349
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
350
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
351
        self.assertEqualDiff('# bzr weave file v5\n'
 
352
                             'w\n'
 
353
                             'W\n',
 
354
                             t.get('inventory.weave').read())
 
355
 
 
356
    def test_supports_external_lookups(self):
 
357
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
358
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
359
        self.assertFalse(repo._format.supports_external_lookups)
209
360
 
210
361
 
211
362
class TestFormatKnit1(TestCaseWithTransport):
313
464
        repo = self.make_repository('.',
314
465
                format=bzrdir.format_registry.get('knit')())
315
466
        inv_xml = '<inventory format="5">\n</inventory>\n'
316
 
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
467
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
317
468
        self.assertEqual('test-rev-id', inv.root.revision)
318
469
 
319
470
    def test_deserialise_uses_global_revision_id(self):
325
476
        # Arguably, the deserialise_inventory should detect a mismatch, and
326
477
        # raise an error, rather than silently using one revision_id over the
327
478
        # other.
328
 
        self.assertRaises(AssertionError, repo._deserialise_inventory,
 
479
        self.assertRaises(AssertionError, repo.deserialise_inventory,
329
480
            'test-rev-id', inv_xml)
330
 
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
481
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
331
482
        self.assertEqual('other-rev-id', inv.root.revision)
332
483
 
333
484
    def test_supports_external_lookups(self):
379
530
        # classes do not barf inappropriately when a surprising repository type
380
531
        # is handed to them.
381
532
        dummy_a = DummyRepository()
382
 
        dummy_a._format = RepositoryFormat()
383
 
        dummy_a._format.supports_full_versioned_files = True
384
533
        dummy_b = DummyRepository()
385
 
        dummy_b._format = RepositoryFormat()
386
 
        dummy_b._format.supports_full_versioned_files = True
387
534
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
388
535
 
389
536
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
393
540
        no actual sane default in the presence of incompatible data models.
394
541
        """
395
542
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
396
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
543
        self.assertEqual(repository.InterSameDataRepository,
397
544
                         inter_repo.__class__)
398
545
        self.assertEqual(repo_a, inter_repo.source)
399
546
        self.assertEqual(repo_b, inter_repo.target)
413
560
        dummy_a._serializer = repo._serializer
414
561
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
415
562
        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
563
        dummy_b._serializer = repo._serializer
418
564
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
419
565
        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
566
        repository.InterRepository.register_optimiser(InterDummy)
422
567
        try:
423
568
            # we should get the default for something InterDummy returns False
436
581
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
437
582
 
438
583
 
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"
 
584
class TestInterWeaveRepo(TestCaseWithTransport):
 
585
 
 
586
    def test_is_compatible_and_registered(self):
 
587
        # InterWeaveRepo is compatible when either side
 
588
        # is a format 5/6/7 branch
 
589
        from bzrlib.repofmt import knitrepo, weaverepo
 
590
        formats = [weaverepo.RepositoryFormat5(),
 
591
                   weaverepo.RepositoryFormat6(),
 
592
                   weaverepo.RepositoryFormat7()]
 
593
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
594
                                knitrepo.RepositoryFormatKnit1(),
 
595
                                ]
 
596
        repo_a = self.make_repository('a')
 
597
        repo_b = self.make_repository('b')
 
598
        is_compatible = repository.InterWeaveRepo.is_compatible
 
599
        for source in incompatible_formats:
 
600
            # force incompatible left then right
 
601
            repo_a._format = source
 
602
            repo_b._format = formats[0]
 
603
            self.assertFalse(is_compatible(repo_a, repo_b))
 
604
            self.assertFalse(is_compatible(repo_b, repo_a))
 
605
        for source in formats:
 
606
            repo_a._format = source
 
607
            for target in formats:
 
608
                repo_b._format = target
 
609
                self.assertTrue(is_compatible(repo_a, repo_b))
 
610
        self.assertEqual(repository.InterWeaveRepo,
 
611
                         repository.InterRepository.get(repo_a,
 
612
                                                        repo_b).__class__)
449
613
 
450
614
 
451
615
class TestRepositoryConverter(TestCaseWithTransport):
452
616
 
453
617
    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()
 
618
        t = get_transport(self.get_url('.'))
463
619
        t.mkdir('repository')
464
620
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
465
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
621
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
622
        target_format = knitrepo.RepositoryFormatKnit1()
466
623
        converter = repository.CopyConverter(target_format)
467
624
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
468
625
        try:
473
630
        self.assertTrue(isinstance(target_format, repo._format.__class__))
474
631
 
475
632
 
 
633
class TestMisc(TestCase):
 
634
 
 
635
    def test_unescape_xml(self):
 
636
        """We get some kind of error when malformed entities are passed"""
 
637
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
638
 
 
639
 
476
640
class TestRepositoryFormatKnit3(TestCaseWithTransport):
477
641
 
478
642
    def test_attribute__fetch_order(self):
527
691
 
528
692
class Test2a(tests.TestCaseWithMemoryTransport):
529
693
 
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
694
    def test_fetch_combines_groups(self):
546
695
        builder = self.make_branch_builder('source', format='2a')
547
696
        builder.start_series()
673
822
        target = self.make_repository('target', format='rich-root-pack')
674
823
        stream = source._get_source(target._format)
675
824
        # We don't want the child GroupCHKStreamSource
676
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
825
        self.assertIs(type(stream), repository.StreamSource)
677
826
 
678
827
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
679
828
        source_builder = self.make_branch_builder('source',
755
904
        source = self.make_repository('source', format='pack-0.92')
756
905
        target = self.make_repository('target', format='pack-0.92')
757
906
        stream_source = source._get_source(target._format)
758
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
907
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
759
908
 
760
909
    def test_source_to_exact_pack_rich_root_pack(self):
761
910
        source = self.make_repository('source', format='rich-root-pack')
762
911
        target = self.make_repository('target', format='rich-root-pack')
763
912
        stream_source = source._get_source(target._format)
764
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
913
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
765
914
 
766
915
    def test_source_to_exact_pack_19(self):
767
916
        source = self.make_repository('source', format='1.9')
768
917
        target = self.make_repository('target', format='1.9')
769
918
        stream_source = source._get_source(target._format)
770
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
919
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
771
920
 
772
921
    def test_source_to_exact_pack_19_rich_root(self):
773
922
        source = self.make_repository('source', format='1.9-rich-root')
774
923
        target = self.make_repository('target', format='1.9-rich-root')
775
924
        stream_source = source._get_source(target._format)
776
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
925
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
777
926
 
778
927
    def test_source_to_remote_exact_pack_19(self):
779
928
        trans = self.make_smart_server('target')
782
931
        target = self.make_repository('target', format='1.9')
783
932
        target = repository.Repository.open(trans.base)
784
933
        stream_source = source._get_source(target._format)
785
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
934
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
786
935
 
787
936
    def test_stream_source_to_non_exact(self):
788
937
        source = self.make_repository('source', format='pack-0.92')
789
938
        target = self.make_repository('target', format='1.9')
790
939
        stream = source._get_source(target._format)
791
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
940
        self.assertIs(type(stream), repository.StreamSource)
792
941
 
793
942
    def test_stream_source_to_non_exact_rich_root(self):
794
943
        source = self.make_repository('source', format='1.9')
795
944
        target = self.make_repository('target', format='1.9-rich-root')
796
945
        stream = source._get_source(target._format)
797
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
946
        self.assertIs(type(stream), repository.StreamSource)
798
947
 
799
948
    def test_source_to_remote_non_exact_pack_19(self):
800
949
        trans = self.make_smart_server('target')
803
952
        target = self.make_repository('target', format='1.6')
804
953
        target = repository.Repository.open(trans.base)
805
954
        stream_source = source._get_source(target._format)
806
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
955
        self.assertIs(type(stream_source), repository.StreamSource)
807
956
 
808
957
    def test_stream_source_to_knit(self):
809
958
        source = self.make_repository('source', format='pack-0.92')
810
959
        target = self.make_repository('target', format='dirstate')
811
960
        stream = source._get_source(target._format)
812
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
961
        self.assertIs(type(stream), repository.StreamSource)
813
962
 
814
963
 
815
964
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
817
966
 
818
967
    def setUp(self):
819
968
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
820
 
        self.builder = self.make_branch_builder('source')
 
969
        self.builder = self.make_branch_builder('source',
 
970
            format='development6-rich-root')
821
971
        self.builder.start_series()
822
972
        self.builder.build_snapshot('initial', None,
823
973
            [('add', ('', 'tree-root', 'directory', None))])
993
1143
        packs.ensure_loaded()
994
1144
        return tree, r, packs, [rev1, rev2, rev3]
995
1145
 
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
1146
    def test__max_pack_count(self):
1022
1147
        """The maximum pack count is a function of the number of revisions."""
1023
1148
        # no revisions - one pack, so that we can have a revision free repo
1043
1168
        # check some arbitrary big numbers
1044
1169
        self.assertEqual(25, packs._max_pack_count(112894))
1045
1170
 
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
1171
    def test_pack_distribution_zero(self):
1074
1172
        packs = self.get_packs()
1075
1173
        self.assertEqual([0], packs.pack_distribution(0))
1243
1341
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
1244
1342
        self.assertFalse(packs.reload_pack_names())
1245
1343
 
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
1344
    def test_autopack_reloads_and_stops(self):
1301
1345
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1302
1346
        # After we have determined what needs to be autopacked, trigger a
1314
1358
        self.assertEqual(tree.branch.repository._pack_collection.names(),
1315
1359
                         packs.names())
1316
1360
 
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
1361
 
1350
1362
class TestPack(TestCaseWithTransport):
1351
1363
    """Tests for the Pack object."""
1451
1463
        # Because of how they were built, they correspond to
1452
1464
        # ['D', 'C', 'B', 'A']
1453
1465
        packs = b.repository._pack_collection.packs
1454
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1466
        packer = pack_repo.Packer(b.repository._pack_collection,
1455
1467
                                  packs, 'testing',
1456
1468
                                  revision_ids=['B', 'C'])
1457
1469
        # Now, when we are copying the B & C revisions, their pack files should
1471
1483
        return repo._pack_collection
1472
1484
 
1473
1485
    def test_open_pack_will_optimise(self):
1474
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1486
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1475
1487
                                            [], '.test')
1476
1488
        new_pack = packer.open_pack()
1477
1489
        self.addCleanup(new_pack.abort) # ensure cleanup
1482
1494
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1483
1495
 
1484
1496
 
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
1497
class TestCrossFormatPacks(TestCaseWithTransport):
1586
1498
 
1587
1499
    def log_pack(self, hint=None):
1602
1514
        self.addCleanup(target.unlock)
1603
1515
        source = source_tree.branch.repository._get_source(target._format)
1604
1516
        self.orig_pack = target.pack
1605
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1517
        target.pack = self.log_pack
1606
1518
        search = target.search_missing_revision_ids(
1607
 
            source_tree.branch.repository, revision_ids=[tip])
 
1519
            source_tree.branch.repository, tip)
1608
1520
        stream = source.get_stream(search)
1609
1521
        from_format = source_tree.branch.repository._format
1610
1522
        sink = target._get_sink()
1626
1538
        self.addCleanup(target.unlock)
1627
1539
        source = source_tree.branch.repository
1628
1540
        self.orig_pack = target.pack
1629
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1541
        target.pack = self.log_pack
1630
1542
        target.fetch(source)
1631
1543
        if expect_pack_called:
1632
1544
            self.assertLength(1, self.calls)
1660
1572
    def test_IDS_format_same_no(self):
1661
1573
        # When the formats are the same, pack is not called.
1662
1574
        self.run_fetch('2a', '2a', False)
1663
 
 
1664
 
 
1665
 
class Test_LazyListJoin(tests.TestCase):
1666
 
 
1667
 
    def test__repr__(self):
1668
 
        lazy = repository._LazyListJoin(['a'], ['b'])
1669
 
        self.assertEqual("bzrlib.repository._LazyListJoin((['a'], ['b']))",
1670
 
                         repr(lazy))