/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2220.2.2 by Martin Pool
Add tag command and basic implementation
1
# Copyright (C) 2006, 2007 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for the Repository facility that are not interface tests.
18
19
For interface tests see tests/repository_implementations/*.py.
20
21
For concrete class tests see this file, and for storage formats tests
22
also see this file.
23
"""
24
2592.3.193 by Robert Collins
Move hash tracking of new packs into NewPack.
25
import md5
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
26
from stat import S_ISDIR
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
27
from StringIO import StringIO
28
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.
29
import bzrlib
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
30
from bzrlib.errors import (NotBranchError,
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
31
                           NoSuchFile,
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
32
                           UnknownFormatError,
33
                           UnsupportedFormatError,
34
                           )
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
35
from bzrlib import graph
2592.3.192 by Robert Collins
Move new revision index management to NewPack.
36
from bzrlib.index import GraphIndex, InMemoryGraphIndex
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
37
from bzrlib.repository import RepositoryFormat
2535.3.41 by Andrew Bennetts
Add tests for InterRemoteToOther.is_compatible.
38
from bzrlib.smart import server
2670.3.5 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
39
from bzrlib.tests import (
40
    TestCase,
41
    TestCaseWithTransport,
3446.2.1 by Martin Pool
Failure to delete an obsolete pack file should not be fatal.
42
    TestSkipped,
2670.3.5 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
43
    test_knit,
44
    )
3446.2.1 by Martin Pool
Failure to delete an obsolete pack file should not be fatal.
45
from bzrlib.transport import (
46
    fakenfs,
47
    get_transport,
48
    )
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
49
from bzrlib.transport.memory import MemoryServer
2535.3.53 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
50
from bzrlib.util import bencode
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
51
from bzrlib import (
2535.3.41 by Andrew Bennetts
Add tests for InterRemoteToOther.is_compatible.
52
    bzrdir,
53
    errors,
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
54
    inventory,
3146.6.1 by Aaron Bentley
InterDifferingSerializer shows a progress bar
55
    progress,
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
56
    repository,
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
57
    revision as _mod_revision,
2535.3.41 by Andrew Bennetts
Add tests for InterRemoteToOther.is_compatible.
58
    symbol_versioning,
2241.1.1 by Martin Pool
Change RepositoryFormat to use a Registry rather than ad-hoc dictionary
59
    upgrade,
60
    workingtree,
61
    )
2592.3.173 by Robert Collins
Basic implementation of all_packs.
62
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
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__
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
70
        old_format = repository.RepositoryFormat.get_default_format()
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, '')
90
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
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
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
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
118
class TestRepositoryFormat(TestCaseWithTransport):
119
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
120
121
    def test_find_format(self):
122
        # is the right format object found for a repository?
123
        # create a branch with a few known format objects.
124
        # this is not quite the same as 
125
        self.build_tree(["foo/", "bar/"])
126
        def check_format(format, url):
127
            dir = format._matchingbzrdir.initialize(url)
128
            format.initialize(dir)
129
            t = get_transport(url)
130
            found_format = repository.RepositoryFormat.find_format(dir)
131
            self.failUnless(isinstance(found_format, format.__class__))
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
132
        check_format(weaverepo.RepositoryFormat7(), "bar")
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
133
        
134
    def test_find_format_no_repository(self):
135
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
136
        self.assertRaises(errors.NoRepositoryPresent,
137
                          repository.RepositoryFormat.find_format,
138
                          dir)
139
140
    def test_find_format_unknown_format(self):
141
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
142
        SampleRepositoryFormat().initialize(dir)
143
        self.assertRaises(UnknownFormatError,
144
                          repository.RepositoryFormat.find_format,
145
                          dir)
146
147
    def test_register_unregister_format(self):
148
        format = SampleRepositoryFormat()
149
        # make a control dir
150
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
151
        # make a repo
152
        format.initialize(dir)
153
        # register a format for it.
154
        repository.RepositoryFormat.register_format(format)
155
        # which repository.Open will refuse (not supported)
156
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
157
        # but open(unsupported) will work
158
        self.assertEqual(format.open(dir), "opened repository.")
159
        # unregister the format
160
        repository.RepositoryFormat.unregister_format(format)
161
162
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
163
class TestFormat6(TestCaseWithTransport):
164
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
165
    def test_attribute__fetch_order(self):
166
        """Weaves need topological data insertion."""
167
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
168
        repo = weaverepo.RepositoryFormat6().initialize(control)
169
        self.assertEqual('topological', repo._fetch_order)
170
171
    def test_attribute__fetch_uses_deltas(self):
172
        """Weaves do not reuse deltas."""
173
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
174
        repo = weaverepo.RepositoryFormat6().initialize(control)
175
        self.assertEqual(False, repo._fetch_uses_deltas)
176
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
177
    def test_no_ancestry_weave(self):
178
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
179
        repo = weaverepo.RepositoryFormat6().initialize(control)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
180
        # We no longer need to create the ancestry.weave file
181
        # since it is *never* used.
182
        self.assertRaises(NoSuchFile,
183
                          control.transport.get,
184
                          'ancestry.weave')
185
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
186
    def test_supports_external_lookups(self):
187
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
188
        repo = weaverepo.RepositoryFormat6().initialize(control)
189
        self.assertFalse(repo._format.supports_external_lookups)
190
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
191
192
class TestFormat7(TestCaseWithTransport):
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
193
194
    def test_attribute__fetch_order(self):
195
        """Weaves need topological data insertion."""
196
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
197
        repo = weaverepo.RepositoryFormat7().initialize(control)
198
        self.assertEqual('topological', repo._fetch_order)
199
200
    def test_attribute__fetch_uses_deltas(self):
201
        """Weaves do not reuse deltas."""
202
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
203
        repo = weaverepo.RepositoryFormat7().initialize(control)
204
        self.assertEqual(False, repo._fetch_uses_deltas)
205
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
206
    def test_disk_layout(self):
207
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
208
        repo = weaverepo.RepositoryFormat7().initialize(control)
1534.5.3 by Robert Collins
Make format 4/5/6 branches share a single LockableFiles instance across wt/branch/repository.
209
        # in case of side effects of locking.
210
        repo.lock_write()
211
        repo.unlock()
1534.4.47 by Robert Collins
Split out repository into .bzr/repository
212
        # we want:
213
        # format 'Bazaar-NG Repository format 7'
214
        # lock ''
215
        # inventory.weave == empty_weave
216
        # empty revision-store directory
217
        # empty weaves directory
218
        t = control.get_repository_transport(None)
219
        self.assertEqualDiff('Bazaar-NG Repository format 7',
220
                             t.get('format').read())
221
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
222
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
223
        self.assertEqualDiff('# bzr weave file v5\n'
224
                             'w\n'
225
                             'W\n',
226
                             t.get('inventory.weave').read())
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.
227
        # Creating a file with id Foo:Bar results in a non-escaped file name on
228
        # disk.
229
        control.create_branch()
230
        tree = control.create_workingtree()
231
        tree.add(['foo'], ['Foo:Bar'], ['file'])
232
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
233
        tree.commit('first post', rev_id='first')
234
        self.assertEqualDiff(
235
            '# bzr weave file v5\n'
236
            'i\n'
237
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
238
            'n first\n'
239
            '\n'
240
            'w\n'
241
            '{ 0\n'
242
            '. content\n'
243
            '}\n'
244
            'W\n',
245
            t.get('weaves/74/Foo%3ABar.weave').read())
1534.6.1 by Robert Collins
allow API creation of shared repositories
246
247
    def test_shared_disk_layout(self):
248
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
249
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
1534.6.1 by Robert Collins
allow API creation of shared repositories
250
        # we want:
251
        # format 'Bazaar-NG Repository format 7'
252
        # inventory.weave == empty_weave
253
        # empty revision-store directory
254
        # empty weaves directory
255
        # a 'shared-storage' marker file.
1553.5.49 by Martin Pool
Use LockDirs for repo format 7
256
        # lock is not present when unlocked
1534.6.1 by Robert Collins
allow API creation of shared repositories
257
        t = control.get_repository_transport(None)
258
        self.assertEqualDiff('Bazaar-NG Repository format 7',
259
                             t.get('format').read())
260
        self.assertEqualDiff('', t.get('shared-storage').read())
261
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
262
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
263
        self.assertEqualDiff('# bzr weave file v5\n'
264
                             'w\n'
265
                             'W\n',
266
                             t.get('inventory.weave').read())
1553.5.49 by Martin Pool
Use LockDirs for repo format 7
267
        self.assertFalse(t.has('branch-lock'))
268
1553.5.56 by Martin Pool
Format 7 repo now uses LockDir!
269
    def test_creates_lockdir(self):
1553.5.49 by Martin Pool
Use LockDirs for repo format 7
270
        """Make sure it appears to be controlled by a LockDir existence"""
271
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
272
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
1553.5.49 by Martin Pool
Use LockDirs for repo format 7
273
        t = control.get_repository_transport(None)
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
274
        # TODO: Should check there is a 'lock' toplevel directory, 
275
        # regardless of contents
276
        self.assertFalse(t.has('lock/held/info'))
1553.5.49 by Martin Pool
Use LockDirs for repo format 7
277
        repo.lock_write()
1658.1.4 by Martin Pool
Quieten warning from TestFormat7.test_creates_lockdir about failing to unlock
278
        try:
279
            self.assertTrue(t.has('lock/held/info'))
280
        finally:
281
            # unlock so we don't get a warning about failing to do so
282
            repo.unlock()
1553.5.56 by Martin Pool
Format 7 repo now uses LockDir!
283
284
    def test_uses_lockdir(self):
285
        """repo format 7 actually locks on lockdir"""
286
        base_url = self.get_url()
287
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
288
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
1553.5.56 by Martin Pool
Format 7 repo now uses LockDir!
289
        t = control.get_repository_transport(None)
290
        repo.lock_write()
291
        repo.unlock()
292
        del repo
293
        # make sure the same lock is created by opening it
294
        repo = repository.Repository.open(base_url)
295
        repo.lock_write()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
296
        self.assertTrue(t.has('lock/held/info'))
1553.5.56 by Martin Pool
Format 7 repo now uses LockDir!
297
        repo.unlock()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
298
        self.assertFalse(t.has('lock/held/info'))
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
299
300
    def test_shared_no_tree_disk_layout(self):
301
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
302
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
303
        repo.set_make_working_trees(False)
304
        # we want:
305
        # format 'Bazaar-NG Repository format 7'
306
        # lock ''
307
        # inventory.weave == empty_weave
308
        # empty revision-store directory
309
        # empty weaves directory
310
        # a 'shared-storage' marker file.
311
        t = control.get_repository_transport(None)
312
        self.assertEqualDiff('Bazaar-NG Repository format 7',
313
                             t.get('format').read())
1553.5.56 by Martin Pool
Format 7 repo now uses LockDir!
314
        ## self.assertEqualDiff('', t.get('lock').read())
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
315
        self.assertEqualDiff('', t.get('shared-storage').read())
316
        self.assertEqualDiff('', t.get('no-working-trees').read())
317
        repo.set_make_working_trees(True)
318
        self.assertFalse(t.has('no-working-trees'))
319
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
320
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
321
        self.assertEqualDiff('# bzr weave file v5\n'
322
                             'w\n'
323
                             'W\n',
324
                             t.get('inventory.weave').read())
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
325
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
326
    def test_supports_external_lookups(self):
327
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
328
        repo = weaverepo.RepositoryFormat7().initialize(control)
329
        self.assertFalse(repo._format.supports_external_lookups)
330
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
331
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
332
class TestFormatKnit1(TestCaseWithTransport):
333
    
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
334
    def test_attribute__fetch_order(self):
335
        """Knits need topological data insertion."""
336
        repo = self.make_repository('.',
337
                format=bzrdir.format_registry.get('knit')())
338
        self.assertEqual('topological', repo._fetch_order)
339
340
    def test_attribute__fetch_uses_deltas(self):
341
        """Knits reuse deltas."""
342
        repo = self.make_repository('.',
343
                format=bzrdir.format_registry.get('knit')())
344
        self.assertEqual(True, repo._fetch_uses_deltas)
345
1556.1.3 by Robert Collins
Rearrangment of Repository logic to be less type code driven, and bugfix InterRepository.missing_revision_ids
346
    def test_disk_layout(self):
347
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
348
        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
349
        # in case of side effects of locking.
350
        repo.lock_write()
351
        repo.unlock()
352
        # we want:
353
        # format 'Bazaar-NG Knit Repository Format 1'
1553.5.62 by Martin Pool
Add tests that MetaDir repositories use LockDirs
354
        # 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
355
        # inventory.weave == empty_weave
356
        # empty revision-store directory
357
        # empty weaves directory
358
        t = control.get_repository_transport(None)
359
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
360
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
361
        # XXX: no locks left when unlocked at the moment
362
        # 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
363
        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.
364
        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.
365
        # Check per-file knits.
366
        branch = control.create_branch()
367
        tree = control.create_workingtree()
368
        tree.add(['foo'], ['Nasty-IdC:'], ['file'])
369
        tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
370
        tree.commit('1st post', rev_id='foo')
371
        self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
372
            '\nfoo fulltext 0 81  :')
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
373
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.
374
    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.
375
        """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.
376
        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.
377
                             t.get(knit_name + '.kndx').read())
378
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
379
    def check_knits(self, t):
380
        """check knit content for a repository."""
1654.1.3 by Robert Collins
Refactor repository knit tests slightly to remove duplication - add a assertHasKnit method.
381
        self.assertHasKnit(t, 'inventory')
382
        self.assertHasKnit(t, 'revisions')
383
        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
384
385
    def test_shared_disk_layout(self):
386
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
387
        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
388
        # we want:
389
        # format 'Bazaar-NG Knit Repository Format 1'
1553.5.62 by Martin Pool
Add tests that MetaDir repositories use LockDirs
390
        # 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
391
        # inventory.weave == empty_weave
392
        # empty revision-store directory
393
        # empty weaves directory
394
        # a 'shared-storage' marker file.
395
        t = control.get_repository_transport(None)
396
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
397
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
398
        # XXX: no locks left when unlocked at the moment
399
        # 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
400
        self.assertEqualDiff('', t.get('shared-storage').read())
401
        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.
402
        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
403
404
    def test_shared_no_tree_disk_layout(self):
405
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
406
        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
407
        repo.set_make_working_trees(False)
408
        # we want:
409
        # format 'Bazaar-NG Knit Repository Format 1'
410
        # lock ''
411
        # inventory.weave == empty_weave
412
        # empty revision-store directory
413
        # empty weaves directory
414
        # a 'shared-storage' marker file.
415
        t = control.get_repository_transport(None)
416
        self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
417
                             t.get('format').read())
1553.5.57 by Martin Pool
[merge] sync from bzr.dev
418
        # XXX: no locks left when unlocked at the moment
419
        # 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
420
        self.assertEqualDiff('', t.get('shared-storage').read())
421
        self.assertEqualDiff('', t.get('no-working-trees').read())
422
        repo.set_make_working_trees(True)
423
        self.assertFalse(t.has('no-working-trees'))
424
        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.
425
        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
426
2917.2.1 by John Arbash Meinel
Fix bug #152360. The xml5 serializer should be using
427
    def test_deserialise_sets_root_revision(self):
428
        """We must have a inventory.root.revision
429
430
        Old versions of the XML5 serializer did not set the revision_id for
431
        the whole inventory. So we grab the one from the expected text. Which
432
        is valid when the api is not being abused.
433
        """
434
        repo = self.make_repository('.',
435
                format=bzrdir.format_registry.get('knit')())
436
        inv_xml = '<inventory format="5">\n</inventory>\n'
437
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
438
        self.assertEqual('test-rev-id', inv.root.revision)
439
440
    def test_deserialise_uses_global_revision_id(self):
441
        """If it is set, then we re-use the global revision id"""
442
        repo = self.make_repository('.',
443
                format=bzrdir.format_registry.get('knit')())
444
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
445
                   '</inventory>\n')
446
        # Arguably, the deserialise_inventory should detect a mismatch, and
447
        # raise an error, rather than silently using one revision_id over the
448
        # other.
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.
449
        self.assertRaises(AssertionError, repo.deserialise_inventory,
450
            'test-rev-id', inv_xml)
451
        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
452
        self.assertEqual('other-rev-id', inv.root.revision)
453
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
454
    def test_supports_external_lookups(self):
455
        repo = self.make_repository('.',
456
                format=bzrdir.format_registry.get('knit')())
457
        self.assertFalse(repo._format.supports_external_lookups)
458
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
459
460
class DummyRepository(object):
461
    """A dummy repository for testing."""
462
463
    _serializer = None
464
465
    def supports_rich_root(self):
466
        return False
467
468
469
class InterDummy(repository.InterRepository):
470
    """An inter-repository optimised code path for DummyRepository.
471
472
    This is for use during testing where we use DummyRepository as repositories
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
473
    so that none of the default regsitered inter-repository classes will
2818.4.2 by Robert Collins
Review feedback.
474
    MATCH.
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
475
    """
476
477
    @staticmethod
478
    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.
479
        """InterDummy is compatible with DummyRepository."""
480
        return (isinstance(repo_source, DummyRepository) and 
481
            isinstance(repo_target, DummyRepository))
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
482
483
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
484
class TestInterRepository(TestCaseWithTransport):
485
486
    def test_get_default_inter_repository(self):
487
        # test that the InterRepository.get(repo_a, repo_b) probes
488
        # for a inter_repo class where is_compatible(repo_a, repo_b) returns
489
        # true and returns a default inter_repo otherwise.
490
        # This also tests that the default registered optimised interrepository
491
        # classes do not barf inappropriately when a surprising repository type
492
        # 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.
493
        dummy_a = DummyRepository()
494
        dummy_b = DummyRepository()
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
495
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
496
497
    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.
498
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default.
499
        
500
        The effective default is now InterSameDataRepository because there is
501
        no actual sane default in the presence of incompatible data models.
502
        """
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
503
        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.
504
        self.assertEqual(repository.InterSameDataRepository,
1534.1.27 by Robert Collins
Start InterRepository with InterRepository.get.
505
                         inter_repo.__class__)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
506
        self.assertEqual(repo_a, inter_repo.source)
507
        self.assertEqual(repo_b, inter_repo.target)
508
509
    def test_register_inter_repository_class(self):
510
        # test that a optimised code path provider - a
511
        # InterRepository subclass can be registered and unregistered
512
        # and that it is correctly selected when given a repository
513
        # pair that it returns true on for the is_compatible static method
514
        # check
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
515
        dummy_a = DummyRepository()
516
        dummy_b = DummyRepository()
517
        repo = self.make_repository('.')
518
        # hack dummies to look like repo somewhat.
519
        dummy_a._serializer = repo._serializer
520
        dummy_b._serializer = repo._serializer
521
        repository.InterRepository.register_optimiser(InterDummy)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
522
        try:
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
523
            # we should get the default for something InterDummy returns False
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
524
            # to
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
525
            self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
526
            self.assertGetsDefaultInterRepository(dummy_a, repo)
527
            # and we should get an InterDummy for a pair it 'likes'
528
            self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
529
            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.
530
            self.assertEqual(InterDummy, inter_repo.__class__)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
531
            self.assertEqual(dummy_a, inter_repo.source)
532
            self.assertEqual(dummy_b, inter_repo.target)
533
        finally:
2305.2.3 by Andrew Bennetts
Bring across test_repository improvements from the hpss branch to fix the last test failures.
534
            repository.InterRepository.unregister_optimiser(InterDummy)
1534.1.28 by Robert Collins
Allow for optimised InterRepository selection.
535
        # now we should get the default InterRepository object again.
536
        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.
537
2241.1.17 by Martin Pool
Restore old InterWeave tests
538
539
class TestInterWeaveRepo(TestCaseWithTransport):
540
541
    def test_is_compatible_and_registered(self):
542
        # InterWeaveRepo is compatible when either side
543
        # is a format 5/6/7 branch
2241.1.20 by mbp at sourcefrog
update tests for new locations of weave repos
544
        from bzrlib.repofmt import knitrepo, weaverepo
545
        formats = [weaverepo.RepositoryFormat5(),
546
                   weaverepo.RepositoryFormat6(),
547
                   weaverepo.RepositoryFormat7()]
548
        incompatible_formats = [weaverepo.RepositoryFormat4(),
549
                                knitrepo.RepositoryFormatKnit1(),
2241.1.17 by Martin Pool
Restore old InterWeave tests
550
                                ]
551
        repo_a = self.make_repository('a')
552
        repo_b = self.make_repository('b')
553
        is_compatible = repository.InterWeaveRepo.is_compatible
554
        for source in incompatible_formats:
555
            # force incompatible left then right
556
            repo_a._format = source
557
            repo_b._format = formats[0]
558
            self.assertFalse(is_compatible(repo_a, repo_b))
559
            self.assertFalse(is_compatible(repo_b, repo_a))
560
        for source in formats:
561
            repo_a._format = source
562
            for target in formats:
563
                repo_b._format = target
564
                self.assertTrue(is_compatible(repo_a, repo_b))
565
        self.assertEqual(repository.InterWeaveRepo,
566
                         repository.InterRepository.get(repo_a,
567
                                                        repo_b).__class__)
568
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.
569
570
class TestRepositoryConverter(TestCaseWithTransport):
571
572
    def test_convert_empty(self):
573
        t = get_transport(self.get_url('.'))
574
        t.mkdir('repository')
575
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
576
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
577
        target_format = knitrepo.RepositoryFormatKnit1()
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.
578
        converter = repository.CopyConverter(target_format)
1594.1.3 by Robert Collins
Fixup pb usage to use nested_progress_bar.
579
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
580
        try:
581
            converter.convert(repo, pb)
582
        finally:
583
            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.
584
        repo = repo_dir.open_repository()
585
        self.assertTrue(isinstance(target_format, repo._format.__class__))
1843.2.5 by Aaron Bentley
Add test of _unescape_xml
586
587
588
class TestMisc(TestCase):
589
    
590
    def test_unescape_xml(self):
591
        """We get some kind of error when malformed entities are passed"""
592
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;') 
1910.2.13 by Aaron Bentley
Start work on converter
593
594
2255.2.211 by Robert Collins
Remove knit2 repository format- it has never been supported.
595
class TestRepositoryFormatKnit3(TestCaseWithTransport):
1910.2.13 by Aaron Bentley
Start work on converter
596
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
597
    def test_attribute__fetch_order(self):
598
        """Knits need topological data insertion."""
599
        format = bzrdir.BzrDirMetaFormat1()
600
        format.repository_format = knitrepo.RepositoryFormatKnit3()
601
        repo = self.make_repository('.', format=format)
602
        self.assertEqual('topological', repo._fetch_order)
603
604
    def test_attribute__fetch_uses_deltas(self):
605
        """Knits reuse deltas."""
606
        format = bzrdir.BzrDirMetaFormat1()
607
        format.repository_format = knitrepo.RepositoryFormatKnit3()
608
        repo = self.make_repository('.', format=format)
609
        self.assertEqual(True, repo._fetch_uses_deltas)
610
1910.2.13 by Aaron Bentley
Start work on converter
611
    def test_convert(self):
612
        """Ensure the upgrade adds weaves for roots"""
1910.2.35 by Aaron Bentley
Better fix for convesion test
613
        format = bzrdir.BzrDirMetaFormat1()
2241.1.6 by Martin Pool
Move Knit repositories into the submodule bzrlib.repofmt.knitrepo and
614
        format.repository_format = knitrepo.RepositoryFormatKnit1()
1910.2.35 by Aaron Bentley
Better fix for convesion test
615
        tree = self.make_branch_and_tree('.', format)
1910.2.13 by Aaron Bentley
Start work on converter
616
        tree.commit("Dull commit", rev_id="dull")
617
        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.
618
        revision_tree.lock_read()
619
        try:
620
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
621
                revision_tree.inventory.root.file_id)
622
        finally:
623
            revision_tree.unlock()
1910.2.13 by Aaron Bentley
Start work on converter
624
        format = bzrdir.BzrDirMetaFormat1()
2255.2.211 by Robert Collins
Remove knit2 repository format- it has never been supported.
625
        format.repository_format = knitrepo.RepositoryFormatKnit3()
1910.2.13 by Aaron Bentley
Start work on converter
626
        upgrade.Convert('.', format)
1910.2.27 by Aaron Bentley
Fixed conversion test
627
        tree = workingtree.WorkingTree.open('.')
1910.2.13 by Aaron Bentley
Start work on converter
628
        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.
629
        revision_tree.lock_read()
630
        try:
631
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
632
        finally:
633
            revision_tree.unlock()
1910.2.27 by Aaron Bentley
Fixed conversion test
634
        tree.commit("Another dull commit", rev_id='dull2')
635
        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.
636
        revision_tree.lock_read()
637
        self.addCleanup(revision_tree.unlock)
1910.2.27 by Aaron Bentley
Fixed conversion test
638
        self.assertEqual('dull', revision_tree.inventory.root.revision)
2220.2.2 by Martin Pool
Add tag command and basic implementation
639
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
640
    def test_supports_external_lookups(self):
641
        format = bzrdir.BzrDirMetaFormat1()
642
        format.repository_format = knitrepo.RepositoryFormatKnit3()
643
        repo = self.make_repository('.', format=format)
644
        self.assertFalse(repo._format.supports_external_lookups)
645
2535.3.57 by Andrew Bennetts
Perform some sanity checking of data streams rather than blindly inserting them into our repository.
646
647
class TestWithBrokenRepo(TestCaseWithTransport):
2592.3.214 by Robert Collins
Merge bzr.dev.
648
    """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.
649
650
    def make_broken_repository(self):
651
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
652
        # parent references" branch which is due to land in bzr.dev soon.  Once
653
        # it does, this duplication should be removed.
654
        repo = self.make_repository('broken-repo')
655
        cleanups = []
656
        try:
657
            repo.lock_write()
658
            cleanups.append(repo.unlock)
659
            repo.start_write_group()
660
            cleanups.append(repo.commit_write_group)
661
            # make rev1a: A well-formed revision, containing 'file1'
662
            inv = inventory.Inventory(revision_id='rev1a')
663
            inv.root.revision = 'rev1a'
664
            self.add_file(repo, inv, 'file1', 'rev1a', [])
665
            repo.add_inventory('rev1a', inv, [])
666
            revision = _mod_revision.Revision('rev1a',
667
                committer='jrandom@example.com', timestamp=0,
668
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
669
            repo.add_revision('rev1a',revision, inv)
670
671
            # make rev1b, which has no Revision, but has an Inventory, and
672
            # file1
673
            inv = inventory.Inventory(revision_id='rev1b')
674
            inv.root.revision = 'rev1b'
675
            self.add_file(repo, inv, 'file1', 'rev1b', [])
676
            repo.add_inventory('rev1b', inv, [])
677
678
            # make rev2, with file1 and file2
679
            # file2 is sane
680
            # file1 has 'rev1b' as an ancestor, even though this is not
681
            # mentioned by 'rev1a', making it an unreferenced ancestor
682
            inv = inventory.Inventory()
683
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
684
            self.add_file(repo, inv, 'file2', 'rev2', [])
685
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
686
687
            # make ghost revision rev1c
688
            inv = inventory.Inventory()
689
            self.add_file(repo, inv, 'file2', 'rev1c', [])
690
691
            # make rev3 with file2
692
            # file2 refers to 'rev1c', which is a ghost in this repository, so
693
            # file2 cannot have rev1c as its ancestor.
694
            inv = inventory.Inventory()
695
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
696
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
697
            return repo
698
        finally:
699
            for cleanup in reversed(cleanups):
700
                cleanup()
701
702
    def add_revision(self, repo, revision_id, inv, parent_ids):
703
        inv.revision_id = revision_id
704
        inv.root.revision = revision_id
705
        repo.add_inventory(revision_id, inv, parent_ids)
706
        revision = _mod_revision.Revision(revision_id,
707
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
708
            timezone=0, message='foo', parent_ids=parent_ids)
709
        repo.add_revision(revision_id,revision, inv)
710
711
    def add_file(self, repo, inv, filename, revision, parents):
712
        file_id = filename + '-id'
713
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
714
        entry.revision = revision
2535.4.10 by Andrew Bennetts
Fix one failing test, disable another.
715
        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.
716
        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.
717
        text_key = (file_id, revision)
718
        parent_keys = [(file_id, parent) for parent in parents]
719
        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.
720
721
    def test_insert_from_broken_repo(self):
722
        """Inserting a data stream from a broken repository won't silently
723
        corrupt the target repository.
724
        """
725
        broken_repo = self.make_broken_repository()
726
        empty_repo = self.make_repository('empty-repo')
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.
727
        self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
2592.3.214 by Robert Collins
Merge bzr.dev.
728
729
2939.2.1 by Ian Clatworthy
use 'knitpack' naming instead of 'experimental' for pack formats
730
class TestKnitPackNoSubtrees(TestCaseWithTransport):
2592.3.24 by Robert Collins
Knit1 disk layout specified.
731
732
    def get_format(self):
3010.3.3 by Martin Pool
Merge trunk
733
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2592.3.24 by Robert Collins
Knit1 disk layout specified.
734
3565.3.1 by Robert Collins
* The generic fetch code now uses two attributes on Repository objects
735
    def test_attribute__fetch_order(self):
736
        """Packs do not need ordered data retrieval."""
737
        format = self.get_format()
738
        repo = self.make_repository('.', format=format)
739
        self.assertEqual('unsorted', repo._fetch_order)
740
741
    def test_attribute__fetch_uses_deltas(self):
742
        """Packs reuse deltas."""
743
        format = self.get_format()
744
        repo = self.make_repository('.', format=format)
745
        self.assertEqual(True, repo._fetch_uses_deltas)
746
2592.3.24 by Robert Collins
Knit1 disk layout specified.
747
    def test_disk_layout(self):
748
        format = self.get_format()
2592.3.36 by Robert Collins
Change the revision index name to NAME.rix.
749
        repo = self.make_repository('.', format=format)
2592.3.24 by Robert Collins
Knit1 disk layout specified.
750
        # in case of side effects of locking.
751
        repo.lock_write()
752
        repo.unlock()
753
        t = repo.bzrdir.get_repository_transport(None)
754
        self.check_format(t)
755
        # XXX: no locks left when unlocked at the moment
756
        # self.assertEqualDiff('', t.get('lock').read())
757
        self.check_databases(t)
758
759
    def check_format(self, t):
2939.2.5 by Ian Clatworthy
review feedback from lifeless
760
        self.assertEqualDiff(
2939.2.7 by Ian Clatworthy
fix strings used in on-disk unit tests
761
            "Bazaar pack repository format 1 (needs bzr 0.92)\n",
2592.3.24 by Robert Collins
Knit1 disk layout specified.
762
                             t.get('format').read())
763
764
    def assertHasNoKndx(self, t, knit_name):
765
        """Assert that knit_name has no index on t."""
766
        self.assertFalse(t.has(knit_name + '.kndx'))
767
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
768
    def assertHasNoKnit(self, t, knit_name):
2592.3.24 by Robert Collins
Knit1 disk layout specified.
769
        """Assert that knit_name exists on t."""
770
        # no default content
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
771
        self.assertFalse(t.has(knit_name + '.knit'))
2592.3.24 by Robert Collins
Knit1 disk layout specified.
772
773
    def check_databases(self, t):
774
        """check knit content for a repository."""
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
775
        # check conversion worked
2592.3.61 by Robert Collins
Remove inventory.kndx.
776
        self.assertHasNoKndx(t, 'inventory')
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
777
        self.assertHasNoKnit(t, 'inventory')
2592.3.24 by Robert Collins
Knit1 disk layout specified.
778
        self.assertHasNoKndx(t, 'revisions')
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
779
        self.assertHasNoKnit(t, 'revisions')
2592.3.39 by Robert Collins
Fugly version to remove signatures.kndx
780
        self.assertHasNoKndx(t, 'signatures')
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
781
        self.assertHasNoKnit(t, 'signatures')
782
        self.assertFalse(t.has('knits'))
2592.3.24 by Robert Collins
Knit1 disk layout specified.
783
        # revision-indexes file-container directory
2592.3.83 by Robert Collins
Merge bzr.dev, dropping FileNames for a trivial GraphIndex layer.
784
        self.assertEqual([],
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
785
            list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
2592.3.81 by Robert Collins
Merge FileName allocation change, leading to hash based pack file names.
786
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
787
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
2592.3.219 by Robert Collins
Review feedback.
788
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
789
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
2592.3.24 by Robert Collins
Knit1 disk layout specified.
790
791
    def test_shared_disk_layout(self):
792
        format = self.get_format()
2592.3.36 by Robert Collins
Change the revision index name to NAME.rix.
793
        repo = self.make_repository('.', shared=True, format=format)
2592.3.24 by Robert Collins
Knit1 disk layout specified.
794
        # we want:
795
        t = repo.bzrdir.get_repository_transport(None)
796
        self.check_format(t)
797
        # XXX: no locks left when unlocked at the moment
798
        # self.assertEqualDiff('', t.get('lock').read())
2592.3.219 by Robert Collins
Review feedback.
799
        # We should have a 'shared-storage' marker file.
2592.3.24 by Robert Collins
Knit1 disk layout specified.
800
        self.assertEqualDiff('', t.get('shared-storage').read())
801
        self.check_databases(t)
802
803
    def test_shared_no_tree_disk_layout(self):
804
        format = self.get_format()
2592.3.36 by Robert Collins
Change the revision index name to NAME.rix.
805
        repo = self.make_repository('.', shared=True, format=format)
2592.3.24 by Robert Collins
Knit1 disk layout specified.
806
        repo.set_make_working_trees(False)
807
        # we want:
808
        t = repo.bzrdir.get_repository_transport(None)
809
        self.check_format(t)
810
        # XXX: no locks left when unlocked at the moment
811
        # self.assertEqualDiff('', t.get('lock').read())
2592.3.219 by Robert Collins
Review feedback.
812
        # We should have a 'shared-storage' marker file.
2592.3.24 by Robert Collins
Knit1 disk layout specified.
813
        self.assertEqualDiff('', t.get('shared-storage').read())
2592.3.219 by Robert Collins
Review feedback.
814
        # We should have a marker for the no-working-trees flag.
2592.3.24 by Robert Collins
Knit1 disk layout specified.
815
        self.assertEqualDiff('', t.get('no-working-trees').read())
2592.3.219 by Robert Collins
Review feedback.
816
        # The marker should go when we toggle the setting.
2592.3.24 by Robert Collins
Knit1 disk layout specified.
817
        repo.set_make_working_trees(True)
818
        self.assertFalse(t.has('no-working-trees'))
819
        self.check_databases(t)
2592.3.25 by Robert Collins
experimental-subtrees layout defined.
820
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
821
    def test_adding_revision_creates_pack_indices(self):
2592.3.118 by Robert Collins
Record the size of the index files in the pack-names index.
822
        format = self.get_format()
823
        tree = self.make_branch_and_tree('.', format=format)
824
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
825
        self.assertEqual([],
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
826
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
2592.3.118 by Robert Collins
Record the size of the index files in the pack-names index.
827
        tree.commit('foobarbaz')
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
828
        index = GraphIndex(trans, 'pack-names', None)
2592.3.219 by Robert Collins
Review feedback.
829
        index_nodes = list(index.iter_all_entries())
830
        self.assertEqual(1, len(index_nodes))
831
        node = index_nodes[0]
2592.3.118 by Robert Collins
Record the size of the index files in the pack-names index.
832
        name = node[1][0]
833
        # the pack sizes should be listed in the index
834
        pack_value = node[2]
835
        sizes = [int(digits) for digits in pack_value.split(' ')]
836
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
837
            stat = trans.stat('indices/%s%s' % (name, suffix))
838
            self.assertEqual(size, stat.st_size)
2592.3.60 by Robert Collins
Nuke per-fileid indices for a single unified index.
839
2592.3.52 by Robert Collins
Stop allocating new names unless new data has been inserted.
840
    def test_pulling_nothing_leads_to_no_new_names(self):
841
        format = self.get_format()
842
        tree1 = self.make_branch_and_tree('1', format=format)
843
        tree2 = self.make_branch_and_tree('2', format=format)
844
        tree1.branch.repository.fetch(tree2.branch.repository)
845
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
2592.3.83 by Robert Collins
Merge bzr.dev, dropping FileNames for a trivial GraphIndex layer.
846
        self.assertEqual([],
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
847
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
2592.3.39 by Robert Collins
Fugly version to remove signatures.kndx
848
2592.3.84 by Robert Collins
Start of autopacking logic.
849
    def test_commit_across_pack_shape_boundary_autopacks(self):
850
        format = self.get_format()
851
        tree = self.make_branch_and_tree('.', format=format)
852
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
853
        # This test could be a little cheaper by replacing the packs
854
        # attribute on the repository to allow a different pack distribution
2592.3.219 by Robert Collins
Review feedback.
855
        # and max packs policy - so we are checking the policy is honoured
2592.3.84 by Robert Collins
Start of autopacking logic.
856
        # in the test. But for now 11 commits is not a big deal in a single
857
        # test.
858
        for x in range(9):
859
            tree.commit('commit %s' % x)
860
        # there should be 9 packs:
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
861
        index = GraphIndex(trans, 'pack-names', None)
2592.3.84 by Robert Collins
Start of autopacking logic.
862
        self.assertEqual(9, len(list(index.iter_all_entries())))
2948.1.1 by Robert Collins
* Obsolete packs are now cleaned up by pack and autopack operations.
863
        # insert some files in obsolete_packs which should be removed by pack.
864
        trans.put_bytes('obsolete_packs/foo', '123')
865
        trans.put_bytes('obsolete_packs/bar', '321')
2592.3.84 by Robert Collins
Start of autopacking logic.
866
        # committing one more should coalesce to 1 of 10.
867
        tree.commit('commit triggering pack')
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
868
        index = GraphIndex(trans, 'pack-names', None)
2592.3.84 by Robert Collins
Start of autopacking logic.
869
        self.assertEqual(1, len(list(index.iter_all_entries())))
870
        # packing should not damage data
871
        tree = tree.bzrdir.open_workingtree()
872
        check_result = tree.branch.repository.check(
873
            [tree.branch.last_revision()])
2948.1.1 by Robert Collins
* Obsolete packs are now cleaned up by pack and autopack operations.
874
        # We should have 50 (10x5) files in the obsolete_packs directory.
875
        obsolete_files = list(trans.list_dir('obsolete_packs'))
876
        self.assertFalse('foo' in obsolete_files)
877
        self.assertFalse('bar' in obsolete_files)
878
        self.assertEqual(50, len(obsolete_files))
2592.3.84 by Robert Collins
Start of autopacking logic.
879
        # XXX: Todo check packs obsoleted correctly - old packs and indices
880
        # in the obsolete_packs directory.
881
        large_pack_name = list(index.iter_all_entries())[0][1][0]
882
        # finally, committing again should not touch the large pack.
883
        tree.commit('commit not triggering pack')
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
884
        index = GraphIndex(trans, 'pack-names', None)
2592.3.84 by Robert Collins
Start of autopacking logic.
885
        self.assertEqual(2, len(list(index.iter_all_entries())))
886
        pack_names = [node[1][0] for node in index.iter_all_entries()]
887
        self.assertTrue(large_pack_name in pack_names)
888
3446.2.1 by Martin Pool
Failure to delete an obsolete pack file should not be fatal.
889
    def test_fail_obsolete_deletion(self):
890
        # failing to delete obsolete packs is not fatal
891
        format = self.get_format()
892
        server = fakenfs.FakeNFSServer()
893
        server.setUp()
894
        self.addCleanup(server.tearDown)
895
        transport = get_transport(server.get_url())
896
        bzrdir = self.get_format().initialize_on_transport(transport)
897
        repo = bzrdir.create_repository()
898
        repo_transport = bzrdir.get_repository_transport(None)
899
        self.assertTrue(repo_transport.has('obsolete_packs'))
900
        # these files are in use by another client and typically can't be deleted
901
        repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
902
        repo._pack_collection._clear_obsolete_packs()
903
        self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
904
2592.3.86 by Robert Collins
Implement the pack commands for knit repositories.
905
    def test_pack_after_two_commits_packs_everything(self):
906
        format = self.get_format()
907
        tree = self.make_branch_and_tree('.', format=format)
908
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
909
        tree.commit('start')
910
        tree.commit('more work')
911
        tree.branch.repository.pack()
2592.3.219 by Robert Collins
Review feedback.
912
        # there should be 1 pack:
2592.3.175 by Robert Collins
Update for GraphIndex constructor changes.
913
        index = GraphIndex(trans, 'pack-names', None)
2592.3.86 by Robert Collins
Implement the pack commands for knit repositories.
914
        self.assertEqual(1, len(list(index.iter_all_entries())))
915
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
916
3070.1.1 by Robert Collins
* ``bzr pack`` now orders revision texts in topological order, with newest
917
    def test_pack_layout(self):
918
        format = self.get_format()
919
        tree = self.make_branch_and_tree('.', format=format)
920
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
921
        tree.commit('start', rev_id='1')
922
        tree.commit('more work', rev_id='2')
923
        tree.branch.repository.pack()
924
        tree.lock_read()
925
        self.addCleanup(tree.unlock)
926
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
927
            tree.branch.repository._pack_collection.names()[0])
928
        # revision access tends to be tip->ancestor, so ordering that way on 
929
        # disk is a good idea.
930
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
931
            if key == ('1',):
932
                pos_1 = int(val[1:].split()[0])
933
            else:
934
                pos_2 = int(val[1:].split()[0])
935
        self.assertTrue(pos_2 < pos_1)
936
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
937
    def test_pack_repositories_support_multiple_write_locks(self):
938
        format = self.get_format()
939
        self.make_repository('.', shared=True, format=format)
940
        r1 = repository.Repository.open('.')
941
        r2 = repository.Repository.open('.')
942
        r1.lock_write()
943
        self.addCleanup(r1.unlock)
944
        r2.lock_write()
945
        r2.unlock()
946
947
    def _add_text(self, repo, fileid):
948
        """Add a text to the repository within a write group."""
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.
949
        repo.texts.add_lines((fileid, 'samplerev+'+fileid), [], [])
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
950
951
    def test_concurrent_writers_merge_new_packs(self):
952
        format = self.get_format()
953
        self.make_repository('.', shared=True, format=format)
954
        r1 = repository.Repository.open('.')
955
        r2 = repository.Repository.open('.')
956
        r1.lock_write()
957
        try:
958
            # access enough data to load the names list
959
            list(r1.all_revision_ids())
960
            r2.lock_write()
961
            try:
962
                # access enough data to load the names list
963
                list(r2.all_revision_ids())
964
                r1.start_write_group()
965
                try:
966
                    r2.start_write_group()
967
                    try:
968
                        self._add_text(r1, 'fileidr1')
969
                        self._add_text(r2, 'fileidr2')
970
                    except:
971
                        r2.abort_write_group()
972
                        raise
973
                except:
974
                    r1.abort_write_group()
975
                    raise
976
                # both r1 and r2 have open write groups with data in them
977
                # created while the other's write group was open.
978
                # Commit both which requires a merge to the pack-names.
979
                try:
980
                    r1.commit_write_group()
981
                except:
2592.3.219 by Robert Collins
Review feedback.
982
                    r1.abort_write_group()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
983
                    r2.abort_write_group()
984
                    raise
985
                r2.commit_write_group()
2592.3.213 by Robert Collins
Retain packs and indices in memory within a lock, even when write groups are entered and exited.
986
                # tell r1 to reload from disk
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
987
                r1._pack_collection.reset()
2592.3.219 by Robert Collins
Review feedback.
988
                # Now both repositories should know about both names
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
989
                r1._pack_collection.ensure_loaded()
990
                r2._pack_collection.ensure_loaded()
991
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
992
                self.assertEqual(2, len(r1._pack_collection.names()))
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
993
            finally:
2939.1.1 by Martin Pool
Fix up mismatched lock/unlock pairs.
994
                r2.unlock()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
995
        finally:
2939.1.1 by Martin Pool
Fix up mismatched lock/unlock pairs.
996
            r1.unlock()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
997
998
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
999
        format = self.get_format()
1000
        self.make_repository('.', shared=True, format=format)
1001
        r1 = repository.Repository.open('.')
1002
        r2 = repository.Repository.open('.')
1003
        # add a pack to drop
1004
        r1.lock_write()
1005
        try:
1006
            r1.start_write_group()
1007
            try:
1008
                self._add_text(r1, 'fileidr1')
1009
            except:
1010
                r1.abort_write_group()
1011
                raise
1012
            else:
1013
                r1.commit_write_group()
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1014
            r1._pack_collection.ensure_loaded()
1015
            name_to_drop = r1._pack_collection.all_packs()[0].name
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1016
        finally:
1017
            r1.unlock()
1018
        r1.lock_write()
1019
        try:
1020
            # access enough data to load the names list
1021
            list(r1.all_revision_ids())
1022
            r2.lock_write()
1023
            try:
1024
                # access enough data to load the names list
1025
                list(r2.all_revision_ids())
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1026
                r1._pack_collection.ensure_loaded()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1027
                try:
1028
                    r2.start_write_group()
1029
                    try:
1030
                        # in r1, drop the pack
2592.3.236 by Martin Pool
Make RepositoryPackCollection.remove_pack_from_memory private
1031
                        r1._pack_collection._remove_pack_from_memory(
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1032
                            r1._pack_collection.get_pack_by_name(name_to_drop))
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1033
                        # in r2, add a pack
1034
                        self._add_text(r2, 'fileidr2')
1035
                    except:
1036
                        r2.abort_write_group()
1037
                        raise
1038
                except:
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1039
                    r1._pack_collection.reset()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1040
                    raise
1041
                # r1 has a changed names list, and r2 an open write groups with
1042
                # changes.
2592.3.208 by Robert Collins
Start refactoring the knit-pack thunking to be clearer.
1043
                # save r1, and then commit the r2 write group, which requires a
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1044
                # merge to the pack-names, which should not reinstate
1045
                # name_to_drop
1046
                try:
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1047
                    r1._pack_collection._save_pack_names()
1048
                    r1._pack_collection.reset()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1049
                except:
1050
                    r2.abort_write_group()
1051
                    raise
1052
                try:
1053
                    r2.commit_write_group()
1054
                except:
1055
                    r2.abort_write_group()
1056
                    raise
1057
                # Now both repositories should now about just one name.
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1058
                r1._pack_collection.ensure_loaded()
1059
                r2._pack_collection.ensure_loaded()
1060
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1061
                self.assertEqual(1, len(r1._pack_collection.names()))
1062
                self.assertFalse(name_to_drop in r1._pack_collection.names())
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1063
            finally:
2939.1.1 by Martin Pool
Fix up mismatched lock/unlock pairs.
1064
                r2.unlock()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1065
        finally:
2939.1.1 by Martin Pool
Fix up mismatched lock/unlock pairs.
1066
            r1.unlock()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1067
1068
    def test_lock_write_does_not_physically_lock(self):
1069
        repo = self.make_repository('.', format=self.get_format())
1070
        repo.lock_write()
1071
        self.addCleanup(repo.unlock)
1072
        self.assertFalse(repo.get_physical_lock_status())
1073
1074
    def prepare_for_break_lock(self):
2592.3.219 by Robert Collins
Review feedback.
1075
        # Setup the global ui factory state so that a break-lock method call
1076
        # will find usable input in the input stream.
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1077
        old_factory = bzrlib.ui.ui_factory
1078
        def restoreFactory():
1079
            bzrlib.ui.ui_factory = old_factory
1080
        self.addCleanup(restoreFactory)
1081
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1082
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1083
1084
    def test_break_lock_breaks_physical_lock(self):
1085
        repo = self.make_repository('.', format=self.get_format())
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1086
        repo._pack_collection.lock_names()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1087
        repo2 = repository.Repository.open('.')
1088
        self.assertTrue(repo.get_physical_lock_status())
1089
        self.prepare_for_break_lock()
1090
        repo2.break_lock()
1091
        self.assertFalse(repo.get_physical_lock_status())
1092
2592.3.240 by Martin Pool
Rename RepositoryPackCollection.release_names to _unlock_names
1093
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1094
        repo = self.make_repository('.', format=self.get_format())
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1095
        repo._pack_collection.lock_names()
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1096
        self.assertTrue(repo.get_physical_lock_status())
1097
        repo2 = repository.Repository.open('.')
1098
        self.prepare_for_break_lock()
1099
        repo2.break_lock()
2592.3.240 by Martin Pool
Rename RepositoryPackCollection.release_names to _unlock_names
1100
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1101
2949.1.2 by Robert Collins
* Fetch with pack repositories will no longer read the entire history graph.
1102
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
1103
        # we want two repositories at this point:
1104
        # one with a revision that is a ghost in the other
1105
        # repository.
1106
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1107
        # 'references' is present in both repositories, and 'tip' is present
1108
        # just in has_ghost.
1109
        # has_ghost       missing_ghost
1110
        #------------------------------
1111
        # 'ghost'             -
1112
        # 'references'    'references'
1113
        # 'tip'               -
1114
        # In this test we fetch 'tip' which should not fetch 'ghost'
1115
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
1116
        missing_ghost = self.make_repository('missing_ghost',
1117
            format=self.get_format())
1118
1119
        def add_commit(repo, revision_id, parent_ids):
1120
            repo.lock_write()
1121
            repo.start_write_group()
1122
            inv = inventory.Inventory(revision_id=revision_id)
1123
            inv.root.revision = revision_id
1124
            root_id = inv.root.file_id
1125
            sha1 = repo.add_inventory(revision_id, inv, [])
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.
1126
            repo.texts.add_lines((root_id, revision_id), [], [])
2949.1.2 by Robert Collins
* Fetch with pack repositories will no longer read the entire history graph.
1127
            rev = bzrlib.revision.Revision(timestamp=0,
1128
                                           timezone=None,
1129
                                           committer="Foo Bar <foo@example.com>",
1130
                                           message="Message",
1131
                                           inventory_sha1=sha1,
1132
                                           revision_id=revision_id)
1133
            rev.parent_ids = parent_ids
1134
            repo.add_revision(revision_id, rev)
1135
            repo.commit_write_group()
1136
            repo.unlock()
1137
        add_commit(has_ghost, 'ghost', [])
1138
        add_commit(has_ghost, 'references', ['ghost'])
1139
        add_commit(missing_ghost, 'references', ['ghost'])
1140
        add_commit(has_ghost, 'tip', ['references'])
1141
        missing_ghost.fetch(has_ghost, 'tip')
1142
        # missing ghost now has tip and not ghost.
1143
        rev = missing_ghost.get_revision('tip')
1144
        inv = missing_ghost.get_inventory('tip')
1145
        self.assertRaises(errors.NoSuchRevision,
1146
            missing_ghost.get_revision, 'ghost')
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.
1147
        self.assertRaises(errors.NoSuchRevision,
2949.1.2 by Robert Collins
* Fetch with pack repositories will no longer read the entire history graph.
1148
            missing_ghost.get_inventory, 'ghost')
1149
3221.3.1 by Robert Collins
* Repository formats have a new supported-feature attribute
1150
    def test_supports_external_lookups(self):
1151
        repo = self.make_repository('.', format=self.get_format())
1152
        self.assertFalse(repo._format.supports_external_lookups)
1153
2592.3.188 by Robert Collins
Allow pack repositories to have multiple writers active at one time, for greater concurrency.
1154
2939.2.1 by Ian Clatworthy
use 'knitpack' naming instead of 'experimental' for pack formats
1155
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1156
1157
    def get_format(self):
2939.2.5 by Ian Clatworthy
review feedback from lifeless
1158
        return bzrdir.format_registry.make_bzrdir(
3010.3.3 by Martin Pool
Merge trunk
1159
            'pack-0.92-subtree')
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1160
1161
    def check_format(self, t):
2939.2.5 by Ian Clatworthy
review feedback from lifeless
1162
        self.assertEqualDiff(
2939.2.7 by Ian Clatworthy
fix strings used in on-disk unit tests
1163
            "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
2939.2.5 by Ian Clatworthy
review feedback from lifeless
1164
            t.get('format').read())
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1165
1166
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1167
class TestDevelopment0(TestKnitPackNoSubtrees):
1168
1169
    def get_format(self):
1170
        return bzrdir.format_registry.make_bzrdir(
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1171
            'development0')
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1172
1173
    def check_format(self, t):
1174
        self.assertEqualDiff(
3152.2.3 by Robert Collins
Merge up with bzr.dev.
1175
            "Bazaar development format 0 (needs bzr.dev from before 1.3)\n",
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1176
            t.get('format').read())
1177
1178
1179
class TestDevelopment0Subtree(TestKnitPackNoSubtrees):
1180
1181
    def get_format(self):
1182
        return bzrdir.format_registry.make_bzrdir(
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1183
            'development0-subtree')
1184
1185
    def check_format(self, t):
1186
        self.assertEqualDiff(
1187
            "Bazaar development format 0 with subtree support "
1188
            "(needs bzr.dev from before 1.3)\n",
1189
            t.get('format').read())
1190
1191
3221.12.4 by Robert Collins
Implement basic repository supporting external references.
1192
class TestExternalDevelopment1(object):
1193
3517.4.16 by Martin Pool
More precise test for stack format compatibility
1194
    # mixin class for testing stack-supporting development formats
1195
1196
    def test_compatible_cross_formats(self):
1197
        # early versions of the packing code relied on pack internals to
1198
        # stack, but the current version should be able to stack on any
1199
        # format.
1200
        repo = self.make_repository('repo', format=self.get_format())
1201
        if repo.supports_rich_root():
1202
            # can only stack on repositories that have compatible internal
1203
            # metadata
1204
            matching_format_name = 'pack-0.92-subtree'
1205
            mismatching_format_name = 'pack-0.92'
1206
        else:
1207
            matching_format_name = 'pack-0.92'
1208
            mismatching_format_name = 'pack-0.92-subtree'
1209
        base = self.make_repository('base', format=matching_format_name)
1210
        repo.add_fallback_repository(base)
1211
        # you can't stack on something with incompatible data
1212
        bad_repo = self.make_repository('mismatch',
1213
            format=mismatching_format_name)
1214
        self.assertRaises(errors.IncompatibleRepositories,
1215
            repo.add_fallback_repository, bad_repo)
1216
3221.12.4 by Robert Collins
Implement basic repository supporting external references.
1217
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
1218
        base = self.make_branch_and_tree('base', format=self.get_format())
1219
        base.commit('foo')
1220
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
1221
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
1222
        referencing.commit('bar')
1223
        new_instance = referencing.bzrdir.open_repository()
1224
        new_instance.lock_read()
1225
        self.addCleanup(new_instance.unlock)
1226
        new_instance._pack_collection.ensure_loaded()
1227
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
1228
1229
    def test_autopack_only_considers_main_repo_packs(self):
1230
        base = self.make_branch_and_tree('base', format=self.get_format())
1231
        base.commit('foo')
1232
        tree = self.make_branch_and_tree('repo', format=self.get_format())
1233
        tree.branch.repository.add_fallback_repository(base.branch.repository)
1234
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
1235
        # This test could be a little cheaper by replacing the packs
1236
        # attribute on the repository to allow a different pack distribution
1237
        # and max packs policy - so we are checking the policy is honoured
1238
        # in the test. But for now 11 commits is not a big deal in a single
1239
        # test.
1240
        for x in range(9):
1241
            tree.commit('commit %s' % x)
1242
        # there should be 9 packs:
1243
        index = GraphIndex(trans, 'pack-names', None)
1244
        self.assertEqual(9, len(list(index.iter_all_entries())))
1245
        # committing one more should coalesce to 1 of 10.
1246
        tree.commit('commit triggering pack')
1247
        index = GraphIndex(trans, 'pack-names', None)
1248
        self.assertEqual(1, len(list(index.iter_all_entries())))
1249
        # packing should not damage data
1250
        tree = tree.bzrdir.open_workingtree()
1251
        check_result = tree.branch.repository.check(
1252
            [tree.branch.last_revision()])
1253
        # We should have 50 (10x5) files in the obsolete_packs directory.
1254
        obsolete_files = list(trans.list_dir('obsolete_packs'))
1255
        self.assertFalse('foo' in obsolete_files)
1256
        self.assertFalse('bar' in obsolete_files)
1257
        self.assertEqual(50, len(obsolete_files))
1258
        # XXX: Todo check packs obsoleted correctly - old packs and indices
1259
        # in the obsolete_packs directory.
1260
        large_pack_name = list(index.iter_all_entries())[0][1][0]
1261
        # finally, committing again should not touch the large pack.
1262
        tree.commit('commit not triggering pack')
1263
        index = GraphIndex(trans, 'pack-names', None)
1264
        self.assertEqual(2, len(list(index.iter_all_entries())))
1265
        pack_names = [node[1][0] for node in index.iter_all_entries()]
1266
        self.assertTrue(large_pack_name in pack_names)
1267
1268
1269
class TestDevelopment1(TestKnitPackNoSubtrees, TestExternalDevelopment1):
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1270
1271
    def get_format(self):
1272
        return bzrdir.format_registry.make_bzrdir(
1273
            'development')
1274
1275
    def check_format(self, t):
1276
        self.assertEqualDiff(
3221.17.1 by Ian Clatworthy
tweak version numbers being released in
1277
            "Bazaar development format 1 (needs bzr.dev from before 1.6)\n",
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1278
            t.get('format').read())
1279
1280
    def test_supports_external_lookups(self):
1281
        repo = self.make_repository('.', format=self.get_format())
1282
        self.assertTrue(repo._format.supports_external_lookups)
1283
1284
3221.12.4 by Robert Collins
Implement basic repository supporting external references.
1285
class TestDevelopment1Subtree(TestKnitPackNoSubtrees, TestExternalDevelopment1):
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1286
1287
    def get_format(self):
1288
        return bzrdir.format_registry.make_bzrdir(
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1289
            'development-subtree')
1290
1291
    def check_format(self, t):
1292
        self.assertEqualDiff(
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1293
            "Bazaar development format 1 with subtree support "
3221.17.1 by Ian Clatworthy
tweak version numbers being released in
1294
            "(needs bzr.dev from before 1.6)\n",
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1295
            t.get('format').read())
1296
3221.12.1 by Robert Collins
Backport development1 format (stackable packs) to before-shallow-branches.
1297
    def test_supports_external_lookups(self):
1298
        repo = self.make_repository('.', format=self.get_format())
1299
        self.assertTrue(repo._format.supports_external_lookups)
1300
3152.2.1 by Robert Collins
* A new repository format 'development' has been added. This format will
1301
2592.3.84 by Robert Collins
Start of autopacking logic.
1302
class TestRepositoryPackCollection(TestCaseWithTransport):
1303
1304
    def get_format(self):
3010.3.3 by Martin Pool
Merge trunk
1305
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2592.3.84 by Robert Collins
Start of autopacking logic.
1306
1307
    def test__max_pack_count(self):
2592.3.219 by Robert Collins
Review feedback.
1308
        """The maximum pack count is a function of the number of revisions."""
2592.3.84 by Robert Collins
Start of autopacking logic.
1309
        format = self.get_format()
1310
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1311
        packs = repo._pack_collection
2592.3.84 by Robert Collins
Start of autopacking logic.
1312
        # no revisions - one pack, so that we can have a revision free repo
1313
        # without it blowing up
1314
        self.assertEqual(1, packs._max_pack_count(0))
1315
        # after that the sum of the digits, - check the first 1-9
1316
        self.assertEqual(1, packs._max_pack_count(1))
1317
        self.assertEqual(2, packs._max_pack_count(2))
1318
        self.assertEqual(3, packs._max_pack_count(3))
1319
        self.assertEqual(4, packs._max_pack_count(4))
1320
        self.assertEqual(5, packs._max_pack_count(5))
1321
        self.assertEqual(6, packs._max_pack_count(6))
1322
        self.assertEqual(7, packs._max_pack_count(7))
1323
        self.assertEqual(8, packs._max_pack_count(8))
1324
        self.assertEqual(9, packs._max_pack_count(9))
1325
        # check the boundary cases with two digits for the next decade
1326
        self.assertEqual(1, packs._max_pack_count(10))
1327
        self.assertEqual(2, packs._max_pack_count(11))
1328
        self.assertEqual(10, packs._max_pack_count(19))
1329
        self.assertEqual(2, packs._max_pack_count(20))
1330
        self.assertEqual(3, packs._max_pack_count(21))
1331
        # check some arbitrary big numbers
1332
        self.assertEqual(25, packs._max_pack_count(112894))
1333
1334
    def test_pack_distribution_zero(self):
1335
        format = self.get_format()
1336
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1337
        packs = repo._pack_collection
2592.3.84 by Robert Collins
Start of autopacking logic.
1338
        self.assertEqual([0], packs.pack_distribution(0))
3052.1.6 by John Arbash Meinel
Change the lock check to raise ObjectNotLocked.
1339
1340
    def test_ensure_loaded_unlocked(self):
1341
        format = self.get_format()
1342
        repo = self.make_repository('.', format=format)
1343
        self.assertRaises(errors.ObjectNotLocked,
1344
                          repo._pack_collection.ensure_loaded)
1345
2592.3.84 by Robert Collins
Start of autopacking logic.
1346
    def test_pack_distribution_one_to_nine(self):
1347
        format = self.get_format()
1348
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1349
        packs = repo._pack_collection
2592.3.84 by Robert Collins
Start of autopacking logic.
1350
        self.assertEqual([1],
1351
            packs.pack_distribution(1))
1352
        self.assertEqual([1, 1],
1353
            packs.pack_distribution(2))
1354
        self.assertEqual([1, 1, 1],
1355
            packs.pack_distribution(3))
1356
        self.assertEqual([1, 1, 1, 1],
1357
            packs.pack_distribution(4))
1358
        self.assertEqual([1, 1, 1, 1, 1],
1359
            packs.pack_distribution(5))
1360
        self.assertEqual([1, 1, 1, 1, 1, 1],
1361
            packs.pack_distribution(6))
1362
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1363
            packs.pack_distribution(7))
1364
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1365
            packs.pack_distribution(8))
1366
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1367
            packs.pack_distribution(9))
1368
1369
    def test_pack_distribution_stable_at_boundaries(self):
1370
        """When there are multi-rev packs the counts are stable."""
1371
        format = self.get_format()
1372
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1373
        packs = repo._pack_collection
2592.3.84 by Robert Collins
Start of autopacking logic.
1374
        # in 10s:
1375
        self.assertEqual([10], packs.pack_distribution(10))
1376
        self.assertEqual([10, 1], packs.pack_distribution(11))
1377
        self.assertEqual([10, 10], packs.pack_distribution(20))
1378
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1379
        # 100s
1380
        self.assertEqual([100], packs.pack_distribution(100))
1381
        self.assertEqual([100, 1], packs.pack_distribution(101))
1382
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1383
        self.assertEqual([100, 100], packs.pack_distribution(200))
1384
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1385
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1386
2592.3.85 by Robert Collins
Finish autopack corner cases.
1387
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1388
        format = self.get_format()
1389
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1390
        packs = repo._pack_collection
2592.3.85 by Robert Collins
Finish autopack corner cases.
1391
        existing_packs = [(2000, "big"), (9, "medium")]
1392
        # rev count - 2009 -> 2x1000 + 9x1
1393
        pack_operations = packs.plan_autopack_combinations(
1394
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1395
        self.assertEqual([], pack_operations)
1396
1397
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1398
        format = self.get_format()
1399
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1400
        packs = repo._pack_collection
2592.3.85 by Robert Collins
Finish autopack corner cases.
1401
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1402
        # rev count - 2010 -> 2x1000 + 1x10
1403
        pack_operations = packs.plan_autopack_combinations(
1404
            existing_packs, [1000, 1000, 10])
1405
        self.assertEqual([], pack_operations)
1406
1407
    def test_plan_pack_operations_2010_combines_smallest_two(self):
1408
        format = self.get_format()
1409
        repo = self.make_repository('.', format=format)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1410
        packs = repo._pack_collection
2592.3.85 by Robert Collins
Finish autopack corner cases.
1411
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1412
            (1, "single1")]
1413
        # rev count - 2010 -> 2x1000 + 1x10 (3)
1414
        pack_operations = packs.plan_autopack_combinations(
1415
            existing_packs, [1000, 1000, 10])
1416
        self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1417
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1418
    def test_all_packs_none(self):
1419
        format = self.get_format()
1420
        tree = self.make_branch_and_tree('.', format=format)
1421
        tree.lock_read()
1422
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1423
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1424
        packs.ensure_loaded()
1425
        self.assertEqual([], packs.all_packs())
1426
1427
    def test_all_packs_one(self):
1428
        format = self.get_format()
1429
        tree = self.make_branch_and_tree('.', format=format)
1430
        tree.commit('start')
1431
        tree.lock_read()
1432
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1433
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1434
        packs.ensure_loaded()
2592.3.176 by Robert Collins
Various pack refactorings.
1435
        self.assertEqual([
1436
            packs.get_pack_by_name(packs.names()[0])],
1437
            packs.all_packs())
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1438
1439
    def test_all_packs_two(self):
1440
        format = self.get_format()
1441
        tree = self.make_branch_and_tree('.', format=format)
1442
        tree.commit('start')
1443
        tree.commit('continue')
1444
        tree.lock_read()
1445
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1446
        packs = tree.branch.repository._pack_collection
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1447
        packs.ensure_loaded()
1448
        self.assertEqual([
2592.3.176 by Robert Collins
Various pack refactorings.
1449
            packs.get_pack_by_name(packs.names()[0]),
1450
            packs.get_pack_by_name(packs.names()[1]),
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1451
            ], packs.all_packs())
1452
2592.3.176 by Robert Collins
Various pack refactorings.
1453
    def test_get_pack_by_name(self):
1454
        format = self.get_format()
1455
        tree = self.make_branch_and_tree('.', format=format)
1456
        tree.commit('start')
1457
        tree.lock_read()
1458
        self.addCleanup(tree.unlock)
2592.3.232 by Martin Pool
Disambiguate two member variables called _packs into _packs_by_name and _pack_collection
1459
        packs = tree.branch.repository._pack_collection
2592.3.176 by Robert Collins
Various pack refactorings.
1460
        packs.ensure_loaded()
1461
        name = packs.names()[0]
1462
        pack_1 = packs.get_pack_by_name(name)
1463
        # the pack should be correctly initialised
3517.4.5 by Martin Pool
Correct use of packs._names in test_get_pack_by_name
1464
        sizes = packs._names[name]
3221.12.4 by Robert Collins
Implement basic repository supporting external references.
1465
        rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
1466
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
1467
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
1468
        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.
1469
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
2592.3.219 by Robert Collins
Review feedback.
1470
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
2592.3.176 by Robert Collins
Various pack refactorings.
1471
        # and the same instance should be returned on successive calls.
1472
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1473
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1474
1475
class TestPack(TestCaseWithTransport):
1476
    """Tests for the Pack object."""
1477
1478
    def assertCurrentlyEqual(self, left, right):
1479
        self.assertTrue(left == right)
1480
        self.assertTrue(right == left)
1481
        self.assertFalse(left != right)
1482
        self.assertFalse(right != left)
1483
1484
    def assertCurrentlyNotEqual(self, left, right):
1485
        self.assertFalse(left == right)
1486
        self.assertFalse(right == left)
1487
        self.assertTrue(left != right)
1488
        self.assertTrue(right != left)
1489
1490
    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.
1491
        left = pack_repo.ExistingPack('', '', '', '', '', '')
1492
        right = pack_repo.ExistingPack('', '', '', '', '', '')
2592.3.173 by Robert Collins
Basic implementation of all_packs.
1493
        self.assertCurrentlyEqual(left, right)
1494
        # change all attributes and ensure equality changes as we do.
1495
        left.revision_index = 'a'
1496
        self.assertCurrentlyNotEqual(left, right)
1497
        right.revision_index = 'a'
1498
        self.assertCurrentlyEqual(left, right)
1499
        left.inventory_index = 'a'
1500
        self.assertCurrentlyNotEqual(left, right)
1501
        right.inventory_index = 'a'
1502
        self.assertCurrentlyEqual(left, right)
1503
        left.text_index = 'a'
1504
        self.assertCurrentlyNotEqual(left, right)
1505
        right.text_index = 'a'
1506
        self.assertCurrentlyEqual(left, right)
1507
        left.signature_index = 'a'
1508
        self.assertCurrentlyNotEqual(left, right)
1509
        right.signature_index = 'a'
1510
        self.assertCurrentlyEqual(left, right)
1511
        left.name = 'a'
1512
        self.assertCurrentlyNotEqual(left, right)
1513
        right.name = 'a'
1514
        self.assertCurrentlyEqual(left, right)
1515
        left.transport = 'a'
1516
        self.assertCurrentlyNotEqual(left, right)
1517
        right.transport = 'a'
1518
        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.
1519
1520
    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.
1521
        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.
1522
        self.assertEqual('a_name.pack', pack.file_name())
2592.3.192 by Robert Collins
Move new revision index management to NewPack.
1523
1524
1525
class TestNewPack(TestCaseWithTransport):
1526
    """Tests for pack_repo.NewPack."""
1527
2592.3.193 by Robert Collins
Move hash tracking of new packs into NewPack.
1528
    def test_new_instance_attributes(self):
2592.3.194 by Robert Collins
Output the revision index from NewPack.finish
1529
        upload_transport = self.get_transport('upload')
1530
        pack_transport = self.get_transport('pack')
1531
        index_transport = self.get_transport('index')
1532
        upload_transport.mkdir('.')
1533
        pack = pack_repo.NewPack(upload_transport, index_transport,
1534
            pack_transport)
2592.3.192 by Robert Collins
Move new revision index management to NewPack.
1535
        self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
2592.3.195 by Robert Collins
Move some inventory index logic to NewPack.
1536
        self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
2592.3.193 by Robert Collins
Move hash tracking of new packs into NewPack.
1537
        self.assertIsInstance(pack._hash, type(md5.new()))
2592.3.194 by Robert Collins
Output the revision index from NewPack.finish
1538
        self.assertTrue(pack.upload_transport is upload_transport)
1539
        self.assertTrue(pack.index_transport is index_transport)
1540
        self.assertTrue(pack.pack_transport is pack_transport)
1541
        self.assertEqual(None, pack.index_sizes)
1542
        self.assertEqual(20, len(pack.random_name))
1543
        self.assertIsInstance(pack.random_name, str)
1544
        self.assertIsInstance(pack.start_time, float)
2951.1.2 by Robert Collins
Partial refactoring of pack_repo to create a Packer object for packing.
1545
1546
1547
class TestPacker(TestCaseWithTransport):
1548
    """Tests for the packs repository Packer class."""
2951.1.10 by Robert Collins
Peer review feedback with Ian.
1549
1550
    # To date, this class has been factored out and nothing new added to it;
1551
    # thus there are not yet any tests.
3146.6.1 by Aaron Bentley
InterDifferingSerializer shows a progress bar
1552
1553
1554
class TestInterDifferingSerializer(TestCaseWithTransport):
1555
1556
    def test_progress_bar(self):
1557
        tree = self.make_branch_and_tree('tree')
1558
        tree.commit('rev1', rev_id='rev-1')
1559
        tree.commit('rev2', rev_id='rev-2')
1560
        tree.commit('rev3', rev_id='rev-3')
1561
        repo = self.make_repository('repo')
1562
        inter_repo = repository.InterDifferingSerializer(
1563
            tree.branch.repository, repo)
1564
        pb = progress.InstrumentedProgress(to_file=StringIO())
1565
        pb.never_throttle = True
1566
        inter_repo.fetch('rev-1', pb)
1567
        self.assertEqual('Transferring revisions', pb.last_msg)
1568
        self.assertEqual(1, pb.last_cnt)
1569
        self.assertEqual(1, pb.last_total)
1570
        inter_repo.fetch('rev-3', pb)
1571
        self.assertEqual(2, pb.last_cnt)
1572
        self.assertEqual(2, pb.last_total)