/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: 2018-11-16 18:15:40 UTC
  • mto: (7143.16.20 even-more-cleanups)
  • mto: This revision was merged to the branch mainline in revision 7175.
  • Revision ID: jelmer@jelmer.uk-20181116181540-7y2wbhqzjk067mqy
Fix repo acquisition.

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')
 
516
        tree.commit("Dull commit", rev_id=b"dull")
 
517
        revision_tree = tree.branch.repository.revision_tree(b'dull')
653
518
        revision_tree.lock_read()
654
519
        try:
655
520
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
656
 
                revision_tree.inventory.root.file_id)
 
521
                              u'', revision_tree.get_root_id())
657
522
        finally:
658
523
            revision_tree.unlock()
659
524
        format = bzrdir.BzrDirMetaFormat1()
660
525
        format.repository_format = knitrepo.RepositoryFormatKnit3()
661
526
        upgrade.Convert('.', format)
662
527
        tree = workingtree.WorkingTree.open('.')
663
 
        revision_tree = tree.branch.repository.revision_tree('dull')
 
528
        revision_tree = tree.branch.repository.revision_tree(b'dull')
664
529
        revision_tree.lock_read()
665
530
        try:
666
 
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
531
            revision_tree.get_file_lines(u'', revision_tree.get_root_id())
667
532
        finally:
668
533
            revision_tree.unlock()
669
 
        tree.commit("Another dull commit", rev_id='dull2')
670
 
        revision_tree = tree.branch.repository.revision_tree('dull2')
 
534
        tree.commit("Another dull commit", rev_id=b'dull2')
 
535
        revision_tree = tree.branch.repository.revision_tree(b'dull2')
671
536
        revision_tree.lock_read()
672
537
        self.addCleanup(revision_tree.unlock)
673
 
        self.assertEqual('dull', revision_tree.inventory.root.revision)
 
538
        self.assertEqual(
 
539
            b'dull',
 
540
            revision_tree.get_file_revision(u'', revision_tree.get_root_id()))
674
541
 
675
542
    def test_supports_external_lookups(self):
676
543
        format = bzrdir.BzrDirMetaFormat1()
681
548
 
682
549
class Test2a(tests.TestCaseWithMemoryTransport):
683
550
 
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')]
 
551
    def test_chk_bytes_uses_custom_btree_parser(self):
 
552
        mt = self.make_branch_and_memory_tree('test', format='2a')
 
553
        mt.lock_write()
 
554
        self.addCleanup(mt.unlock)
 
555
        mt.add([''], [b'root-id'])
 
556
        mt.commit('first')
 
557
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
 
558
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
559
        # It should also work if we re-open the repo
 
560
        repo = mt.branch.repository.controldir.open_repository()
 
561
        repo.lock_read()
 
562
        self.addCleanup(repo.unlock)
 
563
        index = repo.chk_bytes._index._graph_index._indices[0]
 
564
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
 
565
 
 
566
    def test_fetch_combines_groups(self):
 
567
        builder = self.make_branch_builder('source', format='2a')
 
568
        builder.start_series()
 
569
        builder.build_snapshot(None, [
 
570
            ('add', ('', b'root-id', 'directory', '')),
 
571
            ('add', ('file', b'file-id', 'file', b'content\n'))],
 
572
            revision_id=b'1')
 
573
        builder.build_snapshot([b'1'], [
 
574
            ('modify', ('file', b'content-2\n'))],
 
575
            revision_id=b'2')
 
576
        builder.finish_series()
 
577
        source = builder.get_branch()
 
578
        target = self.make_repository('target', format='2a')
 
579
        target.fetch(source.repository)
 
580
        target.lock_read()
 
581
        self.addCleanup(target.unlock)
 
582
        details = target.texts._index.get_build_details(
 
583
            [(b'file-id', b'1',), (b'file-id', b'2',)])
 
584
        file_1_details = details[(b'file-id', b'1')]
 
585
        file_2_details = details[(b'file-id', b'2')]
 
586
        # The index, and what to read off disk, should be the same for both
 
587
        # versions of the file.
 
588
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
589
 
 
590
    def test_fetch_combines_groups(self):
 
591
        builder = self.make_branch_builder('source', format='2a')
 
592
        builder.start_series()
 
593
        builder.build_snapshot(None, [
 
594
            ('add', ('', b'root-id', 'directory', '')),
 
595
            ('add', ('file', b'file-id', 'file', b'content\n'))],
 
596
            revision_id=b'1')
 
597
        builder.build_snapshot([b'1'], [
 
598
            ('modify', ('file', b'content-2\n'))],
 
599
            revision_id=b'2')
 
600
        builder.finish_series()
 
601
        source = builder.get_branch()
 
602
        target = self.make_repository('target', format='2a')
 
603
        target.fetch(source.repository)
 
604
        target.lock_read()
 
605
        self.addCleanup(target.unlock)
 
606
        details = target.texts._index.get_build_details(
 
607
            [(b'file-id', b'1',), (b'file-id', b'2',)])
 
608
        file_1_details = details[(b'file-id', b'1')]
 
609
        file_2_details = details[(b'file-id', b'2')]
 
610
        # The index, and what to read off disk, should be the same for both
 
611
        # versions of the file.
 
612
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
613
 
 
614
    def test_fetch_combines_groups(self):
 
615
        builder = self.make_branch_builder('source', format='2a')
 
616
        builder.start_series()
 
617
        builder.build_snapshot(None, [
 
618
            ('add', ('', b'root-id', 'directory', '')),
 
619
            ('add', ('file', b'file-id', 'file', b'content\n'))],
 
620
            revision_id=b'1')
 
621
        builder.build_snapshot([b'1'], [
 
622
            ('modify', ('file', b'content-2\n'))],
 
623
            revision_id=b'2')
 
624
        builder.finish_series()
 
625
        source = builder.get_branch()
 
626
        target = self.make_repository('target', format='2a')
 
627
        target.fetch(source.repository)
 
628
        target.lock_read()
 
629
        self.addCleanup(target.unlock)
 
630
        details = target.texts._index.get_build_details(
 
631
            [(b'file-id', b'1',), (b'file-id', b'2',)])
 
632
        file_1_details = details[(b'file-id', b'1')]
 
633
        file_2_details = details[(b'file-id', b'2')]
746
634
        # The index, and what to read off disk, should be the same for both
747
635
        # versions of the file.
748
636
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
754
642
    def test_inventories_use_chk_map_with_parent_base_dict(self):
755
643
        tree = self.make_branch_and_memory_tree('repo', format="2a")
756
644
        tree.lock_write()
757
 
        tree.add([''], ['TREE_ROOT'])
 
645
        tree.add([''], [b'TREE_ROOT'])
758
646
        revid = tree.commit("foo")
759
647
        tree.unlock()
760
648
        tree.lock_read()
764
652
        inv.parent_id_basename_to_file_id._ensure_root()
765
653
        inv.id_to_entry._ensure_root()
766
654
        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)
 
655
        self.assertEqual(
 
656
            65536, inv.parent_id_basename_to_file_id._root_node.maximum_size)
769
657
 
770
658
    def test_autopack_unchanged_chk_nodes(self):
771
659
        # at 20 unchanged commits, chk pages are packed that are split into
775
663
        tree = self.make_branch_and_memory_tree('tree', format='2a')
776
664
        tree.lock_write()
777
665
        self.addCleanup(tree.unlock)
778
 
        tree.add([''], ['TREE_ROOT'])
 
666
        tree.add([''], [b'TREE_ROOT'])
779
667
        for pos in range(20):
780
668
            tree.commit(str(pos))
781
669
 
783
671
        tree = self.make_branch_and_memory_tree('tree', format='2a')
784
672
        tree.lock_write()
785
673
        self.addCleanup(tree.unlock)
786
 
        tree.add([''], ['TREE_ROOT'])
 
674
        tree.add([''], [b'TREE_ROOT'])
787
675
        # 1 commit to leave untouched
788
676
        tree.commit('1')
789
677
        to_keep = tree.branch.repository._pack_collection.names()
812
700
        target = self.make_repository('target', format='rich-root-pack')
813
701
        stream = source._get_source(target._format)
814
702
        # We don't want the child GroupCHKStreamSource
815
 
        self.assertIs(type(stream), repository.StreamSource)
 
703
        self.assertIs(type(stream), vf_repository.StreamSource)
816
704
 
817
705
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
818
706
        source_builder = self.make_branch_builder('source',
819
 
                            format='2a')
 
707
                                                  format='2a')
820
708
        # We have to build a fairly large tree, so that we are sure the chk
821
709
        # pages will have split into multiple pages.
822
 
        entries = [('add', ('', 'a-root-id', 'directory', None))]
 
710
        entries = [('add', ('', b'a-root-id', 'directory', None))]
823
711
        for i in 'abcdefghijklmnopqrstuvwxyz123456789':
824
712
            for j in 'abcdefghijklmnopqrstuvwxyz123456789':
825
713
                fname = i + j
826
 
                fid = fname + '-id'
827
 
                content = 'content for %s\n' % (fname,)
 
714
                fid = fname.encode('utf-8') + b'-id'
 
715
                content = b'content for %s\n' % (fname.encode('utf-8'),)
828
716
                entries.append(('add', (fname, fid, 'file', content)))
829
717
        source_builder.start_series()
830
 
        source_builder.build_snapshot('rev-1', None, entries)
 
718
        source_builder.build_snapshot(None, entries, revision_id=b'rev-1')
831
719
        # Now change a few of them, so we get a few new pages for the second
832
720
        # 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
 
            ])
 
721
        source_builder.build_snapshot([b'rev-1'], [
 
722
            ('modify', ('aa', b'new content for aa-id\n')),
 
723
            ('modify', ('cc', b'new content for cc-id\n')),
 
724
            ('modify', ('zz', b'new content for zz-id\n')),
 
725
            ], revision_id=b'rev-2')
838
726
        source_builder.finish_series()
839
727
        source_branch = source_builder.get_branch()
840
728
        source_branch.lock_read()
845
733
 
846
734
        # On a regular pass, getting the inventories and chk pages for rev-2
847
735
        # 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 = []
 
736
        search = vf_search.SearchResult({b'rev-2'}, {b'rev-1'}, 1,
 
737
                                        {b'rev-2'})
 
738
        simple_chk_records = set()
851
739
        for vf_name, substream in source.get_stream(search):
852
740
            if vf_name == 'chk_bytes':
853
741
                for record in substream:
854
 
                    simple_chk_records.append(record.key)
 
742
                    simple_chk_records.add(record.key)
855
743
            else:
856
744
                for _ in substream:
857
745
                    continue
858
746
        # 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)
 
747
        self.assertEqual({(b'sha1:91481f539e802c76542ea5e4c83ad416bf219f73',),
 
748
                          (b'sha1:4ff91971043668583985aec83f4f0ab10a907d3f',),
 
749
                          (b'sha1:81e7324507c5ca132eedaf2d8414ee4bb2226187',),
 
750
                          (b'sha1:b101b7da280596c71a4540e9a1eeba8045985ee0',)},
 
751
                         set(simple_chk_records))
864
752
        # Now, when we do a similar call using 'get_stream_for_missing_keys'
865
753
        # we should get a much larger set of pages.
866
 
        missing = [('inventories', 'rev-2')]
867
 
        full_chk_records = []
 
754
        missing = [('inventories', b'rev-2')]
 
755
        full_chk_records = set()
868
756
        for vf_name, substream in source.get_stream_for_missing_keys(missing):
869
757
            if vf_name == 'inventories':
870
758
                for record in substream:
871
 
                    self.assertEqual(('rev-2',), record.key)
 
759
                    self.assertEqual((b'rev-2',), record.key)
872
760
            elif vf_name == 'chk_bytes':
873
761
                for record in substream:
874
 
                    full_chk_records.append(record.key)
 
762
                    full_chk_records.add(record.key)
875
763
            else:
876
764
                self.fail('Should not be getting a stream of %s' % (vf_name,))
877
765
        # We have 257 records now. This is because we have 1 root page, and 256
894
782
        source = self.make_repository('source', format='pack-0.92')
895
783
        target = self.make_repository('target', format='pack-0.92')
896
784
        stream_source = source._get_source(target._format)
897
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
785
        self.assertIsInstance(
 
786
            stream_source, knitpack_repo.KnitPackStreamSource)
898
787
 
899
788
    def test_source_to_exact_pack_rich_root_pack(self):
900
789
        source = self.make_repository('source', format='rich-root-pack')
901
790
        target = self.make_repository('target', format='rich-root-pack')
902
791
        stream_source = source._get_source(target._format)
903
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
792
        self.assertIsInstance(
 
793
            stream_source, knitpack_repo.KnitPackStreamSource)
904
794
 
905
795
    def test_source_to_exact_pack_19(self):
906
796
        source = self.make_repository('source', format='1.9')
907
797
        target = self.make_repository('target', format='1.9')
908
798
        stream_source = source._get_source(target._format)
909
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
799
        self.assertIsInstance(
 
800
            stream_source, knitpack_repo.KnitPackStreamSource)
910
801
 
911
802
    def test_source_to_exact_pack_19_rich_root(self):
912
803
        source = self.make_repository('source', format='1.9-rich-root')
913
804
        target = self.make_repository('target', format='1.9-rich-root')
914
805
        stream_source = source._get_source(target._format)
915
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
806
        self.assertIsInstance(
 
807
            stream_source, knitpack_repo.KnitPackStreamSource)
916
808
 
917
809
    def test_source_to_remote_exact_pack_19(self):
918
810
        trans = self.make_smart_server('target')
921
813
        target = self.make_repository('target', format='1.9')
922
814
        target = repository.Repository.open(trans.base)
923
815
        stream_source = source._get_source(target._format)
924
 
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
 
816
        self.assertIsInstance(
 
817
            stream_source, knitpack_repo.KnitPackStreamSource)
925
818
 
926
819
    def test_stream_source_to_non_exact(self):
927
820
        source = self.make_repository('source', format='pack-0.92')
928
821
        target = self.make_repository('target', format='1.9')
929
822
        stream = source._get_source(target._format)
930
 
        self.assertIs(type(stream), repository.StreamSource)
 
823
        self.assertIs(type(stream), vf_repository.StreamSource)
931
824
 
932
825
    def test_stream_source_to_non_exact_rich_root(self):
933
826
        source = self.make_repository('source', format='1.9')
934
827
        target = self.make_repository('target', format='1.9-rich-root')
935
828
        stream = source._get_source(target._format)
936
 
        self.assertIs(type(stream), repository.StreamSource)
 
829
        self.assertIs(type(stream), vf_repository.StreamSource)
937
830
 
938
831
    def test_source_to_remote_non_exact_pack_19(self):
939
832
        trans = self.make_smart_server('target')
942
835
        target = self.make_repository('target', format='1.6')
943
836
        target = repository.Repository.open(trans.base)
944
837
        stream_source = source._get_source(target._format)
945
 
        self.assertIs(type(stream_source), repository.StreamSource)
 
838
        self.assertIs(type(stream_source), vf_repository.StreamSource)
946
839
 
947
840
    def test_stream_source_to_knit(self):
948
841
        source = self.make_repository('source', format='pack-0.92')
949
842
        target = self.make_repository('target', format='dirstate')
950
843
        stream = source._get_source(target._format)
951
 
        self.assertIs(type(stream), repository.StreamSource)
 
844
        self.assertIs(type(stream), vf_repository.StreamSource)
952
845
 
953
846
 
954
847
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
956
849
 
957
850
    def setUp(self):
958
851
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
959
 
        self.builder = self.make_branch_builder('source',
960
 
            format='development6-rich-root')
 
852
        self.builder = self.make_branch_builder('source')
961
853
        self.builder.start_series()
962
 
        self.builder.build_snapshot('initial', None,
963
 
            [('add', ('', 'tree-root', 'directory', None))])
 
854
        self.builder.build_snapshot(
 
855
            None,
 
856
            [('add', ('', b'tree-root', 'directory', None))],
 
857
            revision_id=b'initial')
964
858
        self.repo = self.builder.get_branch().repository
965
859
        self.addCleanup(self.builder.finish_series)
966
860
 
967
861
    def assertParentIds(self, expected_result, rev_set):
968
 
        self.assertEqual(sorted(expected_result),
 
862
        self.assertEqual(
 
863
            sorted(expected_result),
969
864
            sorted(self.repo._find_parent_ids_of_revisions(rev_set)))
970
865
 
971
866
    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)
 
867
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
868
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2')
 
869
        rev_set = [b'revid2']
 
870
        self.assertParentIds([b'revid1'], rev_set)
976
871
 
977
872
    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)
 
873
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
874
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2')
 
875
        self.builder.build_snapshot([b'revid2'], [], revision_id=b'revid3')
 
876
        rev_set = [b'revid3', b'revid2']
 
877
        self.assertParentIds([b'revid1'], rev_set)
983
878
 
984
879
    def test_not_null(self):
985
 
        rev_set = ['initial']
 
880
        rev_set = [b'initial']
986
881
        self.assertParentIds([], rev_set)
987
882
 
988
883
    def test_not_null_set(self):
989
 
        self.builder.build_snapshot('revid1', None, [])
 
884
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
990
885
        rev_set = [_mod_revision.NULL_REVISION]
991
886
        self.assertParentIds([], rev_set)
992
887
 
993
888
    def test_ghost(self):
994
 
        self.builder.build_snapshot('revid1', None, [])
995
 
        rev_set = ['ghost', 'revid1']
996
 
        self.assertParentIds(['initial'], rev_set)
 
889
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
890
        rev_set = [b'ghost', b'revid1']
 
891
        self.assertParentIds([b'initial'], rev_set)
997
892
 
998
893
    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)
 
894
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
895
        self.builder.build_snapshot(
 
896
            [b'revid1', b'ghost'], [], revision_id=b'revid2')
 
897
        rev_set = [b'revid2', b'revid1']
 
898
        self.assertParentIds([b'ghost', b'initial'], rev_set)
1003
899
 
1004
900
    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)
 
901
        self.builder.build_snapshot(None, [], revision_id=b'revid1')
 
902
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2a')
 
903
        self.builder.build_snapshot([b'revid1'], [], revision_id=b'revid2b')
 
904
        self.builder.build_snapshot([b'revid2a', b'revid2b'], [],
 
905
                                    revision_id=b'revid3')
 
906
        rev_set = [b'revid3', b'revid2a']
 
907
        self.assertParentIds([b'revid1', b'revid2b'], rev_set)
1011
908
 
1012
909
 
1013
910
class TestWithBrokenRepo(TestCaseWithTransport):
1025
922
            repo.start_write_group()
1026
923
            cleanups.append(repo.commit_write_group)
1027
924
            # 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',
 
925
            inv = inventory.Inventory(revision_id=b'rev1a')
 
926
            inv.root.revision = b'rev1a'
 
927
            self.add_file(repo, inv, 'file1', b'rev1a', [])
 
928
            repo.texts.add_lines((inv.root.file_id, b'rev1a'), [], [])
 
929
            repo.add_inventory(b'rev1a', inv, [])
 
930
            revision = _mod_revision.Revision(
 
931
                b'rev1a',
1034
932
                committer='jrandom@example.com', timestamp=0,
1035
933
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
1036
 
            repo.add_revision('rev1a',revision, inv)
 
934
            repo.add_revision(b'rev1a', revision, inv)
1037
935
 
1038
936
            # make rev1b, which has no Revision, but has an Inventory, and
1039
937
            # 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, [])
 
938
            inv = inventory.Inventory(revision_id=b'rev1b')
 
939
            inv.root.revision = b'rev1b'
 
940
            self.add_file(repo, inv, 'file1', b'rev1b', [])
 
941
            repo.add_inventory(b'rev1b', inv, [])
1044
942
 
1045
943
            # make rev2, with file1 and file2
1046
944
            # file2 is sane
1047
945
            # file1 has 'rev1b' as an ancestor, even though this is not
1048
946
            # mentioned by 'rev1a', making it an unreferenced ancestor
1049
947
            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'])
 
948
            self.add_file(repo, inv, 'file1', b'rev2', [b'rev1a', b'rev1b'])
 
949
            self.add_file(repo, inv, 'file2', b'rev2', [])
 
950
            self.add_revision(repo, b'rev2', inv, [b'rev1a'])
1053
951
 
1054
952
            # make ghost revision rev1c
1055
953
            inv = inventory.Inventory()
1056
 
            self.add_file(repo, inv, 'file2', 'rev1c', [])
 
954
            self.add_file(repo, inv, 'file2', b'rev1c', [])
1057
955
 
1058
956
            # make rev3 with file2
1059
957
            # file2 refers to 'rev1c', which is a ghost in this repository, so
1060
958
            # file2 cannot have rev1c as its ancestor.
1061
959
            inv = inventory.Inventory()
1062
 
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
1063
 
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
 
960
            self.add_file(repo, inv, 'file2', b'rev3', [b'rev1c'])
 
961
            self.add_revision(repo, b'rev3', inv, [b'rev1c'])
1064
962
            return repo
1065
963
        finally:
1066
964
            for cleanup in reversed(cleanups):
1071
969
        inv.root.revision = revision_id
1072
970
        repo.texts.add_lines((inv.root.file_id, revision_id), [], [])
1073
971
        repo.add_inventory(revision_id, inv, parent_ids)
1074
 
        revision = _mod_revision.Revision(revision_id,
 
972
        revision = _mod_revision.Revision(
 
973
            revision_id,
1075
974
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
1076
975
            timezone=0, message='foo', parent_ids=parent_ids)
1077
 
        repo.add_revision(revision_id,revision, inv)
 
976
        repo.add_revision(revision_id, revision, inv)
1078
977
 
1079
978
    def add_file(self, repo, inv, filename, revision, parents):
1080
 
        file_id = filename + '-id'
1081
 
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
 
979
        file_id = filename.encode('utf-8') + b'-id'
 
980
        content = [b'line\n']
 
981
        entry = inventory.InventoryFile(file_id, filename, b'TREE_ROOT')
1082
982
        entry.revision = revision
 
983
        entry.text_sha1 = osutils.sha_strings(content)
1083
984
        entry.text_size = 0
1084
985
        inv.add(entry)
1085
986
        text_key = (file_id, revision)
1086
987
        parent_keys = [(file_id, parent) for parent in parents]
1087
 
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
 
988
        repo.texts.add_lines(text_key, parent_keys, content)
1088
989
 
1089
990
    def test_insert_from_broken_repo(self):
1090
991
        """Inserting a data stream from a broken repository won't silently
1100
1001
            return
1101
1002
        empty_repo.lock_read()
1102
1003
        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'))
 
1004
        text = next(empty_repo.texts.get_record_stream(
 
1005
            [(b'file2-id', b'rev3')], 'topological', True))
 
1006
        self.assertEqual(b'line\n', text.get_bytes_as('fulltext'))
1106
1007
 
1107
1008
 
1108
1009
class TestRepositoryPackCollection(TestCaseWithTransport):
1109
1010
 
1110
1011
    def get_format(self):
1111
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
 
1012
        return controldir.format_registry.make_controldir('pack-0.92')
1112
1013
 
1113
1014
    def get_packs(self):
1114
1015
        format = self.get_format()
1136
1037
    def test__clear_obsolete_packs(self):
1137
1038
        packs = self.get_packs()
1138
1039
        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')
 
1040
        obsolete_pack_trans.put_bytes('a-pack.pack', b'content\n')
 
1041
        obsolete_pack_trans.put_bytes('a-pack.rix', b'content\n')
 
1042
        obsolete_pack_trans.put_bytes('a-pack.iix', b'content\n')
 
1043
        obsolete_pack_trans.put_bytes('another-pack.pack', b'foo\n')
 
1044
        obsolete_pack_trans.put_bytes('not-a-pack.rix', b'foo\n')
1144
1045
        res = packs._clear_obsolete_packs()
1145
1046
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1146
1047
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1148
1049
    def test__clear_obsolete_packs_preserve(self):
1149
1050
        packs = self.get_packs()
1150
1051
        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']))
 
1052
        obsolete_pack_trans.put_bytes('a-pack.pack', b'content\n')
 
1053
        obsolete_pack_trans.put_bytes('a-pack.rix', b'content\n')
 
1054
        obsolete_pack_trans.put_bytes('a-pack.iix', b'content\n')
 
1055
        obsolete_pack_trans.put_bytes('another-pack.pack', b'foo\n')
 
1056
        obsolete_pack_trans.put_bytes('not-a-pack.rix', b'foo\n')
 
1057
        res = packs._clear_obsolete_packs(preserve={'a-pack'})
1157
1058
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1158
1059
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1159
1060
                         sorted(obsolete_pack_trans.list_dir('.')))
1186
1087
    def test_repr(self):
1187
1088
        packs = self.get_packs()
1188
1089
        self.assertContainsRe(repr(packs),
1189
 
            'RepositoryPackCollection(.*Repository(.*))')
 
1090
                              'RepositoryPackCollection(.*Repository(.*))')
1190
1091
 
1191
1092
    def test__obsolete_packs(self):
1192
1093
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1207
1108
                         sorted(packs._pack_transport.list_dir('.')))
1208
1109
        # names[0] should not be present in the index anymore
1209
1110
        self.assertEqual(names[1:],
1210
 
            sorted(set([osutils.splitext(n)[0] for n in
1211
 
                        packs._index_transport.list_dir('.')])))
 
1111
                         sorted({osutils.splitext(n)[0] for n in
 
1112
                                 packs._index_transport.list_dir('.')}))
 
1113
 
 
1114
    def test__obsolete_packs_missing_directory(self):
 
1115
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1116
        r.control_transport.rmdir('obsolete_packs')
 
1117
        names = packs.names()
 
1118
        pack = packs.get_pack_by_name(names[0])
 
1119
        # Schedule this one for removal
 
1120
        packs._remove_pack_from_memory(pack)
 
1121
        # Now trigger the obsoletion, and ensure that all the remaining files
 
1122
        # are still renamed
 
1123
        packs._obsolete_packs([pack])
 
1124
        self.assertEqual([n + '.pack' for n in names[1:]],
 
1125
                         sorted(packs._pack_transport.list_dir('.')))
 
1126
        # names[0] should not be present in the index anymore
 
1127
        self.assertEqual(names[1:],
 
1128
                         sorted({osutils.splitext(n)[0] for n in
 
1129
                                 packs._index_transport.list_dir('.')}))
1212
1130
 
1213
1131
    def test_pack_distribution_zero(self):
1214
1132
        packs = self.get_packs()
1222
1140
    def test_pack_distribution_one_to_nine(self):
1223
1141
        packs = self.get_packs()
1224
1142
        self.assertEqual([1],
1225
 
            packs.pack_distribution(1))
 
1143
                         packs.pack_distribution(1))
1226
1144
        self.assertEqual([1, 1],
1227
 
            packs.pack_distribution(2))
 
1145
                         packs.pack_distribution(2))
1228
1146
        self.assertEqual([1, 1, 1],
1229
 
            packs.pack_distribution(3))
 
1147
                         packs.pack_distribution(3))
1230
1148
        self.assertEqual([1, 1, 1, 1],
1231
 
            packs.pack_distribution(4))
 
1149
                         packs.pack_distribution(4))
1232
1150
        self.assertEqual([1, 1, 1, 1, 1],
1233
 
            packs.pack_distribution(5))
 
1151
                         packs.pack_distribution(5))
1234
1152
        self.assertEqual([1, 1, 1, 1, 1, 1],
1235
 
            packs.pack_distribution(6))
 
1153
                         packs.pack_distribution(6))
1236
1154
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1237
 
            packs.pack_distribution(7))
 
1155
                         packs.pack_distribution(7))
1238
1156
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1239
 
            packs.pack_distribution(8))
 
1157
                         packs.pack_distribution(8))
1240
1158
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1241
 
            packs.pack_distribution(9))
 
1159
                         packs.pack_distribution(9))
1242
1160
 
1243
1161
    def test_pack_distribution_stable_at_boundaries(self):
1244
1162
        """When there are multi-rev packs the counts are stable."""
1275
1193
    def test_plan_pack_operations_2010_combines_smallest_two(self):
1276
1194
        packs = self.get_packs()
1277
1195
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1278
 
            (1, "single1")]
 
1196
                          (1, "single1")]
1279
1197
        # rev count - 2010 -> 2x1000 + 1x10 (3)
1280
1198
        pack_operations = packs.plan_autopack_combinations(
1281
1199
            existing_packs, [1000, 1000, 10])
1348
1266
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
1349
1267
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
1350
1268
        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)
 
1269
        self.assertEqual(
 
1270
            pack_repo.ExistingPack(
 
1271
                packs._pack_transport, name, rev_index, inv_index, txt_index,
 
1272
                sig_index), pack_1)
1353
1273
        # and the same instance should be returned on successive calls.
1354
1274
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1355
1275
 
1367
1287
        self.assertTrue(packs.reload_pack_names())
1368
1288
        self.assertEqual(new_names, packs.names())
1369
1289
        # And the repository can access the new revision
1370
 
        self.assertEqual({rev4:(revs[-1],)}, r.get_parent_map([rev4]))
 
1290
        self.assertEqual({rev4: (revs[-1],)}, r.get_parent_map([rev4]))
1371
1291
        self.assertFalse(packs.reload_pack_names())
1372
1292
 
1373
1293
    def test_reload_pack_names_added_and_removed(self):
1380
1300
        self.assertEqual(names, packs.names())
1381
1301
        self.assertTrue(packs.reload_pack_names())
1382
1302
        self.assertEqual(new_names, packs.names())
1383
 
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
 
1303
        self.assertEqual({revs[-1]: (revs[-2],)}, r.get_parent_map([revs[-1]]))
1384
1304
        self.assertFalse(packs.reload_pack_names())
1385
1305
 
1386
1306
    def test_reload_pack_names_preserves_pending(self):
1390
1310
        # and remove another pack (via _remove_pack_from_memory)
1391
1311
        orig_names = packs.names()
1392
1312
        orig_at_load = packs._packs_at_load
1393
 
        to_remove_name = iter(orig_names).next()
 
1313
        to_remove_name = next(iter(orig_names))
1394
1314
        r.start_write_group()
1395
1315
        self.addCleanup(r.abort_write_group)
1396
1316
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1397
 
            ('text', 'rev'), (), None, 'content\n')])
 
1317
            (b'text', b'rev'), (), None, b'content\n')])
1398
1318
        new_pack = packs._new_pack
1399
1319
        self.assertTrue(new_pack.data_inserted())
1400
1320
        new_pack.finish()
1404
1324
        packs._remove_pack_from_memory(removed_pack)
1405
1325
        names = packs.names()
1406
1326
        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]))
 
1327
        new_names = {x[0] for x in new_nodes}
 
1328
        self.assertEqual(names, sorted([x[0] for x in all_nodes]))
1409
1329
        self.assertEqual(set(names) - set(orig_names), new_names)
1410
 
        self.assertEqual(set([new_pack.name]), new_names)
 
1330
        self.assertEqual({new_pack.name}, new_names)
1411
1331
        self.assertEqual([to_remove_name],
1412
 
                         sorted([x[0][0] for x in deleted_nodes]))
 
1332
                         sorted([x[0] for x in deleted_nodes]))
1413
1333
        packs.reload_pack_names()
1414
1334
        reloaded_names = packs.names()
1415
1335
        self.assertEqual(orig_at_load, packs._packs_at_load)
1416
1336
        self.assertEqual(names, reloaded_names)
1417
1337
        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]))
 
1338
        new_names = {x[0] for x in new_nodes}
 
1339
        self.assertEqual(names, sorted([x[0] for x in all_nodes]))
1420
1340
        self.assertEqual(set(names) - set(orig_names), new_names)
1421
 
        self.assertEqual(set([new_pack.name]), new_names)
 
1341
        self.assertEqual({new_pack.name}, new_names)
1422
1342
        self.assertEqual([to_remove_name],
1423
 
                         sorted([x[0][0] for x in deleted_nodes]))
 
1343
                         sorted([x[0] for x in deleted_nodes]))
1424
1344
 
1425
1345
    def test_autopack_obsoletes_new_pack(self):
1426
1346
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1428
1348
        packs.pack_distribution = lambda x: [10]
1429
1349
        r.start_write_group()
1430
1350
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1431
 
            ('bogus-rev',), (), None, 'bogus-content\n')])
 
1351
            (b'bogus-rev',), (), None, b'bogus-content\n')])
1432
1352
        # This should trigger an autopack, which will combine everything into a
1433
1353
        # single pack file.
1434
 
        new_names = r.commit_write_group()
 
1354
        r.commit_write_group()
1435
1355
        names = packs.names()
1436
1356
        self.assertEqual(1, len(names))
1437
1357
        self.assertEqual([names[0] + '.pack'],
1443
1363
        # full-pack via the other repo which will cause us to re-evaluate and
1444
1364
        # decide we don't need to do anything
1445
1365
        orig_execute = packs._execute_pack_operations
 
1366
 
1446
1367
        def _munged_execute_pack_ops(*args, **kwargs):
1447
1368
            tree.branch.repository.pack()
1448
1369
            return orig_execute(*args, **kwargs)
1464
1385
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1465
1386
        # obsolete_packs will also have stuff like .rix and .iix present.
1466
1387
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1467
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
 
1388
        obsolete_names = {osutils.splitext(n)[0] for n in obsolete_packs}
1468
1389
        self.assertEqual([pack.name], sorted(obsolete_names))
1469
1390
 
1470
1391
    def test__save_pack_names_already_obsoleted(self):
1482
1403
        # Note that while we set clear_obsolete_packs=True, it should not
1483
1404
        # delete a pack file that we have also scheduled for obsoletion.
1484
1405
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1485
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
 
1406
        obsolete_names = {osutils.splitext(n)[0] for n in obsolete_packs}
1486
1407
        self.assertEqual([pack.name], sorted(obsolete_names))
1487
1408
 
 
1409
    def test_pack_no_obsolete_packs_directory(self):
 
1410
        """Bug #314314, don't fail if obsolete_packs directory does
 
1411
        not exist."""
 
1412
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
1413
        r.control_transport.rmdir('obsolete_packs')
 
1414
        packs._clear_obsolete_packs()
1488
1415
 
1489
1416
 
1490
1417
class TestPack(TestCaseWithTransport):
1555
1482
            index_class=BTreeGraphIndex,
1556
1483
            use_chk_index=False)
1557
1484
        pack = pack_repo.NewPack(collection)
1558
 
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
 
1485
        self.addCleanup(pack.abort)  # Make sure the write stream gets closed
1559
1486
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1560
1487
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1561
1488
        self.assertIsInstance(pack._hash, type(osutils.md5()))
1574
1501
    def test_pack_optimizes_pack_order(self):
1575
1502
        builder = self.make_branch_builder('.', format="1.9")
1576
1503
        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'))])
 
1504
        builder.build_snapshot(None, [
 
1505
            ('add', ('', b'root-id', 'directory', None)),
 
1506
            ('add', ('f', b'f-id', 'file', b'content\n'))],
 
1507
            revision_id=b'A')
 
1508
        builder.build_snapshot([b'A'],
 
1509
                               [('modify', ('f', b'new-content\n'))],
 
1510
                               revision_id=b'B')
 
1511
        builder.build_snapshot([b'B'],
 
1512
                               [('modify', ('f', b'third-content\n'))],
 
1513
                               revision_id=b'C')
 
1514
        builder.build_snapshot([b'C'],
 
1515
                               [('modify', ('f', b'fourth-content\n'))],
 
1516
                               revision_id=b'D')
1586
1517
        b = builder.get_branch()
1587
1518
        b.lock_read()
1588
1519
        builder.finish_series()
1591
1522
        # Because of how they were built, they correspond to
1592
1523
        # ['D', 'C', 'B', 'A']
1593
1524
        packs = b.repository._pack_collection.packs
1594
 
        packer = pack_repo.Packer(b.repository._pack_collection,
1595
 
                                  packs, 'testing',
1596
 
                                  revision_ids=['B', 'C'])
 
1525
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1526
                                          packs, 'testing',
 
1527
                                          revision_ids=[b'B', b'C'])
1597
1528
        # Now, when we are copying the B & C revisions, their pack files should
1598
1529
        # be moved to the front of the stack
1599
1530
        # The new ordering moves B & C to the front of the .packs attribute,
1600
1531
        # and leaves the others in the original order.
1601
1532
        new_packs = [packs[1], packs[2], packs[0], packs[3]]
1602
 
        new_pack = packer.pack()
 
1533
        packer.pack()
1603
1534
        self.assertEqual(new_packs, packer.packs)
1604
1535
 
1605
1536
 
1611
1542
        return repo._pack_collection
1612
1543
 
1613
1544
    def test_open_pack_will_optimise(self):
1614
 
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1615
 
                                            [], '.test')
 
1545
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1546
                                                    [], '.test')
1616
1547
        new_pack = packer.open_pack()
1617
 
        self.addCleanup(new_pack.abort) # ensure cleanup
 
1548
        self.addCleanup(new_pack.abort)  # ensure cleanup
1618
1549
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1619
1550
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1620
1551
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1622
1553
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1623
1554
 
1624
1555
 
 
1556
class TestGCCHKPacker(TestCaseWithTransport):
 
1557
 
 
1558
    def make_abc_branch(self):
 
1559
        builder = self.make_branch_builder('source')
 
1560
        builder.start_series()
 
1561
        builder.build_snapshot(None, [
 
1562
            ('add', ('', b'root-id', 'directory', None)),
 
1563
            ('add', ('file', b'file-id', 'file', b'content\n')),
 
1564
            ], revision_id=b'A')
 
1565
        builder.build_snapshot([b'A'], [
 
1566
            ('add', ('dir', b'dir-id', 'directory', None))],
 
1567
            revision_id=b'B')
 
1568
        builder.build_snapshot([b'B'], [
 
1569
            ('modify', ('file', b'new content\n'))],
 
1570
            revision_id=b'C')
 
1571
        builder.finish_series()
 
1572
        return builder.get_branch()
 
1573
 
 
1574
    def make_branch_with_disjoint_inventory_and_revision(self):
 
1575
        """a repo with separate packs for a revisions Revision and Inventory.
 
1576
 
 
1577
        There will be one pack file that holds the Revision content, and one
 
1578
        for the Inventory content.
 
1579
 
 
1580
        :return: (repository,
 
1581
                  pack_name_with_rev_A_Revision,
 
1582
                  pack_name_with_rev_A_Inventory,
 
1583
                  pack_name_with_rev_C_content)
 
1584
        """
 
1585
        b_source = self.make_abc_branch()
 
1586
        b_base = b_source.controldir.sprout(
 
1587
            'base', revision_id=b'A').open_branch()
 
1588
        b_stacked = b_base.controldir.sprout(
 
1589
            'stacked', stacked=True).open_branch()
 
1590
        b_stacked.lock_write()
 
1591
        self.addCleanup(b_stacked.unlock)
 
1592
        b_stacked.fetch(b_source, b'B')
 
1593
        # Now re-open the stacked repo directly (no fallbacks) so that we can
 
1594
        # fill in the A rev.
 
1595
        repo_not_stacked = b_stacked.controldir.open_repository()
 
1596
        repo_not_stacked.lock_write()
 
1597
        self.addCleanup(repo_not_stacked.unlock)
 
1598
        # Now we should have a pack file with A's inventory, but not its
 
1599
        # Revision
 
1600
        self.assertEqual([(b'A',), (b'B',)],
 
1601
                         sorted(repo_not_stacked.inventories.keys()))
 
1602
        self.assertEqual([(b'B',)],
 
1603
                         sorted(repo_not_stacked.revisions.keys()))
 
1604
        stacked_pack_names = repo_not_stacked._pack_collection.names()
 
1605
        # We have a couple names here, figure out which has A's inventory
 
1606
        for name in stacked_pack_names:
 
1607
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
 
1608
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
 
1609
            if (b'A',) in keys:
 
1610
                inv_a_pack_name = name
 
1611
                break
 
1612
        else:
 
1613
            self.fail('Could not find pack containing A\'s inventory')
 
1614
        repo_not_stacked.fetch(b_source.repository, b'A')
 
1615
        self.assertEqual([(b'A',), (b'B',)],
 
1616
                         sorted(repo_not_stacked.revisions.keys()))
 
1617
        new_pack_names = set(repo_not_stacked._pack_collection.names())
 
1618
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
 
1619
        self.assertEqual(1, len(rev_a_pack_names))
 
1620
        rev_a_pack_name = list(rev_a_pack_names)[0]
 
1621
        # Now fetch 'C', so we have a couple pack files to join
 
1622
        repo_not_stacked.fetch(b_source.repository, b'C')
 
1623
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
 
1624
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
 
1625
        self.assertEqual(1, len(rev_c_pack_names))
 
1626
        rev_c_pack_name = list(rev_c_pack_names)[0]
 
1627
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
 
1628
                rev_c_pack_name)
 
1629
 
 
1630
    def test_pack_with_distant_inventories(self):
 
1631
        # See https://bugs.launchpad.net/bzr/+bug/437003
 
1632
        # When repacking, it is possible to have an inventory in a different
 
1633
        # pack file than the associated revision. An autopack can then come
 
1634
        # along, and miss that inventory, and complain.
 
1635
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1636
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1637
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
 
1638
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
 
1639
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1640
                                                [a_pack, c_pack], '.test-pack')
 
1641
        # This would raise ValueError in bug #437003, but should not raise an
 
1642
        # error once fixed.
 
1643
        packer.pack()
 
1644
 
 
1645
    def test_pack_with_missing_inventory(self):
 
1646
        # Similar to test_pack_with_missing_inventory, but this time, we force
 
1647
        # the A inventory to actually be gone from the repository.
 
1648
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
 
1649
         ) = self.make_branch_with_disjoint_inventory_and_revision()
 
1650
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
 
1651
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
 
1652
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
 
1653
                                                repo._pack_collection.all_packs(), '.test-pack')
 
1654
        e = self.assertRaises(ValueError, packer.pack)
 
1655
        packer.new_pack.abort()
 
1656
        self.assertContainsRe(str(e),
 
1657
                              r"We are missing inventories for revisions: .*'A'")
 
1658
 
 
1659
 
1625
1660
class TestCrossFormatPacks(TestCaseWithTransport):
1626
1661
 
1627
1662
    def log_pack(self, hint=None):
1642
1677
        self.addCleanup(target.unlock)
1643
1678
        source = source_tree.branch.repository._get_source(target._format)
1644
1679
        self.orig_pack = target.pack
1645
 
        target.pack = self.log_pack
 
1680
        self.overrideAttr(target, "pack", self.log_pack)
1646
1681
        search = target.search_missing_revision_ids(
1647
 
            source_tree.branch.repository, tip)
 
1682
            source_tree.branch.repository, revision_ids=[tip])
1648
1683
        stream = source.get_stream(search)
1649
1684
        from_format = source_tree.branch.repository._format
1650
1685
        sink = target._get_sink()
1660
1695
        source_tree = self.make_branch_and_tree('src', format=src_fmt)
1661
1696
        source_tree.lock_write()
1662
1697
        self.addCleanup(source_tree.unlock)
1663
 
        tip = source_tree.commit('foo')
 
1698
        source_tree.commit('foo')
1664
1699
        target = self.make_repository('target', format=target_fmt)
1665
1700
        target.lock_write()
1666
1701
        self.addCleanup(target.unlock)
1667
1702
        source = source_tree.branch.repository
1668
1703
        self.orig_pack = target.pack
1669
 
        target.pack = self.log_pack
 
1704
        self.overrideAttr(target, "pack", self.log_pack)
1670
1705
        target.fetch(source)
1671
1706
        if expect_pack_called:
1672
1707
            self.assertLength(1, self.calls)
1700
1735
    def test_IDS_format_same_no(self):
1701
1736
        # When the formats are the same, pack is not called.
1702
1737
        self.run_fetch('2a', '2a', False)
 
1738
 
 
1739
 
 
1740
class Test_LazyListJoin(tests.TestCase):
 
1741
 
 
1742
    def test__repr__(self):
 
1743
        lazy = repository._LazyListJoin(['a'], ['b'])
 
1744
        self.assertEqual("breezy.repository._LazyListJoin((['a'], ['b']))",
 
1745
                         repr(lazy))
 
1746
 
 
1747
 
 
1748
class TestFeatures(tests.TestCaseWithTransport):
 
1749
 
 
1750
    def test_open_with_present_feature(self):
 
1751
        self.addCleanup(
 
1752
            bzrrepository.RepositoryFormatMetaDir.unregister_feature,
 
1753
            b"makes-cheese-sandwich")
 
1754
        bzrrepository.RepositoryFormatMetaDir.register_feature(
 
1755
            b"makes-cheese-sandwich")
 
1756
        repo = self.make_repository('.')
 
1757
        repo.lock_write()
 
1758
        repo._format.features[b"makes-cheese-sandwich"] = b"required"
 
1759
        repo._format.check_support_status(False)
 
1760
        repo.unlock()
 
1761
 
 
1762
    def test_open_with_missing_required_feature(self):
 
1763
        repo = self.make_repository('.')
 
1764
        repo.lock_write()
 
1765
        repo._format.features[b"makes-cheese-sandwich"] = b"required"
 
1766
        self.assertRaises(bzrdir.MissingFeature,
 
1767
                          repo._format.check_support_status, False)