/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4634.170.1 by John Arbash Meinel
Fix bug #437003. Autopacking should not fail for an
1
# Copyright (C) 2006-2011 Canonical Ltd
1685.1.63 by Martin Pool
Small Transport fixups
2
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1685.1.63 by Martin Pool
Small Transport fixups
7
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1685.1.63 by Martin Pool
Small Transport fixups
12
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
16
17
"""Tests for the Repository facility that are not interface tests.
18
3689.1.4 by John Arbash Meinel
Doc strings that reference repository_implementations
19
For interface tests see tests/per_repository/*.py.
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
20
21
For concrete class tests see this file, and for storage formats tests
22
also see this file.
23
"""
24
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
25
from stat import S_ISDIR
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
26
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
27
import bzrlib
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
28
from bzrlib.errors import (
29
    UnknownFormatError,
30
    UnsupportedFormatError,
31
    )
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
32
from bzrlib import (
5365.5.20 by John Arbash Meinel
Add some tests that check the leaf factory is correct.
33
    btree_index,
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
34
    graph,
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
35
    symbol_versioning,
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
36
    tests,
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
37
    transport,
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
38
    )
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
39
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
5121.2.2 by Jelmer Vernooij
Remove more unused imports in the tests.
40
from bzrlib.index import GraphIndex
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
41
from bzrlib.repository import RepositoryFormat
2670.3.5 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
42
from bzrlib.tests import (
43
    TestCase,
44
    TestCaseWithTransport,
45
    )
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
46
from bzrlib import (
2535.3.41 by Andrew Bennetts
Add tests for InterRemoteToOther.is_compatible.
47
    bzrdir,
48
    errors,
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
49
    inventory,
2929.3.5 by Vincent Ladeuil
New files, same warnings, same fixes.
50
    osutils,
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
51
    repository,
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
52
    revision as _mod_revision,
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
53
    upgrade,
4634.126.1 by John Arbash Meinel
(jam) Fix bug #507566, concurrent autopacking correctness.
54
    versionedfile,
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
55
    workingtree,
56
    )
3735.42.5 by John Arbash Meinel
Change the tests so we now just use a direct test that _get_source is
57
from bzrlib.repofmt import (
58
    groupcompress_repo,
59
    knitrepo,
5757.2.2 by Jelmer Vernooij
Fix imports.
60
    knitpack_repo,
3735.42.5 by John Arbash Meinel
Change the tests so we now just use a direct test that _get_source is
61
    pack_repo,
62
    )
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
63
64
65
class TestDefaultFormat(TestCase):
66
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
67
    def test_get_set_default_format(self):
2204.5.3 by Aaron Bentley
zap old repository default handling
68
        old_default = bzrdir.format_registry.get('default')
69
        private_default = old_default().repository_format.__class__
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
70
        old_format = repository.format_registry.get_default()
1910.2.33 by Aaron Bentley
Fix default format test
71
        self.assertTrue(isinstance(old_format, private_default))
2204.5.3 by Aaron Bentley
zap old repository default handling
72
        def make_sample_bzrdir():
73
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
74
            my_bzrdir.repository_format = SampleRepositoryFormat()
75
            return my_bzrdir
76
        bzrdir.format_registry.remove('default')
77
        bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
78
        bzrdir.format_registry.set_default('sample')
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
79
        # creating a repository should now create an instrumented dir.
80
        try:
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
81
            # the default branch format is used by the meta dir format
82
            # which is not the default bzrdir format at this point
1685.1.63 by Martin Pool
Small Transport fixups
83
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
84
            result = dir.create_repository()
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
85
            self.assertEqual(result, 'A bzr repository dir')
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
86
        finally:
2204.5.3 by Aaron Bentley
zap old repository default handling
87
            bzrdir.format_registry.remove('default')
2363.5.14 by Aaron Bentley
Prevent repository.get_set_default_format from corrupting inventory
88
            bzrdir.format_registry.remove('sample')
2204.5.3 by Aaron Bentley
zap old repository default handling
89
            bzrdir.format_registry.register('default', old_default, '')
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
90
        self.assertIsInstance(repository.format_registry.get_default(),
2204.5.3 by Aaron Bentley
zap old repository default handling
91
                              old_format.__class__)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
92
93
94
class SampleRepositoryFormat(repository.RepositoryFormat):
95
    """A sample format
96
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
97
    this format is initializable, unsupported to aid in testing the
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
98
    open and open(unsupported=True) routines.
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
99
    """
100
101
    def get_format_string(self):
102
        """See RepositoryFormat.get_format_string()."""
103
        return "Sample .bzr repository format."
104
1534.6.1 by Robert Collins
allow API creation of shared repositories
105
    def initialize(self, a_bzrdir, shared=False):
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
106
        """Initialize a repository in a BzrDir"""
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
107
        t = a_bzrdir.get_repository_transport(self)
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
108
        t.put_bytes('format', self.get_format_string())
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
109
        return 'A bzr repository dir'
110
111
    def is_supported(self):
112
        return False
113
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
114
    def open(self, a_bzrdir, _found=False):
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
115
        return "opened repository."
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
116
117
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
118
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
119
    """A sample format that can not be used in a metadir
120
121
    """
122
123
    def get_format_string(self):
124
        raise NotImplementedError
125
126
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
127
class TestRepositoryFormat(TestCaseWithTransport):
128
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
129
130
    def test_find_format(self):
131
        # is the right format object found for a repository?
132
        # create a branch with a few known format objects.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
133
        # this is not quite the same as
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
134
        self.build_tree(["foo/", "bar/"])
135
        def check_format(format, url):
136
            dir = format._matchingbzrdir.initialize(url)
137
            format.initialize(dir)
5609.9.1 by Martin
Blindly change all users of get_transport to address the function via the transport module
138
            t = transport.get_transport(url)
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
139
            found_format = repository.RepositoryFormat.find_format(dir)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
140
            self.assertIsInstance(found_format, format.__class__)
5582.10.54 by Jelmer Vernooij
Use default format rather than RepositoryFormat7.
141
        check_format(repository.format_registry.get_default(), "bar")
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
142
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
143
    def test_find_format_no_repository(self):
144
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
145
        self.assertRaises(errors.NoRepositoryPresent,
146
                          repository.RepositoryFormat.find_format,
147
                          dir)
148
149
    def test_find_format_unknown_format(self):
150
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
151
        SampleRepositoryFormat().initialize(dir)
152
        self.assertRaises(UnknownFormatError,
153
                          repository.RepositoryFormat.find_format,
154
                          dir)
155
156
    def test_register_unregister_format(self):
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
157
        # Test deprecated format registration functions
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
158
        format = SampleRepositoryFormat()
159
        # make a control dir
160
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
161
        # make a repo
162
        format.initialize(dir)
163
        # register a format for it.
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
164
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
165
            repository.RepositoryFormat.register_format, format)
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
166
        # which repository.Open will refuse (not supported)
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
167
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
168
            self.get_url())
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
169
        # but open(unsupported) will work
170
        self.assertEqual(format.open(dir), "opened repository.")
171
        # unregister the format
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
172
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
173
            repository.RepositoryFormat.unregister_format, format)
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
174
175
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
176
class TestRepositoryFormatRegistry(TestCase):
177
178
    def setUp(self):
179
        super(TestRepositoryFormatRegistry, self).setUp()
180
        self.registry = repository.RepositoryFormatRegistry()
181
182
    def test_register_unregister_format(self):
183
        format = SampleRepositoryFormat()
184
        self.registry.register(format)
185
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
186
        self.registry.remove(format)
187
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
188
5651.3.2 by Jelmer Vernooij
Fix deprecation warnings in test suite.
189
    def test_get_all(self):
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
190
        format = SampleRepositoryFormat()
5651.3.7 by Jelmer Vernooij
Fix tests.
191
        self.assertEquals([], self.registry._get_all())
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
192
        self.registry.register(format)
5651.3.7 by Jelmer Vernooij
Fix tests.
193
        self.assertEquals([format], self.registry._get_all())
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
194
195
    def test_register_extra(self):
196
        format = SampleExtraRepositoryFormat()
5651.3.7 by Jelmer Vernooij
Fix tests.
197
        self.assertEquals([], self.registry._get_all())
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
198
        self.registry.register_extra(format)
5651.3.7 by Jelmer Vernooij
Fix tests.
199
        self.assertEquals([format], self.registry._get_all())
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
200
201
    def test_register_extra_lazy(self):
5651.3.7 by Jelmer Vernooij
Fix tests.
202
        self.assertEquals([], self.registry._get_all())
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
203
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
204
            "SampleExtraRepositoryFormat")
5651.3.7 by Jelmer Vernooij
Fix tests.
205
        formats = self.registry._get_all()
5651.3.5 by Jelmer Vernooij
add tests for 'extra' repository formats.
206
        self.assertEquals(1, len(formats))
207
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
5651.3.1 by Jelmer Vernooij
Add RepositoryFormatRegistry.
208
209
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
210
class TestFormatKnit1(TestCaseWithTransport):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
211
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
212
    def test_attribute__fetch_order(self):
213
        """Knits need topological data insertion."""
214
        repo = self.make_repository('.',
215
                format=bzrdir.format_registry.get('knit')())
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
216
        self.assertEqual('topological', repo._format._fetch_order)
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
217
218
    def test_attribute__fetch_uses_deltas(self):
219
        """Knits reuse deltas."""
220
        repo = self.make_repository('.',
221
                format=bzrdir.format_registry.get('knit')())
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
222
        self.assertEqual(True, repo._format._fetch_uses_deltas)
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
223
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
224
    def test_disk_layout(self):
225
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
226
        repo = knitrepo.RepositoryFormatKnit1().initialize(control)
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
227
        # in case of side effects of locking.
228
        repo.lock_write()
229
        repo.unlock()
230
        # we want:
231
        # format 'Bazaar-NG Knit Repository Format 1'
1553.5.62 by Martin Pool
Add tests that MetaDir repositories use LockDirs
232
        # lock: is a directory
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
233
        # inventory.weave == empty_weave
234
        # empty revision-store directory
235
        # empty weaves directory
236
        t = control.get_repository_transport(None)
237
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
238
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
239
        # XXX: no locks left when unlocked at the moment
240
        # self.assertEqualDiff('', t.get('lock').read())
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
241
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
242
        self.check_knits(t)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
243
        # Check per-file knits.
244
        branch = control.create_branch()
245
        tree = control.create_workingtree()
246
        tree.add(['foo'], ['Nasty-IdC:'], ['file'])
247
        tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
248
        tree.commit('1st post', rev_id='foo')
249
        self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
250
            '\nfoo fulltext 0 81  :')
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
251
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
252
    def assertHasKnit(self, t, knit_name, extra_content=''):
1654.1.3 by Robert Collins
Refactor repository knit tests slightly to remove duplication - add a assertHasKnit method.
253
        """Assert that knit_name exists on t."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
254
        self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
1654.1.3 by Robert Collins
Refactor repository knit tests slightly to remove duplication - add a assertHasKnit method.
255
                             t.get(knit_name + '.kndx').read())
256
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
257
    def check_knits(self, t):
258
        """check knit content for a repository."""
1654.1.3 by Robert Collins
Refactor repository knit tests slightly to remove duplication - add a assertHasKnit method.
259
        self.assertHasKnit(t, 'inventory')
260
        self.assertHasKnit(t, 'revisions')
261
        self.assertHasKnit(t, 'signatures')
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
262
263
    def test_shared_disk_layout(self):
264
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
265
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
266
        # we want:
267
        # format 'Bazaar-NG Knit Repository Format 1'
1553.5.62 by Martin Pool
Add tests that MetaDir repositories use LockDirs
268
        # lock: is a directory
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
269
        # inventory.weave == empty_weave
270
        # empty revision-store directory
271
        # empty weaves directory
272
        # a 'shared-storage' marker file.
273
        t = control.get_repository_transport(None)
274
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
275
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
276
        # XXX: no locks left when unlocked at the moment
277
        # self.assertEqualDiff('', t.get('lock').read())
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
278
        self.assertEqualDiff('', t.get('shared-storage').read())
279
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
280
        self.check_knits(t)
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
281
282
    def test_shared_no_tree_disk_layout(self):
283
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
284
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
285
        repo.set_make_working_trees(False)
286
        # we want:
287
        # format 'Bazaar-NG Knit Repository Format 1'
288
        # lock ''
289
        # inventory.weave == empty_weave
290
        # empty revision-store directory
291
        # empty weaves directory
292
        # a 'shared-storage' marker file.
293
        t = control.get_repository_transport(None)
294
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
295
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
296
        # XXX: no locks left when unlocked at the moment
297
        # self.assertEqualDiff('', t.get('lock').read())
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
298
        self.assertEqualDiff('', t.get('shared-storage').read())
299
        self.assertEqualDiff('', t.get('no-working-trees').read())
300
        repo.set_make_working_trees(True)
301
        self.assertFalse(t.has('no-working-trees'))
302
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
303
        self.check_knits(t)
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
304
2917.2.1 by John Arbash Meinel
Fix bug #152360. The xml5 serializer should be using
305
    def test_deserialise_sets_root_revision(self):
306
        """We must have a inventory.root.revision
307
308
        Old versions of the XML5 serializer did not set the revision_id for
309
        the whole inventory. So we grab the one from the expected text. Which
310
        is valid when the api is not being abused.
311
        """
312
        repo = self.make_repository('.',
313
                format=bzrdir.format_registry.get('knit')())
314
        inv_xml = '<inventory format="5">\n</inventory>\n'
4988.3.3 by Jelmer Vernooij
rename Repository.deserialise_inventory to Repository._deserialise_inventory.
315
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
2917.2.1 by John Arbash Meinel
Fix bug #152360. The xml5 serializer should be using
316
        self.assertEqual('test-rev-id', inv.root.revision)
317
318
    def test_deserialise_uses_global_revision_id(self):
319
        """If it is set, then we re-use the global revision id"""
320
        repo = self.make_repository('.',
321
                format=bzrdir.format_registry.get('knit')())
322
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
323
                   '</inventory>\n')
324
        # Arguably, the deserialise_inventory should detect a mismatch, and
325
        # raise an error, rather than silently using one revision_id over the
326
        # other.
4988.3.3 by Jelmer Vernooij
rename Repository.deserialise_inventory to Repository._deserialise_inventory.
327
        self.assertRaises(AssertionError, repo._deserialise_inventory,
3169.2.2 by Robert Collins
Add a test to Repository.deserialise_inventory that the resulting ivnentory is the one asked for, and update relevant tests. Also tweak the model 1 to 2 regenerate inventories logic to use the revision trees parent marker which is more accurate in some cases.
328
            'test-rev-id', inv_xml)
4988.3.3 by Jelmer Vernooij
rename Repository.deserialise_inventory to Repository._deserialise_inventory.
329
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
2917.2.1 by John Arbash Meinel
Fix bug #152360. The xml5 serializer should be using
330
        self.assertEqual('other-rev-id', inv.root.revision)
331
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
332
    def test_supports_external_lookups(self):
333
        repo = self.make_repository('.',
334
                format=bzrdir.format_registry.get('knit')())
335
        self.assertFalse(repo._format.supports_external_lookups)
336
2535.3.53 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
337
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
338
class DummyRepository(object):
339
    """A dummy repository for testing."""
340
3452.2.11 by Andrew Bennetts
Merge thread.
341
    _format = None
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
342
    _serializer = None
343
344
    def supports_rich_root(self):
4606.4.1 by Robert Collins
Prepare test_repository's inter_repository tests for 2a.
345
        if self._format is not None:
346
            return self._format.rich_root_data
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
347
        return False
348
3709.5.10 by Andrew Bennetts
Fix test failure caused by missing attributes on DummyRepository.
349
    def get_graph(self):
350
        raise NotImplementedError
351
352
    def get_parent_map(self, revision_ids):
353
        raise NotImplementedError
354
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
355
356
class InterDummy(repository.InterRepository):
357
    """An inter-repository optimised code path for DummyRepository.
358
359
    This is for use during testing where we use DummyRepository as repositories
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
360
    so that none of the default regsitered inter-repository classes will
2818.4.2 by Robert Collins
Review feedback.
361
    MATCH.
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
362
    """
363
364
    @staticmethod
365
    def is_compatible(repo_source, repo_target):
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
366
        """InterDummy is compatible with DummyRepository."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
367
        return (isinstance(repo_source, DummyRepository) and
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
368
            isinstance(repo_target, DummyRepository))
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
369
370
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
371
class TestInterRepository(TestCaseWithTransport):
372
373
    def test_get_default_inter_repository(self):
374
        # test that the InterRepository.get(repo_a, repo_b) probes
375
        # for a inter_repo class where is_compatible(repo_a, repo_b) returns
376
        # true and returns a default inter_repo otherwise.
377
        # This also tests that the default registered optimised interrepository
378
        # classes do not barf inappropriately when a surprising repository type
379
        # is handed to them.
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
380
        dummy_a = DummyRepository()
381
        dummy_b = DummyRepository()
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
382
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
383
384
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
385
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
386
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
387
        The effective default is now InterSameDataRepository because there is
388
        no actual sane default in the presence of incompatible data models.
389
        """
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
390
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
391
        self.assertEqual(repository.InterSameDataRepository,
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
392
                         inter_repo.__class__)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
393
        self.assertEqual(repo_a, inter_repo.source)
394
        self.assertEqual(repo_b, inter_repo.target)
395
396
    def test_register_inter_repository_class(self):
397
        # test that a optimised code path provider - a
398
        # InterRepository subclass can be registered and unregistered
399
        # and that it is correctly selected when given a repository
400
        # pair that it returns true on for the is_compatible static method
401
        # check
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
402
        dummy_a = DummyRepository()
4606.4.1 by Robert Collins
Prepare test_repository's inter_repository tests for 2a.
403
        dummy_a._format = RepositoryFormat()
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
404
        dummy_b = DummyRepository()
4606.4.1 by Robert Collins
Prepare test_repository's inter_repository tests for 2a.
405
        dummy_b._format = RepositoryFormat()
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
406
        repo = self.make_repository('.')
407
        # hack dummies to look like repo somewhat.
408
        dummy_a._serializer = repo._serializer
4606.4.1 by Robert Collins
Prepare test_repository's inter_repository tests for 2a.
409
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
410
        dummy_a._format.rich_root_data = repo._format.rich_root_data
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
411
        dummy_b._serializer = repo._serializer
4606.4.1 by Robert Collins
Prepare test_repository's inter_repository tests for 2a.
412
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
413
        dummy_b._format.rich_root_data = repo._format.rich_root_data
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
414
        repository.InterRepository.register_optimiser(InterDummy)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
415
        try:
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
416
            # we should get the default for something InterDummy returns False
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
417
            # to
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
418
            self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
419
            self.assertGetsDefaultInterRepository(dummy_a, repo)
420
            # and we should get an InterDummy for a pair it 'likes'
421
            self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
422
            inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
423
            self.assertEqual(InterDummy, inter_repo.__class__)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
424
            self.assertEqual(dummy_a, inter_repo.source)
425
            self.assertEqual(dummy_b, inter_repo.target)
426
        finally:
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
427
            repository.InterRepository.unregister_optimiser(InterDummy)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
428
        # now we should get the default InterRepository object again.
429
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
1534.1.33 by Robert Collins
Move copy_content_into into InterRepository and InterWeaveRepo, and disable the default codepath test as we have optimised paths for all current combinations.
430
2241.1.17 by Martin Pool
Restore old InterWeave tests
431
5671.4.2 by Jelmer Vernooij
Use stub formats to test CopyConverter.
432
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
433
434
    def get_format_string(self):
435
        return "Test Format 1"
436
437
438
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
439
440
    def get_format_string(self):
441
        return "Test Format 2"
442
443
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
444
class TestRepositoryConverter(TestCaseWithTransport):
445
446
    def test_convert_empty(self):
5671.4.2 by Jelmer Vernooij
Use stub formats to test CopyConverter.
447
        source_format = TestRepositoryFormat1()
448
        target_format = TestRepositoryFormat2()
449
        repository.format_registry.register(source_format)
450
        self.addCleanup(repository.format_registry.remove,
451
            source_format)
452
        repository.format_registry.register(target_format)
453
        self.addCleanup(repository.format_registry.remove,
454
            target_format)
5609.9.4 by Vincent Ladeuil
Use self.get_transport instead of transport.get_transport where possible.
455
        t = self.get_transport()
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
456
        t.mkdir('repository')
457
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
5671.4.2 by Jelmer Vernooij
Use stub formats to test CopyConverter.
458
        repo = TestRepositoryFormat1().initialize(repo_dir)
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
459
        converter = repository.CopyConverter(target_format)
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
460
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
461
        try:
462
            converter.convert(repo, pb)
463
        finally:
464
            pb.finished()
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
465
        repo = repo_dir.open_repository()
466
        self.assertTrue(isinstance(target_format, repo._format.__class__))
1843.2.5 by Aaron Bentley
Add test of _unescape_xml
467
468
2255.2.211 by Robert Collins
Remove knit2 repository format- it has never been supported.
469
class TestRepositoryFormatKnit3(TestCaseWithTransport):
1910.2.13 by Aaron Bentley
Start work on converter
470
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
471
    def test_attribute__fetch_order(self):
472
        """Knits need topological data insertion."""
473
        format = bzrdir.BzrDirMetaFormat1()
474
        format.repository_format = knitrepo.RepositoryFormatKnit3()
475
        repo = self.make_repository('.', format=format)
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
476
        self.assertEqual('topological', repo._format._fetch_order)
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
477
478
    def test_attribute__fetch_uses_deltas(self):
479
        """Knits reuse deltas."""
480
        format = bzrdir.BzrDirMetaFormat1()
481
        format.repository_format = knitrepo.RepositoryFormatKnit3()
482
        repo = self.make_repository('.', format=format)
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
483
        self.assertEqual(True, repo._format._fetch_uses_deltas)
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
484
1910.2.13 by Aaron Bentley
Start work on converter
485
    def test_convert(self):
486
        """Ensure the upgrade adds weaves for roots"""
1910.2.35 by Aaron Bentley
Better fix for convesion test
487
        format = bzrdir.BzrDirMetaFormat1()
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
488
        format.repository_format = knitrepo.RepositoryFormatKnit1()
1910.2.35 by Aaron Bentley
Better fix for convesion test
489
        tree = self.make_branch_and_tree('.', format)
1910.2.13 by Aaron Bentley
Start work on converter
490
        tree.commit("Dull commit", rev_id="dull")
491
        revision_tree = tree.branch.repository.revision_tree('dull')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
492
        revision_tree.lock_read()
493
        try:
494
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
495
                revision_tree.inventory.root.file_id)
496
        finally:
497
            revision_tree.unlock()
1910.2.13 by Aaron Bentley
Start work on converter
498
        format = bzrdir.BzrDirMetaFormat1()
2255.2.211 by Robert Collins
Remove knit2 repository format- it has never been supported.
499
        format.repository_format = knitrepo.RepositoryFormatKnit3()
1910.2.13 by Aaron Bentley
Start work on converter
500
        upgrade.Convert('.', format)
1910.2.27 by Aaron Bentley
Fixed conversion test
501
        tree = workingtree.WorkingTree.open('.')
1910.2.13 by Aaron Bentley
Start work on converter
502
        revision_tree = tree.branch.repository.revision_tree('dull')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
503
        revision_tree.lock_read()
504
        try:
505
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
506
        finally:
507
            revision_tree.unlock()
1910.2.27 by Aaron Bentley
Fixed conversion test
508
        tree.commit("Another dull commit", rev_id='dull2')
509
        revision_tree = tree.branch.repository.revision_tree('dull2')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
510
        revision_tree.lock_read()
511
        self.addCleanup(revision_tree.unlock)
1910.2.27 by Aaron Bentley
Fixed conversion test
512
        self.assertEqual('dull', revision_tree.inventory.root.revision)
2220.2.2 by Martin Pool
Add tag command and basic implementation
513
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
514
    def test_supports_external_lookups(self):
515
        format = bzrdir.BzrDirMetaFormat1()
516
        format.repository_format = knitrepo.RepositoryFormatKnit3()
517
        repo = self.make_repository('.', format=format)
518
        self.assertFalse(repo._format.supports_external_lookups)
519
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
520
4667.1.1 by John Arbash Meinel
Drop the Test2a test times from 5+s down to 1.4s
521
class Test2a(tests.TestCaseWithMemoryTransport):
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
522
5365.5.20 by John Arbash Meinel
Add some tests that check the leaf factory is correct.
523
    def test_chk_bytes_uses_custom_btree_parser(self):
524
        mt = self.make_branch_and_memory_tree('test', format='2a')
525
        mt.lock_write()
526
        self.addCleanup(mt.unlock)
527
        mt.add([''], ['root-id'])
528
        mt.commit('first')
529
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
530
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
531
        # It should also work if we re-open the repo
532
        repo = mt.branch.repository.bzrdir.open_repository()
533
        repo.lock_read()
534
        self.addCleanup(repo.unlock)
535
        index = repo.chk_bytes._index._graph_index._indices[0]
536
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
537
4634.20.1 by Robert Collins
Fix bug 402652 by recompressing all texts that are streamed - slightly slower at fetch, substantially faster and more compact at read.
538
    def test_fetch_combines_groups(self):
539
        builder = self.make_branch_builder('source', format='2a')
540
        builder.start_series()
541
        builder.build_snapshot('1', None, [
542
            ('add', ('', 'root-id', 'directory', '')),
543
            ('add', ('file', 'file-id', 'file', 'content\n'))])
544
        builder.build_snapshot('2', ['1'], [
545
            ('modify', ('file-id', 'content-2\n'))])
546
        builder.finish_series()
547
        source = builder.get_branch()
548
        target = self.make_repository('target', format='2a')
549
        target.fetch(source.repository)
550
        target.lock_read()
4665.3.2 by John Arbash Meinel
An alternative implementation that passes both tests.
551
        self.addCleanup(target.unlock)
4634.20.1 by Robert Collins
Fix bug 402652 by recompressing all texts that are streamed - slightly slower at fetch, substantially faster and more compact at read.
552
        details = target.texts._index.get_build_details(
553
            [('file-id', '1',), ('file-id', '2',)])
554
        file_1_details = details[('file-id', '1')]
555
        file_2_details = details[('file-id', '2')]
556
        # The index, and what to read off disk, should be the same for both
557
        # versions of the file.
558
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
559
4634.23.1 by Robert Collins
Cherrypick from bzr.dev: Fix bug 402652: recompress badly packed groups during fetch. (John Arbash Meinel, Robert Collins)
560
    def test_fetch_combines_groups(self):
561
        builder = self.make_branch_builder('source', format='2a')
562
        builder.start_series()
563
        builder.build_snapshot('1', None, [
564
            ('add', ('', 'root-id', 'directory', '')),
565
            ('add', ('file', 'file-id', 'file', 'content\n'))])
566
        builder.build_snapshot('2', ['1'], [
567
            ('modify', ('file-id', 'content-2\n'))])
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
            [('file-id', '1',), ('file-id', '2',)])
576
        file_1_details = details[('file-id', '1')]
577
        file_2_details = details[('file-id', '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('1', None, [
586
            ('add', ('', 'root-id', 'directory', '')),
587
            ('add', ('file', 'file-id', 'file', 'content\n'))])
588
        builder.build_snapshot('2', ['1'], [
589
            ('modify', ('file-id', 'content-2\n'))])
590
        builder.finish_series()
591
        source = builder.get_branch()
592
        target = self.make_repository('target', format='2a')
593
        target.fetch(source.repository)
594
        target.lock_read()
595
        self.addCleanup(target.unlock)
596
        details = target.texts._index.get_build_details(
597
            [('file-id', '1',), ('file-id', '2',)])
598
        file_1_details = details[('file-id', '1')]
599
        file_2_details = details[('file-id', '2')]
600
        # The index, and what to read off disk, should be the same for both
601
        # versions of the file.
602
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
603
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
604
    def test_format_pack_compresses_True(self):
605
        repo = self.make_repository('repo', format='2a')
606
        self.assertTrue(repo._format.pack_compresses)
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
607
608
    def test_inventories_use_chk_map_with_parent_base_dict(self):
4667.1.1 by John Arbash Meinel
Drop the Test2a test times from 5+s down to 1.4s
609
        tree = self.make_branch_and_memory_tree('repo', format="2a")
610
        tree.lock_write()
611
        tree.add([''], ['TREE_ROOT'])
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
612
        revid = tree.commit("foo")
4667.1.1 by John Arbash Meinel
Drop the Test2a test times from 5+s down to 1.4s
613
        tree.unlock()
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
614
        tree.lock_read()
615
        self.addCleanup(tree.unlock)
616
        inv = tree.branch.repository.get_inventory(revid)
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
617
        self.assertNotEqual(None, inv.parent_id_basename_to_file_id)
618
        inv.parent_id_basename_to_file_id._ensure_root()
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
619
        inv.id_to_entry._ensure_root()
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
620
        self.assertEqual(65536, inv.id_to_entry._root_node.maximum_size)
621
        self.assertEqual(65536,
3735.2.41 by Robert Collins
Make the parent_id_basename index be updated during CHKInventory.apply_delta.
622
            inv.parent_id_basename_to_file_id._root_node.maximum_size)
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
623
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
624
    def test_autopack_unchanged_chk_nodes(self):
625
        # at 20 unchanged commits, chk pages are packed that are split into
626
        # two groups such that the new pack being made doesn't have all its
627
        # pages in the source packs (though they are in the repository).
4667.1.1 by John Arbash Meinel
Drop the Test2a test times from 5+s down to 1.4s
628
        # Use a memory backed repository, we don't need to hit disk for this
629
        tree = self.make_branch_and_memory_tree('tree', format='2a')
630
        tree.lock_write()
631
        self.addCleanup(tree.unlock)
632
        tree.add([''], ['TREE_ROOT'])
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
633
        for pos in range(20):
634
            tree.commit(str(pos))
635
636
    def test_pack_with_hint(self):
4667.1.1 by John Arbash Meinel
Drop the Test2a test times from 5+s down to 1.4s
637
        tree = self.make_branch_and_memory_tree('tree', format='2a')
638
        tree.lock_write()
639
        self.addCleanup(tree.unlock)
640
        tree.add([''], ['TREE_ROOT'])
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
641
        # 1 commit to leave untouched
642
        tree.commit('1')
643
        to_keep = tree.branch.repository._pack_collection.names()
644
        # 2 to combine
645
        tree.commit('2')
646
        tree.commit('3')
647
        all = tree.branch.repository._pack_collection.names()
648
        combine = list(set(all) - set(to_keep))
649
        self.assertLength(3, all)
650
        self.assertLength(2, combine)
651
        tree.branch.repository.pack(hint=combine)
652
        final = tree.branch.repository._pack_collection.names()
653
        self.assertLength(2, final)
654
        self.assertFalse(combine[0] in final)
655
        self.assertFalse(combine[1] in final)
656
        self.assertSubset(to_keep, final)
657
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
658
    def test_stream_source_to_gc(self):
4462.2.1 by Robert Collins
Add new attribute to RepositoryFormat pack_compresses, hinting when pack can be useful.
659
        source = self.make_repository('source', format='2a')
660
        target = self.make_repository('target', format='2a')
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
661
        stream = source._get_source(target._format)
662
        self.assertIsInstance(stream, groupcompress_repo.GroupCHKStreamSource)
663
664
    def test_stream_source_to_non_gc(self):
4462.2.1 by Robert Collins
Add new attribute to RepositoryFormat pack_compresses, hinting when pack can be useful.
665
        source = self.make_repository('source', format='2a')
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
666
        target = self.make_repository('target', format='rich-root-pack')
667
        stream = source._get_source(target._format)
668
        # We don't want the child GroupCHKStreamSource
669
        self.assertIs(type(stream), repository.StreamSource)
670
4360.4.9 by John Arbash Meinel
Merge bzr.dev, bringing in the gc stacking fixes.
671
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
672
        source_builder = self.make_branch_builder('source',
4462.2.1 by Robert Collins
Add new attribute to RepositoryFormat pack_compresses, hinting when pack can be useful.
673
                            format='2a')
4360.4.9 by John Arbash Meinel
Merge bzr.dev, bringing in the gc stacking fixes.
674
        # We have to build a fairly large tree, so that we are sure the chk
675
        # pages will have split into multiple pages.
676
        entries = [('add', ('', 'a-root-id', 'directory', None))]
677
        for i in 'abcdefghijklmnopqrstuvwxyz123456789':
678
            for j in 'abcdefghijklmnopqrstuvwxyz123456789':
679
                fname = i + j
680
                fid = fname + '-id'
681
                content = 'content for %s\n' % (fname,)
682
                entries.append(('add', (fname, fid, 'file', content)))
683
        source_builder.start_series()
684
        source_builder.build_snapshot('rev-1', None, entries)
685
        # Now change a few of them, so we get a few new pages for the second
686
        # revision
687
        source_builder.build_snapshot('rev-2', ['rev-1'], [
688
            ('modify', ('aa-id', 'new content for aa-id\n')),
689
            ('modify', ('cc-id', 'new content for cc-id\n')),
690
            ('modify', ('zz-id', 'new content for zz-id\n')),
691
            ])
692
        source_builder.finish_series()
693
        source_branch = source_builder.get_branch()
694
        source_branch.lock_read()
695
        self.addCleanup(source_branch.unlock)
4462.2.1 by Robert Collins
Add new attribute to RepositoryFormat pack_compresses, hinting when pack can be useful.
696
        target = self.make_repository('target', format='2a')
4360.4.9 by John Arbash Meinel
Merge bzr.dev, bringing in the gc stacking fixes.
697
        source = source_branch.repository._get_source(target._format)
698
        self.assertIsInstance(source, groupcompress_repo.GroupCHKStreamSource)
699
700
        # On a regular pass, getting the inventories and chk pages for rev-2
701
        # would only get the newly created chk pages
702
        search = graph.SearchResult(set(['rev-2']), set(['rev-1']), 1,
703
                                    set(['rev-2']))
704
        simple_chk_records = []
705
        for vf_name, substream in source.get_stream(search):
706
            if vf_name == 'chk_bytes':
707
                for record in substream:
708
                    simple_chk_records.append(record.key)
709
            else:
710
                for _ in substream:
711
                    continue
712
        # 3 pages, the root (InternalNode), + 2 pages which actually changed
713
        self.assertEqual([('sha1:91481f539e802c76542ea5e4c83ad416bf219f73',),
714
                          ('sha1:4ff91971043668583985aec83f4f0ab10a907d3f',),
715
                          ('sha1:81e7324507c5ca132eedaf2d8414ee4bb2226187',),
716
                          ('sha1:b101b7da280596c71a4540e9a1eeba8045985ee0',)],
717
                         simple_chk_records)
718
        # Now, when we do a similar call using 'get_stream_for_missing_keys'
719
        # we should get a much larger set of pages.
720
        missing = [('inventories', 'rev-2')]
721
        full_chk_records = []
722
        for vf_name, substream in source.get_stream_for_missing_keys(missing):
723
            if vf_name == 'inventories':
724
                for record in substream:
725
                    self.assertEqual(('rev-2',), record.key)
726
            elif vf_name == 'chk_bytes':
727
                for record in substream:
728
                    full_chk_records.append(record.key)
729
            else:
730
                self.fail('Should not be getting a stream of %s' % (vf_name,))
731
        # We have 257 records now. This is because we have 1 root page, and 256
732
        # leaf pages in a complete listing.
733
        self.assertEqual(257, len(full_chk_records))
734
        self.assertSubset(simple_chk_records, full_chk_records)
735
4465.2.7 by Aaron Bentley
Move test_inconsistency_fatal to test_repository
736
    def test_inconsistency_fatal(self):
737
        repo = self.make_repository('repo', format='2a')
738
        self.assertTrue(repo.revisions._index._inconsistency_fatal)
739
        self.assertFalse(repo.texts._index._inconsistency_fatal)
740
        self.assertFalse(repo.inventories._index._inconsistency_fatal)
741
        self.assertFalse(repo.signatures._index._inconsistency_fatal)
742
        self.assertFalse(repo.chk_bytes._index._inconsistency_fatal)
743
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
744
745
class TestKnitPackStreamSource(tests.TestCaseWithMemoryTransport):
746
747
    def test_source_to_exact_pack_092(self):
748
        source = self.make_repository('source', format='pack-0.92')
749
        target = self.make_repository('target', format='pack-0.92')
750
        stream_source = source._get_source(target._format)
5757.2.2 by Jelmer Vernooij
Fix imports.
751
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
752
753
    def test_source_to_exact_pack_rich_root_pack(self):
754
        source = self.make_repository('source', format='rich-root-pack')
755
        target = self.make_repository('target', format='rich-root-pack')
756
        stream_source = source._get_source(target._format)
5757.2.2 by Jelmer Vernooij
Fix imports.
757
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
758
759
    def test_source_to_exact_pack_19(self):
760
        source = self.make_repository('source', format='1.9')
761
        target = self.make_repository('target', format='1.9')
762
        stream_source = source._get_source(target._format)
5757.2.2 by Jelmer Vernooij
Fix imports.
763
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
764
765
    def test_source_to_exact_pack_19_rich_root(self):
766
        source = self.make_repository('source', format='1.9-rich-root')
767
        target = self.make_repository('target', format='1.9-rich-root')
768
        stream_source = source._get_source(target._format)
5757.2.2 by Jelmer Vernooij
Fix imports.
769
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
770
771
    def test_source_to_remote_exact_pack_19(self):
772
        trans = self.make_smart_server('target')
773
        trans.ensure_base()
774
        source = self.make_repository('source', format='1.9')
775
        target = self.make_repository('target', format='1.9')
776
        target = repository.Repository.open(trans.base)
777
        stream_source = source._get_source(target._format)
5757.2.2 by Jelmer Vernooij
Fix imports.
778
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
4360.4.3 by John Arbash Meinel
Introduce a KnitPackStreamSource which is used when
779
780
    def test_stream_source_to_non_exact(self):
781
        source = self.make_repository('source', format='pack-0.92')
782
        target = self.make_repository('target', format='1.9')
783
        stream = source._get_source(target._format)
784
        self.assertIs(type(stream), repository.StreamSource)
785
786
    def test_stream_source_to_non_exact_rich_root(self):
787
        source = self.make_repository('source', format='1.9')
788
        target = self.make_repository('target', format='1.9-rich-root')
789
        stream = source._get_source(target._format)
790
        self.assertIs(type(stream), repository.StreamSource)
791
792
    def test_source_to_remote_non_exact_pack_19(self):
793
        trans = self.make_smart_server('target')
794
        trans.ensure_base()
795
        source = self.make_repository('source', format='1.9')
796
        target = self.make_repository('target', format='1.6')
797
        target = repository.Repository.open(trans.base)
798
        stream_source = source._get_source(target._format)
799
        self.assertIs(type(stream_source), repository.StreamSource)
800
801
    def test_stream_source_to_knit(self):
802
        source = self.make_repository('source', format='pack-0.92')
803
        target = self.make_repository('target', format='dirstate')
804
        stream = source._get_source(target._format)
805
        self.assertIs(type(stream), repository.StreamSource)
806
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
807
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
808
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
809
    """Tests for _find_parent_ids_of_revisions."""
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
810
811
    def setUp(self):
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
812
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
5546.1.1 by Andrew Bennetts
Remove RepositoryFormatCHK1 and RepositoryFormatCHK2.
813
        self.builder = self.make_branch_builder('source')
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
814
        self.builder.start_series()
815
        self.builder.build_snapshot('initial', None,
816
            [('add', ('', 'tree-root', 'directory', None))])
817
        self.repo = self.builder.get_branch().repository
818
        self.addCleanup(self.builder.finish_series)
3735.2.99 by John Arbash Meinel
Merge bzr.dev 4034. Whitespace cleanup
819
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
820
    def assertParentIds(self, expected_result, rev_set):
821
        self.assertEqual(sorted(expected_result),
822
            sorted(self.repo._find_parent_ids_of_revisions(rev_set)))
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
823
824
    def test_simple(self):
825
        self.builder.build_snapshot('revid1', None, [])
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
826
        self.builder.build_snapshot('revid2', ['revid1'], [])
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
827
        rev_set = ['revid2']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
828
        self.assertParentIds(['revid1'], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
829
830
    def test_not_first_parent(self):
831
        self.builder.build_snapshot('revid1', None, [])
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
832
        self.builder.build_snapshot('revid2', ['revid1'], [])
833
        self.builder.build_snapshot('revid3', ['revid2'], [])
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
834
        rev_set = ['revid3', 'revid2']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
835
        self.assertParentIds(['revid1'], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
836
837
    def test_not_null(self):
838
        rev_set = ['initial']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
839
        self.assertParentIds([], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
840
841
    def test_not_null_set(self):
842
        self.builder.build_snapshot('revid1', None, [])
843
        rev_set = [_mod_revision.NULL_REVISION]
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
844
        self.assertParentIds([], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
845
846
    def test_ghost(self):
847
        self.builder.build_snapshot('revid1', None, [])
848
        rev_set = ['ghost', 'revid1']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
849
        self.assertParentIds(['initial'], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
850
851
    def test_ghost_parent(self):
852
        self.builder.build_snapshot('revid1', None, [])
853
        self.builder.build_snapshot('revid2', ['revid1', 'ghost'], [])
854
        rev_set = ['revid2', 'revid1']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
855
        self.assertParentIds(['ghost', 'initial'], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
856
857
    def test_righthand_parent(self):
858
        self.builder.build_snapshot('revid1', None, [])
859
        self.builder.build_snapshot('revid2a', ['revid1'], [])
860
        self.builder.build_snapshot('revid2b', ['revid1'], [])
861
        self.builder.build_snapshot('revid3', ['revid2a', 'revid2b'], [])
862
        rev_set = ['revid3', 'revid2a']
4343.3.32 by John Arbash Meinel
Change the tests for _find_revision_outside_set to the new _find_parent_ids function.
863
        self.assertParentIds(['revid1', 'revid2b'], rev_set)
3735.4.1 by Andrew Bennetts
Add _find_revision_outside_set.
864
865
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
866
class TestWithBrokenRepo(TestCaseWithTransport):
2592.3.214 by Robert Collins
Merge bzr.dev.
867
    """These tests seem to be more appropriate as interface tests?"""
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
868
869
    def make_broken_repository(self):
870
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
871
        # parent references" branch which is due to land in bzr.dev soon.  Once
872
        # it does, this duplication should be removed.
873
        repo = self.make_repository('broken-repo')
874
        cleanups = []
875
        try:
876
            repo.lock_write()
877
            cleanups.append(repo.unlock)
878
            repo.start_write_group()
879
            cleanups.append(repo.commit_write_group)
880
            # make rev1a: A well-formed revision, containing 'file1'
881
            inv = inventory.Inventory(revision_id='rev1a')
882
            inv.root.revision = 'rev1a'
883
            self.add_file(repo, inv, 'file1', 'rev1a', [])
4634.35.21 by Andrew Bennetts
Fix test_insert_from_broken_repo in test_repository.
884
            repo.texts.add_lines((inv.root.file_id, 'rev1a'), [], [])
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
885
            repo.add_inventory('rev1a', inv, [])
886
            revision = _mod_revision.Revision('rev1a',
887
                committer='jrandom@example.com', timestamp=0,
888
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
889
            repo.add_revision('rev1a',revision, inv)
890
891
            # make rev1b, which has no Revision, but has an Inventory, and
892
            # file1
893
            inv = inventory.Inventory(revision_id='rev1b')
894
            inv.root.revision = 'rev1b'
895
            self.add_file(repo, inv, 'file1', 'rev1b', [])
896
            repo.add_inventory('rev1b', inv, [])
897
898
            # make rev2, with file1 and file2
899
            # file2 is sane
900
            # file1 has 'rev1b' as an ancestor, even though this is not
901
            # mentioned by 'rev1a', making it an unreferenced ancestor
902
            inv = inventory.Inventory()
903
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
904
            self.add_file(repo, inv, 'file2', 'rev2', [])
905
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
906
907
            # make ghost revision rev1c
908
            inv = inventory.Inventory()
909
            self.add_file(repo, inv, 'file2', 'rev1c', [])
910
911
            # make rev3 with file2
912
            # file2 refers to 'rev1c', which is a ghost in this repository, so
913
            # file2 cannot have rev1c as its ancestor.
914
            inv = inventory.Inventory()
915
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
916
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
917
            return repo
918
        finally:
919
            for cleanup in reversed(cleanups):
920
                cleanup()
921
922
    def add_revision(self, repo, revision_id, inv, parent_ids):
923
        inv.revision_id = revision_id
924
        inv.root.revision = revision_id
4634.35.21 by Andrew Bennetts
Fix test_insert_from_broken_repo in test_repository.
925
        repo.texts.add_lines((inv.root.file_id, revision_id), [], [])
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
926
        repo.add_inventory(revision_id, inv, parent_ids)
927
        revision = _mod_revision.Revision(revision_id,
928
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
929
            timezone=0, message='foo', parent_ids=parent_ids)
930
        repo.add_revision(revision_id,revision, inv)
931
932
    def add_file(self, repo, inv, filename, revision, parents):
933
        file_id = filename + '-id'
934
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
935
        entry.revision = revision
2535.4.10 by Andrew Bennetts
Fix one failing test, disable another.
936
        entry.text_size = 0
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
937
        inv.add(entry)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
938
        text_key = (file_id, revision)
939
        parent_keys = [(file_id, parent) for parent in parents]
940
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
941
942
    def test_insert_from_broken_repo(self):
943
        """Inserting a data stream from a broken repository won't silently
944
        corrupt the target repository.
945
        """
946
        broken_repo = self.make_broken_repository()
947
        empty_repo = self.make_repository('empty-repo')
4606.1.1 by Robert Collins
Change test_insert_from_broken_repo from a known failure to a working test.
948
        try:
949
            empty_repo.fetch(broken_repo)
950
        except (errors.RevisionNotPresent, errors.BzrCheckError):
951
            # Test successful: compression parent not being copied leads to
952
            # error.
953
            return
954
        empty_repo.lock_read()
955
        self.addCleanup(empty_repo.unlock)
956
        text = empty_repo.texts.get_record_stream(
957
            [('file2-id', 'rev3')], 'topological', True).next()
958
        self.assertEqual('line\n', text.get_bytes_as('fulltext'))
2592.3.214 by Robert Collins
Merge bzr.dev.
959
960
2592.3.84 by Robert Collins
Start of autopacking logic.
961
class TestRepositoryPackCollection(TestCaseWithTransport):
962
963
    def get_format(self):
3010.3.3 by Martin Pool
Merge trunk
964
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2592.3.84 by Robert Collins
Start of autopacking logic.
965
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
966
    def get_packs(self):
967
        format = self.get_format()
968
        repo = self.make_repository('.', format=format)
969
        return repo._pack_collection
970
3789.2.20 by John Arbash Meinel
The autopack code can now trigger itself to retry when _copy_revision_texts fails.
971
    def make_packs_and_alt_repo(self, write_lock=False):
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
972
        """Create a pack repo with 3 packs, and access it via a second repo."""
4617.4.1 by Robert Collins
Fix a pack specific test which didn't lock its format down.
973
        tree = self.make_branch_and_tree('.', format=self.get_format())
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
974
        tree.lock_write()
975
        self.addCleanup(tree.unlock)
976
        rev1 = tree.commit('one')
977
        rev2 = tree.commit('two')
978
        rev3 = tree.commit('three')
979
        r = repository.Repository.open('.')
3789.2.20 by John Arbash Meinel
The autopack code can now trigger itself to retry when _copy_revision_texts fails.
980
        if write_lock:
981
            r.lock_write()
982
        else:
983
            r.lock_read()
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
984
        self.addCleanup(r.unlock)
985
        packs = r._pack_collection
986
        packs.ensure_loaded()
987
        return tree, r, packs, [rev1, rev2, rev3]
988
4634.127.1 by John Arbash Meinel
Partial fix for bug #507557.
989
    def test__clear_obsolete_packs(self):
990
        packs = self.get_packs()
991
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
992
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
993
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
994
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
995
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
996
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
997
        res = packs._clear_obsolete_packs()
998
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
999
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1000
1001
    def test__clear_obsolete_packs_preserve(self):
1002
        packs = self.get_packs()
1003
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1004
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1005
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1006
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1007
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1008
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1009
        res = packs._clear_obsolete_packs(preserve=set(['a-pack']))
1010
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1011
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1012
                         sorted(obsolete_pack_trans.list_dir('.')))
1013
2592.3.84 by Robert Collins
Start of autopacking logic.
1014
    def test__max_pack_count(self):
2592.3.219 by Robert Collins
Review feedback.
1015
        """The maximum pack count is a function of the number of revisions."""
2592.3.84 by Robert Collins
Start of autopacking logic.
1016
        # no revisions - one pack, so that we can have a revision free repo
1017
        # without it blowing up
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1018
        packs = self.get_packs()
2592.3.84 by Robert Collins
Start of autopacking logic.
1019
        self.assertEqual(1, packs._max_pack_count(0))
1020
        # after that the sum of the digits, - check the first 1-9
1021
        self.assertEqual(1, packs._max_pack_count(1))
1022
        self.assertEqual(2, packs._max_pack_count(2))
1023
        self.assertEqual(3, packs._max_pack_count(3))
1024
        self.assertEqual(4, packs._max_pack_count(4))
1025
        self.assertEqual(5, packs._max_pack_count(5))
1026
        self.assertEqual(6, packs._max_pack_count(6))
1027
        self.assertEqual(7, packs._max_pack_count(7))
1028
        self.assertEqual(8, packs._max_pack_count(8))
1029
        self.assertEqual(9, packs._max_pack_count(9))
1030
        # check the boundary cases with two digits for the next decade
1031
        self.assertEqual(1, packs._max_pack_count(10))
1032
        self.assertEqual(2, packs._max_pack_count(11))
1033
        self.assertEqual(10, packs._max_pack_count(19))
1034
        self.assertEqual(2, packs._max_pack_count(20))
1035
        self.assertEqual(3, packs._max_pack_count(21))
1036
        # check some arbitrary big numbers
1037
        self.assertEqual(25, packs._max_pack_count(112894))
1038
4928.1.1 by Martin Pool
Give RepositoryPackCollection a repr
1039
    def test_repr(self):
1040
        packs = self.get_packs()
1041
        self.assertContainsRe(repr(packs),
1042
            'RepositoryPackCollection(.*Repository(.*))')
1043
4634.127.2 by John Arbash Meinel
Change the _obsolete_packs code to handle files that are already gone.
1044
    def test__obsolete_packs(self):
1045
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1046
        names = packs.names()
1047
        pack = packs.get_pack_by_name(names[0])
1048
        # Schedule this one for removal
1049
        packs._remove_pack_from_memory(pack)
1050
        # Simulate a concurrent update by renaming the .pack file and one of
1051
        # the indices
1052
        packs.transport.rename('packs/%s.pack' % (names[0],),
1053
                               'obsolete_packs/%s.pack' % (names[0],))
1054
        packs.transport.rename('indices/%s.iix' % (names[0],),
1055
                               'obsolete_packs/%s.iix' % (names[0],))
1056
        # Now trigger the obsoletion, and ensure that all the remaining files
1057
        # are still renamed
1058
        packs._obsolete_packs([pack])
1059
        self.assertEqual([n + '.pack' for n in names[1:]],
1060
                         sorted(packs._pack_transport.list_dir('.')))
1061
        # names[0] should not be present in the index anymore
1062
        self.assertEqual(names[1:],
1063
            sorted(set([osutils.splitext(n)[0] for n in
1064
                        packs._index_transport.list_dir('.')])))
1065
2592.3.84 by Robert Collins
Start of autopacking logic.
1066
    def test_pack_distribution_zero(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1067
        packs = self.get_packs()
2592.3.84 by Robert Collins
Start of autopacking logic.
1068
        self.assertEqual([0], packs.pack_distribution(0))
3052.1.6 by John Arbash Meinel
Change the lock check to raise ObjectNotLocked.
1069
1070
    def test_ensure_loaded_unlocked(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1071
        packs = self.get_packs()
3052.1.6 by John Arbash Meinel
Change the lock check to raise ObjectNotLocked.
1072
        self.assertRaises(errors.ObjectNotLocked,
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1073
                          packs.ensure_loaded)
3052.1.6 by John Arbash Meinel
Change the lock check to raise ObjectNotLocked.
1074
2592.3.84 by Robert Collins
Start of autopacking logic.
1075
    def test_pack_distribution_one_to_nine(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1076
        packs = self.get_packs()
2592.3.84 by Robert Collins
Start of autopacking logic.
1077
        self.assertEqual([1],
1078
            packs.pack_distribution(1))
1079
        self.assertEqual([1, 1],
1080
            packs.pack_distribution(2))
1081
        self.assertEqual([1, 1, 1],
1082
            packs.pack_distribution(3))
1083
        self.assertEqual([1, 1, 1, 1],
1084
            packs.pack_distribution(4))
1085
        self.assertEqual([1, 1, 1, 1, 1],
1086
            packs.pack_distribution(5))
1087
        self.assertEqual([1, 1, 1, 1, 1, 1],
1088
            packs.pack_distribution(6))
1089
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1090
            packs.pack_distribution(7))
1091
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1092
            packs.pack_distribution(8))
1093
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1094
            packs.pack_distribution(9))
1095
1096
    def test_pack_distribution_stable_at_boundaries(self):
1097
        """When there are multi-rev packs the counts are stable."""
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1098
        packs = self.get_packs()
2592.3.84 by Robert Collins
Start of autopacking logic.
1099
        # in 10s:
1100
        self.assertEqual([10], packs.pack_distribution(10))
1101
        self.assertEqual([10, 1], packs.pack_distribution(11))
1102
        self.assertEqual([10, 10], packs.pack_distribution(20))
1103
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1104
        # 100s
1105
        self.assertEqual([100], packs.pack_distribution(100))
1106
        self.assertEqual([100, 1], packs.pack_distribution(101))
1107
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1108
        self.assertEqual([100, 100], packs.pack_distribution(200))
1109
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1110
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1111
2592.3.85 by Robert Collins
Finish autopack corner cases.
1112
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1113
        packs = self.get_packs()
2592.3.85 by Robert Collins
Finish autopack corner cases.
1114
        existing_packs = [(2000, "big"), (9, "medium")]
1115
        # rev count - 2009 -> 2x1000 + 9x1
1116
        pack_operations = packs.plan_autopack_combinations(
1117
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1118
        self.assertEqual([], pack_operations)
1119
1120
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1121
        packs = self.get_packs()
2592.3.85 by Robert Collins
Finish autopack corner cases.
1122
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1123
        # rev count - 2010 -> 2x1000 + 1x10
1124
        pack_operations = packs.plan_autopack_combinations(
1125
            existing_packs, [1000, 1000, 10])
1126
        self.assertEqual([], pack_operations)
1127
1128
    def test_plan_pack_operations_2010_combines_smallest_two(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1129
        packs = self.get_packs()
2592.3.85 by Robert Collins
Finish autopack corner cases.
1130
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1131
            (1, "single1")]
1132
        # rev count - 2010 -> 2x1000 + 1x10 (3)
1133
        pack_operations = packs.plan_autopack_combinations(
1134
            existing_packs, [1000, 1000, 10])
3711.4.2 by John Arbash Meinel
Change the logic to solve it in a different way.
1135
        self.assertEqual([[2, ["single2", "single1"]]], pack_operations)
2592.3.85 by Robert Collins
Finish autopack corner cases.
1136
3711.4.2 by John Arbash Meinel
Change the logic to solve it in a different way.
1137
    def test_plan_pack_operations_creates_a_single_op(self):
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1138
        packs = self.get_packs()
3711.4.2 by John Arbash Meinel
Change the logic to solve it in a different way.
1139
        existing_packs = [(50, 'a'), (40, 'b'), (30, 'c'), (10, 'd'),
1140
                          (10, 'e'), (6, 'f'), (4, 'g')]
1141
        # rev count 150 -> 1x100 and 5x10
1142
        # The two size 10 packs do not need to be touched. The 50, 40, 30 would
1143
        # be combined into a single 120 size pack, and the 6 & 4 would
1144
        # becombined into a size 10 pack. However, if we have to rewrite them,
1145
        # we save a pack file with no increased I/O by putting them into the
1146
        # same file.
1147
        distribution = packs.pack_distribution(150)
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1148
        pack_operations = packs.plan_autopack_combinations(existing_packs,
3711.4.2 by John Arbash Meinel
Change the logic to solve it in a different way.
1149
                                                           distribution)
1150
        self.assertEqual([[130, ['a', 'b', 'c', 'f', 'g']]], pack_operations)
3711.4.1 by John Arbash Meinel
Fix bug #242510, when determining the autopack sequence,
1151
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1152
    def test_all_packs_none(self):
1153
        format = self.get_format()
1154
        tree = self.make_branch_and_tree('.', format=format)
1155
        tree.lock_read()
1156
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1157
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1158
        packs.ensure_loaded()
1159
        self.assertEqual([], packs.all_packs())
1160
1161
    def test_all_packs_one(self):
1162
        format = self.get_format()
1163
        tree = self.make_branch_and_tree('.', format=format)
1164
        tree.commit('start')
1165
        tree.lock_read()
1166
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1167
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1168
        packs.ensure_loaded()
2592.3.176 by Robert Collins
Various pack refactorings.
1169
        self.assertEqual([
1170
            packs.get_pack_by_name(packs.names()[0])],
1171
            packs.all_packs())
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1172
1173
    def test_all_packs_two(self):
1174
        format = self.get_format()
1175
        tree = self.make_branch_and_tree('.', format=format)
1176
        tree.commit('start')
1177
        tree.commit('continue')
1178
        tree.lock_read()
1179
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1180
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1181
        packs.ensure_loaded()
1182
        self.assertEqual([
2592.3.176 by Robert Collins
Various pack refactorings.
1183
            packs.get_pack_by_name(packs.names()[0]),
1184
            packs.get_pack_by_name(packs.names()[1]),
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1185
            ], packs.all_packs())
1186
2592.3.176 by Robert Collins
Various pack refactorings.
1187
    def test_get_pack_by_name(self):
1188
        format = self.get_format()
1189
        tree = self.make_branch_and_tree('.', format=format)
1190
        tree.commit('start')
1191
        tree.lock_read()
1192
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1193
        packs = tree.branch.repository._pack_collection
4145.1.6 by Robert Collins
More test fallout, but all caught now.
1194
        packs.reset()
2592.3.176 by Robert Collins
Various pack refactorings.
1195
        packs.ensure_loaded()
1196
        name = packs.names()[0]
1197
        pack_1 = packs.get_pack_by_name(name)
1198
        # the pack should be correctly initialised
3517.4.5 by Martin Pool
Correct use of packs._names in test_get_pack_by_name
1199
        sizes = packs._names[name]
3221.12.4 by Robert Collins
Implement basic repository supporting external references.
1200
        rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
1201
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
1202
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
1203
        sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
2592.3.191 by Robert Collins
Give Pack responsibility for index naming, and two concrete classes - NewPack for new packs and ExistingPack for packs we read from disk.
1204
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
2592.3.219 by Robert Collins
Review feedback.
1205
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
2592.3.176 by Robert Collins
Various pack refactorings.
1206
        # and the same instance should be returned on successive calls.
1207
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1208
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1209
    def test_reload_pack_names_new_entry(self):
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
1210
        tree, r, packs, revs = self.make_packs_and_alt_repo()
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1211
        names = packs.names()
1212
        # Add a new pack file into the repository
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
1213
        rev4 = tree.commit('four')
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1214
        new_names = tree.branch.repository._pack_collection.names()
1215
        new_name = set(new_names).difference(names)
1216
        self.assertEqual(1, len(new_name))
1217
        new_name = new_name.pop()
1218
        # The old collection hasn't noticed yet
1219
        self.assertEqual(names, packs.names())
3789.1.8 by John Arbash Meinel
Change the api of reload_pack_names().
1220
        self.assertTrue(packs.reload_pack_names())
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1221
        self.assertEqual(new_names, packs.names())
1222
        # And the repository can access the new revision
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
1223
        self.assertEqual({rev4:(revs[-1],)}, r.get_parent_map([rev4]))
3789.1.8 by John Arbash Meinel
Change the api of reload_pack_names().
1224
        self.assertFalse(packs.reload_pack_names())
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1225
1226
    def test_reload_pack_names_added_and_removed(self):
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
1227
        tree, r, packs, revs = self.make_packs_and_alt_repo()
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1228
        names = packs.names()
1229
        # Now repack the whole thing
1230
        tree.branch.repository.pack()
1231
        new_names = tree.branch.repository._pack_collection.names()
1232
        # The other collection hasn't noticed yet
1233
        self.assertEqual(names, packs.names())
3789.1.8 by John Arbash Meinel
Change the api of reload_pack_names().
1234
        self.assertTrue(packs.reload_pack_names())
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1235
        self.assertEqual(new_names, packs.names())
3789.2.19 by John Arbash Meinel
Refactor to make the tests a bit simpler
1236
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
3789.1.8 by John Arbash Meinel
Change the api of reload_pack_names().
1237
        self.assertFalse(packs.reload_pack_names())
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
1238
4634.126.1 by John Arbash Meinel
(jam) Fix bug #507566, concurrent autopacking correctness.
1239
    def test_reload_pack_names_preserves_pending(self):
1240
        # TODO: Update this to also test for pending-deleted names
1241
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1242
        # We will add one pack (via start_write_group + insert_record_stream),
1243
        # and remove another pack (via _remove_pack_from_memory)
1244
        orig_names = packs.names()
1245
        orig_at_load = packs._packs_at_load
1246
        to_remove_name = iter(orig_names).next()
1247
        r.start_write_group()
1248
        self.addCleanup(r.abort_write_group)
1249
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1250
            ('text', 'rev'), (), None, 'content\n')])
1251
        new_pack = packs._new_pack
1252
        self.assertTrue(new_pack.data_inserted())
1253
        new_pack.finish()
1254
        packs.allocate(new_pack)
1255
        packs._new_pack = None
1256
        removed_pack = packs.get_pack_by_name(to_remove_name)
1257
        packs._remove_pack_from_memory(removed_pack)
1258
        names = packs.names()
4634.127.3 by John Arbash Meinel
Add code so we don't try to obsolete files someone else has 'claimed'.
1259
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
4634.126.1 by John Arbash Meinel
(jam) Fix bug #507566, concurrent autopacking correctness.
1260
        new_names = set([x[0][0] for x in new_nodes])
1261
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1262
        self.assertEqual(set(names) - set(orig_names), new_names)
1263
        self.assertEqual(set([new_pack.name]), new_names)
1264
        self.assertEqual([to_remove_name],
1265
                         sorted([x[0][0] for x in deleted_nodes]))
1266
        packs.reload_pack_names()
1267
        reloaded_names = packs.names()
1268
        self.assertEqual(orig_at_load, packs._packs_at_load)
1269
        self.assertEqual(names, reloaded_names)
4634.127.3 by John Arbash Meinel
Add code so we don't try to obsolete files someone else has 'claimed'.
1270
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
4634.126.1 by John Arbash Meinel
(jam) Fix bug #507566, concurrent autopacking correctness.
1271
        new_names = set([x[0][0] for x in new_nodes])
1272
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1273
        self.assertEqual(set(names) - set(orig_names), new_names)
1274
        self.assertEqual(set([new_pack.name]), new_names)
1275
        self.assertEqual([to_remove_name],
1276
                         sorted([x[0][0] for x in deleted_nodes]))
1277
4634.127.5 by John Arbash Meinel
Possible fix for making sure packs triggering autopacking get cleaned up.
1278
    def test_autopack_obsoletes_new_pack(self):
1279
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1280
        packs._max_pack_count = lambda x: 1
1281
        packs.pack_distribution = lambda x: [10]
1282
        r.start_write_group()
1283
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1284
            ('bogus-rev',), (), None, 'bogus-content\n')])
1285
        # This should trigger an autopack, which will combine everything into a
1286
        # single pack file.
1287
        new_names = r.commit_write_group()
1288
        names = packs.names()
1289
        self.assertEqual(1, len(names))
1290
        self.assertEqual([names[0] + '.pack'],
1291
                         packs._pack_transport.list_dir('.'))
1292
3789.2.20 by John Arbash Meinel
The autopack code can now trigger itself to retry when _copy_revision_texts fails.
1293
    def test_autopack_reloads_and_stops(self):
1294
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1295
        # After we have determined what needs to be autopacked, trigger a
1296
        # full-pack via the other repo which will cause us to re-evaluate and
1297
        # decide we don't need to do anything
1298
        orig_execute = packs._execute_pack_operations
1299
        def _munged_execute_pack_ops(*args, **kwargs):
1300
            tree.branch.repository.pack()
1301
            return orig_execute(*args, **kwargs)
1302
        packs._execute_pack_operations = _munged_execute_pack_ops
1303
        packs._max_pack_count = lambda x: 1
1304
        packs.pack_distribution = lambda x: [10]
1305
        self.assertFalse(packs.autopack())
1306
        self.assertEqual(1, len(packs.names()))
1307
        self.assertEqual(tree.branch.repository._pack_collection.names(),
1308
                         packs.names())
1309
4634.127.1 by John Arbash Meinel
Partial fix for bug #507557.
1310
    def test__save_pack_names(self):
1311
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1312
        names = packs.names()
1313
        pack = packs.get_pack_by_name(names[0])
1314
        packs._remove_pack_from_memory(pack)
1315
        packs._save_pack_names(obsolete_packs=[pack])
1316
        cur_packs = packs._pack_transport.list_dir('.')
1317
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1318
        # obsolete_packs will also have stuff like .rix and .iix present.
1319
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1320
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1321
        self.assertEqual([pack.name], sorted(obsolete_names))
1322
1323
    def test__save_pack_names_already_obsoleted(self):
1324
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1325
        names = packs.names()
1326
        pack = packs.get_pack_by_name(names[0])
1327
        packs._remove_pack_from_memory(pack)
1328
        # We are going to simulate a concurrent autopack by manually obsoleting
1329
        # the pack directly.
1330
        packs._obsolete_packs([pack])
1331
        packs._save_pack_names(clear_obsolete_packs=True,
1332
                               obsolete_packs=[pack])
1333
        cur_packs = packs._pack_transport.list_dir('.')
1334
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1335
        # Note that while we set clear_obsolete_packs=True, it should not
1336
        # delete a pack file that we have also scheduled for obsoletion.
1337
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1338
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1339
        self.assertEqual([pack.name], sorted(obsolete_names))
1340
4634.127.3 by John Arbash Meinel
Add code so we don't try to obsolete files someone else has 'claimed'.
1341
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1342
1343
class TestPack(TestCaseWithTransport):
1344
    """Tests for the Pack object."""
1345
1346
    def assertCurrentlyEqual(self, left, right):
1347
        self.assertTrue(left == right)
1348
        self.assertTrue(right == left)
1349
        self.assertFalse(left != right)
1350
        self.assertFalse(right != left)
1351
1352
    def assertCurrentlyNotEqual(self, left, right):
1353
        self.assertFalse(left == right)
1354
        self.assertFalse(right == left)
1355
        self.assertTrue(left != right)
1356
        self.assertTrue(right != left)
1357
1358
    def test___eq____ne__(self):
2592.3.191 by Robert Collins
Give Pack responsibility for index naming, and two concrete classes - NewPack for new packs and ExistingPack for packs we read from disk.
1359
        left = pack_repo.ExistingPack('', '', '', '', '', '')
1360
        right = pack_repo.ExistingPack('', '', '', '', '', '')
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1361
        self.assertCurrentlyEqual(left, right)
1362
        # change all attributes and ensure equality changes as we do.
1363
        left.revision_index = 'a'
1364
        self.assertCurrentlyNotEqual(left, right)
1365
        right.revision_index = 'a'
1366
        self.assertCurrentlyEqual(left, right)
1367
        left.inventory_index = 'a'
1368
        self.assertCurrentlyNotEqual(left, right)
1369
        right.inventory_index = 'a'
1370
        self.assertCurrentlyEqual(left, right)
1371
        left.text_index = 'a'
1372
        self.assertCurrentlyNotEqual(left, right)
1373
        right.text_index = 'a'
1374
        self.assertCurrentlyEqual(left, right)
1375
        left.signature_index = 'a'
1376
        self.assertCurrentlyNotEqual(left, right)
1377
        right.signature_index = 'a'
1378
        self.assertCurrentlyEqual(left, right)
1379
        left.name = 'a'
1380
        self.assertCurrentlyNotEqual(left, right)
1381
        right.name = 'a'
1382
        self.assertCurrentlyEqual(left, right)
1383
        left.transport = 'a'
1384
        self.assertCurrentlyNotEqual(left, right)
1385
        right.transport = 'a'
1386
        self.assertCurrentlyEqual(left, right)
2592.3.179 by Robert Collins
Generate the revision_index_map for packing during the core operation, from the pack objects.
1387
1388
    def test_file_name(self):
2592.3.191 by Robert Collins
Give Pack responsibility for index naming, and two concrete classes - NewPack for new packs and ExistingPack for packs we read from disk.
1389
        pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
2592.3.179 by Robert Collins
Generate the revision_index_map for packing during the core operation, from the pack objects.
1390
        self.assertEqual('a_name.pack', pack.file_name())
2592.3.192 by Robert Collins
Move new revision index management to NewPack.
1391
1392
1393
class TestNewPack(TestCaseWithTransport):
1394
    """Tests for pack_repo.NewPack."""
1395
2592.3.193 by Robert Collins
Move hash tracking of new packs into NewPack.
1396
    def test_new_instance_attributes(self):
2592.3.194 by Robert Collins
Output the revision index from NewPack.finish
1397
        upload_transport = self.get_transport('upload')
1398
        pack_transport = self.get_transport('pack')
1399
        index_transport = self.get_transport('index')
1400
        upload_transport.mkdir('.')
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
1401
        collection = pack_repo.RepositoryPackCollection(
1402
            repo=None,
3830.3.1 by Martin Pool
NewPack should be constructed from the PackCollection, rather than attributes of it
1403
            transport=self.get_transport('.'),
1404
            index_transport=index_transport,
1405
            upload_transport=upload_transport,
1406
            pack_transport=pack_transport,
1407
            index_builder_class=BTreeBuilder,
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
1408
            index_class=BTreeGraphIndex,
1409
            use_chk_index=False)
3830.3.1 by Martin Pool
NewPack should be constructed from the PackCollection, rather than attributes of it
1410
        pack = pack_repo.NewPack(collection)
4857.2.1 by John Arbash Meinel
2 test_repository tests weren't adding cleanups when opening files.
1411
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1412
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1413
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
2929.3.5 by Vincent Ladeuil
New files, same warnings, same fixes.
1414
        self.assertIsInstance(pack._hash, type(osutils.md5()))
2592.3.194 by Robert Collins
Output the revision index from NewPack.finish
1415
        self.assertTrue(pack.upload_transport is upload_transport)
1416
        self.assertTrue(pack.index_transport is index_transport)
1417
        self.assertTrue(pack.pack_transport is pack_transport)
1418
        self.assertEqual(None, pack.index_sizes)
1419
        self.assertEqual(20, len(pack.random_name))
1420
        self.assertIsInstance(pack.random_name, str)
1421
        self.assertIsInstance(pack.start_time, float)
2951.1.2 by Robert Collins
Partial refactoring of pack_repo to create a Packer object for packing.
1422
1423
1424
class TestPacker(TestCaseWithTransport):
1425
    """Tests for the packs repository Packer class."""
2951.1.10 by Robert Collins
Peer review feedback with Ian.
1426
3824.2.4 by John Arbash Meinel
Add a test that ensures the pack ordering changes as part of calling .pack()
1427
    def test_pack_optimizes_pack_order(self):
4617.8.1 by Robert Collins
Lock down another test assuming the default was a PackRepository.
1428
        builder = self.make_branch_builder('.', format="1.9")
3824.2.4 by John Arbash Meinel
Add a test that ensures the pack ordering changes as part of calling .pack()
1429
        builder.start_series()
1430
        builder.build_snapshot('A', None, [
1431
            ('add', ('', 'root-id', 'directory', None)),
1432
            ('add', ('f', 'f-id', 'file', 'content\n'))])
1433
        builder.build_snapshot('B', ['A'],
1434
            [('modify', ('f-id', 'new-content\n'))])
1435
        builder.build_snapshot('C', ['B'],
1436
            [('modify', ('f-id', 'third-content\n'))])
1437
        builder.build_snapshot('D', ['C'],
1438
            [('modify', ('f-id', 'fourth-content\n'))])
1439
        b = builder.get_branch()
1440
        b.lock_read()
1441
        builder.finish_series()
1442
        self.addCleanup(b.unlock)
1443
        # At this point, we should have 4 pack files available
1444
        # Because of how they were built, they correspond to
1445
        # ['D', 'C', 'B', 'A']
1446
        packs = b.repository._pack_collection.packs
5757.7.5 by Jelmer Vernooij
Fix imports in tests.
1447
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
3824.2.4 by John Arbash Meinel
Add a test that ensures the pack ordering changes as part of calling .pack()
1448
                                  packs, 'testing',
1449
                                  revision_ids=['B', 'C'])
1450
        # Now, when we are copying the B & C revisions, their pack files should
1451
        # be moved to the front of the stack
3824.2.5 by Andrew Bennetts
Minor tweaks to comments etc.
1452
        # The new ordering moves B & C to the front of the .packs attribute,
1453
        # and leaves the others in the original order.
3824.2.4 by John Arbash Meinel
Add a test that ensures the pack ordering changes as part of calling .pack()
1454
        new_packs = [packs[1], packs[2], packs[0], packs[3]]
1455
        new_pack = packer.pack()
1456
        self.assertEqual(new_packs, packer.packs)
3146.6.1 by Aaron Bentley
InterDifferingSerializer shows a progress bar
1457
1458
3777.5.4 by John Arbash Meinel
OptimisingPacker now sets the optimize flags for the indexes being built.
1459
class TestOptimisingPacker(TestCaseWithTransport):
1460
    """Tests for the OptimisingPacker class."""
1461
1462
    def get_pack_collection(self):
1463
        repo = self.make_repository('.')
1464
        return repo._pack_collection
1465
1466
    def test_open_pack_will_optimise(self):
5757.7.3 by Jelmer Vernooij
Move more knitpack-specific functionality out of Packer.
1467
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
3777.5.4 by John Arbash Meinel
OptimisingPacker now sets the optimize flags for the indexes being built.
1468
                                            [], '.test')
1469
        new_pack = packer.open_pack()
4857.2.1 by John Arbash Meinel
2 test_repository tests weren't adding cleanups when opening files.
1470
        self.addCleanup(new_pack.abort) # ensure cleanup
3777.5.4 by John Arbash Meinel
OptimisingPacker now sets the optimize flags for the indexes being built.
1471
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1472
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1473
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1474
        self.assertTrue(new_pack.text_index._optimize_for_size)
1475
        self.assertTrue(new_pack.signature_index._optimize_for_size)
4462.2.6 by Robert Collins
Cause StreamSink to partially pack repositories after cross format fetches when beneficial.
1476
1477
4634.170.1 by John Arbash Meinel
Fix bug #437003. Autopacking should not fail for an
1478
class TestGCCHKPacker(TestCaseWithTransport):
1479
1480
    def make_abc_branch(self):
1481
        builder = self.make_branch_builder('source')
1482
        builder.start_series()
1483
        builder.build_snapshot('A', None, [
1484
            ('add', ('', 'root-id', 'directory', None)),
1485
            ('add', ('file', 'file-id', 'file', 'content\n')),
1486
            ])
1487
        builder.build_snapshot('B', ['A'], [
1488
            ('add', ('dir', 'dir-id', 'directory', None))])
1489
        builder.build_snapshot('C', ['B'], [
1490
            ('modify', ('file-id', 'new content\n'))])
1491
        builder.finish_series()
1492
        return builder.get_branch()
1493
1494
    def make_branch_with_disjoint_inventory_and_revision(self):
1495
        """a repo with separate packs for a revisions Revision and Inventory.
1496
1497
        There will be one pack file that holds the Revision content, and one
1498
        for the Inventory content.
1499
1500
        :return: (repository,
1501
                  pack_name_with_rev_A_Revision,
1502
                  pack_name_with_rev_A_Inventory,
1503
                  pack_name_with_rev_C_content)
1504
        """
1505
        b_source = self.make_abc_branch()
1506
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1507
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1508
        b_stacked.lock_write()
1509
        self.addCleanup(b_stacked.unlock)
1510
        b_stacked.fetch(b_source, 'B')
1511
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1512
        # fill in the A rev.
1513
        repo_not_stacked = b_stacked.bzrdir.open_repository()
1514
        repo_not_stacked.lock_write()
1515
        self.addCleanup(repo_not_stacked.unlock)
1516
        # Now we should have a pack file with A's inventory, but not its
1517
        # Revision
1518
        self.assertEqual([('A',), ('B',)],
1519
                         sorted(repo_not_stacked.inventories.keys()))
1520
        self.assertEqual([('B',)],
1521
                         sorted(repo_not_stacked.revisions.keys()))
1522
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1523
        # We have a couple names here, figure out which has A's inventory
1524
        for name in stacked_pack_names:
1525
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1526
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1527
            if ('A',) in keys:
1528
                inv_a_pack_name = name
1529
                break
1530
        else:
1531
            self.fail('Could not find pack containing A\'s inventory')
1532
        repo_not_stacked.fetch(b_source.repository, 'A')
1533
        self.assertEqual([('A',), ('B',)],
1534
                         sorted(repo_not_stacked.revisions.keys()))
1535
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1536
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1537
        self.assertEqual(1, len(rev_a_pack_names))
1538
        rev_a_pack_name = list(rev_a_pack_names)[0]
1539
        # Now fetch 'C', so we have a couple pack files to join
1540
        repo_not_stacked.fetch(b_source.repository, 'C')
1541
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1542
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1543
        self.assertEqual(1, len(rev_c_pack_names))
1544
        rev_c_pack_name = list(rev_c_pack_names)[0]
1545
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1546
                rev_c_pack_name)
1547
1548
    def test_pack_with_distant_inventories(self):
1549
        # See https://bugs.launchpad.net/bzr/+bug/437003
1550
        # When repacking, it is possible to have an inventory in a different
1551
        # pack file than the associated revision. An autopack can then come
1552
        # along, and miss that inventory, and complain.
1553
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1554
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1555
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1556
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1557
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1558
                    [a_pack, c_pack], '.test-pack')
1559
        # This would raise ValueError in bug #437003, but should not raise an
1560
        # error once fixed.
1561
        packer.pack()
1562
1563
    def test_pack_with_missing_inventory(self):
1564
        # Similar to test_pack_with_missing_inventory, but this time, we force
1565
        # the A inventory to actually be gone from the repository.
1566
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1567
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1568
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1569
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1570
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1571
            repo._pack_collection.all_packs(), '.test-pack')
1572
        e = self.assertRaises(ValueError, packer.pack)
1573
        packer.new_pack.abort()
4634.170.2 by John Arbash Meinel
Loosen the match a bit. Newer versions have StaticTuple.
1574
        self.assertContainsRe(str(e),
1575
            r"We are missing inventories for revisions: .*'A'")
4634.170.1 by John Arbash Meinel
Fix bug #437003. Autopacking should not fail for an
1576
1577
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
1578
class TestCrossFormatPacks(TestCaseWithTransport):
1579
1580
    def log_pack(self, hint=None):
1581
        self.calls.append(('pack', hint))
1582
        self.orig_pack(hint=hint)
1583
        if self.expect_hint:
1584
            self.assertTrue(hint)
1585
1586
    def run_stream(self, src_fmt, target_fmt, expect_pack_called):
1587
        self.expect_hint = expect_pack_called
1588
        self.calls = []
1589
        source_tree = self.make_branch_and_tree('src', format=src_fmt)
1590
        source_tree.lock_write()
1591
        self.addCleanup(source_tree.unlock)
1592
        tip = source_tree.commit('foo')
1593
        target = self.make_repository('target', format=target_fmt)
1594
        target.lock_write()
1595
        self.addCleanup(target.unlock)
1596
        source = source_tree.branch.repository._get_source(target._format)
1597
        self.orig_pack = target.pack
1598
        target.pack = self.log_pack
1599
        search = target.search_missing_revision_ids(
5539.2.11 by Andrew Bennetts
Fix deprecation warning from test suite.
1600
            source_tree.branch.repository, revision_ids=[tip])
4431.3.7 by Jonathan Lange
Cherrypick bzr.dev 4470, resolving conflicts.
1601
        stream = source.get_stream(search)
1602
        from_format = source_tree.branch.repository._format
1603
        sink = target._get_sink()
1604
        sink.insert_stream(stream, from_format, [])
1605
        if expect_pack_called:
1606
            self.assertLength(1, self.calls)
1607
        else:
1608
            self.assertLength(0, self.calls)
1609
1610
    def run_fetch(self, src_fmt, target_fmt, expect_pack_called):
1611
        self.expect_hint = expect_pack_called
1612
        self.calls = []
1613
        source_tree = self.make_branch_and_tree('src', format=src_fmt)
1614
        source_tree.lock_write()
1615
        self.addCleanup(source_tree.unlock)
1616
        tip = source_tree.commit('foo')
1617
        target = self.make_repository('target', format=target_fmt)
1618
        target.lock_write()
1619
        self.addCleanup(target.unlock)
1620
        source = source_tree.branch.repository
1621
        self.orig_pack = target.pack
1622
        target.pack = self.log_pack
1623
        target.fetch(source)
1624
        if expect_pack_called:
1625
            self.assertLength(1, self.calls)
1626
        else:
1627
            self.assertLength(0, self.calls)
1628
1629
    def test_sink_format_hint_no(self):
1630
        # When the target format says packing makes no difference, pack is not
1631
        # called.
1632
        self.run_stream('1.9', 'rich-root-pack', False)
1633
1634
    def test_sink_format_hint_yes(self):
1635
        # When the target format says packing makes a difference, pack is
1636
        # called.
1637
        self.run_stream('1.9', '2a', True)
1638
1639
    def test_sink_format_same_no(self):
1640
        # When the formats are the same, pack is not called.
1641
        self.run_stream('2a', '2a', False)
1642
1643
    def test_IDS_format_hint_no(self):
1644
        # When the target format says packing makes no difference, pack is not
1645
        # called.
1646
        self.run_fetch('1.9', 'rich-root-pack', False)
1647
1648
    def test_IDS_format_hint_yes(self):
1649
        # When the target format says packing makes a difference, pack is
1650
        # called.
1651
        self.run_fetch('1.9', '2a', True)
1652
1653
    def test_IDS_format_same_no(self):
1654
        # When the formats are the same, pack is not called.
1655
        self.run_fetch('2a', '2a', False)