~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-20 14:15:25 UTC
  • mto: (6471.1.4 iter-child-entries)
  • mto: This revision was merged to the branch mainline in revision 6472.
  • Revision ID: jelmer@samba.org-20120220141525-9azkfei62st8yc7w
Use inventories directly in fewer places.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
26
 
from StringIO import StringIO
27
 
import sys
28
26
 
29
27
import bzrlib
30
 
from bzrlib.errors import (NotBranchError,
31
 
                           NoSuchFile,
32
 
                           UnknownFormatError,
33
 
                           UnsupportedFormatError,
34
 
                           )
 
28
from bzrlib.errors import (
 
29
    UnknownFormatError,
 
30
    UnsupportedFormatError,
 
31
    )
35
32
from bzrlib import (
36
 
    graph,
 
33
    btree_index,
 
34
    symbol_versioning,
37
35
    tests,
 
36
    transport,
 
37
    vf_search,
38
38
    )
39
 
from bzrlib.branchbuilder import BranchBuilder
40
39
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
41
 
from bzrlib.index import GraphIndex, InMemoryGraphIndex
 
40
from bzrlib.index import GraphIndex
42
41
from bzrlib.repository import RepositoryFormat
43
 
from bzrlib.smart import server
44
42
from bzrlib.tests import (
45
43
    TestCase,
46
44
    TestCaseWithTransport,
47
 
    TestSkipped,
48
 
    test_knit,
49
 
    )
50
 
from bzrlib.transport import (
51
 
    fakenfs,
52
 
    get_transport,
53
45
    )
54
46
from bzrlib import (
55
 
    bencode,
56
47
    bzrdir,
57
48
    errors,
58
49
    inventory,
59
50
    osutils,
60
 
    progress,
61
51
    repository,
62
52
    revision as _mod_revision,
63
 
    symbol_versioning,
64
53
    upgrade,
65
54
    versionedfile,
 
55
    vf_repository,
66
56
    workingtree,
67
57
    )
68
58
from bzrlib.repofmt import (
69
59
    groupcompress_repo,
70
60
    knitrepo,
 
61
    knitpack_repo,
71
62
    pack_repo,
72
 
    weaverepo,
73
63
    )
74
64
 
75
65
 
78
68
    def test_get_set_default_format(self):
79
69
        old_default = bzrdir.format_registry.get('default')
80
70
        private_default = old_default().repository_format.__class__
81
 
        old_format = repository.RepositoryFormat.get_default_format()
 
71
        old_format = repository.format_registry.get_default()
82
72
        self.assertTrue(isinstance(old_format, private_default))
83
73
        def make_sample_bzrdir():
84
74
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
98
88
            bzrdir.format_registry.remove('default')
99
89
            bzrdir.format_registry.remove('sample')
100
90
            bzrdir.format_registry.register('default', old_default, '')
101
 
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
 
91
        self.assertIsInstance(repository.format_registry.get_default(),
102
92
                              old_format.__class__)
103
93
 
104
94
 
105
 
class SampleRepositoryFormat(repository.RepositoryFormat):
 
95
class SampleRepositoryFormat(repository.RepositoryFormatMetaDir):
106
96
    """A sample format
107
97
 
108
98
    this format is initializable, unsupported to aid in testing the
109
99
    open and open(unsupported=True) routines.
110
100
    """
111
101
 
112
 
    def get_format_string(self):
 
102
    @classmethod
 
103
    def get_format_string(cls):
113
104
        """See RepositoryFormat.get_format_string()."""
114
105
        return "Sample .bzr repository format."
115
106
 
126
117
        return "opened repository."
127
118
 
128
119
 
 
120
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
 
121
    """A sample format that can not be used in a metadir
 
122
 
 
123
    """
 
124
 
 
125
    def get_format_string(self):
 
126
        raise NotImplementedError
 
127
 
 
128
 
129
129
class TestRepositoryFormat(TestCaseWithTransport):
130
130
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
131
131
 
137
137
        def check_format(format, url):
138
138
            dir = format._matchingbzrdir.initialize(url)
139
139
            format.initialize(dir)
140
 
            t = get_transport(url)
141
 
            found_format = repository.RepositoryFormat.find_format(dir)
142
 
            self.failUnless(isinstance(found_format, format.__class__))
143
 
        check_format(weaverepo.RepositoryFormat7(), "bar")
 
140
            t = transport.get_transport_from_path(url)
 
141
            found_format = repository.RepositoryFormatMetaDir.find_format(dir)
 
142
            self.assertIsInstance(found_format, format.__class__)
 
143
        check_format(repository.format_registry.get_default(), "bar")
144
144
 
145
145
    def test_find_format_no_repository(self):
146
146
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
147
147
        self.assertRaises(errors.NoRepositoryPresent,
148
 
                          repository.RepositoryFormat.find_format,
 
148
                          repository.RepositoryFormatMetaDir.find_format,
149
149
                          dir)
150
150
 
 
151
    def test_from_string(self):
 
152
        self.assertIsInstance(
 
153
            SampleRepositoryFormat.from_string(
 
154
                "Sample .bzr repository format."),
 
155
            SampleRepositoryFormat)
 
156
        self.assertRaises(AssertionError,
 
157
            SampleRepositoryFormat.from_string,
 
158
                "Different .bzr repository format.")
 
159
 
151
160
    def test_find_format_unknown_format(self):
152
161
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
153
162
        SampleRepositoryFormat().initialize(dir)
154
163
        self.assertRaises(UnknownFormatError,
155
 
                          repository.RepositoryFormat.find_format,
 
164
                          repository.RepositoryFormatMetaDir.find_format,
156
165
                          dir)
157
166
 
 
167
    def test_find_format_with_features(self):
 
168
        tree = self.make_branch_and_tree('.', format='2a')
 
169
        tree.branch.repository.update_feature_flags({"name": "necessity"})
 
170
        found_format = repository.RepositoryFormatMetaDir.find_format(tree.bzrdir)
 
171
        self.assertIsInstance(found_format, repository.RepositoryFormatMetaDir)
 
172
        self.assertEquals(found_format.features.get("name"), "necessity")
 
173
        self.assertRaises(errors.MissingFeature, found_format.check_support_status,
 
174
            True)
 
175
        self.addCleanup(repository.RepositoryFormatMetaDir.unregister_feature,
 
176
            "name")
 
177
        repository.RepositoryFormatMetaDir.register_feature("name")
 
178
        found_format.check_support_status(True)
 
179
 
158
180
    def test_register_unregister_format(self):
 
181
        # Test deprecated format registration functions
159
182
        format = SampleRepositoryFormat()
160
183
        # make a control dir
161
184
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
185
        # make a repo
163
186
        format.initialize(dir)
164
187
        # register a format for it.
165
 
        repository.RepositoryFormat.register_format(format)
 
188
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
189
            repository.RepositoryFormat.register_format, format)
166
190
        # which repository.Open will refuse (not supported)
167
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
 
191
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
 
192
            self.get_url())
168
193
        # but open(unsupported) will work
169
194
        self.assertEqual(format.open(dir), "opened repository.")
170
195
        # unregister the format
171
 
        repository.RepositoryFormat.unregister_format(format)
172
 
 
173
 
 
174
 
class TestFormat6(TestCaseWithTransport):
175
 
 
176
 
    def test_attribute__fetch_order(self):
177
 
        """Weaves need topological data insertion."""
178
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
179
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
180
 
        self.assertEqual('topological', repo._format._fetch_order)
181
 
 
182
 
    def test_attribute__fetch_uses_deltas(self):
183
 
        """Weaves do not reuse deltas."""
184
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
185
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
186
 
        self.assertEqual(False, repo._format._fetch_uses_deltas)
187
 
 
188
 
    def test_attribute__fetch_reconcile(self):
189
 
        """Weave repositories need a reconcile after fetch."""
190
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
191
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
192
 
        self.assertEqual(True, repo._format._fetch_reconcile)
193
 
 
194
 
    def test_no_ancestry_weave(self):
195
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
196
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
197
 
        # We no longer need to create the ancestry.weave file
198
 
        # since it is *never* used.
199
 
        self.assertRaises(NoSuchFile,
200
 
                          control.transport.get,
201
 
                          'ancestry.weave')
202
 
 
203
 
    def test_supports_external_lookups(self):
204
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
205
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
206
 
        self.assertFalse(repo._format.supports_external_lookups)
207
 
 
208
 
 
209
 
class TestFormat7(TestCaseWithTransport):
210
 
 
211
 
    def test_attribute__fetch_order(self):
212
 
        """Weaves need topological data insertion."""
213
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
214
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
215
 
        self.assertEqual('topological', repo._format._fetch_order)
216
 
 
217
 
    def test_attribute__fetch_uses_deltas(self):
218
 
        """Weaves do not reuse deltas."""
219
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
220
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
221
 
        self.assertEqual(False, repo._format._fetch_uses_deltas)
222
 
 
223
 
    def test_attribute__fetch_reconcile(self):
224
 
        """Weave repositories need a reconcile after fetch."""
225
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
226
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
227
 
        self.assertEqual(True, repo._format._fetch_reconcile)
228
 
 
229
 
    def test_disk_layout(self):
230
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
231
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
232
 
        # in case of side effects of locking.
233
 
        repo.lock_write()
234
 
        repo.unlock()
235
 
        # we want:
236
 
        # format 'Bazaar-NG Repository format 7'
237
 
        # lock ''
238
 
        # inventory.weave == empty_weave
239
 
        # empty revision-store directory
240
 
        # empty weaves directory
241
 
        t = control.get_repository_transport(None)
242
 
        self.assertEqualDiff('Bazaar-NG Repository format 7',
243
 
                             t.get('format').read())
244
 
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
245
 
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
246
 
        self.assertEqualDiff('# bzr weave file v5\n'
247
 
                             'w\n'
248
 
                             'W\n',
249
 
                             t.get('inventory.weave').read())
250
 
        # Creating a file with id Foo:Bar results in a non-escaped file name on
251
 
        # disk.
252
 
        control.create_branch()
253
 
        tree = control.create_workingtree()
254
 
        tree.add(['foo'], ['Foo:Bar'], ['file'])
255
 
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
256
 
        try:
257
 
            tree.commit('first post', rev_id='first')
258
 
        except errors.IllegalPath:
259
 
            if sys.platform != 'win32':
260
 
                raise
261
 
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
262
 
                              ' in repo format 7')
263
 
            return
264
 
        self.assertEqualDiff(
265
 
            '# bzr weave file v5\n'
266
 
            'i\n'
267
 
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
268
 
            'n first\n'
269
 
            '\n'
270
 
            'w\n'
271
 
            '{ 0\n'
272
 
            '. content\n'
273
 
            '}\n'
274
 
            'W\n',
275
 
            t.get('weaves/74/Foo%3ABar.weave').read())
276
 
 
277
 
    def test_shared_disk_layout(self):
278
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
279
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
280
 
        # we want:
281
 
        # format 'Bazaar-NG Repository format 7'
282
 
        # inventory.weave == empty_weave
283
 
        # empty revision-store directory
284
 
        # empty weaves directory
285
 
        # a 'shared-storage' marker file.
286
 
        # lock is not present when unlocked
287
 
        t = control.get_repository_transport(None)
288
 
        self.assertEqualDiff('Bazaar-NG Repository format 7',
289
 
                             t.get('format').read())
290
 
        self.assertEqualDiff('', t.get('shared-storage').read())
291
 
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
292
 
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
293
 
        self.assertEqualDiff('# bzr weave file v5\n'
294
 
                             'w\n'
295
 
                             'W\n',
296
 
                             t.get('inventory.weave').read())
297
 
        self.assertFalse(t.has('branch-lock'))
298
 
 
299
 
    def test_creates_lockdir(self):
300
 
        """Make sure it appears to be controlled by a LockDir existence"""
301
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
302
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
303
 
        t = control.get_repository_transport(None)
304
 
        # TODO: Should check there is a 'lock' toplevel directory,
305
 
        # regardless of contents
306
 
        self.assertFalse(t.has('lock/held/info'))
307
 
        repo.lock_write()
308
 
        try:
309
 
            self.assertTrue(t.has('lock/held/info'))
310
 
        finally:
311
 
            # unlock so we don't get a warning about failing to do so
312
 
            repo.unlock()
313
 
 
314
 
    def test_uses_lockdir(self):
315
 
        """repo format 7 actually locks on lockdir"""
316
 
        base_url = self.get_url()
317
 
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
318
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
319
 
        t = control.get_repository_transport(None)
320
 
        repo.lock_write()
321
 
        repo.unlock()
322
 
        del repo
323
 
        # make sure the same lock is created by opening it
324
 
        repo = repository.Repository.open(base_url)
325
 
        repo.lock_write()
326
 
        self.assertTrue(t.has('lock/held/info'))
327
 
        repo.unlock()
328
 
        self.assertFalse(t.has('lock/held/info'))
329
 
 
330
 
    def test_shared_no_tree_disk_layout(self):
331
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
332
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
333
 
        repo.set_make_working_trees(False)
334
 
        # we want:
335
 
        # format 'Bazaar-NG Repository format 7'
336
 
        # lock ''
337
 
        # inventory.weave == empty_weave
338
 
        # empty revision-store directory
339
 
        # empty weaves directory
340
 
        # a 'shared-storage' marker file.
341
 
        t = control.get_repository_transport(None)
342
 
        self.assertEqualDiff('Bazaar-NG Repository format 7',
343
 
                             t.get('format').read())
344
 
        ## self.assertEqualDiff('', t.get('lock').read())
345
 
        self.assertEqualDiff('', t.get('shared-storage').read())
346
 
        self.assertEqualDiff('', t.get('no-working-trees').read())
347
 
        repo.set_make_working_trees(True)
348
 
        self.assertFalse(t.has('no-working-trees'))
349
 
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
350
 
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
351
 
        self.assertEqualDiff('# bzr weave file v5\n'
352
 
                             'w\n'
353
 
                             'W\n',
354
 
                             t.get('inventory.weave').read())
355
 
 
356
 
    def test_supports_external_lookups(self):
357
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
358
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
359
 
        self.assertFalse(repo._format.supports_external_lookups)
 
196
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
 
197
            repository.RepositoryFormat.unregister_format, format)
 
198
 
 
199
 
 
200
class TestRepositoryFormatRegistry(TestCase):
 
201
 
 
202
    def setUp(self):
 
203
        super(TestRepositoryFormatRegistry, self).setUp()
 
204
        self.registry = repository.RepositoryFormatRegistry()
 
205
 
 
206
    def test_register_unregister_format(self):
 
207
        format = SampleRepositoryFormat()
 
208
        self.registry.register(format)
 
209
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
 
210
        self.registry.remove(format)
 
211
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
 
212
 
 
213
    def test_get_all(self):
 
214
        format = SampleRepositoryFormat()
 
215
        self.assertEquals([], self.registry._get_all())
 
216
        self.registry.register(format)
 
217
        self.assertEquals([format], self.registry._get_all())
 
218
 
 
219
    def test_register_extra(self):
 
220
        format = SampleExtraRepositoryFormat()
 
221
        self.assertEquals([], self.registry._get_all())
 
222
        self.registry.register_extra(format)
 
223
        self.assertEquals([format], self.registry._get_all())
 
224
 
 
225
    def test_register_extra_lazy(self):
 
226
        self.assertEquals([], self.registry._get_all())
 
227
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
 
228
            "SampleExtraRepositoryFormat")
 
229
        formats = self.registry._get_all()
 
230
        self.assertEquals(1, len(formats))
 
231
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
360
232
 
361
233
 
362
234
class TestFormatKnit1(TestCaseWithTransport):
530
402
        # classes do not barf inappropriately when a surprising repository type
531
403
        # is handed to them.
532
404
        dummy_a = DummyRepository()
 
405
        dummy_a._format = RepositoryFormat()
 
406
        dummy_a._format.supports_full_versioned_files = True
533
407
        dummy_b = DummyRepository()
 
408
        dummy_b._format = RepositoryFormat()
 
409
        dummy_b._format.supports_full_versioned_files = True
534
410
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
535
411
 
536
412
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
540
416
        no actual sane default in the presence of incompatible data models.
541
417
        """
542
418
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
543
 
        self.assertEqual(repository.InterSameDataRepository,
 
419
        self.assertEqual(vf_repository.InterSameDataRepository,
544
420
                         inter_repo.__class__)
545
421
        self.assertEqual(repo_a, inter_repo.source)
546
422
        self.assertEqual(repo_b, inter_repo.target)
560
436
        dummy_a._serializer = repo._serializer
561
437
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
562
438
        dummy_a._format.rich_root_data = repo._format.rich_root_data
 
439
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
563
440
        dummy_b._serializer = repo._serializer
564
441
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
565
442
        dummy_b._format.rich_root_data = repo._format.rich_root_data
 
443
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
566
444
        repository.InterRepository.register_optimiser(InterDummy)
567
445
        try:
568
446
            # we should get the default for something InterDummy returns False
581
459
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
582
460
 
583
461
 
584
 
class TestInterWeaveRepo(TestCaseWithTransport):
585
 
 
586
 
    def test_is_compatible_and_registered(self):
587
 
        # InterWeaveRepo is compatible when either side
588
 
        # is a format 5/6/7 branch
589
 
        from bzrlib.repofmt import knitrepo, weaverepo
590
 
        formats = [weaverepo.RepositoryFormat5(),
591
 
                   weaverepo.RepositoryFormat6(),
592
 
                   weaverepo.RepositoryFormat7()]
593
 
        incompatible_formats = [weaverepo.RepositoryFormat4(),
594
 
                                knitrepo.RepositoryFormatKnit1(),
595
 
                                ]
596
 
        repo_a = self.make_repository('a')
597
 
        repo_b = self.make_repository('b')
598
 
        is_compatible = repository.InterWeaveRepo.is_compatible
599
 
        for source in incompatible_formats:
600
 
            # force incompatible left then right
601
 
            repo_a._format = source
602
 
            repo_b._format = formats[0]
603
 
            self.assertFalse(is_compatible(repo_a, repo_b))
604
 
            self.assertFalse(is_compatible(repo_b, repo_a))
605
 
        for source in formats:
606
 
            repo_a._format = source
607
 
            for target in formats:
608
 
                repo_b._format = target
609
 
                self.assertTrue(is_compatible(repo_a, repo_b))
610
 
        self.assertEqual(repository.InterWeaveRepo,
611
 
                         repository.InterRepository.get(repo_a,
612
 
                                                        repo_b).__class__)
 
462
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
 
463
 
 
464
    @classmethod
 
465
    def get_format_string(cls):
 
466
        return "Test Format 1"
 
467
 
 
468
 
 
469
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
 
470
 
 
471
    @classmethod
 
472
    def get_format_string(cls):
 
473
        return "Test Format 2"
613
474
 
614
475
 
615
476
class TestRepositoryConverter(TestCaseWithTransport):
616
477
 
617
478
    def test_convert_empty(self):
618
 
        t = get_transport(self.get_url('.'))
 
479
        source_format = TestRepositoryFormat1()
 
480
        target_format = TestRepositoryFormat2()
 
481
        repository.format_registry.register(source_format)
 
482
        self.addCleanup(repository.format_registry.remove,
 
483
            source_format)
 
484
        repository.format_registry.register(target_format)
 
485
        self.addCleanup(repository.format_registry.remove,
 
486
            target_format)
 
487
        t = self.get_transport()
619
488
        t.mkdir('repository')
620
489
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
621
 
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
622
 
        target_format = knitrepo.RepositoryFormatKnit1()
 
490
        repo = TestRepositoryFormat1().initialize(repo_dir)
623
491
        converter = repository.CopyConverter(target_format)
624
492
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
625
493
        try:
630
498
        self.assertTrue(isinstance(target_format, repo._format.__class__))
631
499
 
632
500
 
633
 
class TestMisc(TestCase):
634
 
 
635
 
    def test_unescape_xml(self):
636
 
        """We get some kind of error when malformed entities are passed"""
637
 
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
638
 
 
639
 
 
640
501
class TestRepositoryFormatKnit3(TestCaseWithTransport):
641
502
 
642
503
    def test_attribute__fetch_order(self):
663
524
        revision_tree.lock_read()
664
525
        try:
665
526
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
666
 
                revision_tree.inventory.root.file_id)
 
527
                revision_tree.get_root_id())
667
528
        finally:
668
529
            revision_tree.unlock()
669
530
        format = bzrdir.BzrDirMetaFormat1()
673
534
        revision_tree = tree.branch.repository.revision_tree('dull')
674
535
        revision_tree.lock_read()
675
536
        try:
676
 
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
537
            revision_tree.get_file_lines(revision_tree.get_root_id())
677
538
        finally:
678
539
            revision_tree.unlock()
679
540
        tree.commit("Another dull commit", rev_id='dull2')
680
541
        revision_tree = tree.branch.repository.revision_tree('dull2')
681
542
        revision_tree.lock_read()
682
543
        self.addCleanup(revision_tree.unlock)
683
 
        self.assertEqual('dull', revision_tree.inventory.root.revision)
 
544
        self.assertEqual('dull',
 
545
                revision_tree.get_file_revision(revision_tree.get_root_id()))
684
546
 
685
547
    def test_supports_external_lookups(self):
686
548
        format = bzrdir.BzrDirMetaFormat1()
691
553
 
692
554
class Test2a(tests.TestCaseWithMemoryTransport):
693
555
 
 
556
    def test_chk_bytes_uses_custom_btree_parser(self):
 
557
        mt = self.make_branch_and_memory_tree('test', format='2a')
 
558
        mt.lock_write()
 
559
        self.addCleanup(mt.unlock)
 
560
        mt.add([''], ['root-id'])
 
561
        mt.commit('first')
 
562
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
 
563
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
564
        # It should also work if we re-open the repo
 
565
        repo = mt.branch.repository.bzrdir.open_repository()
 
566
        repo.lock_read()
 
567
        self.addCleanup(repo.unlock)
 
568
        index = repo.chk_bytes._index._graph_index._indices[0]
 
569
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
570
 
694
571
    def test_fetch_combines_groups(self):
695
572
        builder = self.make_branch_builder('source', format='2a')
696
573
        builder.start_series()
822
699
        target = self.make_repository('target', format='rich-root-pack')
823
700
        stream = source._get_source(target._format)
824
701
        # We don't want the child GroupCHKStreamSource
825
 
        self.assertIs(type(stream), repository.StreamSource)
 
702
        self.assertIs(type(stream), vf_repository.StreamSource)
826
703
 
827
704
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
828
705
        source_builder = self.make_branch_builder('source',
855
732
 
856
733
        # On a regular pass, getting the inventories and chk pages for rev-2
857
734
        # would only get the newly created chk pages
858
 
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
 
735
        search = vf_search.SearchResult(set(['rev-2']), set(['rev-1']), 1,
859
736
                                    set(['rev-2']))
860
737
        simple_chk_records = []
861
738
        for vf_name, substream in source.get_stream(search):
904
781
        source = self.make_repository('source', format='pack-0.92')
905
782
        target = self.make_repository('target', format='pack-0.92')
906
783
        stream_source = source._get_source(target._format)
907
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
784
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
908
785
 
909
786
    def test_source_to_exact_pack_rich_root_pack(self):
910
787
        source = self.make_repository('source', format='rich-root-pack')
911
788
        target = self.make_repository('target', format='rich-root-pack')
912
789
        stream_source = source._get_source(target._format)
913
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
790
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
914
791
 
915
792
    def test_source_to_exact_pack_19(self):
916
793
        source = self.make_repository('source', format='1.9')
917
794
        target = self.make_repository('target', format='1.9')
918
795
        stream_source = source._get_source(target._format)
919
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
796
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
920
797
 
921
798
    def test_source_to_exact_pack_19_rich_root(self):
922
799
        source = self.make_repository('source', format='1.9-rich-root')
923
800
        target = self.make_repository('target', format='1.9-rich-root')
924
801
        stream_source = source._get_source(target._format)
925
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
802
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
926
803
 
927
804
    def test_source_to_remote_exact_pack_19(self):
928
805
        trans = self.make_smart_server('target')
931
808
        target = self.make_repository('target', format='1.9')
932
809
        target = repository.Repository.open(trans.base)
933
810
        stream_source = source._get_source(target._format)
934
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
811
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
935
812
 
936
813
    def test_stream_source_to_non_exact(self):
937
814
        source = self.make_repository('source', format='pack-0.92')
938
815
        target = self.make_repository('target', format='1.9')
939
816
        stream = source._get_source(target._format)
940
 
        self.assertIs(type(stream), repository.StreamSource)
 
817
        self.assertIs(type(stream), vf_repository.StreamSource)
941
818
 
942
819
    def test_stream_source_to_non_exact_rich_root(self):
943
820
        source = self.make_repository('source', format='1.9')
944
821
        target = self.make_repository('target', format='1.9-rich-root')
945
822
        stream = source._get_source(target._format)
946
 
        self.assertIs(type(stream), repository.StreamSource)
 
823
        self.assertIs(type(stream), vf_repository.StreamSource)
947
824
 
948
825
    def test_source_to_remote_non_exact_pack_19(self):
949
826
        trans = self.make_smart_server('target')
952
829
        target = self.make_repository('target', format='1.6')
953
830
        target = repository.Repository.open(trans.base)
954
831
        stream_source = source._get_source(target._format)
955
 
        self.assertIs(type(stream_source), repository.StreamSource)
 
832
        self.assertIs(type(stream_source), vf_repository.StreamSource)
956
833
 
957
834
    def test_stream_source_to_knit(self):
958
835
        source = self.make_repository('source', format='pack-0.92')
959
836
        target = self.make_repository('target', format='dirstate')
960
837
        stream = source._get_source(target._format)
961
 
        self.assertIs(type(stream), repository.StreamSource)
 
838
        self.assertIs(type(stream), vf_repository.StreamSource)
962
839
 
963
840
 
964
841
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
966
843
 
967
844
    def setUp(self):
968
845
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
969
 
        self.builder = self.make_branch_builder('source',
970
 
            format='development6-rich-root')
 
846
        self.builder = self.make_branch_builder('source')
971
847
        self.builder.start_series()
972
848
        self.builder.build_snapshot('initial', None,
973
849
            [('add', ('', 'tree-root', 'directory', None))])
1043
919
            revision = _mod_revision.Revision('rev1a',
1044
920
                committer='jrandom@example.com', timestamp=0,
1045
921
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
1046
 
            repo.add_revision('rev1a',revision, inv)
 
922
            repo.add_revision('rev1a', revision, inv)
1047
923
 
1048
924
            # make rev1b, which has no Revision, but has an Inventory, and
1049
925
            # file1
1084
960
        revision = _mod_revision.Revision(revision_id,
1085
961
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
1086
962
            timezone=0, message='foo', parent_ids=parent_ids)
1087
 
        repo.add_revision(revision_id,revision, inv)
 
963
        repo.add_revision(revision_id, revision, inv)
1088
964
 
1089
965
    def add_file(self, repo, inv, filename, revision, parents):
1090
966
        file_id = filename + '-id'
1220
1096
            sorted(set([osutils.splitext(n)[0] for n in
1221
1097
                        packs._index_transport.list_dir('.')])))
1222
1098
 
 
1099
    def test__obsolete_packs_missing_directory(self):
 
1100
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1101
        r.control_transport.rmdir('obsolete_packs')
 
1102
        names = packs.names()
 
1103
        pack = packs.get_pack_by_name(names[0])
 
1104
        # Schedule this one for removal
 
1105
        packs._remove_pack_from_memory(pack)
 
1106
        # Now trigger the obsoletion, and ensure that all the remaining files
 
1107
        # are still renamed
 
1108
        packs._obsolete_packs([pack])
 
1109
        self.assertEqual([n + '.pack' for n in names[1:]],
 
1110
                         sorted(packs._pack_transport.list_dir('.')))
 
1111
        # names[0] should not be present in the index anymore
 
1112
        self.assertEqual(names[1:],
 
1113
            sorted(set([osutils.splitext(n)[0] for n in
 
1114
                        packs._index_transport.list_dir('.')])))
 
1115
 
1223
1116
    def test_pack_distribution_zero(self):
1224
1117
        packs = self.get_packs()
1225
1118
        self.assertEqual([0], packs.pack_distribution(0))
1495
1388
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1496
1389
        self.assertEqual([pack.name], sorted(obsolete_names))
1497
1390
 
 
1391
    def test_pack_no_obsolete_packs_directory(self):
 
1392
        """Bug #314314, don't fail if obsolete_packs directory does
 
1393
        not exist."""
 
1394
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1395
        r.control_transport.rmdir('obsolete_packs')
 
1396
        packs._clear_obsolete_packs()
1498
1397
 
1499
1398
 
1500
1399
class TestPack(TestCaseWithTransport):
1601
1500
        # Because of how they were built, they correspond to
1602
1501
        # ['D', 'C', 'B', 'A']
1603
1502
        packs = b.repository._pack_collection.packs
1604
 
        packer = pack_repo.Packer(b.repository._pack_collection,
 
1503
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
1605
1504
                                  packs, 'testing',
1606
1505
                                  revision_ids=['B', 'C'])
1607
1506
        # Now, when we are copying the B & C revisions, their pack files should
1621
1520
        return repo._pack_collection
1622
1521
 
1623
1522
    def test_open_pack_will_optimise(self):
1624
 
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
 
1523
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
1625
1524
                                            [], '.test')
1626
1525
        new_pack = packer.open_pack()
1627
1526
        self.addCleanup(new_pack.abort) # ensure cleanup
1632
1531
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1633
1532
 
1634
1533
 
 
1534
class TestGCCHKPacker(TestCaseWithTransport):
 
1535
 
 
1536
    def make_abc_branch(self):
 
1537
        builder = self.make_branch_builder('source')
 
1538
        builder.start_series()
 
1539
        builder.build_snapshot('A', None, [
 
1540
            ('add', ('', 'root-id', 'directory', None)),
 
1541
            ('add', ('file', 'file-id', 'file', 'content\n')),
 
1542
            ])
 
1543
        builder.build_snapshot('B', ['A'], [
 
1544
            ('add', ('dir', 'dir-id', 'directory', None))])
 
1545
        builder.build_snapshot('C', ['B'], [
 
1546
            ('modify', ('file-id', 'new content\n'))])
 
1547
        builder.finish_series()
 
1548
        return builder.get_branch()
 
1549
 
 
1550
    def make_branch_with_disjoint_inventory_and_revision(self):
 
1551
        """a repo with separate packs for a revisions Revision and Inventory.
 
1552
 
 
1553
        There will be one pack file that holds the Revision content, and one
 
1554
        for the Inventory content.
 
1555
 
 
1556
        :return: (repository,
 
1557
                  pack_name_with_rev_A_Revision,
 
1558
                  pack_name_with_rev_A_Inventory,
 
1559
                  pack_name_with_rev_C_content)
 
1560
        """
 
1561
        b_source = self.make_abc_branch()
 
1562
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
 
1563
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
 
1564
        b_stacked.lock_write()
 
1565
        self.addCleanup(b_stacked.unlock)
 
1566
        b_stacked.fetch(b_source, 'B')
 
1567
        # Now re-open the stacked repo directly (no fallbacks) so that we can
 
1568
        # fill in the A rev.
 
1569
        repo_not_stacked = b_stacked.bzrdir.open_repository()
 
1570
        repo_not_stacked.lock_write()
 
1571
        self.addCleanup(repo_not_stacked.unlock)
 
1572
        # Now we should have a pack file with A's inventory, but not its
 
1573
        # Revision
 
1574
        self.assertEqual([('A',), ('B',)],
 
1575
                         sorted(repo_not_stacked.inventories.keys()))
 
1576
        self.assertEqual([('B',)],
 
1577
                         sorted(repo_not_stacked.revisions.keys()))
 
1578
        stacked_pack_names = repo_not_stacked._pack_collection.names()
 
1579
        # We have a couple names here, figure out which has A's inventory
 
1580
        for name in stacked_pack_names:
 
1581
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
 
1582
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
 
1583
            if ('A',) in keys:
 
1584
                inv_a_pack_name = name
 
1585
                break
 
1586
        else:
 
1587
            self.fail('Could not find pack containing A\'s inventory')
 
1588
        repo_not_stacked.fetch(b_source.repository, 'A')
 
1589
        self.assertEqual([('A',), ('B',)],
 
1590
                         sorted(repo_not_stacked.revisions.keys()))
 
1591
        new_pack_names = set(repo_not_stacked._pack_collection.names())
 
1592
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
 
1593
        self.assertEqual(1, len(rev_a_pack_names))
 
1594
        rev_a_pack_name = list(rev_a_pack_names)[0]
 
1595
        # Now fetch 'C', so we have a couple pack files to join
 
1596
        repo_not_stacked.fetch(b_source.repository, 'C')
 
1597
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
 
1598
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
 
1599
        self.assertEqual(1, len(rev_c_pack_names))
 
1600
        rev_c_pack_name = list(rev_c_pack_names)[0]
 
1601
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
 
1602
                rev_c_pack_name)
 
1603
 
 
1604
    def test_pack_with_distant_inventories(self):
 
1605
        # See https://bugs.launchpad.net/bzr/+bug/437003
 
1606
        # When repacking, it is possible to have an inventory in a different
 
1607
        # pack file than the associated revision. An autopack can then come
 
1608
        # along, and miss that inventory, and complain.
 
1609
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1610
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1611
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
 
1612
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
 
1613
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1614
                    [a_pack, c_pack], '.test-pack')
 
1615
        # This would raise ValueError in bug #437003, but should not raise an
 
1616
        # error once fixed.
 
1617
        packer.pack()
 
1618
 
 
1619
    def test_pack_with_missing_inventory(self):
 
1620
        # Similar to test_pack_with_missing_inventory, but this time, we force
 
1621
        # the A inventory to actually be gone from the repository.
 
1622
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1623
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1624
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
 
1625
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
 
1626
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1627
            repo._pack_collection.all_packs(), '.test-pack')
 
1628
        e = self.assertRaises(ValueError, packer.pack)
 
1629
        packer.new_pack.abort()
 
1630
        self.assertContainsRe(str(e),
 
1631
            r"We are missing inventories for revisions: .*'A'")
 
1632
 
 
1633
 
1635
1634
class TestCrossFormatPacks(TestCaseWithTransport):
1636
1635
 
1637
1636
    def log_pack(self, hint=None):
1652
1651
        self.addCleanup(target.unlock)
1653
1652
        source = source_tree.branch.repository._get_source(target._format)
1654
1653
        self.orig_pack = target.pack
1655
 
        target.pack = self.log_pack
 
1654
        self.overrideAttr(target, "pack", self.log_pack)
1656
1655
        search = target.search_missing_revision_ids(
1657
 
            source_tree.branch.repository, tip)
 
1656
            source_tree.branch.repository, revision_ids=[tip])
1658
1657
        stream = source.get_stream(search)
1659
1658
        from_format = source_tree.branch.repository._format
1660
1659
        sink = target._get_sink()
1676
1675
        self.addCleanup(target.unlock)
1677
1676
        source = source_tree.branch.repository
1678
1677
        self.orig_pack = target.pack
1679
 
        target.pack = self.log_pack
 
1678
        self.overrideAttr(target, "pack", self.log_pack)
1680
1679
        target.fetch(source)
1681
1680
        if expect_pack_called:
1682
1681
            self.assertLength(1, self.calls)
1710
1709
    def test_IDS_format_same_no(self):
1711
1710
        # When the formats are the same, pack is not called.
1712
1711
        self.run_fetch('2a', '2a', False)
 
1712
 
 
1713
 
 
1714
class Test_LazyListJoin(tests.TestCase):
 
1715
 
 
1716
    def test__repr__(self):
 
1717
        lazy = repository._LazyListJoin(['a'], ['b'])
 
1718
        self.assertEqual("bzrlib.repository._LazyListJoin((['a'], ['b']))",
 
1719
                         repr(lazy))
 
1720
 
 
1721
 
 
1722
class TestFeatures(tests.TestCaseWithTransport):
 
1723
 
 
1724
    def test_open_with_present_feature(self):
 
1725
        self.addCleanup(
 
1726
            repository.RepositoryFormatMetaDir.unregister_feature,
 
1727
            "makes-cheese-sandwich")
 
1728
        repository.RepositoryFormatMetaDir.register_feature(
 
1729
            "makes-cheese-sandwich")
 
1730
        repo = self.make_repository('.')
 
1731
        repo.lock_write()
 
1732
        repo._format.features["makes-cheese-sandwich"] = "required"
 
1733
        repo._format.check_support_status(False)
 
1734
        repo.unlock()
 
1735
 
 
1736
    def test_open_with_missing_required_feature(self):
 
1737
        repo = self.make_repository('.')
 
1738
        repo.lock_write()
 
1739
        repo._format.features["makes-cheese-sandwich"] = "required"
 
1740
        self.assertRaises(errors.MissingFeature,
 
1741
            repo._format.check_support_status, False)