/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Robert Collins
  • Date: 2010-05-11 08:36:16 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100511083616-b8fjb19zomwupid0
Make all lock methods return Result objects, rather than lock_read returning self, as per John's review.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012, 2016 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
 
26
import sys
26
27
 
27
 
import breezy
28
 
from breezy.errors import (
29
 
    UnknownFormatError,
30
 
    )
31
 
from breezy import (
 
28
import bzrlib
 
29
from bzrlib.errors import (NoSuchFile,
 
30
                           UnknownFormatError,
 
31
                           UnsupportedFormatError,
 
32
                           )
 
33
from bzrlib import (
 
34
    graph,
32
35
    tests,
33
 
    transport,
34
 
    )
35
 
from breezy.bzr import (
36
 
    bzrdir,
37
 
    btree_index,
38
 
    inventory,
39
 
    repository as bzrrepository,
40
 
    versionedfile,
41
 
    vf_repository,
42
 
    vf_search,
43
 
    )
44
 
from breezy.bzr.btree_index import BTreeBuilder, BTreeGraphIndex
45
 
from breezy.bzr.index import GraphIndex
46
 
from breezy.repository import RepositoryFormat
47
 
from breezy.tests import (
 
36
    )
 
37
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
 
38
from bzrlib.index import GraphIndex
 
39
from bzrlib.repository import RepositoryFormat
 
40
from bzrlib.tests import (
48
41
    TestCase,
49
42
    TestCaseWithTransport,
50
43
    )
51
 
from breezy import (
52
 
    controldir,
 
44
from bzrlib.transport import (
 
45
    get_transport,
 
46
    )
 
47
from bzrlib import (
 
48
    bzrdir,
53
49
    errors,
 
50
    inventory,
54
51
    osutils,
55
52
    repository,
56
53
    revision as _mod_revision,
57
54
    upgrade,
 
55
    versionedfile,
58
56
    workingtree,
59
57
    )
60
 
from breezy.bzr import (
 
58
from bzrlib.repofmt import (
61
59
    groupcompress_repo,
62
60
    knitrepo,
63
 
    knitpack_repo,
64
61
    pack_repo,
 
62
    weaverepo,
65
63
    )
66
64
 
67
65
 
68
66
class TestDefaultFormat(TestCase):
69
67
 
70
68
    def test_get_set_default_format(self):
71
 
        old_default = controldir.format_registry.get('default')
72
 
        old_default_help = controldir.format_registry.get_help('default')
 
69
        old_default = bzrdir.format_registry.get('default')
73
70
        private_default = old_default().repository_format.__class__
74
 
        old_format = repository.format_registry.get_default()
 
71
        old_format = repository.RepositoryFormat.get_default_format()
75
72
        self.assertTrue(isinstance(old_format, private_default))
76
 
 
77
73
        def make_sample_bzrdir():
78
74
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
79
75
            my_bzrdir.repository_format = SampleRepositoryFormat()
80
76
            return my_bzrdir
81
 
        controldir.format_registry.remove('default')
82
 
        controldir.format_registry.register('sample', make_sample_bzrdir, '')
83
 
        controldir.format_registry.set_default('sample')
 
77
        bzrdir.format_registry.remove('default')
 
78
        bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
 
79
        bzrdir.format_registry.set_default('sample')
84
80
        # creating a repository should now create an instrumented dir.
85
81
        try:
86
82
            # the default branch format is used by the meta dir format
89
85
            result = dir.create_repository()
90
86
            self.assertEqual(result, 'A bzr repository dir')
91
87
        finally:
92
 
            controldir.format_registry.remove('default')
93
 
            controldir.format_registry.remove('sample')
94
 
            controldir.format_registry.register(
95
 
                'default', old_default, old_default_help)
96
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
88
            bzrdir.format_registry.remove('default')
 
89
            bzrdir.format_registry.remove('sample')
 
90
            bzrdir.format_registry.register('default', old_default, '')
 
91
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
97
92
                              old_format.__class__)
98
93
 
99
94
 
100
 
class SampleRepositoryFormat(bzrrepository.RepositoryFormatMetaDir):
 
95
class SampleRepositoryFormat(repository.RepositoryFormat):
101
96
    """A sample format
102
97
 
103
98
    this format is initializable, unsupported to aid in testing the
104
99
    open and open(unsupported=True) routines.
105
100
    """
106
101
 
107
 
    @classmethod
108
 
    def get_format_string(cls):
 
102
    def get_format_string(self):
109
103
        """See RepositoryFormat.get_format_string()."""
110
 
        return b"Sample .bzr repository format."
 
104
        return "Sample .bzr repository format."
111
105
 
112
 
    def initialize(self, a_controldir, shared=False):
 
106
    def initialize(self, a_bzrdir, shared=False):
113
107
        """Initialize a repository in a BzrDir"""
114
 
        t = a_controldir.get_repository_transport(self)
 
108
        t = a_bzrdir.get_repository_transport(self)
115
109
        t.put_bytes('format', self.get_format_string())
116
110
        return 'A bzr repository dir'
117
111
 
118
112
    def is_supported(self):
119
113
        return False
120
114
 
121
 
    def open(self, a_controldir, _found=False):
 
115
    def open(self, a_bzrdir, _found=False):
122
116
        return "opened repository."
123
117
 
124
118
 
125
 
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
126
 
    """A sample format that can not be used in a metadir
127
 
 
128
 
    """
129
 
 
130
 
    def get_format_string(self):
131
 
        raise NotImplementedError
132
 
 
133
 
 
134
119
class TestRepositoryFormat(TestCaseWithTransport):
135
120
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
136
121
 
139
124
        # create a branch with a few known format objects.
140
125
        # this is not quite the same as
141
126
        self.build_tree(["foo/", "bar/"])
142
 
 
143
127
        def check_format(format, url):
144
 
            dir = format._matchingcontroldir.initialize(url)
 
128
            dir = format._matchingbzrdir.initialize(url)
145
129
            format.initialize(dir)
146
 
            found_format = bzrrepository.RepositoryFormatMetaDir.find_format(
147
 
                dir)
148
 
            self.assertIsInstance(found_format, format.__class__)
149
 
        check_format(repository.format_registry.get_default(), "bar")
 
130
            t = get_transport(url)
 
131
            found_format = repository.RepositoryFormat.find_format(dir)
 
132
            self.failUnless(isinstance(found_format, format.__class__))
 
133
        check_format(weaverepo.RepositoryFormat7(), "bar")
150
134
 
151
135
    def test_find_format_no_repository(self):
152
136
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
153
137
        self.assertRaises(errors.NoRepositoryPresent,
154
 
                          bzrrepository.RepositoryFormatMetaDir.find_format,
 
138
                          repository.RepositoryFormat.find_format,
155
139
                          dir)
156
140
 
157
 
    def test_from_string(self):
158
 
        self.assertIsInstance(
159
 
            SampleRepositoryFormat.from_string(
160
 
                b"Sample .bzr repository format."),
161
 
            SampleRepositoryFormat)
162
 
        self.assertRaises(AssertionError,
163
 
                          SampleRepositoryFormat.from_string,
164
 
                          b"Different .bzr repository format.")
165
 
 
166
141
    def test_find_format_unknown_format(self):
167
142
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
168
143
        SampleRepositoryFormat().initialize(dir)
169
144
        self.assertRaises(UnknownFormatError,
170
 
                          bzrrepository.RepositoryFormatMetaDir.find_format,
 
145
                          repository.RepositoryFormat.find_format,
171
146
                          dir)
172
147
 
173
 
    def test_find_format_with_features(self):
174
 
        tree = self.make_branch_and_tree('.', format='2a')
175
 
        tree.branch.repository.update_feature_flags({b"name": b"necessity"})
176
 
        found_format = bzrrepository.RepositoryFormatMetaDir.find_format(
177
 
            tree.controldir)
178
 
        self.assertIsInstance(
179
 
            found_format, bzrrepository.RepositoryFormatMetaDir)
180
 
        self.assertEqual(found_format.features.get(b"name"), b"necessity")
181
 
        self.assertRaises(
182
 
            bzrdir.MissingFeature, found_format.check_support_status, True)
183
 
        self.addCleanup(
184
 
            bzrrepository.RepositoryFormatMetaDir.unregister_feature, b"name")
185
 
        bzrrepository.RepositoryFormatMetaDir.register_feature(b"name")
186
 
        found_format.check_support_status(True)
187
 
 
188
 
 
189
 
class TestRepositoryFormatRegistry(TestCase):
190
 
 
191
 
    def setUp(self):
192
 
        super(TestRepositoryFormatRegistry, self).setUp()
193
 
        self.registry = repository.RepositoryFormatRegistry()
194
 
 
195
148
    def test_register_unregister_format(self):
196
149
        format = SampleRepositoryFormat()
197
 
        self.registry.register(format)
198
 
        self.assertEqual(format, self.registry.get(
199
 
            b"Sample .bzr repository format."))
200
 
        self.registry.remove(format)
201
 
        self.assertRaises(KeyError, self.registry.get,
202
 
                          b"Sample .bzr repository format.")
203
 
 
204
 
    def test_get_all(self):
205
 
        format = SampleRepositoryFormat()
206
 
        self.assertEqual([], self.registry._get_all())
207
 
        self.registry.register(format)
208
 
        self.assertEqual([format], self.registry._get_all())
209
 
 
210
 
    def test_register_extra(self):
211
 
        format = SampleExtraRepositoryFormat()
212
 
        self.assertEqual([], self.registry._get_all())
213
 
        self.registry.register_extra(format)
214
 
        self.assertEqual([format], self.registry._get_all())
215
 
 
216
 
    def test_register_extra_lazy(self):
217
 
        self.assertEqual([], self.registry._get_all())
218
 
        self.registry.register_extra_lazy(__name__,
219
 
                                          "SampleExtraRepositoryFormat")
220
 
        formats = self.registry._get_all()
221
 
        self.assertEqual(1, len(formats))
222
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
150
        # make a control dir
 
151
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
152
        # make a repo
 
153
        format.initialize(dir)
 
154
        # register a format for it.
 
155
        repository.RepositoryFormat.register_format(format)
 
156
        # which repository.Open will refuse (not supported)
 
157
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
 
158
        # but open(unsupported) will work
 
159
        self.assertEqual(format.open(dir), "opened repository.")
 
160
        # unregister the format
 
161
        repository.RepositoryFormat.unregister_format(format)
 
162
 
 
163
 
 
164
class TestFormat6(TestCaseWithTransport):
 
165
 
 
166
    def test_attribute__fetch_order(self):
 
167
        """Weaves need topological data insertion."""
 
168
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
169
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
170
        self.assertEqual('topological', repo._format._fetch_order)
 
171
 
 
172
    def test_attribute__fetch_uses_deltas(self):
 
173
        """Weaves do not reuse deltas."""
 
174
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
175
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
176
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
177
 
 
178
    def test_attribute__fetch_reconcile(self):
 
179
        """Weave repositories need a reconcile after fetch."""
 
180
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
181
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
182
        self.assertEqual(True, repo._format._fetch_reconcile)
 
183
 
 
184
    def test_no_ancestry_weave(self):
 
185
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
186
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
187
        # We no longer need to create the ancestry.weave file
 
188
        # since it is *never* used.
 
189
        self.assertRaises(NoSuchFile,
 
190
                          control.transport.get,
 
191
                          'ancestry.weave')
 
192
 
 
193
    def test_supports_external_lookups(self):
 
194
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
195
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
196
        self.assertFalse(repo._format.supports_external_lookups)
 
197
 
 
198
 
 
199
class TestFormat7(TestCaseWithTransport):
 
200
 
 
201
    def test_attribute__fetch_order(self):
 
202
        """Weaves need topological data insertion."""
 
203
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
204
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
205
        self.assertEqual('topological', repo._format._fetch_order)
 
206
 
 
207
    def test_attribute__fetch_uses_deltas(self):
 
208
        """Weaves do not reuse deltas."""
 
209
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
210
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
211
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
212
 
 
213
    def test_attribute__fetch_reconcile(self):
 
214
        """Weave repositories need a reconcile after fetch."""
 
215
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
216
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
217
        self.assertEqual(True, repo._format._fetch_reconcile)
 
218
 
 
219
    def test_disk_layout(self):
 
220
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
221
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
222
        # in case of side effects of locking.
 
223
        repo.lock_write()
 
224
        repo.unlock()
 
225
        # we want:
 
226
        # format 'Bazaar-NG Repository format 7'
 
227
        # lock ''
 
228
        # inventory.weave == empty_weave
 
229
        # empty revision-store directory
 
230
        # empty weaves directory
 
231
        t = control.get_repository_transport(None)
 
232
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
233
                             t.get('format').read())
 
234
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
235
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
236
        self.assertEqualDiff('# bzr weave file v5\n'
 
237
                             'w\n'
 
238
                             'W\n',
 
239
                             t.get('inventory.weave').read())
 
240
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
241
        # disk.
 
242
        control.create_branch()
 
243
        tree = control.create_workingtree()
 
244
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
245
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
246
        try:
 
247
            tree.commit('first post', rev_id='first')
 
248
        except errors.IllegalPath:
 
249
            if sys.platform != 'win32':
 
250
                raise
 
251
            self.knownFailure('Foo:Bar cannot be used as a file-id on windows'
 
252
                              ' in repo format 7')
 
253
            return
 
254
        self.assertEqualDiff(
 
255
            '# bzr weave file v5\n'
 
256
            'i\n'
 
257
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
258
            'n first\n'
 
259
            '\n'
 
260
            'w\n'
 
261
            '{ 0\n'
 
262
            '. content\n'
 
263
            '}\n'
 
264
            'W\n',
 
265
            t.get('weaves/74/Foo%3ABar.weave').read())
 
266
 
 
267
    def test_shared_disk_layout(self):
 
268
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
269
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
270
        # we want:
 
271
        # format 'Bazaar-NG Repository format 7'
 
272
        # inventory.weave == empty_weave
 
273
        # empty revision-store directory
 
274
        # empty weaves directory
 
275
        # a 'shared-storage' marker file.
 
276
        # lock is not present when unlocked
 
277
        t = control.get_repository_transport(None)
 
278
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
279
                             t.get('format').read())
 
280
        self.assertEqualDiff('', t.get('shared-storage').read())
 
281
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
282
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
283
        self.assertEqualDiff('# bzr weave file v5\n'
 
284
                             'w\n'
 
285
                             'W\n',
 
286
                             t.get('inventory.weave').read())
 
287
        self.assertFalse(t.has('branch-lock'))
 
288
 
 
289
    def test_creates_lockdir(self):
 
290
        """Make sure it appears to be controlled by a LockDir existence"""
 
291
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
292
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
293
        t = control.get_repository_transport(None)
 
294
        # TODO: Should check there is a 'lock' toplevel directory,
 
295
        # regardless of contents
 
296
        self.assertFalse(t.has('lock/held/info'))
 
297
        repo.lock_write()
 
298
        try:
 
299
            self.assertTrue(t.has('lock/held/info'))
 
300
        finally:
 
301
            # unlock so we don't get a warning about failing to do so
 
302
            repo.unlock()
 
303
 
 
304
    def test_uses_lockdir(self):
 
305
        """repo format 7 actually locks on lockdir"""
 
306
        base_url = self.get_url()
 
307
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
308
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
309
        t = control.get_repository_transport(None)
 
310
        repo.lock_write()
 
311
        repo.unlock()
 
312
        del repo
 
313
        # make sure the same lock is created by opening it
 
314
        repo = repository.Repository.open(base_url)
 
315
        repo.lock_write()
 
316
        self.assertTrue(t.has('lock/held/info'))
 
317
        repo.unlock()
 
318
        self.assertFalse(t.has('lock/held/info'))
 
319
 
 
320
    def test_shared_no_tree_disk_layout(self):
 
321
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
322
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
323
        repo.set_make_working_trees(False)
 
324
        # we want:
 
325
        # format 'Bazaar-NG Repository format 7'
 
326
        # lock ''
 
327
        # inventory.weave == empty_weave
 
328
        # empty revision-store directory
 
329
        # empty weaves directory
 
330
        # a 'shared-storage' marker file.
 
331
        t = control.get_repository_transport(None)
 
332
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
333
                             t.get('format').read())
 
334
        ## self.assertEqualDiff('', t.get('lock').read())
 
335
        self.assertEqualDiff('', t.get('shared-storage').read())
 
336
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
337
        repo.set_make_working_trees(True)
 
338
        self.assertFalse(t.has('no-working-trees'))
 
339
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
340
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
341
        self.assertEqualDiff('# bzr weave file v5\n'
 
342
                             'w\n'
 
343
                             'W\n',
 
344
                             t.get('inventory.weave').read())
 
345
 
 
346
    def test_supports_external_lookups(self):
 
347
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
348
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
349
        self.assertFalse(repo._format.supports_external_lookups)
223
350
 
224
351
 
225
352
class TestFormatKnit1(TestCaseWithTransport):
226
353
 
227
354
    def test_attribute__fetch_order(self):
228
355
        """Knits need topological data insertion."""
229
 
        repo = self.make_repository(
230
 
            '.', format=controldir.format_registry.get('knit')())
 
356
        repo = self.make_repository('.',
 
357
                format=bzrdir.format_registry.get('knit')())
231
358
        self.assertEqual('topological', repo._format._fetch_order)
232
359
 
233
360
    def test_attribute__fetch_uses_deltas(self):
234
361
        """Knits reuse deltas."""
235
 
        repo = self.make_repository(
236
 
            '.', format=controldir.format_registry.get('knit')())
 
362
        repo = self.make_repository('.',
 
363
                format=bzrdir.format_registry.get('knit')())
237
364
        self.assertEqual(True, repo._format._fetch_uses_deltas)
238
365
 
239
366
    def test_disk_layout(self):
249
376
        # empty revision-store directory
250
377
        # empty weaves directory
251
378
        t = control.get_repository_transport(None)
252
 
        with t.get('format') as f:
253
 
            self.assertEqualDiff(b'Bazaar-NG Knit Repository Format 1',
254
 
                                 f.read())
 
379
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
 
380
                             t.get('format').read())
255
381
        # XXX: no locks left when unlocked at the moment
256
382
        # self.assertEqualDiff('', t.get('lock').read())
257
383
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
258
384
        self.check_knits(t)
259
385
        # Check per-file knits.
260
 
        control.create_branch()
 
386
        branch = control.create_branch()
261
387
        tree = control.create_workingtree()
262
 
        tree.add(['foo'], [b'Nasty-IdC:'], ['file'])
263
 
        tree.put_file_bytes_non_atomic('foo', b'')
264
 
        tree.commit('1st post', rev_id=b'foo')
 
388
        tree.add(['foo'], ['Nasty-IdC:'], ['file'])
 
389
        tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
 
390
        tree.commit('1st post', rev_id='foo')
265
391
        self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
266
 
                           b'\nfoo fulltext 0 81  :')
 
392
            '\nfoo fulltext 0 81  :')
267
393
 
268
 
    def assertHasKnit(self, t, knit_name, extra_content=b''):
 
394
    def assertHasKnit(self, t, knit_name, extra_content=''):
269
395
        """Assert that knit_name exists on t."""
270
 
        self.assertEqualDiff(b'# bzr knit index 8\n' + extra_content,
 
396
        self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
271
397
                             t.get(knit_name + '.kndx').read())
272
398
 
273
399
    def check_knits(self, t):
278
404
 
279
405
    def test_shared_disk_layout(self):
280
406
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
281
 
        knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
 
407
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
282
408
        # we want:
283
409
        # format 'Bazaar-NG Knit Repository Format 1'
284
410
        # lock: is a directory
287
413
        # empty weaves directory
288
414
        # a 'shared-storage' marker file.
289
415
        t = control.get_repository_transport(None)
290
 
        with t.get('format') as f:
291
 
            self.assertEqualDiff(b'Bazaar-NG Knit Repository Format 1',
292
 
                                 f.read())
 
416
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
 
417
                             t.get('format').read())
293
418
        # XXX: no locks left when unlocked at the moment
294
419
        # self.assertEqualDiff('', t.get('lock').read())
295
 
        self.assertEqualDiff(b'', t.get('shared-storage').read())
 
420
        self.assertEqualDiff('', t.get('shared-storage').read())
296
421
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
297
422
        self.check_knits(t)
298
423
 
299
424
    def test_shared_no_tree_disk_layout(self):
300
425
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
301
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(
302
 
            control, shared=True)
 
426
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
303
427
        repo.set_make_working_trees(False)
304
428
        # we want:
305
429
        # format 'Bazaar-NG Knit Repository Format 1'
309
433
        # empty weaves directory
310
434
        # a 'shared-storage' marker file.
311
435
        t = control.get_repository_transport(None)
312
 
        with t.get('format') as f:
313
 
            self.assertEqualDiff(b'Bazaar-NG Knit Repository Format 1',
314
 
                                 f.read())
 
436
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
 
437
                             t.get('format').read())
315
438
        # XXX: no locks left when unlocked at the moment
316
439
        # self.assertEqualDiff('', t.get('lock').read())
317
 
        self.assertEqualDiff(b'', t.get('shared-storage').read())
318
 
        self.assertEqualDiff(b'', t.get('no-working-trees').read())
 
440
        self.assertEqualDiff('', t.get('shared-storage').read())
 
441
        self.assertEqualDiff('', t.get('no-working-trees').read())
319
442
        repo.set_make_working_trees(True)
320
443
        self.assertFalse(t.has('no-working-trees'))
321
444
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
328
451
        the whole inventory. So we grab the one from the expected text. Which
329
452
        is valid when the api is not being abused.
330
453
        """
331
 
        repo = self.make_repository(
332
 
            '.', format=controldir.format_registry.get('knit')())
333
 
        inv_xml = b'<inventory format="5">\n</inventory>\n'
334
 
        inv = repo._deserialise_inventory(b'test-rev-id', [inv_xml])
335
 
        self.assertEqual(b'test-rev-id', inv.root.revision)
 
454
        repo = self.make_repository('.',
 
455
                format=bzrdir.format_registry.get('knit')())
 
456
        inv_xml = '<inventory format="5">\n</inventory>\n'
 
457
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
458
        self.assertEqual('test-rev-id', inv.root.revision)
336
459
 
337
460
    def test_deserialise_uses_global_revision_id(self):
338
461
        """If it is set, then we re-use the global revision id"""
339
 
        repo = self.make_repository(
340
 
            '.', format=controldir.format_registry.get('knit')())
341
 
        inv_xml = (b'<inventory format="5" revision_id="other-rev-id">\n'
342
 
                   b'</inventory>\n')
 
462
        repo = self.make_repository('.',
 
463
                format=bzrdir.format_registry.get('knit')())
 
464
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
 
465
                   '</inventory>\n')
343
466
        # Arguably, the deserialise_inventory should detect a mismatch, and
344
467
        # raise an error, rather than silently using one revision_id over the
345
468
        # other.
346
469
        self.assertRaises(AssertionError, repo._deserialise_inventory,
347
 
                          b'test-rev-id', [inv_xml])
348
 
        inv = repo._deserialise_inventory(b'other-rev-id', [inv_xml])
349
 
        self.assertEqual(b'other-rev-id', inv.root.revision)
 
470
            'test-rev-id', inv_xml)
 
471
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
472
        self.assertEqual('other-rev-id', inv.root.revision)
350
473
 
351
474
    def test_supports_external_lookups(self):
352
 
        repo = self.make_repository(
353
 
            '.', format=controldir.format_registry.get('knit')())
 
475
        repo = self.make_repository('.',
 
476
                format=bzrdir.format_registry.get('knit')())
354
477
        self.assertFalse(repo._format.supports_external_lookups)
355
478
 
356
479
 
384
507
    def is_compatible(repo_source, repo_target):
385
508
        """InterDummy is compatible with DummyRepository."""
386
509
        return (isinstance(repo_source, DummyRepository) and
387
 
                isinstance(repo_target, DummyRepository))
 
510
            isinstance(repo_target, DummyRepository))
388
511
 
389
512
 
390
513
class TestInterRepository(TestCaseWithTransport):
397
520
        # classes do not barf inappropriately when a surprising repository type
398
521
        # is handed to them.
399
522
        dummy_a = DummyRepository()
400
 
        dummy_a._format = RepositoryFormat()
401
 
        dummy_a._format.supports_full_versioned_files = True
402
523
        dummy_b = DummyRepository()
403
 
        dummy_b._format = RepositoryFormat()
404
 
        dummy_b._format.supports_full_versioned_files = True
405
524
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
406
525
 
407
526
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
411
530
        no actual sane default in the presence of incompatible data models.
412
531
        """
413
532
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
414
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
533
        self.assertEqual(repository.InterSameDataRepository,
415
534
                         inter_repo.__class__)
416
535
        self.assertEqual(repo_a, inter_repo.source)
417
536
        self.assertEqual(repo_b, inter_repo.target)
429
548
        repo = self.make_repository('.')
430
549
        # hack dummies to look like repo somewhat.
431
550
        dummy_a._serializer = repo._serializer
432
 
        dummy_a._format.supports_tree_reference = (
433
 
            repo._format.supports_tree_reference)
 
551
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
434
552
        dummy_a._format.rich_root_data = repo._format.rich_root_data
435
 
        dummy_a._format.supports_full_versioned_files = (
436
 
            repo._format.supports_full_versioned_files)
437
553
        dummy_b._serializer = repo._serializer
438
 
        dummy_b._format.supports_tree_reference = (
439
 
            repo._format.supports_tree_reference)
 
554
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
440
555
        dummy_b._format.rich_root_data = repo._format.rich_root_data
441
 
        dummy_b._format.supports_full_versioned_files = (
442
 
            repo._format.supports_full_versioned_files)
443
556
        repository.InterRepository.register_optimiser(InterDummy)
444
557
        try:
445
558
            # we should get the default for something InterDummy returns False
458
571
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
459
572
 
460
573
 
461
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
462
 
 
463
 
    @classmethod
464
 
    def get_format_string(cls):
465
 
        return b"Test Format 1"
466
 
 
467
 
 
468
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
469
 
 
470
 
    @classmethod
471
 
    def get_format_string(cls):
472
 
        return b"Test Format 2"
 
574
class TestInterWeaveRepo(TestCaseWithTransport):
 
575
 
 
576
    def test_is_compatible_and_registered(self):
 
577
        # InterWeaveRepo is compatible when either side
 
578
        # is a format 5/6/7 branch
 
579
        from bzrlib.repofmt import knitrepo, weaverepo
 
580
        formats = [weaverepo.RepositoryFormat5(),
 
581
                   weaverepo.RepositoryFormat6(),
 
582
                   weaverepo.RepositoryFormat7()]
 
583
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
584
                                knitrepo.RepositoryFormatKnit1(),
 
585
                                ]
 
586
        repo_a = self.make_repository('a')
 
587
        repo_b = self.make_repository('b')
 
588
        is_compatible = repository.InterWeaveRepo.is_compatible
 
589
        for source in incompatible_formats:
 
590
            # force incompatible left then right
 
591
            repo_a._format = source
 
592
            repo_b._format = formats[0]
 
593
            self.assertFalse(is_compatible(repo_a, repo_b))
 
594
            self.assertFalse(is_compatible(repo_b, repo_a))
 
595
        for source in formats:
 
596
            repo_a._format = source
 
597
            for target in formats:
 
598
                repo_b._format = target
 
599
                self.assertTrue(is_compatible(repo_a, repo_b))
 
600
        self.assertEqual(repository.InterWeaveRepo,
 
601
                         repository.InterRepository.get(repo_a,
 
602
                                                        repo_b).__class__)
473
603
 
474
604
 
475
605
class TestRepositoryConverter(TestCaseWithTransport):
476
606
 
477
607
    def test_convert_empty(self):
478
 
        source_format = TestRepositoryFormat1()
479
 
        target_format = TestRepositoryFormat2()
480
 
        repository.format_registry.register(source_format)
481
 
        self.addCleanup(repository.format_registry.remove,
482
 
                        source_format)
483
 
        repository.format_registry.register(target_format)
484
 
        self.addCleanup(repository.format_registry.remove,
485
 
                        target_format)
486
 
        t = self.get_transport()
 
608
        t = get_transport(self.get_url('.'))
487
609
        t.mkdir('repository')
488
610
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
489
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
611
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
612
        target_format = knitrepo.RepositoryFormatKnit1()
490
613
        converter = repository.CopyConverter(target_format)
491
 
        with breezy.ui.ui_factory.nested_progress_bar() as pb:
 
614
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
615
        try:
492
616
            converter.convert(repo, pb)
 
617
        finally:
 
618
            pb.finished()
493
619
        repo = repo_dir.open_repository()
494
620
        self.assertTrue(isinstance(target_format, repo._format.__class__))
495
621
 
496
622
 
 
623
class TestMisc(TestCase):
 
624
 
 
625
    def test_unescape_xml(self):
 
626
        """We get some kind of error when malformed entities are passed"""
 
627
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
628
 
 
629
 
497
630
class TestRepositoryFormatKnit3(TestCaseWithTransport):
498
631
 
499
632
    def test_attribute__fetch_order(self):
515
648
        format = bzrdir.BzrDirMetaFormat1()
516
649
        format.repository_format = knitrepo.RepositoryFormatKnit1()
517
650
        tree = self.make_branch_and_tree('.', format)
518
 
        tree.commit("Dull commit", rev_id=b"dull")
519
 
        revision_tree = tree.branch.repository.revision_tree(b'dull')
520
 
        with revision_tree.lock_read():
521
 
            self.assertRaises(
522
 
                errors.NoSuchFile, revision_tree.get_file_lines, u'')
 
651
        tree.commit("Dull commit", rev_id="dull")
 
652
        revision_tree = tree.branch.repository.revision_tree('dull')
 
653
        revision_tree.lock_read()
 
654
        try:
 
655
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
 
656
                revision_tree.inventory.root.file_id)
 
657
        finally:
 
658
            revision_tree.unlock()
523
659
        format = bzrdir.BzrDirMetaFormat1()
524
660
        format.repository_format = knitrepo.RepositoryFormatKnit3()
525
661
        upgrade.Convert('.', format)
526
662
        tree = workingtree.WorkingTree.open('.')
527
 
        revision_tree = tree.branch.repository.revision_tree(b'dull')
528
 
        with revision_tree.lock_read():
529
 
            revision_tree.get_file_lines(u'')
530
 
        tree.commit("Another dull commit", rev_id=b'dull2')
531
 
        revision_tree = tree.branch.repository.revision_tree(b'dull2')
 
663
        revision_tree = tree.branch.repository.revision_tree('dull')
 
664
        revision_tree.lock_read()
 
665
        try:
 
666
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
667
        finally:
 
668
            revision_tree.unlock()
 
669
        tree.commit("Another dull commit", rev_id='dull2')
 
670
        revision_tree = tree.branch.repository.revision_tree('dull2')
532
671
        revision_tree.lock_read()
533
672
        self.addCleanup(revision_tree.unlock)
534
 
        self.assertEqual(b'dull', revision_tree.get_file_revision(u''))
 
673
        self.assertEqual('dull', revision_tree.inventory.root.revision)
535
674
 
536
675
    def test_supports_external_lookups(self):
537
676
        format = bzrdir.BzrDirMetaFormat1()
542
681
 
543
682
class Test2a(tests.TestCaseWithMemoryTransport):
544
683
 
545
 
    def test_chk_bytes_uses_custom_btree_parser(self):
546
 
        mt = self.make_branch_and_memory_tree('test', format='2a')
547
 
        mt.lock_write()
548
 
        self.addCleanup(mt.unlock)
549
 
        mt.add([''], [b'root-id'])
550
 
        mt.commit('first')
551
 
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
552
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
553
 
        # It should also work if we re-open the repo
554
 
        repo = mt.branch.repository.controldir.open_repository()
555
 
        repo.lock_read()
556
 
        self.addCleanup(repo.unlock)
557
 
        index = repo.chk_bytes._index._graph_index._indices[0]
558
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
559
 
 
560
 
    def test_fetch_combines_groups(self):
561
 
        builder = self.make_branch_builder('source', format='2a')
562
 
        builder.start_series()
563
 
        builder.build_snapshot(None, [
564
 
            ('add', ('', b'root-id', 'directory', '')),
565
 
            ('add', ('file', b'file-id', 'file', b'content\n'))],
566
 
            revision_id=b'1')
567
 
        builder.build_snapshot([b'1'], [
568
 
            ('modify', ('file', b'content-2\n'))],
569
 
            revision_id=b'2')
570
 
        builder.finish_series()
571
 
        source = builder.get_branch()
572
 
        target = self.make_repository('target', format='2a')
573
 
        target.fetch(source.repository)
574
 
        target.lock_read()
575
 
        self.addCleanup(target.unlock)
576
 
        details = target.texts._index.get_build_details(
577
 
            [(b'file-id', b'1',), (b'file-id', b'2',)])
578
 
        file_1_details = details[(b'file-id', b'1')]
579
 
        file_2_details = details[(b'file-id', b'2')]
580
 
        # The index, and what to read off disk, should be the same for both
581
 
        # versions of the file.
582
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
583
 
 
584
 
    def test_fetch_combines_groups(self):
585
 
        builder = self.make_branch_builder('source', format='2a')
586
 
        builder.start_series()
587
 
        builder.build_snapshot(None, [
588
 
            ('add', ('', b'root-id', 'directory', '')),
589
 
            ('add', ('file', b'file-id', 'file', b'content\n'))],
590
 
            revision_id=b'1')
591
 
        builder.build_snapshot([b'1'], [
592
 
            ('modify', ('file', b'content-2\n'))],
593
 
            revision_id=b'2')
594
 
        builder.finish_series()
595
 
        source = builder.get_branch()
596
 
        target = self.make_repository('target', format='2a')
597
 
        target.fetch(source.repository)
598
 
        target.lock_read()
599
 
        self.addCleanup(target.unlock)
600
 
        details = target.texts._index.get_build_details(
601
 
            [(b'file-id', b'1',), (b'file-id', b'2',)])
602
 
        file_1_details = details[(b'file-id', b'1')]
603
 
        file_2_details = details[(b'file-id', b'2')]
604
 
        # The index, and what to read off disk, should be the same for both
605
 
        # versions of the file.
606
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
607
 
 
608
 
    def test_fetch_combines_groups(self):
609
 
        builder = self.make_branch_builder('source', format='2a')
610
 
        builder.start_series()
611
 
        builder.build_snapshot(None, [
612
 
            ('add', ('', b'root-id', 'directory', '')),
613
 
            ('add', ('file', b'file-id', 'file', b'content\n'))],
614
 
            revision_id=b'1')
615
 
        builder.build_snapshot([b'1'], [
616
 
            ('modify', ('file', b'content-2\n'))],
617
 
            revision_id=b'2')
618
 
        builder.finish_series()
619
 
        source = builder.get_branch()
620
 
        target = self.make_repository('target', format='2a')
621
 
        target.fetch(source.repository)
622
 
        target.lock_read()
623
 
        self.addCleanup(target.unlock)
624
 
        details = target.texts._index.get_build_details(
625
 
            [(b'file-id', b'1',), (b'file-id', b'2',)])
626
 
        file_1_details = details[(b'file-id', b'1')]
627
 
        file_2_details = details[(b'file-id', b'2')]
 
684
    def test_fetch_combines_groups(self):
 
685
        builder = self.make_branch_builder('source', format='2a')
 
686
        builder.start_series()
 
687
        builder.build_snapshot('1', None, [
 
688
            ('add', ('', 'root-id', 'directory', '')),
 
689
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
690
        builder.build_snapshot('2', ['1'], [
 
691
            ('modify', ('file-id', 'content-2\n'))])
 
692
        builder.finish_series()
 
693
        source = builder.get_branch()
 
694
        target = self.make_repository('target', format='2a')
 
695
        target.fetch(source.repository)
 
696
        target.lock_read()
 
697
        self.addCleanup(target.unlock)
 
698
        details = target.texts._index.get_build_details(
 
699
            [('file-id', '1',), ('file-id', '2',)])
 
700
        file_1_details = details[('file-id', '1')]
 
701
        file_2_details = details[('file-id', '2')]
 
702
        # The index, and what to read off disk, should be the same for both
 
703
        # versions of the file.
 
704
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
705
 
 
706
    def test_fetch_combines_groups(self):
 
707
        builder = self.make_branch_builder('source', format='2a')
 
708
        builder.start_series()
 
709
        builder.build_snapshot('1', None, [
 
710
            ('add', ('', 'root-id', 'directory', '')),
 
711
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
712
        builder.build_snapshot('2', ['1'], [
 
713
            ('modify', ('file-id', 'content-2\n'))])
 
714
        builder.finish_series()
 
715
        source = builder.get_branch()
 
716
        target = self.make_repository('target', format='2a')
 
717
        target.fetch(source.repository)
 
718
        target.lock_read()
 
719
        self.addCleanup(target.unlock)
 
720
        details = target.texts._index.get_build_details(
 
721
            [('file-id', '1',), ('file-id', '2',)])
 
722
        file_1_details = details[('file-id', '1')]
 
723
        file_2_details = details[('file-id', '2')]
 
724
        # The index, and what to read off disk, should be the same for both
 
725
        # versions of the file.
 
726
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
727
 
 
728
    def test_fetch_combines_groups(self):
 
729
        builder = self.make_branch_builder('source', format='2a')
 
730
        builder.start_series()
 
731
        builder.build_snapshot('1', None, [
 
732
            ('add', ('', 'root-id', 'directory', '')),
 
733
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
734
        builder.build_snapshot('2', ['1'], [
 
735
            ('modify', ('file-id', 'content-2\n'))])
 
736
        builder.finish_series()
 
737
        source = builder.get_branch()
 
738
        target = self.make_repository('target', format='2a')
 
739
        target.fetch(source.repository)
 
740
        target.lock_read()
 
741
        self.addCleanup(target.unlock)
 
742
        details = target.texts._index.get_build_details(
 
743
            [('file-id', '1',), ('file-id', '2',)])
 
744
        file_1_details = details[('file-id', '1')]
 
745
        file_2_details = details[('file-id', '2')]
628
746
        # The index, and what to read off disk, should be the same for both
629
747
        # versions of the file.
630
748
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
636
754
    def test_inventories_use_chk_map_with_parent_base_dict(self):
637
755
        tree = self.make_branch_and_memory_tree('repo', format="2a")
638
756
        tree.lock_write()
639
 
        tree.add([''], [b'TREE_ROOT'])
 
757
        tree.add([''], ['TREE_ROOT'])
640
758
        revid = tree.commit("foo")
641
759
        tree.unlock()
642
760
        tree.lock_read()
646
764
        inv.parent_id_basename_to_file_id._ensure_root()
647
765
        inv.id_to_entry._ensure_root()
648
766
        self.assertEqual(65536, inv.id_to_entry._root_node.maximum_size)
649
 
        self.assertEqual(
650
 
            65536, inv.parent_id_basename_to_file_id._root_node.maximum_size)
 
767
        self.assertEqual(65536,
 
768
            inv.parent_id_basename_to_file_id._root_node.maximum_size)
651
769
 
652
770
    def test_autopack_unchanged_chk_nodes(self):
653
771
        # at 20 unchanged commits, chk pages are packed that are split into
657
775
        tree = self.make_branch_and_memory_tree('tree', format='2a')
658
776
        tree.lock_write()
659
777
        self.addCleanup(tree.unlock)
660
 
        tree.add([''], [b'TREE_ROOT'])
 
778
        tree.add([''], ['TREE_ROOT'])
661
779
        for pos in range(20):
662
780
            tree.commit(str(pos))
663
781
 
665
783
        tree = self.make_branch_and_memory_tree('tree', format='2a')
666
784
        tree.lock_write()
667
785
        self.addCleanup(tree.unlock)
668
 
        tree.add([''], [b'TREE_ROOT'])
 
786
        tree.add([''], ['TREE_ROOT'])
669
787
        # 1 commit to leave untouched
670
788
        tree.commit('1')
671
789
        to_keep = tree.branch.repository._pack_collection.names()
694
812
        target = self.make_repository('target', format='rich-root-pack')
695
813
        stream = source._get_source(target._format)
696
814
        # We don't want the child GroupCHKStreamSource
697
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
815
        self.assertIs(type(stream), repository.StreamSource)
698
816
 
699
817
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
700
818
        source_builder = self.make_branch_builder('source',
701
 
                                                  format='2a')
 
819
                            format='2a')
702
820
        # We have to build a fairly large tree, so that we are sure the chk
703
821
        # pages will have split into multiple pages.
704
 
        entries = [('add', ('', b'a-root-id', 'directory', None))]
 
822
        entries = [('add', ('', 'a-root-id', 'directory', None))]
705
823
        for i in 'abcdefghijklmnopqrstuvwxyz123456789':
706
824
            for j in 'abcdefghijklmnopqrstuvwxyz123456789':
707
825
                fname = i + j
708
 
                fid = fname.encode('utf-8') + b'-id'
709
 
                content = b'content for %s\n' % (fname.encode('utf-8'),)
 
826
                fid = fname + '-id'
 
827
                content = 'content for %s\n' % (fname,)
710
828
                entries.append(('add', (fname, fid, 'file', content)))
711
829
        source_builder.start_series()
712
 
        source_builder.build_snapshot(None, entries, revision_id=b'rev-1')
 
830
        source_builder.build_snapshot('rev-1', None, entries)
713
831
        # Now change a few of them, so we get a few new pages for the second
714
832
        # revision
715
 
        source_builder.build_snapshot([b'rev-1'], [
716
 
            ('modify', ('aa', b'new content for aa-id\n')),
717
 
            ('modify', ('cc', b'new content for cc-id\n')),
718
 
            ('modify', ('zz', b'new content for zz-id\n')),
719
 
            ], revision_id=b'rev-2')
 
833
        source_builder.build_snapshot('rev-2', ['rev-1'], [
 
834
            ('modify', ('aa-id', 'new content for aa-id\n')),
 
835
            ('modify', ('cc-id', 'new content for cc-id\n')),
 
836
            ('modify', ('zz-id', 'new content for zz-id\n')),
 
837
            ])
720
838
        source_builder.finish_series()
721
839
        source_branch = source_builder.get_branch()
722
840
        source_branch.lock_read()
727
845
 
728
846
        # On a regular pass, getting the inventories and chk pages for rev-2
729
847
        # would only get the newly created chk pages
730
 
        search = vf_search.SearchResult({b'rev-2'}, {b'rev-1'}, 1,
731
 
                                        {b'rev-2'})
732
 
        simple_chk_records = set()
 
848
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
 
849
                                    set(['rev-2']))
 
850
        simple_chk_records = []
733
851
        for vf_name, substream in source.get_stream(search):
734
852
            if vf_name == 'chk_bytes':
735
853
                for record in substream:
736
 
                    simple_chk_records.add(record.key)
 
854
                    simple_chk_records.append(record.key)
737
855
            else:
738
856
                for _ in substream:
739
857
                    continue
740
858
        # 3 pages, the root (InternalNode), + 2 pages which actually changed
741
 
        self.assertEqual({(b'sha1:91481f539e802c76542ea5e4c83ad416bf219f73',),
742
 
                          (b'sha1:4ff91971043668583985aec83f4f0ab10a907d3f',),
743
 
                          (b'sha1:81e7324507c5ca132eedaf2d8414ee4bb2226187',),
744
 
                          (b'sha1:b101b7da280596c71a4540e9a1eeba8045985ee0',)},
745
 
                         set(simple_chk_records))
 
859
        self.assertEqual([('sha1:91481f539e802c76542ea5e4c83ad416bf219f73',),
 
860
                          ('sha1:4ff91971043668583985aec83f4f0ab10a907d3f',),
 
861
                          ('sha1:81e7324507c5ca132eedaf2d8414ee4bb2226187',),
 
862
                          ('sha1:b101b7da280596c71a4540e9a1eeba8045985ee0',)],
 
863
                         simple_chk_records)
746
864
        # Now, when we do a similar call using 'get_stream_for_missing_keys'
747
865
        # we should get a much larger set of pages.
748
 
        missing = [('inventories', b'rev-2')]
749
 
        full_chk_records = set()
 
866
        missing = [('inventories', 'rev-2')]
 
867
        full_chk_records = []
750
868
        for vf_name, substream in source.get_stream_for_missing_keys(missing):
751
869
            if vf_name == 'inventories':
752
870
                for record in substream:
753
 
                    self.assertEqual((b'rev-2',), record.key)
 
871
                    self.assertEqual(('rev-2',), record.key)
754
872
            elif vf_name == 'chk_bytes':
755
873
                for record in substream:
756
 
                    full_chk_records.add(record.key)
 
874
                    full_chk_records.append(record.key)
757
875
            else:
758
876
                self.fail('Should not be getting a stream of %s' % (vf_name,))
759
877
        # We have 257 records now. This is because we have 1 root page, and 256
776
894
        source = self.make_repository('source', format='pack-0.92')
777
895
        target = self.make_repository('target', format='pack-0.92')
778
896
        stream_source = source._get_source(target._format)
779
 
        self.assertIsInstance(
780
 
            stream_source, knitpack_repo.KnitPackStreamSource)
 
897
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
781
898
 
782
899
    def test_source_to_exact_pack_rich_root_pack(self):
783
900
        source = self.make_repository('source', format='rich-root-pack')
784
901
        target = self.make_repository('target', format='rich-root-pack')
785
902
        stream_source = source._get_source(target._format)
786
 
        self.assertIsInstance(
787
 
            stream_source, knitpack_repo.KnitPackStreamSource)
 
903
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
788
904
 
789
905
    def test_source_to_exact_pack_19(self):
790
906
        source = self.make_repository('source', format='1.9')
791
907
        target = self.make_repository('target', format='1.9')
792
908
        stream_source = source._get_source(target._format)
793
 
        self.assertIsInstance(
794
 
            stream_source, knitpack_repo.KnitPackStreamSource)
 
909
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
795
910
 
796
911
    def test_source_to_exact_pack_19_rich_root(self):
797
912
        source = self.make_repository('source', format='1.9-rich-root')
798
913
        target = self.make_repository('target', format='1.9-rich-root')
799
914
        stream_source = source._get_source(target._format)
800
 
        self.assertIsInstance(
801
 
            stream_source, knitpack_repo.KnitPackStreamSource)
 
915
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
802
916
 
803
917
    def test_source_to_remote_exact_pack_19(self):
804
918
        trans = self.make_smart_server('target')
807
921
        target = self.make_repository('target', format='1.9')
808
922
        target = repository.Repository.open(trans.base)
809
923
        stream_source = source._get_source(target._format)
810
 
        self.assertIsInstance(
811
 
            stream_source, knitpack_repo.KnitPackStreamSource)
 
924
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
812
925
 
813
926
    def test_stream_source_to_non_exact(self):
814
927
        source = self.make_repository('source', format='pack-0.92')
815
928
        target = self.make_repository('target', format='1.9')
816
929
        stream = source._get_source(target._format)
817
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
930
        self.assertIs(type(stream), repository.StreamSource)
818
931
 
819
932
    def test_stream_source_to_non_exact_rich_root(self):
820
933
        source = self.make_repository('source', format='1.9')
821
934
        target = self.make_repository('target', format='1.9-rich-root')
822
935
        stream = source._get_source(target._format)
823
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
936
        self.assertIs(type(stream), repository.StreamSource)
824
937
 
825
938
    def test_source_to_remote_non_exact_pack_19(self):
826
939
        trans = self.make_smart_server('target')
829
942
        target = self.make_repository('target', format='1.6')
830
943
        target = repository.Repository.open(trans.base)
831
944
        stream_source = source._get_source(target._format)
832
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
945
        self.assertIs(type(stream_source), repository.StreamSource)
833
946
 
834
947
    def test_stream_source_to_knit(self):
835
948
        source = self.make_repository('source', format='pack-0.92')
836
949
        target = self.make_repository('target', format='dirstate')
837
950
        stream = source._get_source(target._format)
838
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
951
        self.assertIs(type(stream), repository.StreamSource)
839
952
 
840
953
 
841
954
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
843
956
 
844
957
    def setUp(self):
845
958
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
846
 
        self.builder = self.make_branch_builder('source')
 
959
        self.builder = self.make_branch_builder('source',
 
960
            format='development6-rich-root')
847
961
        self.builder.start_series()
848
 
        self.builder.build_snapshot(
849
 
            None,
850
 
            [('add', ('', b'tree-root', 'directory', None))],
851
 
            revision_id=b'initial')
 
962
        self.builder.build_snapshot('initial', None,
 
963
            [('add', ('', 'tree-root', 'directory', None))])
852
964
        self.repo = self.builder.get_branch().repository
853
965
        self.addCleanup(self.builder.finish_series)
854
966
 
855
967
    def assertParentIds(self, expected_result, rev_set):
856
 
        self.assertEqual(
857
 
            sorted(expected_result),
 
968
        self.assertEqual(sorted(expected_result),
858
969
            sorted(self.repo._find_parent_ids_of_revisions(rev_set)))
859
970
 
860
971
    def test_simple(self):
861
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
862
 
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2')
863
 
        rev_set = [b'revid2']
864
 
        self.assertParentIds([b'revid1'], rev_set)
 
972
        self.builder.build_snapshot('revid1', None, [])
 
973
        self.builder.build_snapshot('revid2', ['revid1'], [])
 
974
        rev_set = ['revid2']
 
975
        self.assertParentIds(['revid1'], rev_set)
865
976
 
866
977
    def test_not_first_parent(self):
867
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
868
 
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2')
869
 
        self.builder.build_snapshot([b'revid2'], [], revision_id=b'revid3')
870
 
        rev_set = [b'revid3', b'revid2']
871
 
        self.assertParentIds([b'revid1'], rev_set)
 
978
        self.builder.build_snapshot('revid1', None, [])
 
979
        self.builder.build_snapshot('revid2', ['revid1'], [])
 
980
        self.builder.build_snapshot('revid3', ['revid2'], [])
 
981
        rev_set = ['revid3', 'revid2']
 
982
        self.assertParentIds(['revid1'], rev_set)
872
983
 
873
984
    def test_not_null(self):
874
 
        rev_set = [b'initial']
 
985
        rev_set = ['initial']
875
986
        self.assertParentIds([], rev_set)
876
987
 
877
988
    def test_not_null_set(self):
878
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
989
        self.builder.build_snapshot('revid1', None, [])
879
990
        rev_set = [_mod_revision.NULL_REVISION]
880
991
        self.assertParentIds([], rev_set)
881
992
 
882
993
    def test_ghost(self):
883
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
884
 
        rev_set = [b'ghost', b'revid1']
885
 
        self.assertParentIds([b'initial'], rev_set)
 
994
        self.builder.build_snapshot('revid1', None, [])
 
995
        rev_set = ['ghost', 'revid1']
 
996
        self.assertParentIds(['initial'], rev_set)
886
997
 
887
998
    def test_ghost_parent(self):
888
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
889
 
        self.builder.build_snapshot(
890
 
            [b'revid1', b'ghost'], [], revision_id=b'revid2')
891
 
        rev_set = [b'revid2', b'revid1']
892
 
        self.assertParentIds([b'ghost', b'initial'], rev_set)
 
999
        self.builder.build_snapshot('revid1', None, [])
 
1000
        self.builder.build_snapshot('revid2', ['revid1', 'ghost'], [])
 
1001
        rev_set = ['revid2', 'revid1']
 
1002
        self.assertParentIds(['ghost', 'initial'], rev_set)
893
1003
 
894
1004
    def test_righthand_parent(self):
895
 
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
896
 
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2a')
897
 
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2b')
898
 
        self.builder.build_snapshot([b'revid2a', b'revid2b'], [],
899
 
                                    revision_id=b'revid3')
900
 
        rev_set = [b'revid3', b'revid2a']
901
 
        self.assertParentIds([b'revid1', b'revid2b'], rev_set)
 
1005
        self.builder.build_snapshot('revid1', None, [])
 
1006
        self.builder.build_snapshot('revid2a', ['revid1'], [])
 
1007
        self.builder.build_snapshot('revid2b', ['revid1'], [])
 
1008
        self.builder.build_snapshot('revid3', ['revid2a', 'revid2b'], [])
 
1009
        rev_set = ['revid3', 'revid2a']
 
1010
        self.assertParentIds(['revid1', 'revid2b'], rev_set)
902
1011
 
903
1012
 
904
1013
class TestWithBrokenRepo(TestCaseWithTransport):
916
1025
            repo.start_write_group()
917
1026
            cleanups.append(repo.commit_write_group)
918
1027
            # make rev1a: A well-formed revision, containing 'file1'
919
 
            inv = inventory.Inventory(revision_id=b'rev1a')
920
 
            inv.root.revision = b'rev1a'
921
 
            self.add_file(repo, inv, 'file1', b'rev1a', [])
922
 
            repo.texts.add_lines((inv.root.file_id, b'rev1a'), [], [])
923
 
            repo.add_inventory(b'rev1a', inv, [])
924
 
            revision = _mod_revision.Revision(
925
 
                b'rev1a',
 
1028
            inv = inventory.Inventory(revision_id='rev1a')
 
1029
            inv.root.revision = 'rev1a'
 
1030
            self.add_file(repo, inv, 'file1', 'rev1a', [])
 
1031
            repo.texts.add_lines((inv.root.file_id, 'rev1a'), [], [])
 
1032
            repo.add_inventory('rev1a', inv, [])
 
1033
            revision = _mod_revision.Revision('rev1a',
926
1034
                committer='jrandom@example.com', timestamp=0,
927
1035
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
928
 
            repo.add_revision(b'rev1a', revision, inv)
 
1036
            repo.add_revision('rev1a',revision, inv)
929
1037
 
930
1038
            # make rev1b, which has no Revision, but has an Inventory, and
931
1039
            # file1
932
 
            inv = inventory.Inventory(revision_id=b'rev1b')
933
 
            inv.root.revision = b'rev1b'
934
 
            self.add_file(repo, inv, 'file1', b'rev1b', [])
935
 
            repo.add_inventory(b'rev1b', inv, [])
 
1040
            inv = inventory.Inventory(revision_id='rev1b')
 
1041
            inv.root.revision = 'rev1b'
 
1042
            self.add_file(repo, inv, 'file1', 'rev1b', [])
 
1043
            repo.add_inventory('rev1b', inv, [])
936
1044
 
937
1045
            # make rev2, with file1 and file2
938
1046
            # file2 is sane
939
1047
            # file1 has 'rev1b' as an ancestor, even though this is not
940
1048
            # mentioned by 'rev1a', making it an unreferenced ancestor
941
1049
            inv = inventory.Inventory()
942
 
            self.add_file(repo, inv, 'file1', b'rev2', [b'rev1a', b'rev1b'])
943
 
            self.add_file(repo, inv, 'file2', b'rev2', [])
944
 
            self.add_revision(repo, b'rev2', inv, [b'rev1a'])
 
1050
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
 
1051
            self.add_file(repo, inv, 'file2', 'rev2', [])
 
1052
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
945
1053
 
946
1054
            # make ghost revision rev1c
947
1055
            inv = inventory.Inventory()
948
 
            self.add_file(repo, inv, 'file2', b'rev1c', [])
 
1056
            self.add_file(repo, inv, 'file2', 'rev1c', [])
949
1057
 
950
1058
            # make rev3 with file2
951
1059
            # file2 refers to 'rev1c', which is a ghost in this repository, so
952
1060
            # file2 cannot have rev1c as its ancestor.
953
1061
            inv = inventory.Inventory()
954
 
            self.add_file(repo, inv, 'file2', b'rev3', [b'rev1c'])
955
 
            self.add_revision(repo, b'rev3', inv, [b'rev1c'])
 
1062
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
 
1063
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
956
1064
            return repo
957
1065
        finally:
958
1066
            for cleanup in reversed(cleanups):
963
1071
        inv.root.revision = revision_id
964
1072
        repo.texts.add_lines((inv.root.file_id, revision_id), [], [])
965
1073
        repo.add_inventory(revision_id, inv, parent_ids)
966
 
        revision = _mod_revision.Revision(
967
 
            revision_id,
 
1074
        revision = _mod_revision.Revision(revision_id,
968
1075
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
969
1076
            timezone=0, message='foo', parent_ids=parent_ids)
970
 
        repo.add_revision(revision_id, revision, inv)
 
1077
        repo.add_revision(revision_id,revision, inv)
971
1078
 
972
1079
    def add_file(self, repo, inv, filename, revision, parents):
973
 
        file_id = filename.encode('utf-8') + b'-id'
974
 
        content = [b'line\n']
975
 
        entry = inventory.InventoryFile(file_id, filename, b'TREE_ROOT')
 
1080
        file_id = filename + '-id'
 
1081
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
976
1082
        entry.revision = revision
977
 
        entry.text_sha1 = osutils.sha_strings(content)
978
1083
        entry.text_size = 0
979
1084
        inv.add(entry)
980
1085
        text_key = (file_id, revision)
981
1086
        parent_keys = [(file_id, parent) for parent in parents]
982
 
        repo.texts.add_lines(text_key, parent_keys, content)
 
1087
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
983
1088
 
984
1089
    def test_insert_from_broken_repo(self):
985
1090
        """Inserting a data stream from a broken repository won't silently
995
1100
            return
996
1101
        empty_repo.lock_read()
997
1102
        self.addCleanup(empty_repo.unlock)
998
 
        text = next(empty_repo.texts.get_record_stream(
999
 
            [(b'file2-id', b'rev3')], 'topological', True))
1000
 
        self.assertEqual(b'line\n', text.get_bytes_as('fulltext'))
 
1103
        text = empty_repo.texts.get_record_stream(
 
1104
            [('file2-id', 'rev3')], 'topological', True).next()
 
1105
        self.assertEqual('line\n', text.get_bytes_as('fulltext'))
1001
1106
 
1002
1107
 
1003
1108
class TestRepositoryPackCollection(TestCaseWithTransport):
1004
1109
 
1005
1110
    def get_format(self):
1006
 
        return controldir.format_registry.make_controldir('pack-0.92')
 
1111
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
1007
1112
 
1008
1113
    def get_packs(self):
1009
1114
        format = self.get_format()
1031
1136
    def test__clear_obsolete_packs(self):
1032
1137
        packs = self.get_packs()
1033
1138
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1034
 
        obsolete_pack_trans.put_bytes('a-pack.pack', b'content\n')
1035
 
        obsolete_pack_trans.put_bytes('a-pack.rix', b'content\n')
1036
 
        obsolete_pack_trans.put_bytes('a-pack.iix', b'content\n')
1037
 
        obsolete_pack_trans.put_bytes('another-pack.pack', b'foo\n')
1038
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', b'foo\n')
 
1139
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
 
1140
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
 
1141
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
 
1142
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
 
1143
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1039
1144
        res = packs._clear_obsolete_packs()
1040
1145
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1041
1146
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1043
1148
    def test__clear_obsolete_packs_preserve(self):
1044
1149
        packs = self.get_packs()
1045
1150
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1046
 
        obsolete_pack_trans.put_bytes('a-pack.pack', b'content\n')
1047
 
        obsolete_pack_trans.put_bytes('a-pack.rix', b'content\n')
1048
 
        obsolete_pack_trans.put_bytes('a-pack.iix', b'content\n')
1049
 
        obsolete_pack_trans.put_bytes('another-pack.pack', b'foo\n')
1050
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', b'foo\n')
1051
 
        res = packs._clear_obsolete_packs(preserve={'a-pack'})
 
1151
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
 
1152
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
 
1153
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
 
1154
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
 
1155
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
 
1156
        res = packs._clear_obsolete_packs(preserve=set(['a-pack']))
1052
1157
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1053
1158
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1054
1159
                         sorted(obsolete_pack_trans.list_dir('.')))
1081
1186
    def test_repr(self):
1082
1187
        packs = self.get_packs()
1083
1188
        self.assertContainsRe(repr(packs),
1084
 
                              'RepositoryPackCollection(.*Repository(.*))')
 
1189
            'RepositoryPackCollection(.*Repository(.*))')
1085
1190
 
1086
1191
    def test__obsolete_packs(self):
1087
1192
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1102
1207
                         sorted(packs._pack_transport.list_dir('.')))
1103
1208
        # names[0] should not be present in the index anymore
1104
1209
        self.assertEqual(names[1:],
1105
 
                         sorted({osutils.splitext(n)[0] for n in
1106
 
                                 packs._index_transport.list_dir('.')}))
1107
 
 
1108
 
    def test__obsolete_packs_missing_directory(self):
1109
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1110
 
        r.control_transport.rmdir('obsolete_packs')
1111
 
        names = packs.names()
1112
 
        pack = packs.get_pack_by_name(names[0])
1113
 
        # Schedule this one for removal
1114
 
        packs._remove_pack_from_memory(pack)
1115
 
        # Now trigger the obsoletion, and ensure that all the remaining files
1116
 
        # are still renamed
1117
 
        packs._obsolete_packs([pack])
1118
 
        self.assertEqual([n + '.pack' for n in names[1:]],
1119
 
                         sorted(packs._pack_transport.list_dir('.')))
1120
 
        # names[0] should not be present in the index anymore
1121
 
        self.assertEqual(names[1:],
1122
 
                         sorted({osutils.splitext(n)[0] for n in
1123
 
                                 packs._index_transport.list_dir('.')}))
 
1210
            sorted(set([osutils.splitext(n)[0] for n in
 
1211
                        packs._index_transport.list_dir('.')])))
1124
1212
 
1125
1213
    def test_pack_distribution_zero(self):
1126
1214
        packs = self.get_packs()
1134
1222
    def test_pack_distribution_one_to_nine(self):
1135
1223
        packs = self.get_packs()
1136
1224
        self.assertEqual([1],
1137
 
                         packs.pack_distribution(1))
 
1225
            packs.pack_distribution(1))
1138
1226
        self.assertEqual([1, 1],
1139
 
                         packs.pack_distribution(2))
 
1227
            packs.pack_distribution(2))
1140
1228
        self.assertEqual([1, 1, 1],
1141
 
                         packs.pack_distribution(3))
 
1229
            packs.pack_distribution(3))
1142
1230
        self.assertEqual([1, 1, 1, 1],
1143
 
                         packs.pack_distribution(4))
 
1231
            packs.pack_distribution(4))
1144
1232
        self.assertEqual([1, 1, 1, 1, 1],
1145
 
                         packs.pack_distribution(5))
 
1233
            packs.pack_distribution(5))
1146
1234
        self.assertEqual([1, 1, 1, 1, 1, 1],
1147
 
                         packs.pack_distribution(6))
 
1235
            packs.pack_distribution(6))
1148
1236
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1149
 
                         packs.pack_distribution(7))
 
1237
            packs.pack_distribution(7))
1150
1238
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1151
 
                         packs.pack_distribution(8))
 
1239
            packs.pack_distribution(8))
1152
1240
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1153
 
                         packs.pack_distribution(9))
 
1241
            packs.pack_distribution(9))
1154
1242
 
1155
1243
    def test_pack_distribution_stable_at_boundaries(self):
1156
1244
        """When there are multi-rev packs the counts are stable."""
1187
1275
    def test_plan_pack_operations_2010_combines_smallest_two(self):
1188
1276
        packs = self.get_packs()
1189
1277
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1190
 
                          (1, "single1")]
 
1278
            (1, "single1")]
1191
1279
        # rev count - 2010 -> 2x1000 + 1x10 (3)
1192
1280
        pack_operations = packs.plan_autopack_combinations(
1193
1281
            existing_packs, [1000, 1000, 10])
1260
1348
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
1261
1349
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
1262
1350
        sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
1263
 
        self.assertEqual(
1264
 
            pack_repo.ExistingPack(
1265
 
                packs._pack_transport, name, rev_index, inv_index, txt_index,
1266
 
                sig_index), pack_1)
 
1351
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
 
1352
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
1267
1353
        # and the same instance should be returned on successive calls.
1268
1354
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1269
1355
 
1281
1367
        self.assertTrue(packs.reload_pack_names())
1282
1368
        self.assertEqual(new_names, packs.names())
1283
1369
        # And the repository can access the new revision
1284
 
        self.assertEqual({rev4: (revs[-1],)}, r.get_parent_map([rev4]))
 
1370
        self.assertEqual({rev4:(revs[-1],)}, r.get_parent_map([rev4]))
1285
1371
        self.assertFalse(packs.reload_pack_names())
1286
1372
 
1287
1373
    def test_reload_pack_names_added_and_removed(self):
1294
1380
        self.assertEqual(names, packs.names())
1295
1381
        self.assertTrue(packs.reload_pack_names())
1296
1382
        self.assertEqual(new_names, packs.names())
1297
 
        self.assertEqual({revs[-1]: (revs[-2],)}, r.get_parent_map([revs[-1]]))
 
1383
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
1298
1384
        self.assertFalse(packs.reload_pack_names())
1299
1385
 
1300
1386
    def test_reload_pack_names_preserves_pending(self):
1304
1390
        # and remove another pack (via _remove_pack_from_memory)
1305
1391
        orig_names = packs.names()
1306
1392
        orig_at_load = packs._packs_at_load
1307
 
        to_remove_name = next(iter(orig_names))
 
1393
        to_remove_name = iter(orig_names).next()
1308
1394
        r.start_write_group()
1309
1395
        self.addCleanup(r.abort_write_group)
1310
1396
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1311
 
            (b'text', b'rev'), (), None, b'content\n')])
 
1397
            ('text', 'rev'), (), None, 'content\n')])
1312
1398
        new_pack = packs._new_pack
1313
1399
        self.assertTrue(new_pack.data_inserted())
1314
1400
        new_pack.finish()
1318
1404
        packs._remove_pack_from_memory(removed_pack)
1319
1405
        names = packs.names()
1320
1406
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1321
 
        new_names = {x[0] for x in new_nodes}
1322
 
        self.assertEqual(names, sorted([x[0] for x in all_nodes]))
 
1407
        new_names = set([x[0][0] for x in new_nodes])
 
1408
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1323
1409
        self.assertEqual(set(names) - set(orig_names), new_names)
1324
 
        self.assertEqual({new_pack.name}, new_names)
 
1410
        self.assertEqual(set([new_pack.name]), new_names)
1325
1411
        self.assertEqual([to_remove_name],
1326
 
                         sorted([x[0] for x in deleted_nodes]))
 
1412
                         sorted([x[0][0] for x in deleted_nodes]))
1327
1413
        packs.reload_pack_names()
1328
1414
        reloaded_names = packs.names()
1329
1415
        self.assertEqual(orig_at_load, packs._packs_at_load)
1330
1416
        self.assertEqual(names, reloaded_names)
1331
1417
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1332
 
        new_names = {x[0] for x in new_nodes}
1333
 
        self.assertEqual(names, sorted([x[0] for x in all_nodes]))
 
1418
        new_names = set([x[0][0] for x in new_nodes])
 
1419
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1334
1420
        self.assertEqual(set(names) - set(orig_names), new_names)
1335
 
        self.assertEqual({new_pack.name}, new_names)
 
1421
        self.assertEqual(set([new_pack.name]), new_names)
1336
1422
        self.assertEqual([to_remove_name],
1337
 
                         sorted([x[0] for x in deleted_nodes]))
 
1423
                         sorted([x[0][0] for x in deleted_nodes]))
1338
1424
 
1339
1425
    def test_autopack_obsoletes_new_pack(self):
1340
1426
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1342
1428
        packs.pack_distribution = lambda x: [10]
1343
1429
        r.start_write_group()
1344
1430
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1345
 
            (b'bogus-rev',), (), None, b'bogus-content\n')])
 
1431
            ('bogus-rev',), (), None, 'bogus-content\n')])
1346
1432
        # This should trigger an autopack, which will combine everything into a
1347
1433
        # single pack file.
1348
 
        r.commit_write_group()
 
1434
        new_names = r.commit_write_group()
1349
1435
        names = packs.names()
1350
1436
        self.assertEqual(1, len(names))
1351
1437
        self.assertEqual([names[0] + '.pack'],
1357
1443
        # full-pack via the other repo which will cause us to re-evaluate and
1358
1444
        # decide we don't need to do anything
1359
1445
        orig_execute = packs._execute_pack_operations
1360
 
 
1361
1446
        def _munged_execute_pack_ops(*args, **kwargs):
1362
1447
            tree.branch.repository.pack()
1363
1448
            return orig_execute(*args, **kwargs)
1379
1464
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1380
1465
        # obsolete_packs will also have stuff like .rix and .iix present.
1381
1466
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1382
 
        obsolete_names = {osutils.splitext(n)[0] for n in obsolete_packs}
 
1467
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1383
1468
        self.assertEqual([pack.name], sorted(obsolete_names))
1384
1469
 
1385
1470
    def test__save_pack_names_already_obsoleted(self):
1397
1482
        # Note that while we set clear_obsolete_packs=True, it should not
1398
1483
        # delete a pack file that we have also scheduled for obsoletion.
1399
1484
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1400
 
        obsolete_names = {osutils.splitext(n)[0] for n in obsolete_packs}
 
1485
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1401
1486
        self.assertEqual([pack.name], sorted(obsolete_names))
1402
1487
 
1403
 
    def test_pack_no_obsolete_packs_directory(self):
1404
 
        """Bug #314314, don't fail if obsolete_packs directory does
1405
 
        not exist."""
1406
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1407
 
        r.control_transport.rmdir('obsolete_packs')
1408
 
        packs._clear_obsolete_packs()
1409
1488
 
1410
1489
 
1411
1490
class TestPack(TestCaseWithTransport):
1476
1555
            index_class=BTreeGraphIndex,
1477
1556
            use_chk_index=False)
1478
1557
        pack = pack_repo.NewPack(collection)
1479
 
        self.addCleanup(pack.abort)  # Make sure the write stream gets closed
 
1558
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
1480
1559
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1481
1560
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1482
1561
        self.assertIsInstance(pack._hash, type(osutils.md5()))
1495
1574
    def test_pack_optimizes_pack_order(self):
1496
1575
        builder = self.make_branch_builder('.', format="1.9")
1497
1576
        builder.start_series()
1498
 
        builder.build_snapshot(None, [
1499
 
            ('add', ('', b'root-id', 'directory', None)),
1500
 
            ('add', ('f', b'f-id', 'file', b'content\n'))],
1501
 
            revision_id=b'A')
1502
 
        builder.build_snapshot([b'A'],
1503
 
                               [('modify', ('f', b'new-content\n'))],
1504
 
                               revision_id=b'B')
1505
 
        builder.build_snapshot([b'B'],
1506
 
                               [('modify', ('f', b'third-content\n'))],
1507
 
                               revision_id=b'C')
1508
 
        builder.build_snapshot([b'C'],
1509
 
                               [('modify', ('f', b'fourth-content\n'))],
1510
 
                               revision_id=b'D')
 
1577
        builder.build_snapshot('A', None, [
 
1578
            ('add', ('', 'root-id', 'directory', None)),
 
1579
            ('add', ('f', 'f-id', 'file', 'content\n'))])
 
1580
        builder.build_snapshot('B', ['A'],
 
1581
            [('modify', ('f-id', 'new-content\n'))])
 
1582
        builder.build_snapshot('C', ['B'],
 
1583
            [('modify', ('f-id', 'third-content\n'))])
 
1584
        builder.build_snapshot('D', ['C'],
 
1585
            [('modify', ('f-id', 'fourth-content\n'))])
1511
1586
        b = builder.get_branch()
1512
1587
        b.lock_read()
1513
1588
        builder.finish_series()
1516
1591
        # Because of how they were built, they correspond to
1517
1592
        # ['D', 'C', 'B', 'A']
1518
1593
        packs = b.repository._pack_collection.packs
1519
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
1520
 
                                          packs, 'testing',
1521
 
                                          revision_ids=[b'B', b'C'])
 
1594
        packer = pack_repo.Packer(b.repository._pack_collection,
 
1595
                                  packs, 'testing',
 
1596
                                  revision_ids=['B', 'C'])
1522
1597
        # Now, when we are copying the B & C revisions, their pack files should
1523
1598
        # be moved to the front of the stack
1524
1599
        # The new ordering moves B & C to the front of the .packs attribute,
1525
1600
        # and leaves the others in the original order.
1526
1601
        new_packs = [packs[1], packs[2], packs[0], packs[3]]
1527
 
        packer.pack()
 
1602
        new_pack = packer.pack()
1528
1603
        self.assertEqual(new_packs, packer.packs)
1529
1604
 
1530
1605
 
1536
1611
        return repo._pack_collection
1537
1612
 
1538
1613
    def test_open_pack_will_optimise(self):
1539
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
1540
 
                                                    [], '.test')
 
1614
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
 
1615
                                            [], '.test')
1541
1616
        new_pack = packer.open_pack()
1542
 
        self.addCleanup(new_pack.abort)  # ensure cleanup
 
1617
        self.addCleanup(new_pack.abort) # ensure cleanup
1543
1618
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1544
1619
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1545
1620
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1547
1622
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1548
1623
 
1549
1624
 
1550
 
class TestGCCHKPacker(TestCaseWithTransport):
1551
 
 
1552
 
    def make_abc_branch(self):
1553
 
        builder = self.make_branch_builder('source')
1554
 
        builder.start_series()
1555
 
        builder.build_snapshot(None, [
1556
 
            ('add', ('', b'root-id', 'directory', None)),
1557
 
            ('add', ('file', b'file-id', 'file', b'content\n')),
1558
 
            ], revision_id=b'A')
1559
 
        builder.build_snapshot([b'A'], [
1560
 
            ('add', ('dir', b'dir-id', 'directory', None))],
1561
 
            revision_id=b'B')
1562
 
        builder.build_snapshot([b'B'], [
1563
 
            ('modify', ('file', b'new content\n'))],
1564
 
            revision_id=b'C')
1565
 
        builder.finish_series()
1566
 
        return builder.get_branch()
1567
 
 
1568
 
    def make_branch_with_disjoint_inventory_and_revision(self):
1569
 
        """a repo with separate packs for a revisions Revision and Inventory.
1570
 
 
1571
 
        There will be one pack file that holds the Revision content, and one
1572
 
        for the Inventory content.
1573
 
 
1574
 
        :return: (repository,
1575
 
                  pack_name_with_rev_A_Revision,
1576
 
                  pack_name_with_rev_A_Inventory,
1577
 
                  pack_name_with_rev_C_content)
1578
 
        """
1579
 
        b_source = self.make_abc_branch()
1580
 
        b_base = b_source.controldir.sprout(
1581
 
            'base', revision_id=b'A').open_branch()
1582
 
        b_stacked = b_base.controldir.sprout(
1583
 
            'stacked', stacked=True).open_branch()
1584
 
        b_stacked.lock_write()
1585
 
        self.addCleanup(b_stacked.unlock)
1586
 
        b_stacked.fetch(b_source, b'B')
1587
 
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1588
 
        # fill in the A rev.
1589
 
        repo_not_stacked = b_stacked.controldir.open_repository()
1590
 
        repo_not_stacked.lock_write()
1591
 
        self.addCleanup(repo_not_stacked.unlock)
1592
 
        # Now we should have a pack file with A's inventory, but not its
1593
 
        # Revision
1594
 
        self.assertEqual([(b'A',), (b'B',)],
1595
 
                         sorted(repo_not_stacked.inventories.keys()))
1596
 
        self.assertEqual([(b'B',)],
1597
 
                         sorted(repo_not_stacked.revisions.keys()))
1598
 
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1599
 
        # We have a couple names here, figure out which has A's inventory
1600
 
        for name in stacked_pack_names:
1601
 
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1602
 
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1603
 
            if (b'A',) in keys:
1604
 
                inv_a_pack_name = name
1605
 
                break
1606
 
        else:
1607
 
            self.fail('Could not find pack containing A\'s inventory')
1608
 
        repo_not_stacked.fetch(b_source.repository, b'A')
1609
 
        self.assertEqual([(b'A',), (b'B',)],
1610
 
                         sorted(repo_not_stacked.revisions.keys()))
1611
 
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1612
 
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1613
 
        self.assertEqual(1, len(rev_a_pack_names))
1614
 
        rev_a_pack_name = list(rev_a_pack_names)[0]
1615
 
        # Now fetch 'C', so we have a couple pack files to join
1616
 
        repo_not_stacked.fetch(b_source.repository, b'C')
1617
 
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1618
 
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1619
 
        self.assertEqual(1, len(rev_c_pack_names))
1620
 
        rev_c_pack_name = list(rev_c_pack_names)[0]
1621
 
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1622
 
                rev_c_pack_name)
1623
 
 
1624
 
    def test_pack_with_distant_inventories(self):
1625
 
        # See https://bugs.launchpad.net/bzr/+bug/437003
1626
 
        # When repacking, it is possible to have an inventory in a different
1627
 
        # pack file than the associated revision. An autopack can then come
1628
 
        # along, and miss that inventory, and complain.
1629
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1630
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1631
 
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1632
 
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1633
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1634
 
                                                [a_pack, c_pack], '.test-pack')
1635
 
        # This would raise ValueError in bug #437003, but should not raise an
1636
 
        # error once fixed.
1637
 
        packer.pack()
1638
 
 
1639
 
    def test_pack_with_missing_inventory(self):
1640
 
        # Similar to test_pack_with_missing_inventory, but this time, we force
1641
 
        # the A inventory to actually be gone from the repository.
1642
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1643
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1644
 
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1645
 
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1646
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1647
 
                                                repo._pack_collection.all_packs(), '.test-pack')
1648
 
        e = self.assertRaises(ValueError, packer.pack)
1649
 
        packer.new_pack.abort()
1650
 
        self.assertContainsRe(str(e),
1651
 
                              r"We are missing inventories for revisions: .*'A'")
1652
 
 
1653
 
 
1654
1625
class TestCrossFormatPacks(TestCaseWithTransport):
1655
1626
 
1656
1627
    def log_pack(self, hint=None):
1671
1642
        self.addCleanup(target.unlock)
1672
1643
        source = source_tree.branch.repository._get_source(target._format)
1673
1644
        self.orig_pack = target.pack
1674
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1645
        target.pack = self.log_pack
1675
1646
        search = target.search_missing_revision_ids(
1676
 
            source_tree.branch.repository, revision_ids=[tip])
 
1647
            source_tree.branch.repository, tip)
1677
1648
        stream = source.get_stream(search)
1678
1649
        from_format = source_tree.branch.repository._format
1679
1650
        sink = target._get_sink()
1689
1660
        source_tree = self.make_branch_and_tree('src', format=src_fmt)
1690
1661
        source_tree.lock_write()
1691
1662
        self.addCleanup(source_tree.unlock)
1692
 
        source_tree.commit('foo')
 
1663
        tip = source_tree.commit('foo')
1693
1664
        target = self.make_repository('target', format=target_fmt)
1694
1665
        target.lock_write()
1695
1666
        self.addCleanup(target.unlock)
1696
1667
        source = source_tree.branch.repository
1697
1668
        self.orig_pack = target.pack
1698
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1669
        target.pack = self.log_pack
1699
1670
        target.fetch(source)
1700
1671
        if expect_pack_called:
1701
1672
            self.assertLength(1, self.calls)
1729
1700
    def test_IDS_format_same_no(self):
1730
1701
        # When the formats are the same, pack is not called.
1731
1702
        self.run_fetch('2a', '2a', False)
1732
 
 
1733
 
 
1734
 
class Test_LazyListJoin(tests.TestCase):
1735
 
 
1736
 
    def test__repr__(self):
1737
 
        lazy = repository._LazyListJoin(['a'], ['b'])
1738
 
        self.assertEqual("breezy.repository._LazyListJoin((['a'], ['b']))",
1739
 
                         repr(lazy))
1740
 
 
1741
 
 
1742
 
class TestFeatures(tests.TestCaseWithTransport):
1743
 
 
1744
 
    def test_open_with_present_feature(self):
1745
 
        self.addCleanup(
1746
 
            bzrrepository.RepositoryFormatMetaDir.unregister_feature,
1747
 
            b"makes-cheese-sandwich")
1748
 
        bzrrepository.RepositoryFormatMetaDir.register_feature(
1749
 
            b"makes-cheese-sandwich")
1750
 
        repo = self.make_repository('.')
1751
 
        repo.lock_write()
1752
 
        repo._format.features[b"makes-cheese-sandwich"] = b"required"
1753
 
        repo._format.check_support_status(False)
1754
 
        repo.unlock()
1755
 
 
1756
 
    def test_open_with_missing_required_feature(self):
1757
 
        repo = self.make_repository('.')
1758
 
        repo.lock_write()
1759
 
        repo._format.features[b"makes-cheese-sandwich"] = b"required"
1760
 
        self.assertRaises(bzrdir.MissingFeature,
1761
 
                          repo._format.check_support_status, False)