/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 breezy/tests/test_repository.py

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

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