/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

Show diffs side-by-side

added added

removed removed

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