~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: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
 
26
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.assertEquals(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
149
    def test_register_unregister_format(self):
182
 
        # Test deprecated format registration functions
183
150
        format = SampleRepositoryFormat()
184
151
        # make a control dir
185
152
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
186
153
        # make a repo
187
154
        format.initialize(dir)
188
155
        # register a format for it.
189
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
190
 
            repository.RepositoryFormat.register_format, format)
 
156
        repository.RepositoryFormat.register_format(format)
191
157
        # which repository.Open will refuse (not supported)
192
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
193
 
            self.get_url())
 
158
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
194
159
        # but open(unsupported) will work
195
160
        self.assertEqual(format.open(dir), "opened repository.")
196
161
        # unregister the format
197
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
198
 
            repository.RepositoryFormat.unregister_format, format)
199
 
 
200
 
 
201
 
class TestRepositoryFormatRegistry(TestCase):
202
 
 
203
 
    def setUp(self):
204
 
        super(TestRepositoryFormatRegistry, self).setUp()
205
 
        self.registry = repository.RepositoryFormatRegistry()
206
 
 
207
 
    def test_register_unregister_format(self):
208
 
        format = SampleRepositoryFormat()
209
 
        self.registry.register(format)
210
 
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
211
 
        self.registry.remove(format)
212
 
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
213
 
 
214
 
    def test_get_all(self):
215
 
        format = SampleRepositoryFormat()
216
 
        self.assertEquals([], self.registry._get_all())
217
 
        self.registry.register(format)
218
 
        self.assertEquals([format], self.registry._get_all())
219
 
 
220
 
    def test_register_extra(self):
221
 
        format = SampleExtraRepositoryFormat()
222
 
        self.assertEquals([], self.registry._get_all())
223
 
        self.registry.register_extra(format)
224
 
        self.assertEquals([format], self.registry._get_all())
225
 
 
226
 
    def test_register_extra_lazy(self):
227
 
        self.assertEquals([], self.registry._get_all())
228
 
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
229
 
            "SampleExtraRepositoryFormat")
230
 
        formats = self.registry._get_all()
231
 
        self.assertEquals(1, len(formats))
232
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
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)
233
351
 
234
352
 
235
353
class TestFormatKnit1(TestCaseWithTransport):
237
355
    def test_attribute__fetch_order(self):
238
356
        """Knits need topological data insertion."""
239
357
        repo = self.make_repository('.',
240
 
                format=controldir.format_registry.get('knit')())
 
358
                format=bzrdir.format_registry.get('knit')())
241
359
        self.assertEqual('topological', repo._format._fetch_order)
242
360
 
243
361
    def test_attribute__fetch_uses_deltas(self):
244
362
        """Knits reuse deltas."""
245
363
        repo = self.make_repository('.',
246
 
                format=controldir.format_registry.get('knit')())
 
364
                format=bzrdir.format_registry.get('knit')())
247
365
        self.assertEqual(True, repo._format._fetch_uses_deltas)
248
366
 
249
367
    def test_disk_layout(self):
335
453
        is valid when the api is not being abused.
336
454
        """
337
455
        repo = self.make_repository('.',
338
 
                format=controldir.format_registry.get('knit')())
 
456
                format=bzrdir.format_registry.get('knit')())
339
457
        inv_xml = '<inventory format="5">\n</inventory>\n'
340
458
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
341
459
        self.assertEqual('test-rev-id', inv.root.revision)
343
461
    def test_deserialise_uses_global_revision_id(self):
344
462
        """If it is set, then we re-use the global revision id"""
345
463
        repo = self.make_repository('.',
346
 
                format=controldir.format_registry.get('knit')())
 
464
                format=bzrdir.format_registry.get('knit')())
347
465
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
348
466
                   '</inventory>\n')
349
467
        # Arguably, the deserialise_inventory should detect a mismatch, and
356
474
 
357
475
    def test_supports_external_lookups(self):
358
476
        repo = self.make_repository('.',
359
 
                format=controldir.format_registry.get('knit')())
 
477
                format=bzrdir.format_registry.get('knit')())
360
478
        self.assertFalse(repo._format.supports_external_lookups)
361
479
 
362
480
 
403
521
        # classes do not barf inappropriately when a surprising repository type
404
522
        # is handed to them.
405
523
        dummy_a = DummyRepository()
406
 
        dummy_a._format = RepositoryFormat()
407
 
        dummy_a._format.supports_full_versioned_files = True
408
524
        dummy_b = DummyRepository()
409
 
        dummy_b._format = RepositoryFormat()
410
 
        dummy_b._format.supports_full_versioned_files = True
411
525
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
412
526
 
413
527
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
417
531
        no actual sane default in the presence of incompatible data models.
418
532
        """
419
533
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
420
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
534
        self.assertEqual(repository.InterSameDataRepository,
421
535
                         inter_repo.__class__)
422
536
        self.assertEqual(repo_a, inter_repo.source)
423
537
        self.assertEqual(repo_b, inter_repo.target)
437
551
        dummy_a._serializer = repo._serializer
438
552
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
439
553
        dummy_a._format.rich_root_data = repo._format.rich_root_data
440
 
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
441
554
        dummy_b._serializer = repo._serializer
442
555
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
443
556
        dummy_b._format.rich_root_data = repo._format.rich_root_data
444
 
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
445
557
        repository.InterRepository.register_optimiser(InterDummy)
446
558
        try:
447
559
            # we should get the default for something InterDummy returns False
460
572
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
461
573
 
462
574
 
463
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
464
 
 
465
 
    @classmethod
466
 
    def get_format_string(cls):
467
 
        return "Test Format 1"
468
 
 
469
 
 
470
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
471
 
 
472
 
    @classmethod
473
 
    def get_format_string(cls):
474
 
        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__)
475
604
 
476
605
 
477
606
class TestRepositoryConverter(TestCaseWithTransport):
478
607
 
479
608
    def test_convert_empty(self):
480
 
        source_format = TestRepositoryFormat1()
481
 
        target_format = TestRepositoryFormat2()
482
 
        repository.format_registry.register(source_format)
483
 
        self.addCleanup(repository.format_registry.remove,
484
 
            source_format)
485
 
        repository.format_registry.register(target_format)
486
 
        self.addCleanup(repository.format_registry.remove,
487
 
            target_format)
488
 
        t = self.get_transport()
 
609
        t = get_transport(self.get_url('.'))
489
610
        t.mkdir('repository')
490
611
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
491
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
612
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
613
        target_format = knitrepo.RepositoryFormatKnit1()
492
614
        converter = repository.CopyConverter(target_format)
493
615
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
494
616
        try:
499
621
        self.assertTrue(isinstance(target_format, repo._format.__class__))
500
622
 
501
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
 
502
631
class TestRepositoryFormatKnit3(TestCaseWithTransport):
503
632
 
504
633
    def test_attribute__fetch_order(self):
525
654
        revision_tree.lock_read()
526
655
        try:
527
656
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
528
 
                revision_tree.get_root_id())
 
657
                revision_tree.inventory.root.file_id)
529
658
        finally:
530
659
            revision_tree.unlock()
531
660
        format = bzrdir.BzrDirMetaFormat1()
535
664
        revision_tree = tree.branch.repository.revision_tree('dull')
536
665
        revision_tree.lock_read()
537
666
        try:
538
 
            revision_tree.get_file_lines(revision_tree.get_root_id())
 
667
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
539
668
        finally:
540
669
            revision_tree.unlock()
541
670
        tree.commit("Another dull commit", rev_id='dull2')
542
671
        revision_tree = tree.branch.repository.revision_tree('dull2')
543
672
        revision_tree.lock_read()
544
673
        self.addCleanup(revision_tree.unlock)
545
 
        self.assertEqual('dull',
546
 
                revision_tree.get_file_revision(revision_tree.get_root_id()))
 
674
        self.assertEqual('dull', revision_tree.inventory.root.revision)
547
675
 
548
676
    def test_supports_external_lookups(self):
549
677
        format = bzrdir.BzrDirMetaFormat1()
700
828
        target = self.make_repository('target', format='rich-root-pack')
701
829
        stream = source._get_source(target._format)
702
830
        # We don't want the child GroupCHKStreamSource
703
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
831
        self.assertIs(type(stream), repository.StreamSource)
704
832
 
705
833
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
706
834
        source_builder = self.make_branch_builder('source',
733
861
 
734
862
        # On a regular pass, getting the inventories and chk pages for rev-2
735
863
        # would only get the newly created chk pages
736
 
        search = vf_search.SearchResult(set(['rev-2']), set(['rev-1']), 1,
 
864
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
737
865
                                    set(['rev-2']))
738
866
        simple_chk_records = []
739
867
        for vf_name, substream in source.get_stream(search):
782
910
        source = self.make_repository('source', format='pack-0.92')
783
911
        target = self.make_repository('target', format='pack-0.92')
784
912
        stream_source = source._get_source(target._format)
785
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
913
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
786
914
 
787
915
    def test_source_to_exact_pack_rich_root_pack(self):
788
916
        source = self.make_repository('source', format='rich-root-pack')
789
917
        target = self.make_repository('target', format='rich-root-pack')
790
918
        stream_source = source._get_source(target._format)
791
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
919
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
792
920
 
793
921
    def test_source_to_exact_pack_19(self):
794
922
        source = self.make_repository('source', format='1.9')
795
923
        target = self.make_repository('target', format='1.9')
796
924
        stream_source = source._get_source(target._format)
797
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
925
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
798
926
 
799
927
    def test_source_to_exact_pack_19_rich_root(self):
800
928
        source = self.make_repository('source', format='1.9-rich-root')
801
929
        target = self.make_repository('target', format='1.9-rich-root')
802
930
        stream_source = source._get_source(target._format)
803
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
931
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
804
932
 
805
933
    def test_source_to_remote_exact_pack_19(self):
806
934
        trans = self.make_smart_server('target')
809
937
        target = self.make_repository('target', format='1.9')
810
938
        target = repository.Repository.open(trans.base)
811
939
        stream_source = source._get_source(target._format)
812
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
940
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
813
941
 
814
942
    def test_stream_source_to_non_exact(self):
815
943
        source = self.make_repository('source', format='pack-0.92')
816
944
        target = self.make_repository('target', format='1.9')
817
945
        stream = source._get_source(target._format)
818
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
946
        self.assertIs(type(stream), repository.StreamSource)
819
947
 
820
948
    def test_stream_source_to_non_exact_rich_root(self):
821
949
        source = self.make_repository('source', format='1.9')
822
950
        target = self.make_repository('target', format='1.9-rich-root')
823
951
        stream = source._get_source(target._format)
824
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
952
        self.assertIs(type(stream), repository.StreamSource)
825
953
 
826
954
    def test_source_to_remote_non_exact_pack_19(self):
827
955
        trans = self.make_smart_server('target')
830
958
        target = self.make_repository('target', format='1.6')
831
959
        target = repository.Repository.open(trans.base)
832
960
        stream_source = source._get_source(target._format)
833
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
961
        self.assertIs(type(stream_source), repository.StreamSource)
834
962
 
835
963
    def test_stream_source_to_knit(self):
836
964
        source = self.make_repository('source', format='pack-0.92')
837
965
        target = self.make_repository('target', format='dirstate')
838
966
        stream = source._get_source(target._format)
839
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
967
        self.assertIs(type(stream), repository.StreamSource)
840
968
 
841
969
 
842
970
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
844
972
 
845
973
    def setUp(self):
846
974
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
847
 
        self.builder = self.make_branch_builder('source')
 
975
        self.builder = self.make_branch_builder('source',
 
976
            format='development6-rich-root')
848
977
        self.builder.start_series()
849
978
        self.builder.build_snapshot('initial', None,
850
979
            [('add', ('', 'tree-root', 'directory', None))])
920
1049
            revision = _mod_revision.Revision('rev1a',
921
1050
                committer='jrandom@example.com', timestamp=0,
922
1051
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
923
 
            repo.add_revision('rev1a', revision, inv)
 
1052
            repo.add_revision('rev1a',revision, inv)
924
1053
 
925
1054
            # make rev1b, which has no Revision, but has an Inventory, and
926
1055
            # file1
961
1090
        revision = _mod_revision.Revision(revision_id,
962
1091
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
963
1092
            timezone=0, message='foo', parent_ids=parent_ids)
964
 
        repo.add_revision(revision_id, revision, inv)
 
1093
        repo.add_revision(revision_id,revision, inv)
965
1094
 
966
1095
    def add_file(self, repo, inv, filename, revision, parents):
967
1096
        file_id = filename + '-id'
995
1124
class TestRepositoryPackCollection(TestCaseWithTransport):
996
1125
 
997
1126
    def get_format(self):
998
 
        return controldir.format_registry.make_bzrdir('pack-0.92')
 
1127
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
999
1128
 
1000
1129
    def get_packs(self):
1001
1130
        format = self.get_format()
1097
1226
            sorted(set([osutils.splitext(n)[0] for n in
1098
1227
                        packs._index_transport.list_dir('.')])))
1099
1228
 
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
 
 
1117
1229
    def test_pack_distribution_zero(self):
1118
1230
        packs = self.get_packs()
1119
1231
        self.assertEqual([0], packs.pack_distribution(0))
1389
1501
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1390
1502
        self.assertEqual([pack.name], sorted(obsolete_names))
1391
1503
 
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()
1398
1504
 
1399
1505
 
1400
1506
class TestPack(TestCaseWithTransport):
1501
1607
        # Because of how they were built, they correspond to
1502
1608
        # ['D', 'C', 'B', 'A']
1503
1609
        packs = b.repository._pack_collection.packs
1504
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1610
        packer = pack_repo.Packer(b.repository._pack_collection,
1505
1611
                                  packs, 'testing',
1506
1612
                                  revision_ids=['B', 'C'])
1507
1613
        # Now, when we are copying the B & C revisions, their pack files should
1521
1627
        return repo._pack_collection
1522
1628
 
1523
1629
    def test_open_pack_will_optimise(self):
1524
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1630
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1525
1631
                                            [], '.test')
1526
1632
        new_pack = packer.open_pack()
1527
1633
        self.addCleanup(new_pack.abort) # ensure cleanup
1532
1638
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1533
1639
 
1534
1640
 
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
1641
class TestCrossFormatPacks(TestCaseWithTransport):
1636
1642
 
1637
1643
    def log_pack(self, hint=None):
1652
1658
        self.addCleanup(target.unlock)
1653
1659
        source = source_tree.branch.repository._get_source(target._format)
1654
1660
        self.orig_pack = target.pack
1655
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1661
        target.pack = self.log_pack
1656
1662
        search = target.search_missing_revision_ids(
1657
 
            source_tree.branch.repository, revision_ids=[tip])
 
1663
            source_tree.branch.repository, tip)
1658
1664
        stream = source.get_stream(search)
1659
1665
        from_format = source_tree.branch.repository._format
1660
1666
        sink = target._get_sink()
1676
1682
        self.addCleanup(target.unlock)
1677
1683
        source = source_tree.branch.repository
1678
1684
        self.orig_pack = target.pack
1679
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1685
        target.pack = self.log_pack
1680
1686
        target.fetch(source)
1681
1687
        if expect_pack_called:
1682
1688
            self.assertLength(1, self.calls)
1710
1716
    def test_IDS_format_same_no(self):
1711
1717
        # When the formats are the same, pack is not called.
1712
1718
        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)