~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Vincent Ladeuil
  • Date: 2010-09-28 08:57:31 UTC
  • mto: (5490.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 5492.
  • Revision ID: v.ladeuil+lp@free.fr-20100928085731-8h0duqj5wf4acsgy
Add -m to search for a regexp in news entries instead of the bug number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012, 2016 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
import sys
26
27
 
27
28
import bzrlib
28
 
from bzrlib.errors import (
29
 
    UnknownFormatError,
30
 
    UnsupportedFormatError,
31
 
    )
 
29
from bzrlib.errors import (NoSuchFile,
 
30
                           UnknownFormatError,
 
31
                           UnsupportedFormatError,
 
32
                           )
32
33
from bzrlib import (
33
34
    btree_index,
34
 
    symbol_versioning,
 
35
    graph,
35
36
    tests,
36
 
    transport,
37
 
    vf_search,
38
37
    )
39
38
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
40
39
from bzrlib.index import GraphIndex
43
42
    TestCase,
44
43
    TestCaseWithTransport,
45
44
    )
 
45
from bzrlib.transport import (
 
46
    get_transport,
 
47
    )
46
48
from bzrlib import (
47
49
    bzrdir,
48
 
    controldir,
49
50
    errors,
50
51
    inventory,
51
52
    osutils,
53
54
    revision as _mod_revision,
54
55
    upgrade,
55
56
    versionedfile,
56
 
    vf_repository,
57
57
    workingtree,
58
58
    )
59
59
from bzrlib.repofmt import (
60
60
    groupcompress_repo,
61
61
    knitrepo,
62
 
    knitpack_repo,
63
62
    pack_repo,
 
63
    weaverepo,
64
64
    )
65
65
 
66
66
 
67
67
class TestDefaultFormat(TestCase):
68
68
 
69
69
    def test_get_set_default_format(self):
70
 
        old_default = controldir.format_registry.get('default')
 
70
        old_default = bzrdir.format_registry.get('default')
71
71
        private_default = old_default().repository_format.__class__
72
 
        old_format = repository.format_registry.get_default()
 
72
        old_format = repository.RepositoryFormat.get_default_format()
73
73
        self.assertTrue(isinstance(old_format, private_default))
74
74
        def make_sample_bzrdir():
75
75
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
76
76
            my_bzrdir.repository_format = SampleRepositoryFormat()
77
77
            return my_bzrdir
78
 
        controldir.format_registry.remove('default')
79
 
        controldir.format_registry.register('sample', make_sample_bzrdir, '')
80
 
        controldir.format_registry.set_default('sample')
 
78
        bzrdir.format_registry.remove('default')
 
79
        bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
 
80
        bzrdir.format_registry.set_default('sample')
81
81
        # creating a repository should now create an instrumented dir.
82
82
        try:
83
83
            # the default branch format is used by the meta dir format
86
86
            result = dir.create_repository()
87
87
            self.assertEqual(result, 'A bzr repository dir')
88
88
        finally:
89
 
            controldir.format_registry.remove('default')
90
 
            controldir.format_registry.remove('sample')
91
 
            controldir.format_registry.register('default', old_default, '')
92
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
89
            bzrdir.format_registry.remove('default')
 
90
            bzrdir.format_registry.remove('sample')
 
91
            bzrdir.format_registry.register('default', old_default, '')
 
92
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
93
93
                              old_format.__class__)
94
94
 
95
95
 
96
 
class SampleRepositoryFormat(repository.RepositoryFormatMetaDir):
 
96
class SampleRepositoryFormat(repository.RepositoryFormat):
97
97
    """A sample format
98
98
 
99
99
    this format is initializable, unsupported to aid in testing the
100
100
    open and open(unsupported=True) routines.
101
101
    """
102
102
 
103
 
    @classmethod
104
 
    def get_format_string(cls):
 
103
    def get_format_string(self):
105
104
        """See RepositoryFormat.get_format_string()."""
106
105
        return "Sample .bzr repository format."
107
106
 
118
117
        return "opened repository."
119
118
 
120
119
 
121
 
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
122
 
    """A sample format that can not be used in a metadir
123
 
 
124
 
    """
125
 
 
126
 
    def get_format_string(self):
127
 
        raise NotImplementedError
128
 
 
129
 
 
130
120
class TestRepositoryFormat(TestCaseWithTransport):
131
121
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
132
122
 
138
128
        def check_format(format, url):
139
129
            dir = format._matchingbzrdir.initialize(url)
140
130
            format.initialize(dir)
141
 
            t = transport.get_transport_from_path(url)
142
 
            found_format = repository.RepositoryFormatMetaDir.find_format(dir)
143
 
            self.assertIsInstance(found_format, format.__class__)
144
 
        check_format(repository.format_registry.get_default(), "bar")
 
131
            t = get_transport(url)
 
132
            found_format = repository.RepositoryFormat.find_format(dir)
 
133
            self.failUnless(isinstance(found_format, format.__class__))
 
134
        check_format(weaverepo.RepositoryFormat7(), "bar")
145
135
 
146
136
    def test_find_format_no_repository(self):
147
137
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
148
138
        self.assertRaises(errors.NoRepositoryPresent,
149
 
                          repository.RepositoryFormatMetaDir.find_format,
 
139
                          repository.RepositoryFormat.find_format,
150
140
                          dir)
151
141
 
152
 
    def test_from_string(self):
153
 
        self.assertIsInstance(
154
 
            SampleRepositoryFormat.from_string(
155
 
                "Sample .bzr repository format."),
156
 
            SampleRepositoryFormat)
157
 
        self.assertRaises(AssertionError,
158
 
            SampleRepositoryFormat.from_string,
159
 
                "Different .bzr repository format.")
160
 
 
161
142
    def test_find_format_unknown_format(self):
162
143
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
163
144
        SampleRepositoryFormat().initialize(dir)
164
145
        self.assertRaises(UnknownFormatError,
165
 
                          repository.RepositoryFormatMetaDir.find_format,
 
146
                          repository.RepositoryFormat.find_format,
166
147
                          dir)
167
148
 
168
 
    def test_find_format_with_features(self):
169
 
        tree = self.make_branch_and_tree('.', format='2a')
170
 
        tree.branch.repository.update_feature_flags({"name": "necessity"})
171
 
        found_format = repository.RepositoryFormatMetaDir.find_format(tree.bzrdir)
172
 
        self.assertIsInstance(found_format, repository.RepositoryFormatMetaDir)
173
 
        self.assertEqual(found_format.features.get("name"), "necessity")
174
 
        self.assertRaises(errors.MissingFeature, found_format.check_support_status,
175
 
            True)
176
 
        self.addCleanup(repository.RepositoryFormatMetaDir.unregister_feature,
177
 
            "name")
178
 
        repository.RepositoryFormatMetaDir.register_feature("name")
179
 
        found_format.check_support_status(True)
180
 
 
181
 
 
182
 
class TestRepositoryFormatRegistry(TestCase):
183
 
 
184
 
    def setUp(self):
185
 
        super(TestRepositoryFormatRegistry, self).setUp()
186
 
        self.registry = repository.RepositoryFormatRegistry()
187
 
 
188
149
    def test_register_unregister_format(self):
189
150
        format = SampleRepositoryFormat()
190
 
        self.registry.register(format)
191
 
        self.assertEqual(format, self.registry.get("Sample .bzr repository format."))
192
 
        self.registry.remove(format)
193
 
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
194
 
 
195
 
    def test_get_all(self):
196
 
        format = SampleRepositoryFormat()
197
 
        self.assertEqual([], self.registry._get_all())
198
 
        self.registry.register(format)
199
 
        self.assertEqual([format], self.registry._get_all())
200
 
 
201
 
    def test_register_extra(self):
202
 
        format = SampleExtraRepositoryFormat()
203
 
        self.assertEqual([], self.registry._get_all())
204
 
        self.registry.register_extra(format)
205
 
        self.assertEqual([format], self.registry._get_all())
206
 
 
207
 
    def test_register_extra_lazy(self):
208
 
        self.assertEqual([], self.registry._get_all())
209
 
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
210
 
            "SampleExtraRepositoryFormat")
211
 
        formats = self.registry._get_all()
212
 
        self.assertEqual(1, len(formats))
213
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
151
        # make a control dir
 
152
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
153
        # make a repo
 
154
        format.initialize(dir)
 
155
        # register a format for it.
 
156
        repository.RepositoryFormat.register_format(format)
 
157
        # which repository.Open will refuse (not supported)
 
158
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
 
159
        # but open(unsupported) will work
 
160
        self.assertEqual(format.open(dir), "opened repository.")
 
161
        # unregister the format
 
162
        repository.RepositoryFormat.unregister_format(format)
 
163
 
 
164
 
 
165
class TestFormat6(TestCaseWithTransport):
 
166
 
 
167
    def test_attribute__fetch_order(self):
 
168
        """Weaves need topological data insertion."""
 
169
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
170
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
171
        self.assertEqual('topological', repo._format._fetch_order)
 
172
 
 
173
    def test_attribute__fetch_uses_deltas(self):
 
174
        """Weaves do not reuse deltas."""
 
175
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
176
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
177
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
178
 
 
179
    def test_attribute__fetch_reconcile(self):
 
180
        """Weave repositories need a reconcile after fetch."""
 
181
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
182
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
183
        self.assertEqual(True, repo._format._fetch_reconcile)
 
184
 
 
185
    def test_no_ancestry_weave(self):
 
186
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
187
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
188
        # We no longer need to create the ancestry.weave file
 
189
        # since it is *never* used.
 
190
        self.assertRaises(NoSuchFile,
 
191
                          control.transport.get,
 
192
                          'ancestry.weave')
 
193
 
 
194
    def test_supports_external_lookups(self):
 
195
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
196
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
197
        self.assertFalse(repo._format.supports_external_lookups)
 
198
 
 
199
 
 
200
class TestFormat7(TestCaseWithTransport):
 
201
 
 
202
    def test_attribute__fetch_order(self):
 
203
        """Weaves need topological data insertion."""
 
204
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
205
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
206
        self.assertEqual('topological', repo._format._fetch_order)
 
207
 
 
208
    def test_attribute__fetch_uses_deltas(self):
 
209
        """Weaves do not reuse deltas."""
 
210
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
211
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
212
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
213
 
 
214
    def test_attribute__fetch_reconcile(self):
 
215
        """Weave repositories need a reconcile after fetch."""
 
216
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
217
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
218
        self.assertEqual(True, repo._format._fetch_reconcile)
 
219
 
 
220
    def test_disk_layout(self):
 
221
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
222
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
223
        # in case of side effects of locking.
 
224
        repo.lock_write()
 
225
        repo.unlock()
 
226
        # we want:
 
227
        # format 'Bazaar-NG Repository format 7'
 
228
        # lock ''
 
229
        # inventory.weave == empty_weave
 
230
        # empty revision-store directory
 
231
        # empty weaves directory
 
232
        t = control.get_repository_transport(None)
 
233
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
234
                             t.get('format').read())
 
235
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
236
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
237
        self.assertEqualDiff('# bzr weave file v5\n'
 
238
                             'w\n'
 
239
                             'W\n',
 
240
                             t.get('inventory.weave').read())
 
241
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
242
        # disk.
 
243
        control.create_branch()
 
244
        tree = control.create_workingtree()
 
245
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
246
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
247
        try:
 
248
            tree.commit('first post', rev_id='first')
 
249
        except errors.IllegalPath:
 
250
            if sys.platform != 'win32':
 
251
                raise
 
252
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
 
253
                              ' in repo format 7')
 
254
            return
 
255
        self.assertEqualDiff(
 
256
            '# bzr weave file v5\n'
 
257
            'i\n'
 
258
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
259
            'n first\n'
 
260
            '\n'
 
261
            'w\n'
 
262
            '{ 0\n'
 
263
            '. content\n'
 
264
            '}\n'
 
265
            'W\n',
 
266
            t.get('weaves/74/Foo%3ABar.weave').read())
 
267
 
 
268
    def test_shared_disk_layout(self):
 
269
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
270
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
271
        # we want:
 
272
        # format 'Bazaar-NG Repository format 7'
 
273
        # inventory.weave == empty_weave
 
274
        # empty revision-store directory
 
275
        # empty weaves directory
 
276
        # a 'shared-storage' marker file.
 
277
        # lock is not present when unlocked
 
278
        t = control.get_repository_transport(None)
 
279
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
280
                             t.get('format').read())
 
281
        self.assertEqualDiff('', t.get('shared-storage').read())
 
282
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
283
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
284
        self.assertEqualDiff('# bzr weave file v5\n'
 
285
                             'w\n'
 
286
                             'W\n',
 
287
                             t.get('inventory.weave').read())
 
288
        self.assertFalse(t.has('branch-lock'))
 
289
 
 
290
    def test_creates_lockdir(self):
 
291
        """Make sure it appears to be controlled by a LockDir existence"""
 
292
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
293
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
294
        t = control.get_repository_transport(None)
 
295
        # TODO: Should check there is a 'lock' toplevel directory,
 
296
        # regardless of contents
 
297
        self.assertFalse(t.has('lock/held/info'))
 
298
        repo.lock_write()
 
299
        try:
 
300
            self.assertTrue(t.has('lock/held/info'))
 
301
        finally:
 
302
            # unlock so we don't get a warning about failing to do so
 
303
            repo.unlock()
 
304
 
 
305
    def test_uses_lockdir(self):
 
306
        """repo format 7 actually locks on lockdir"""
 
307
        base_url = self.get_url()
 
308
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
309
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
310
        t = control.get_repository_transport(None)
 
311
        repo.lock_write()
 
312
        repo.unlock()
 
313
        del repo
 
314
        # make sure the same lock is created by opening it
 
315
        repo = repository.Repository.open(base_url)
 
316
        repo.lock_write()
 
317
        self.assertTrue(t.has('lock/held/info'))
 
318
        repo.unlock()
 
319
        self.assertFalse(t.has('lock/held/info'))
 
320
 
 
321
    def test_shared_no_tree_disk_layout(self):
 
322
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
323
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
324
        repo.set_make_working_trees(False)
 
325
        # we want:
 
326
        # format 'Bazaar-NG Repository format 7'
 
327
        # lock ''
 
328
        # inventory.weave == empty_weave
 
329
        # empty revision-store directory
 
330
        # empty weaves directory
 
331
        # a 'shared-storage' marker file.
 
332
        t = control.get_repository_transport(None)
 
333
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
334
                             t.get('format').read())
 
335
        ## self.assertEqualDiff('', t.get('lock').read())
 
336
        self.assertEqualDiff('', t.get('shared-storage').read())
 
337
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
338
        repo.set_make_working_trees(True)
 
339
        self.assertFalse(t.has('no-working-trees'))
 
340
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
341
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
342
        self.assertEqualDiff('# bzr weave file v5\n'
 
343
                             'w\n'
 
344
                             'W\n',
 
345
                             t.get('inventory.weave').read())
 
346
 
 
347
    def test_supports_external_lookups(self):
 
348
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
349
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
350
        self.assertFalse(repo._format.supports_external_lookups)
214
351
 
215
352
 
216
353
class TestFormatKnit1(TestCaseWithTransport):
218
355
    def test_attribute__fetch_order(self):
219
356
        """Knits need topological data insertion."""
220
357
        repo = self.make_repository('.',
221
 
                format=controldir.format_registry.get('knit')())
 
358
                format=bzrdir.format_registry.get('knit')())
222
359
        self.assertEqual('topological', repo._format._fetch_order)
223
360
 
224
361
    def test_attribute__fetch_uses_deltas(self):
225
362
        """Knits reuse deltas."""
226
363
        repo = self.make_repository('.',
227
 
                format=controldir.format_registry.get('knit')())
 
364
                format=bzrdir.format_registry.get('knit')())
228
365
        self.assertEqual(True, repo._format._fetch_uses_deltas)
229
366
 
230
367
    def test_disk_layout(self):
316
453
        is valid when the api is not being abused.
317
454
        """
318
455
        repo = self.make_repository('.',
319
 
                format=controldir.format_registry.get('knit')())
 
456
                format=bzrdir.format_registry.get('knit')())
320
457
        inv_xml = '<inventory format="5">\n</inventory>\n'
321
458
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
322
459
        self.assertEqual('test-rev-id', inv.root.revision)
324
461
    def test_deserialise_uses_global_revision_id(self):
325
462
        """If it is set, then we re-use the global revision id"""
326
463
        repo = self.make_repository('.',
327
 
                format=controldir.format_registry.get('knit')())
 
464
                format=bzrdir.format_registry.get('knit')())
328
465
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
329
466
                   '</inventory>\n')
330
467
        # Arguably, the deserialise_inventory should detect a mismatch, and
337
474
 
338
475
    def test_supports_external_lookups(self):
339
476
        repo = self.make_repository('.',
340
 
                format=controldir.format_registry.get('knit')())
 
477
                format=bzrdir.format_registry.get('knit')())
341
478
        self.assertFalse(repo._format.supports_external_lookups)
342
479
 
343
480
 
384
521
        # classes do not barf inappropriately when a surprising repository type
385
522
        # is handed to them.
386
523
        dummy_a = DummyRepository()
387
 
        dummy_a._format = RepositoryFormat()
388
 
        dummy_a._format.supports_full_versioned_files = True
389
524
        dummy_b = DummyRepository()
390
 
        dummy_b._format = RepositoryFormat()
391
 
        dummy_b._format.supports_full_versioned_files = True
392
525
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
393
526
 
394
527
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
398
531
        no actual sane default in the presence of incompatible data models.
399
532
        """
400
533
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
401
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
534
        self.assertEqual(repository.InterSameDataRepository,
402
535
                         inter_repo.__class__)
403
536
        self.assertEqual(repo_a, inter_repo.source)
404
537
        self.assertEqual(repo_b, inter_repo.target)
418
551
        dummy_a._serializer = repo._serializer
419
552
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
420
553
        dummy_a._format.rich_root_data = repo._format.rich_root_data
421
 
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
422
554
        dummy_b._serializer = repo._serializer
423
555
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
424
556
        dummy_b._format.rich_root_data = repo._format.rich_root_data
425
 
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
426
557
        repository.InterRepository.register_optimiser(InterDummy)
427
558
        try:
428
559
            # we should get the default for something InterDummy returns False
441
572
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
442
573
 
443
574
 
444
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
445
 
 
446
 
    @classmethod
447
 
    def get_format_string(cls):
448
 
        return "Test Format 1"
449
 
 
450
 
 
451
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
452
 
 
453
 
    @classmethod
454
 
    def get_format_string(cls):
455
 
        return "Test Format 2"
 
575
class TestInterWeaveRepo(TestCaseWithTransport):
 
576
 
 
577
    def test_is_compatible_and_registered(self):
 
578
        # InterWeaveRepo is compatible when either side
 
579
        # is a format 5/6/7 branch
 
580
        from bzrlib.repofmt import knitrepo, weaverepo
 
581
        formats = [weaverepo.RepositoryFormat5(),
 
582
                   weaverepo.RepositoryFormat6(),
 
583
                   weaverepo.RepositoryFormat7()]
 
584
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
585
                                knitrepo.RepositoryFormatKnit1(),
 
586
                                ]
 
587
        repo_a = self.make_repository('a')
 
588
        repo_b = self.make_repository('b')
 
589
        is_compatible = repository.InterWeaveRepo.is_compatible
 
590
        for source in incompatible_formats:
 
591
            # force incompatible left then right
 
592
            repo_a._format = source
 
593
            repo_b._format = formats[0]
 
594
            self.assertFalse(is_compatible(repo_a, repo_b))
 
595
            self.assertFalse(is_compatible(repo_b, repo_a))
 
596
        for source in formats:
 
597
            repo_a._format = source
 
598
            for target in formats:
 
599
                repo_b._format = target
 
600
                self.assertTrue(is_compatible(repo_a, repo_b))
 
601
        self.assertEqual(repository.InterWeaveRepo,
 
602
                         repository.InterRepository.get(repo_a,
 
603
                                                        repo_b).__class__)
456
604
 
457
605
 
458
606
class TestRepositoryConverter(TestCaseWithTransport):
459
607
 
460
608
    def test_convert_empty(self):
461
 
        source_format = TestRepositoryFormat1()
462
 
        target_format = TestRepositoryFormat2()
463
 
        repository.format_registry.register(source_format)
464
 
        self.addCleanup(repository.format_registry.remove,
465
 
            source_format)
466
 
        repository.format_registry.register(target_format)
467
 
        self.addCleanup(repository.format_registry.remove,
468
 
            target_format)
469
 
        t = self.get_transport()
 
609
        t = get_transport(self.get_url('.'))
470
610
        t.mkdir('repository')
471
611
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
472
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
612
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
613
        target_format = knitrepo.RepositoryFormatKnit1()
473
614
        converter = repository.CopyConverter(target_format)
474
615
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
475
616
        try:
480
621
        self.assertTrue(isinstance(target_format, repo._format.__class__))
481
622
 
482
623
 
 
624
class TestMisc(TestCase):
 
625
 
 
626
    def test_unescape_xml(self):
 
627
        """We get some kind of error when malformed entities are passed"""
 
628
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
629
 
 
630
 
483
631
class TestRepositoryFormatKnit3(TestCaseWithTransport):
484
632
 
485
633
    def test_attribute__fetch_order(self):
506
654
        revision_tree.lock_read()
507
655
        try:
508
656
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
509
 
                revision_tree.get_root_id())
 
657
                revision_tree.inventory.root.file_id)
510
658
        finally:
511
659
            revision_tree.unlock()
512
660
        format = bzrdir.BzrDirMetaFormat1()
516
664
        revision_tree = tree.branch.repository.revision_tree('dull')
517
665
        revision_tree.lock_read()
518
666
        try:
519
 
            revision_tree.get_file_lines(revision_tree.get_root_id())
 
667
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
520
668
        finally:
521
669
            revision_tree.unlock()
522
670
        tree.commit("Another dull commit", rev_id='dull2')
523
671
        revision_tree = tree.branch.repository.revision_tree('dull2')
524
672
        revision_tree.lock_read()
525
673
        self.addCleanup(revision_tree.unlock)
526
 
        self.assertEqual('dull',
527
 
                revision_tree.get_file_revision(revision_tree.get_root_id()))
 
674
        self.assertEqual('dull', revision_tree.inventory.root.revision)
528
675
 
529
676
    def test_supports_external_lookups(self):
530
677
        format = bzrdir.BzrDirMetaFormat1()
681
828
        target = self.make_repository('target', format='rich-root-pack')
682
829
        stream = source._get_source(target._format)
683
830
        # We don't want the child GroupCHKStreamSource
684
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
831
        self.assertIs(type(stream), repository.StreamSource)
685
832
 
686
833
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
687
834
        source_builder = self.make_branch_builder('source',
714
861
 
715
862
        # On a regular pass, getting the inventories and chk pages for rev-2
716
863
        # would only get the newly created chk pages
717
 
        search = vf_search.SearchResult(set(['rev-2']), set(['rev-1']), 1,
 
864
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
718
865
                                    set(['rev-2']))
719
866
        simple_chk_records = []
720
867
        for vf_name, substream in source.get_stream(search):
763
910
        source = self.make_repository('source', format='pack-0.92')
764
911
        target = self.make_repository('target', format='pack-0.92')
765
912
        stream_source = source._get_source(target._format)
766
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
913
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
767
914
 
768
915
    def test_source_to_exact_pack_rich_root_pack(self):
769
916
        source = self.make_repository('source', format='rich-root-pack')
770
917
        target = self.make_repository('target', format='rich-root-pack')
771
918
        stream_source = source._get_source(target._format)
772
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
919
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
773
920
 
774
921
    def test_source_to_exact_pack_19(self):
775
922
        source = self.make_repository('source', format='1.9')
776
923
        target = self.make_repository('target', format='1.9')
777
924
        stream_source = source._get_source(target._format)
778
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
925
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
779
926
 
780
927
    def test_source_to_exact_pack_19_rich_root(self):
781
928
        source = self.make_repository('source', format='1.9-rich-root')
782
929
        target = self.make_repository('target', format='1.9-rich-root')
783
930
        stream_source = source._get_source(target._format)
784
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
931
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
785
932
 
786
933
    def test_source_to_remote_exact_pack_19(self):
787
934
        trans = self.make_smart_server('target')
790
937
        target = self.make_repository('target', format='1.9')
791
938
        target = repository.Repository.open(trans.base)
792
939
        stream_source = source._get_source(target._format)
793
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
940
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
794
941
 
795
942
    def test_stream_source_to_non_exact(self):
796
943
        source = self.make_repository('source', format='pack-0.92')
797
944
        target = self.make_repository('target', format='1.9')
798
945
        stream = source._get_source(target._format)
799
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
946
        self.assertIs(type(stream), repository.StreamSource)
800
947
 
801
948
    def test_stream_source_to_non_exact_rich_root(self):
802
949
        source = self.make_repository('source', format='1.9')
803
950
        target = self.make_repository('target', format='1.9-rich-root')
804
951
        stream = source._get_source(target._format)
805
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
952
        self.assertIs(type(stream), repository.StreamSource)
806
953
 
807
954
    def test_source_to_remote_non_exact_pack_19(self):
808
955
        trans = self.make_smart_server('target')
811
958
        target = self.make_repository('target', format='1.6')
812
959
        target = repository.Repository.open(trans.base)
813
960
        stream_source = source._get_source(target._format)
814
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
961
        self.assertIs(type(stream_source), repository.StreamSource)
815
962
 
816
963
    def test_stream_source_to_knit(self):
817
964
        source = self.make_repository('source', format='pack-0.92')
818
965
        target = self.make_repository('target', format='dirstate')
819
966
        stream = source._get_source(target._format)
820
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
967
        self.assertIs(type(stream), repository.StreamSource)
821
968
 
822
969
 
823
970
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
825
972
 
826
973
    def setUp(self):
827
974
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
828
 
        self.builder = self.make_branch_builder('source')
 
975
        self.builder = self.make_branch_builder('source',
 
976
            format='development6-rich-root')
829
977
        self.builder.start_series()
830
978
        self.builder.build_snapshot('initial', None,
831
979
            [('add', ('', 'tree-root', 'directory', None))])
901
1049
            revision = _mod_revision.Revision('rev1a',
902
1050
                committer='jrandom@example.com', timestamp=0,
903
1051
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
904
 
            repo.add_revision('rev1a', revision, inv)
 
1052
            repo.add_revision('rev1a',revision, inv)
905
1053
 
906
1054
            # make rev1b, which has no Revision, but has an Inventory, and
907
1055
            # file1
942
1090
        revision = _mod_revision.Revision(revision_id,
943
1091
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
944
1092
            timezone=0, message='foo', parent_ids=parent_ids)
945
 
        repo.add_revision(revision_id, revision, inv)
 
1093
        repo.add_revision(revision_id,revision, inv)
946
1094
 
947
1095
    def add_file(self, repo, inv, filename, revision, parents):
948
1096
        file_id = filename + '-id'
976
1124
class TestRepositoryPackCollection(TestCaseWithTransport):
977
1125
 
978
1126
    def get_format(self):
979
 
        return controldir.format_registry.make_bzrdir('pack-0.92')
 
1127
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
980
1128
 
981
1129
    def get_packs(self):
982
1130
        format = self.get_format()
1078
1226
            sorted(set([osutils.splitext(n)[0] for n in
1079
1227
                        packs._index_transport.list_dir('.')])))
1080
1228
 
1081
 
    def test__obsolete_packs_missing_directory(self):
1082
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1083
 
        r.control_transport.rmdir('obsolete_packs')
1084
 
        names = packs.names()
1085
 
        pack = packs.get_pack_by_name(names[0])
1086
 
        # Schedule this one for removal
1087
 
        packs._remove_pack_from_memory(pack)
1088
 
        # Now trigger the obsoletion, and ensure that all the remaining files
1089
 
        # are still renamed
1090
 
        packs._obsolete_packs([pack])
1091
 
        self.assertEqual([n + '.pack' for n in names[1:]],
1092
 
                         sorted(packs._pack_transport.list_dir('.')))
1093
 
        # names[0] should not be present in the index anymore
1094
 
        self.assertEqual(names[1:],
1095
 
            sorted(set([osutils.splitext(n)[0] for n in
1096
 
                        packs._index_transport.list_dir('.')])))
1097
 
 
1098
1229
    def test_pack_distribution_zero(self):
1099
1230
        packs = self.get_packs()
1100
1231
        self.assertEqual([0], packs.pack_distribution(0))
1370
1501
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1371
1502
        self.assertEqual([pack.name], sorted(obsolete_names))
1372
1503
 
1373
 
    def test_pack_no_obsolete_packs_directory(self):
1374
 
        """Bug #314314, don't fail if obsolete_packs directory does
1375
 
        not exist."""
1376
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1377
 
        r.control_transport.rmdir('obsolete_packs')
1378
 
        packs._clear_obsolete_packs()
1379
1504
 
1380
1505
 
1381
1506
class TestPack(TestCaseWithTransport):
1482
1607
        # Because of how they were built, they correspond to
1483
1608
        # ['D', 'C', 'B', 'A']
1484
1609
        packs = b.repository._pack_collection.packs
1485
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1610
        packer = pack_repo.Packer(b.repository._pack_collection,
1486
1611
                                  packs, 'testing',
1487
1612
                                  revision_ids=['B', 'C'])
1488
1613
        # Now, when we are copying the B & C revisions, their pack files should
1502
1627
        return repo._pack_collection
1503
1628
 
1504
1629
    def test_open_pack_will_optimise(self):
1505
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1630
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1506
1631
                                            [], '.test')
1507
1632
        new_pack = packer.open_pack()
1508
1633
        self.addCleanup(new_pack.abort) # ensure cleanup
1513
1638
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1514
1639
 
1515
1640
 
1516
 
class TestGCCHKPacker(TestCaseWithTransport):
1517
 
 
1518
 
    def make_abc_branch(self):
1519
 
        builder = self.make_branch_builder('source')
1520
 
        builder.start_series()
1521
 
        builder.build_snapshot('A', None, [
1522
 
            ('add', ('', 'root-id', 'directory', None)),
1523
 
            ('add', ('file', 'file-id', 'file', 'content\n')),
1524
 
            ])
1525
 
        builder.build_snapshot('B', ['A'], [
1526
 
            ('add', ('dir', 'dir-id', 'directory', None))])
1527
 
        builder.build_snapshot('C', ['B'], [
1528
 
            ('modify', ('file-id', 'new content\n'))])
1529
 
        builder.finish_series()
1530
 
        return builder.get_branch()
1531
 
 
1532
 
    def make_branch_with_disjoint_inventory_and_revision(self):
1533
 
        """a repo with separate packs for a revisions Revision and Inventory.
1534
 
 
1535
 
        There will be one pack file that holds the Revision content, and one
1536
 
        for the Inventory content.
1537
 
 
1538
 
        :return: (repository,
1539
 
                  pack_name_with_rev_A_Revision,
1540
 
                  pack_name_with_rev_A_Inventory,
1541
 
                  pack_name_with_rev_C_content)
1542
 
        """
1543
 
        b_source = self.make_abc_branch()
1544
 
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1545
 
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1546
 
        b_stacked.lock_write()
1547
 
        self.addCleanup(b_stacked.unlock)
1548
 
        b_stacked.fetch(b_source, 'B')
1549
 
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1550
 
        # fill in the A rev.
1551
 
        repo_not_stacked = b_stacked.bzrdir.open_repository()
1552
 
        repo_not_stacked.lock_write()
1553
 
        self.addCleanup(repo_not_stacked.unlock)
1554
 
        # Now we should have a pack file with A's inventory, but not its
1555
 
        # Revision
1556
 
        self.assertEqual([('A',), ('B',)],
1557
 
                         sorted(repo_not_stacked.inventories.keys()))
1558
 
        self.assertEqual([('B',)],
1559
 
                         sorted(repo_not_stacked.revisions.keys()))
1560
 
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1561
 
        # We have a couple names here, figure out which has A's inventory
1562
 
        for name in stacked_pack_names:
1563
 
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1564
 
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1565
 
            if ('A',) in keys:
1566
 
                inv_a_pack_name = name
1567
 
                break
1568
 
        else:
1569
 
            self.fail('Could not find pack containing A\'s inventory')
1570
 
        repo_not_stacked.fetch(b_source.repository, 'A')
1571
 
        self.assertEqual([('A',), ('B',)],
1572
 
                         sorted(repo_not_stacked.revisions.keys()))
1573
 
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1574
 
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1575
 
        self.assertEqual(1, len(rev_a_pack_names))
1576
 
        rev_a_pack_name = list(rev_a_pack_names)[0]
1577
 
        # Now fetch 'C', so we have a couple pack files to join
1578
 
        repo_not_stacked.fetch(b_source.repository, 'C')
1579
 
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1580
 
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1581
 
        self.assertEqual(1, len(rev_c_pack_names))
1582
 
        rev_c_pack_name = list(rev_c_pack_names)[0]
1583
 
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1584
 
                rev_c_pack_name)
1585
 
 
1586
 
    def test_pack_with_distant_inventories(self):
1587
 
        # See https://bugs.launchpad.net/bzr/+bug/437003
1588
 
        # When repacking, it is possible to have an inventory in a different
1589
 
        # pack file than the associated revision. An autopack can then come
1590
 
        # along, and miss that inventory, and complain.
1591
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1592
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1593
 
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1594
 
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1595
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1596
 
                    [a_pack, c_pack], '.test-pack')
1597
 
        # This would raise ValueError in bug #437003, but should not raise an
1598
 
        # error once fixed.
1599
 
        packer.pack()
1600
 
 
1601
 
    def test_pack_with_missing_inventory(self):
1602
 
        # Similar to test_pack_with_missing_inventory, but this time, we force
1603
 
        # the A inventory to actually be gone from the repository.
1604
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1605
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1606
 
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1607
 
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1608
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1609
 
            repo._pack_collection.all_packs(), '.test-pack')
1610
 
        e = self.assertRaises(ValueError, packer.pack)
1611
 
        packer.new_pack.abort()
1612
 
        self.assertContainsRe(str(e),
1613
 
            r"We are missing inventories for revisions: .*'A'")
1614
 
 
1615
 
 
1616
1641
class TestCrossFormatPacks(TestCaseWithTransport):
1617
1642
 
1618
1643
    def log_pack(self, hint=None):
1633
1658
        self.addCleanup(target.unlock)
1634
1659
        source = source_tree.branch.repository._get_source(target._format)
1635
1660
        self.orig_pack = target.pack
1636
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1661
        target.pack = self.log_pack
1637
1662
        search = target.search_missing_revision_ids(
1638
 
            source_tree.branch.repository, revision_ids=[tip])
 
1663
            source_tree.branch.repository, tip)
1639
1664
        stream = source.get_stream(search)
1640
1665
        from_format = source_tree.branch.repository._format
1641
1666
        sink = target._get_sink()
1657
1682
        self.addCleanup(target.unlock)
1658
1683
        source = source_tree.branch.repository
1659
1684
        self.orig_pack = target.pack
1660
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1685
        target.pack = self.log_pack
1661
1686
        target.fetch(source)
1662
1687
        if expect_pack_called:
1663
1688
            self.assertLength(1, self.calls)
1691
1716
    def test_IDS_format_same_no(self):
1692
1717
        # When the formats are the same, pack is not called.
1693
1718
        self.run_fetch('2a', '2a', False)
1694
 
 
1695
 
 
1696
 
class Test_LazyListJoin(tests.TestCase):
1697
 
 
1698
 
    def test__repr__(self):
1699
 
        lazy = repository._LazyListJoin(['a'], ['b'])
1700
 
        self.assertEqual("bzrlib.repository._LazyListJoin((['a'], ['b']))",
1701
 
                         repr(lazy))
1702
 
 
1703
 
 
1704
 
class TestFeatures(tests.TestCaseWithTransport):
1705
 
 
1706
 
    def test_open_with_present_feature(self):
1707
 
        self.addCleanup(
1708
 
            repository.RepositoryFormatMetaDir.unregister_feature,
1709
 
            "makes-cheese-sandwich")
1710
 
        repository.RepositoryFormatMetaDir.register_feature(
1711
 
            "makes-cheese-sandwich")
1712
 
        repo = self.make_repository('.')
1713
 
        repo.lock_write()
1714
 
        repo._format.features["makes-cheese-sandwich"] = "required"
1715
 
        repo._format.check_support_status(False)
1716
 
        repo.unlock()
1717
 
 
1718
 
    def test_open_with_missing_required_feature(self):
1719
 
        repo = self.make_repository('.')
1720
 
        repo.lock_write()
1721
 
        repo._format.features["makes-cheese-sandwich"] = "required"
1722
 
        self.assertRaises(errors.MissingFeature,
1723
 
            repo._format.check_support_status, False)