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