~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-30 12:52:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6418.
  • Revision ID: jelmer@samba.org-20111230125254-igy1abnixsvulfqd
Simplify code a bit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
28
26
 
29
27
import bzrlib
30
 
from bzrlib.errors import (NotBranchError,
31
 
                           NoSuchFile,
32
 
                           UnknownFormatError,
33
 
                           UnsupportedFormatError,
34
 
                           )
 
28
from bzrlib.errors import (
 
29
    UnknownFormatError,
 
30
    UnsupportedFormatError,
 
31
    )
35
32
from bzrlib import (
36
 
    graph,
 
33
    btree_index,
 
34
    symbol_versioning,
37
35
    tests,
 
36
    transport,
 
37
    vf_search,
38
38
    )
39
 
from bzrlib.branchbuilder import BranchBuilder
40
39
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
41
 
from bzrlib.index import GraphIndex, InMemoryGraphIndex
 
40
from bzrlib.index import GraphIndex
42
41
from bzrlib.repository import RepositoryFormat
43
 
from bzrlib.smart import server
44
42
from bzrlib.tests import (
45
43
    TestCase,
46
44
    TestCaseWithTransport,
47
 
    TestSkipped,
48
 
    test_knit,
49
 
    )
50
 
from bzrlib.transport import (
51
 
    fakenfs,
52
 
    get_transport,
53
45
    )
54
46
from bzrlib import (
55
 
    bencode,
56
47
    bzrdir,
57
48
    errors,
58
49
    inventory,
59
50
    osutils,
60
 
    progress,
61
51
    repository,
62
52
    revision as _mod_revision,
63
 
    symbol_versioning,
64
53
    upgrade,
65
54
    versionedfile,
 
55
    vf_repository,
66
56
    workingtree,
67
57
    )
68
58
from bzrlib.repofmt import (
69
59
    groupcompress_repo,
70
60
    knitrepo,
 
61
    knitpack_repo,
71
62
    pack_repo,
72
 
    weaverepo,
73
63
    )
74
64
 
75
65
 
78
68
    def test_get_set_default_format(self):
79
69
        old_default = bzrdir.format_registry.get('default')
80
70
        private_default = old_default().repository_format.__class__
81
 
        old_format = repository.RepositoryFormat.get_default_format()
 
71
        old_format = repository.format_registry.get_default()
82
72
        self.assertTrue(isinstance(old_format, private_default))
83
73
        def make_sample_bzrdir():
84
74
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
98
88
            bzrdir.format_registry.remove('default')
99
89
            bzrdir.format_registry.remove('sample')
100
90
            bzrdir.format_registry.register('default', old_default, '')
101
 
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
 
91
        self.assertIsInstance(repository.format_registry.get_default(),
102
92
                              old_format.__class__)
103
93
 
104
94
 
105
 
class SampleRepositoryFormat(repository.RepositoryFormat):
 
95
class SampleRepositoryFormat(repository.RepositoryFormatMetaDir):
106
96
    """A sample format
107
97
 
108
98
    this format is initializable, unsupported to aid in testing the
109
99
    open and open(unsupported=True) routines.
110
100
    """
111
101
 
112
 
    def get_format_string(self):
 
102
    @classmethod
 
103
    def get_format_string(cls):
113
104
        """See RepositoryFormat.get_format_string()."""
114
105
        return "Sample .bzr repository format."
115
106
 
126
117
        return "opened repository."
127
118
 
128
119
 
 
120
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
 
121
    """A sample format that can not be used in a metadir
 
122
 
 
123
    """
 
124
 
 
125
    def get_format_string(self):
 
126
        raise NotImplementedError
 
127
 
 
128
 
129
129
class TestRepositoryFormat(TestCaseWithTransport):
130
130
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
131
131
 
137
137
        def check_format(format, url):
138
138
            dir = format._matchingbzrdir.initialize(url)
139
139
            format.initialize(dir)
140
 
            t = get_transport(url)
141
 
            found_format = repository.RepositoryFormat.find_format(dir)
142
 
            self.failUnless(isinstance(found_format, format.__class__))
143
 
        check_format(weaverepo.RepositoryFormat7(), "bar")
 
140
            t = transport.get_transport_from_path(url)
 
141
            found_format = repository.RepositoryFormatMetaDir.find_format(dir)
 
142
            self.assertIsInstance(found_format, format.__class__)
 
143
        check_format(repository.format_registry.get_default(), "bar")
144
144
 
145
145
    def test_find_format_no_repository(self):
146
146
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
147
147
        self.assertRaises(errors.NoRepositoryPresent,
148
 
                          repository.RepositoryFormat.find_format,
 
148
                          repository.RepositoryFormatMetaDir.find_format,
149
149
                          dir)
150
150
 
 
151
    def test_from_string(self):
 
152
        self.assertIsInstance(
 
153
            SampleRepositoryFormat.from_string(
 
154
                "Sample .bzr repository format."),
 
155
            SampleRepositoryFormat)
 
156
        self.assertRaises(AssertionError,
 
157
            SampleRepositoryFormat.from_string,
 
158
                "Different .bzr repository format.")
 
159
 
151
160
    def test_find_format_unknown_format(self):
152
161
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
153
162
        SampleRepositoryFormat().initialize(dir)
154
163
        self.assertRaises(UnknownFormatError,
155
 
                          repository.RepositoryFormat.find_format,
 
164
                          repository.RepositoryFormatMetaDir.find_format,
156
165
                          dir)
157
166
 
 
167
    def test_find_format_with_features(self):
 
168
        tree = self.make_branch_and_tree('.', format='2a')
 
169
        tree.branch.repository.control_transport.put_bytes('format',
 
170
            tree.branch.repository._format.get_format_string() +
 
171
            "necessity name\n")
 
172
        found_format = repository.RepositoryFormatMetaDir.find_format(tree.bzrdir)
 
173
        self.assertIsInstance(found_format, repository.RepositoryFormatMetaDir)
 
174
        self.assertEquals(found_format.features.get("name"), "necessity")
 
175
        self.assertRaises(errors.MissingFeature, found_format.check_support_status,
 
176
            True)
 
177
        self.addCleanup(repository.RepositoryFormatMetaDir.unregister_feature,
 
178
            "name")
 
179
        repository.RepositoryFormatMetaDir.register_feature("name")
 
180
        found_format.check_support_status(True)
 
181
 
158
182
    def test_register_unregister_format(self):
 
183
        # Test deprecated format registration functions
159
184
        format = SampleRepositoryFormat()
160
185
        # make a control dir
161
186
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
187
        # make a repo
163
188
        format.initialize(dir)
164
189
        # register a format for it.
165
 
        repository.RepositoryFormat.register_format(format)
 
190
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
191
            repository.RepositoryFormat.register_format, format)
166
192
        # which repository.Open will refuse (not supported)
167
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
 
193
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
 
194
            self.get_url())
168
195
        # but open(unsupported) will work
169
196
        self.assertEqual(format.open(dir), "opened repository.")
170
197
        # unregister the format
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)
 
198
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
199
            repository.RepositoryFormat.unregister_format, format)
 
200
 
 
201
 
 
202
class TestRepositoryFormatRegistry(TestCase):
 
203
 
 
204
    def setUp(self):
 
205
        super(TestRepositoryFormatRegistry, self).setUp()
 
206
        self.registry = repository.RepositoryFormatRegistry()
 
207
 
 
208
    def test_register_unregister_format(self):
 
209
        format = SampleRepositoryFormat()
 
210
        self.registry.register(format)
 
211
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
 
212
        self.registry.remove(format)
 
213
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
 
214
 
 
215
    def test_get_all(self):
 
216
        format = SampleRepositoryFormat()
 
217
        self.assertEquals([], self.registry._get_all())
 
218
        self.registry.register(format)
 
219
        self.assertEquals([format], self.registry._get_all())
 
220
 
 
221
    def test_register_extra(self):
 
222
        format = SampleExtraRepositoryFormat()
 
223
        self.assertEquals([], self.registry._get_all())
 
224
        self.registry.register_extra(format)
 
225
        self.assertEquals([format], self.registry._get_all())
 
226
 
 
227
    def test_register_extra_lazy(self):
 
228
        self.assertEquals([], self.registry._get_all())
 
229
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
 
230
            "SampleExtraRepositoryFormat")
 
231
        formats = self.registry._get_all()
 
232
        self.assertEquals(1, len(formats))
 
233
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
360
234
 
361
235
 
362
236
class TestFormatKnit1(TestCaseWithTransport):
530
404
        # classes do not barf inappropriately when a surprising repository type
531
405
        # is handed to them.
532
406
        dummy_a = DummyRepository()
 
407
        dummy_a._format = RepositoryFormat()
 
408
        dummy_a._format.supports_full_versioned_files = True
533
409
        dummy_b = DummyRepository()
 
410
        dummy_b._format = RepositoryFormat()
 
411
        dummy_b._format.supports_full_versioned_files = True
534
412
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
535
413
 
536
414
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
540
418
        no actual sane default in the presence of incompatible data models.
541
419
        """
542
420
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
543
 
        self.assertEqual(repository.InterSameDataRepository,
 
421
        self.assertEqual(vf_repository.InterSameDataRepository,
544
422
                         inter_repo.__class__)
545
423
        self.assertEqual(repo_a, inter_repo.source)
546
424
        self.assertEqual(repo_b, inter_repo.target)
560
438
        dummy_a._serializer = repo._serializer
561
439
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
562
440
        dummy_a._format.rich_root_data = repo._format.rich_root_data
 
441
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
563
442
        dummy_b._serializer = repo._serializer
564
443
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
565
444
        dummy_b._format.rich_root_data = repo._format.rich_root_data
 
445
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
566
446
        repository.InterRepository.register_optimiser(InterDummy)
567
447
        try:
568
448
            # we should get the default for something InterDummy returns False
581
461
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
582
462
 
583
463
 
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__)
 
464
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
 
465
 
 
466
    @classmethod
 
467
    def get_format_string(cls):
 
468
        return "Test Format 1"
 
469
 
 
470
 
 
471
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
 
472
 
 
473
    @classmethod
 
474
    def get_format_string(cls):
 
475
        return "Test Format 2"
613
476
 
614
477
 
615
478
class TestRepositoryConverter(TestCaseWithTransport):
616
479
 
617
480
    def test_convert_empty(self):
618
 
        t = get_transport(self.get_url('.'))
 
481
        source_format = TestRepositoryFormat1()
 
482
        target_format = TestRepositoryFormat2()
 
483
        repository.format_registry.register(source_format)
 
484
        self.addCleanup(repository.format_registry.remove,
 
485
            source_format)
 
486
        repository.format_registry.register(target_format)
 
487
        self.addCleanup(repository.format_registry.remove,
 
488
            target_format)
 
489
        t = self.get_transport()
619
490
        t.mkdir('repository')
620
491
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
621
 
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
622
 
        target_format = knitrepo.RepositoryFormatKnit1()
 
492
        repo = TestRepositoryFormat1().initialize(repo_dir)
623
493
        converter = repository.CopyConverter(target_format)
624
494
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
625
495
        try:
630
500
        self.assertTrue(isinstance(target_format, repo._format.__class__))
631
501
 
632
502
 
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
 
 
640
503
class TestRepositoryFormatKnit3(TestCaseWithTransport):
641
504
 
642
505
    def test_attribute__fetch_order(self):
691
554
 
692
555
class Test2a(tests.TestCaseWithMemoryTransport):
693
556
 
 
557
    def test_chk_bytes_uses_custom_btree_parser(self):
 
558
        mt = self.make_branch_and_memory_tree('test', format='2a')
 
559
        mt.lock_write()
 
560
        self.addCleanup(mt.unlock)
 
561
        mt.add([''], ['root-id'])
 
562
        mt.commit('first')
 
563
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
 
564
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
565
        # It should also work if we re-open the repo
 
566
        repo = mt.branch.repository.bzrdir.open_repository()
 
567
        repo.lock_read()
 
568
        self.addCleanup(repo.unlock)
 
569
        index = repo.chk_bytes._index._graph_index._indices[0]
 
570
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
571
 
694
572
    def test_fetch_combines_groups(self):
695
573
        builder = self.make_branch_builder('source', format='2a')
696
574
        builder.start_series()
822
700
        target = self.make_repository('target', format='rich-root-pack')
823
701
        stream = source._get_source(target._format)
824
702
        # We don't want the child GroupCHKStreamSource
825
 
        self.assertIs(type(stream), repository.StreamSource)
 
703
        self.assertIs(type(stream), vf_repository.StreamSource)
826
704
 
827
705
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
828
706
        source_builder = self.make_branch_builder('source',
855
733
 
856
734
        # On a regular pass, getting the inventories and chk pages for rev-2
857
735
        # would only get the newly created chk pages
858
 
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
 
736
        search = vf_search.SearchResult(set(['rev-2']), set(['rev-1']), 1,
859
737
                                    set(['rev-2']))
860
738
        simple_chk_records = []
861
739
        for vf_name, substream in source.get_stream(search):
904
782
        source = self.make_repository('source', format='pack-0.92')
905
783
        target = self.make_repository('target', format='pack-0.92')
906
784
        stream_source = source._get_source(target._format)
907
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
785
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
908
786
 
909
787
    def test_source_to_exact_pack_rich_root_pack(self):
910
788
        source = self.make_repository('source', format='rich-root-pack')
911
789
        target = self.make_repository('target', format='rich-root-pack')
912
790
        stream_source = source._get_source(target._format)
913
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
791
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
914
792
 
915
793
    def test_source_to_exact_pack_19(self):
916
794
        source = self.make_repository('source', format='1.9')
917
795
        target = self.make_repository('target', format='1.9')
918
796
        stream_source = source._get_source(target._format)
919
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
797
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
920
798
 
921
799
    def test_source_to_exact_pack_19_rich_root(self):
922
800
        source = self.make_repository('source', format='1.9-rich-root')
923
801
        target = self.make_repository('target', format='1.9-rich-root')
924
802
        stream_source = source._get_source(target._format)
925
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
803
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
926
804
 
927
805
    def test_source_to_remote_exact_pack_19(self):
928
806
        trans = self.make_smart_server('target')
931
809
        target = self.make_repository('target', format='1.9')
932
810
        target = repository.Repository.open(trans.base)
933
811
        stream_source = source._get_source(target._format)
934
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
812
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
935
813
 
936
814
    def test_stream_source_to_non_exact(self):
937
815
        source = self.make_repository('source', format='pack-0.92')
938
816
        target = self.make_repository('target', format='1.9')
939
817
        stream = source._get_source(target._format)
940
 
        self.assertIs(type(stream), repository.StreamSource)
 
818
        self.assertIs(type(stream), vf_repository.StreamSource)
941
819
 
942
820
    def test_stream_source_to_non_exact_rich_root(self):
943
821
        source = self.make_repository('source', format='1.9')
944
822
        target = self.make_repository('target', format='1.9-rich-root')
945
823
        stream = source._get_source(target._format)
946
 
        self.assertIs(type(stream), repository.StreamSource)
 
824
        self.assertIs(type(stream), vf_repository.StreamSource)
947
825
 
948
826
    def test_source_to_remote_non_exact_pack_19(self):
949
827
        trans = self.make_smart_server('target')
952
830
        target = self.make_repository('target', format='1.6')
953
831
        target = repository.Repository.open(trans.base)
954
832
        stream_source = source._get_source(target._format)
955
 
        self.assertIs(type(stream_source), repository.StreamSource)
 
833
        self.assertIs(type(stream_source), vf_repository.StreamSource)
956
834
 
957
835
    def test_stream_source_to_knit(self):
958
836
        source = self.make_repository('source', format='pack-0.92')
959
837
        target = self.make_repository('target', format='dirstate')
960
838
        stream = source._get_source(target._format)
961
 
        self.assertIs(type(stream), repository.StreamSource)
 
839
        self.assertIs(type(stream), vf_repository.StreamSource)
962
840
 
963
841
 
964
842
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
966
844
 
967
845
    def setUp(self):
968
846
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
969
 
        self.builder = self.make_branch_builder('source',
970
 
            format='development6-rich-root')
 
847
        self.builder = self.make_branch_builder('source')
971
848
        self.builder.start_series()
972
849
        self.builder.build_snapshot('initial', None,
973
850
            [('add', ('', 'tree-root', 'directory', None))])
1043
920
            revision = _mod_revision.Revision('rev1a',
1044
921
                committer='jrandom@example.com', timestamp=0,
1045
922
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
1046
 
            repo.add_revision('rev1a',revision, inv)
 
923
            repo.add_revision('rev1a', revision, inv)
1047
924
 
1048
925
            # make rev1b, which has no Revision, but has an Inventory, and
1049
926
            # file1
1084
961
        revision = _mod_revision.Revision(revision_id,
1085
962
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
1086
963
            timezone=0, message='foo', parent_ids=parent_ids)
1087
 
        repo.add_revision(revision_id,revision, inv)
 
964
        repo.add_revision(revision_id, revision, inv)
1088
965
 
1089
966
    def add_file(self, repo, inv, filename, revision, parents):
1090
967
        file_id = filename + '-id'
1220
1097
            sorted(set([osutils.splitext(n)[0] for n in
1221
1098
                        packs._index_transport.list_dir('.')])))
1222
1099
 
 
1100
    def test__obsolete_packs_missing_directory(self):
 
1101
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1102
        r.control_transport.rmdir('obsolete_packs')
 
1103
        names = packs.names()
 
1104
        pack = packs.get_pack_by_name(names[0])
 
1105
        # Schedule this one for removal
 
1106
        packs._remove_pack_from_memory(pack)
 
1107
        # Now trigger the obsoletion, and ensure that all the remaining files
 
1108
        # are still renamed
 
1109
        packs._obsolete_packs([pack])
 
1110
        self.assertEqual([n + '.pack' for n in names[1:]],
 
1111
                         sorted(packs._pack_transport.list_dir('.')))
 
1112
        # names[0] should not be present in the index anymore
 
1113
        self.assertEqual(names[1:],
 
1114
            sorted(set([osutils.splitext(n)[0] for n in
 
1115
                        packs._index_transport.list_dir('.')])))
 
1116
 
1223
1117
    def test_pack_distribution_zero(self):
1224
1118
        packs = self.get_packs()
1225
1119
        self.assertEqual([0], packs.pack_distribution(0))
1495
1389
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1496
1390
        self.assertEqual([pack.name], sorted(obsolete_names))
1497
1391
 
 
1392
    def test_pack_no_obsolete_packs_directory(self):
 
1393
        """Bug #314314, don't fail if obsolete_packs directory does
 
1394
        not exist."""
 
1395
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1396
        r.control_transport.rmdir('obsolete_packs')
 
1397
        packs._clear_obsolete_packs()
1498
1398
 
1499
1399
 
1500
1400
class TestPack(TestCaseWithTransport):
1601
1501
        # Because of how they were built, they correspond to
1602
1502
        # ['D', 'C', 'B', 'A']
1603
1503
        packs = b.repository._pack_collection.packs
1604
 
        packer = pack_repo.Packer(b.repository._pack_collection,
 
1504
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
1605
1505
                                  packs, 'testing',
1606
1506
                                  revision_ids=['B', 'C'])
1607
1507
        # Now, when we are copying the B & C revisions, their pack files should
1621
1521
        return repo._pack_collection
1622
1522
 
1623
1523
    def test_open_pack_will_optimise(self):
1624
 
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
 
1524
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
1625
1525
                                            [], '.test')
1626
1526
        new_pack = packer.open_pack()
1627
1527
        self.addCleanup(new_pack.abort) # ensure cleanup
1632
1532
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1633
1533
 
1634
1534
 
 
1535
class TestGCCHKPacker(TestCaseWithTransport):
 
1536
 
 
1537
    def make_abc_branch(self):
 
1538
        builder = self.make_branch_builder('source')
 
1539
        builder.start_series()
 
1540
        builder.build_snapshot('A', None, [
 
1541
            ('add', ('', 'root-id', 'directory', None)),
 
1542
            ('add', ('file', 'file-id', 'file', 'content\n')),
 
1543
            ])
 
1544
        builder.build_snapshot('B', ['A'], [
 
1545
            ('add', ('dir', 'dir-id', 'directory', None))])
 
1546
        builder.build_snapshot('C', ['B'], [
 
1547
            ('modify', ('file-id', 'new content\n'))])
 
1548
        builder.finish_series()
 
1549
        return builder.get_branch()
 
1550
 
 
1551
    def make_branch_with_disjoint_inventory_and_revision(self):
 
1552
        """a repo with separate packs for a revisions Revision and Inventory.
 
1553
 
 
1554
        There will be one pack file that holds the Revision content, and one
 
1555
        for the Inventory content.
 
1556
 
 
1557
        :return: (repository,
 
1558
                  pack_name_with_rev_A_Revision,
 
1559
                  pack_name_with_rev_A_Inventory,
 
1560
                  pack_name_with_rev_C_content)
 
1561
        """
 
1562
        b_source = self.make_abc_branch()
 
1563
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
 
1564
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
 
1565
        b_stacked.lock_write()
 
1566
        self.addCleanup(b_stacked.unlock)
 
1567
        b_stacked.fetch(b_source, 'B')
 
1568
        # Now re-open the stacked repo directly (no fallbacks) so that we can
 
1569
        # fill in the A rev.
 
1570
        repo_not_stacked = b_stacked.bzrdir.open_repository()
 
1571
        repo_not_stacked.lock_write()
 
1572
        self.addCleanup(repo_not_stacked.unlock)
 
1573
        # Now we should have a pack file with A's inventory, but not its
 
1574
        # Revision
 
1575
        self.assertEqual([('A',), ('B',)],
 
1576
                         sorted(repo_not_stacked.inventories.keys()))
 
1577
        self.assertEqual([('B',)],
 
1578
                         sorted(repo_not_stacked.revisions.keys()))
 
1579
        stacked_pack_names = repo_not_stacked._pack_collection.names()
 
1580
        # We have a couple names here, figure out which has A's inventory
 
1581
        for name in stacked_pack_names:
 
1582
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
 
1583
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
 
1584
            if ('A',) in keys:
 
1585
                inv_a_pack_name = name
 
1586
                break
 
1587
        else:
 
1588
            self.fail('Could not find pack containing A\'s inventory')
 
1589
        repo_not_stacked.fetch(b_source.repository, 'A')
 
1590
        self.assertEqual([('A',), ('B',)],
 
1591
                         sorted(repo_not_stacked.revisions.keys()))
 
1592
        new_pack_names = set(repo_not_stacked._pack_collection.names())
 
1593
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
 
1594
        self.assertEqual(1, len(rev_a_pack_names))
 
1595
        rev_a_pack_name = list(rev_a_pack_names)[0]
 
1596
        # Now fetch 'C', so we have a couple pack files to join
 
1597
        repo_not_stacked.fetch(b_source.repository, 'C')
 
1598
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
 
1599
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
 
1600
        self.assertEqual(1, len(rev_c_pack_names))
 
1601
        rev_c_pack_name = list(rev_c_pack_names)[0]
 
1602
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
 
1603
                rev_c_pack_name)
 
1604
 
 
1605
    def test_pack_with_distant_inventories(self):
 
1606
        # See https://bugs.launchpad.net/bzr/+bug/437003
 
1607
        # When repacking, it is possible to have an inventory in a different
 
1608
        # pack file than the associated revision. An autopack can then come
 
1609
        # along, and miss that inventory, and complain.
 
1610
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1611
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1612
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
 
1613
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
 
1614
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1615
                    [a_pack, c_pack], '.test-pack')
 
1616
        # This would raise ValueError in bug #437003, but should not raise an
 
1617
        # error once fixed.
 
1618
        packer.pack()
 
1619
 
 
1620
    def test_pack_with_missing_inventory(self):
 
1621
        # Similar to test_pack_with_missing_inventory, but this time, we force
 
1622
        # the A inventory to actually be gone from the repository.
 
1623
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1624
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1625
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
 
1626
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
 
1627
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1628
            repo._pack_collection.all_packs(), '.test-pack')
 
1629
        e = self.assertRaises(ValueError, packer.pack)
 
1630
        packer.new_pack.abort()
 
1631
        self.assertContainsRe(str(e),
 
1632
            r"We are missing inventories for revisions: .*'A'")
 
1633
 
 
1634
 
1635
1635
class TestCrossFormatPacks(TestCaseWithTransport):
1636
1636
 
1637
1637
    def log_pack(self, hint=None):
1652
1652
        self.addCleanup(target.unlock)
1653
1653
        source = source_tree.branch.repository._get_source(target._format)
1654
1654
        self.orig_pack = target.pack
1655
 
        target.pack = self.log_pack
 
1655
        self.overrideAttr(target, "pack", self.log_pack)
1656
1656
        search = target.search_missing_revision_ids(
1657
 
            source_tree.branch.repository, tip)
 
1657
            source_tree.branch.repository, revision_ids=[tip])
1658
1658
        stream = source.get_stream(search)
1659
1659
        from_format = source_tree.branch.repository._format
1660
1660
        sink = target._get_sink()
1676
1676
        self.addCleanup(target.unlock)
1677
1677
        source = source_tree.branch.repository
1678
1678
        self.orig_pack = target.pack
1679
 
        target.pack = self.log_pack
 
1679
        self.overrideAttr(target, "pack", self.log_pack)
1680
1680
        target.fetch(source)
1681
1681
        if expect_pack_called:
1682
1682
            self.assertLength(1, self.calls)
1710
1710
    def test_IDS_format_same_no(self):
1711
1711
        # When the formats are the same, pack is not called.
1712
1712
        self.run_fetch('2a', '2a', False)
 
1713
 
 
1714
 
 
1715
class Test_LazyListJoin(tests.TestCase):
 
1716
 
 
1717
    def test__repr__(self):
 
1718
        lazy = repository._LazyListJoin(['a'], ['b'])
 
1719
        self.assertEqual("bzrlib.repository._LazyListJoin((['a'], ['b']))",
 
1720
                         repr(lazy))
 
1721
 
 
1722
 
 
1723
class TestFeatures(tests.TestCaseWithTransport):
 
1724
 
 
1725
    def test_open_with_present_feature(self):
 
1726
        self.addCleanup(
 
1727
            repository.RepositoryFormatMetaDir.unregister_feature,
 
1728
            "makes-cheese-sandwich")
 
1729
        repository.RepositoryFormatMetaDir.register_feature(
 
1730
            "makes-cheese-sandwich")
 
1731
        repo = self.make_repository('.')
 
1732
        repo.lock_write()
 
1733
        repo._format.features["makes-cheese-sandwich"] = "required"
 
1734
        repo._format.check_support_status(False)
 
1735
        repo.unlock()
 
1736
 
 
1737
    def test_open_with_missing_required_feature(self):
 
1738
        repo = self.make_repository('.')
 
1739
        repo.lock_write()
 
1740
        repo._format.features["makes-cheese-sandwich"] = "required"
 
1741
        self.assertRaises(errors.MissingFeature,
 
1742
            repo._format.check_support_status, False)