/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
1
# (C) 2005, 2006 Canonical Ltd
2
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.
7
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.
12
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 bzrdir implementations - tests a bzrdir format."""
18
19
import os
20
import sys
21
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
22
import bzrlib
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
23
import bzrlib.bzrdir as bzrdir
24
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
25
from bzrlib.commit import commit
26
import bzrlib.errors as errors
27
from bzrlib.errors import (FileExists,
28
                           NoSuchRevision,
29
                           NoSuchFile,
30
                           UninitializableFormat,
31
                           NotBranchError,
32
                           )
33
import bzrlib.repository as repository
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
34
from bzrlib.revision import NULL_REVISION
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
35
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
36
from bzrlib.tests.bzrdir_implementations.test_bzrdir import TestCaseWithBzrDir
37
from bzrlib.trace import mutter
38
import bzrlib.transactions as transactions
39
from bzrlib.transport import get_transport
40
from bzrlib.upgrade import upgrade
41
from bzrlib.workingtree import WorkingTree
42
43
44
class TestCaseWithRepository(TestCaseWithBzrDir):
45
46
    def setUp(self):
47
        super(TestCaseWithRepository, self).setUp()
48
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
49
    def make_branch(self, relpath):
50
        repo = self.make_repository(relpath)
51
        return repo.bzrdir.create_branch()
52
53
    def make_bzrdir(self, relpath):
54
        try:
55
            url = self.get_url(relpath)
56
            segments = url.split('/')
57
            if segments and segments[-1] not in ('', '.'):
58
                parent = '/'.join(segments[:-1])
59
                t = get_transport(parent)
60
                try:
61
                    t.mkdir(segments[-1])
62
                except FileExists:
63
                    pass
64
            return self.bzrdir_format.initialize(url)
65
        except UninitializableFormat:
66
            raise TestSkipped("Format %s is not initializable.")
67
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
68
    def make_repository(self, relpath):
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
69
        made_control = self.make_bzrdir(relpath)
70
        return self.repository_format.initialize(made_control)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
71
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
72
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
73
class TestRepository(TestCaseWithRepository):
74
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
75
    def test_clone_to_default_format(self):
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
76
        #TODO: Test that cloning a repository preserves all the information
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
77
        # such as signatures[not tested yet] etc etc.
78
        # when changing to the current default format.
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
79
        tree_a = self.make_branch_and_tree('a')
80
        self.build_tree(['a/foo'])
81
        tree_a.add('foo', 'file1')
82
        tree_a.commit('rev1', rev_id='rev1')
83
        bzrdirb = self.make_bzrdir('b')
84
        repo_b = tree_a.branch.repository.clone(bzrdirb)
85
        tree_b = repo_b.revision_tree('rev1')
86
        tree_b.get_file_text('file1')
87
        rev1 = repo_b.get_revision('rev1')
88
89
    def test_clone_specific_format(self):
90
        """todo"""
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
91
92
    def test_format_initialize_find_open(self):
93
        # loopback test to check the current format initializes to itself.
94
        if not self.repository_format.is_supported():
95
            # unsupported formats are not loopback testable
96
            # because the default open will not open them and
97
            # they may not be initializable.
98
            return
99
        # supported formats must be able to init and open
100
        t = get_transport(self.get_url())
101
        readonly_t = get_transport(self.get_readonly_url())
102
        made_control = self.bzrdir_format.initialize(t.base)
103
        made_repo = self.repository_format.initialize(made_control)
104
        self.failUnless(isinstance(made_repo, repository.Repository))
105
        self.assertEqual(made_control, made_repo.bzrdir)
106
107
        # find it via bzrdir opening:
108
        opened_control = bzrdir.BzrDir.open(readonly_t.base)
109
        direct_opened_repo = opened_control.open_repository()
110
        self.assertEqual(direct_opened_repo.__class__, made_repo.__class__)
111
        self.assertEqual(opened_control, direct_opened_repo.bzrdir)
112
113
        self.failUnless(isinstance(direct_opened_repo._format,
114
                        self.repository_format.__class__))
115
        # find it via Repository.open
116
        opened_repo = repository.Repository.open(readonly_t.base)
117
        self.failUnless(isinstance(opened_repo, made_repo.__class__))
118
        self.assertEqual(made_repo._format.__class__,
119
                         opened_repo._format.__class__)
120
        # if it has a unique id string, can we probe for it ?
121
        try:
122
            self.repository_format.get_format_string()
123
        except NotImplementedError:
124
            return
125
        self.assertEqual(self.repository_format,
126
                         repository.RepositoryFormat.find_format(opened_control))
127
128
    def test_create_repository(self):
1534.6.1 by Robert Collins
allow API creation of shared repositories
129
        # bzrdir can construct a repository for itself.
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
130
        if not self.bzrdir_format.is_supported():
131
            # unsupported formats are not loopback testable
132
            # because the default open will not open them and
133
            # they may not be initializable.
134
            return
135
        t = get_transport(self.get_url())
136
        made_control = self.bzrdir_format.initialize(t.base)
137
        made_repo = made_control.create_repository()
138
        self.failUnless(isinstance(made_repo, repository.Repository))
139
        self.assertEqual(made_control, made_repo.bzrdir)
1534.6.1 by Robert Collins
allow API creation of shared repositories
140
        
141
    def test_create_repository_shared(self):
142
        # bzrdir can construct a shared repository.
143
        if not self.bzrdir_format.is_supported():
144
            # unsupported formats are not loopback testable
145
            # because the default open will not open them and
146
            # they may not be initializable.
147
            return
148
        t = get_transport(self.get_url())
149
        made_control = self.bzrdir_format.initialize(t.base)
150
        try:
151
            made_repo = made_control.create_repository(shared=True)
152
        except errors.IncompatibleFormat:
153
            # not all repository formats understand being shared, or
154
            # may only be shared in some circumstances.
155
            return
156
        self.failUnless(isinstance(made_repo, repository.Repository))
157
        self.assertEqual(made_control, made_repo.bzrdir)
1534.6.3 by Robert Collins
find_repository sufficiently robust.
158
        self.assertTrue(made_repo.is_shared())
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
159
160
    def test_revision_tree(self):
161
        wt = self.make_branch_and_tree('.')
162
        wt.commit('lala!', rev_id='revision-1', allow_pointless=True)
163
        tree = wt.branch.repository.revision_tree('revision-1')
164
        self.assertEqual(list(tree.list_files()), [])
165
        tree = wt.branch.repository.revision_tree(None)
166
        self.assertEqual(len(tree.list_files()), 0)
167
        tree = wt.branch.repository.revision_tree(NULL_REVISION)
168
        self.assertEqual(len(tree.list_files()), 0)
169
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
170
    def test_fetch(self):
1534.1.29 by Robert Collins
Add a test environment for InterRepository objects, and remove the fetch corner case tests from test_repository.
171
        # smoke test fetch to ensure that the convenience function works.
172
        # it is defined as a convenience function with the underlying 
173
        # functionality provided by an InterRepository
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
174
        tree_a = self.make_branch_and_tree('a')
175
        self.build_tree(['a/foo'])
176
        tree_a.add('foo', 'file1')
177
        tree_a.commit('rev1', rev_id='rev1')
1534.1.29 by Robert Collins
Add a test environment for InterRepository objects, and remove the fetch corner case tests from test_repository.
178
        # fetch with a default limit (grab everything)
179
        repo = bzrdir.BzrDir.create_repository(self.get_url('b'))
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
180
        repo.fetch(tree_a.branch.repository,
181
                   revision_id=None,
182
                   pb=bzrlib.progress.DummyProgress())
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
183
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
184
    def test_clone_bzrdir_repository_revision(self):
185
        # make a repository with some revisions,
186
        # and clone it, this should not have unreferenced revisions.
187
        # also: test cloning with a revision id of NULL_REVISION -> empty repo.
188
        raise TestSkipped('revision limiting is not implemented yet.')
189
190
    def test_clone_repository_basis_revision(self):
191
        raise TestSkipped('the use of a basis should not add noise data to the result.')
192
193
    def test_clone_repository_incomplete_source_with_basis(self):
194
        # ensure that basis really does grab from the basis by having incomplete source
195
        tree = self.make_branch_and_tree('commit_tree')
196
        self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
197
        tree.add('foo')
198
        tree.commit('revision 1', rev_id='1')
199
        source = self.make_repository('source')
200
        # this gives us an incomplete repository
201
        tree.bzrdir.open_repository().copy_content_into(source)
202
        tree.commit('revision 2', rev_id='2', allow_pointless=True)
203
        self.assertFalse(source.has_revision('2'))
204
        target = source.bzrdir.clone(self.get_url('target'), basis=tree.bzrdir)
205
        self.assertTrue(target.open_repository().has_revision('2'))
206
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
207
    def test_clone_shared_no_tree(self):
208
        # cloning a shared repository keeps it shared
209
        # and preserves the make_working_tree setting.
210
        made_control = self.make_bzrdir('source')
211
        try:
212
            made_repo = made_control.create_repository(shared=True)
213
        except errors.IncompatibleFormat:
214
            # not all repository formats understand being shared, or
215
            # may only be shared in some circumstances.
216
            return
217
        made_repo.set_make_working_trees(False)
218
        result = made_control.clone(self.get_url('target'))
219
        self.failUnless(isinstance(made_repo, repository.Repository))
220
        self.assertEqual(made_control, made_repo.bzrdir)
221
        self.assertTrue(result.open_repository().is_shared())
222
        self.assertFalse(result.open_repository().make_working_trees())
223
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.
224
    def test_upgrade_preserves_signatures(self):
225
        wt = self.make_branch_and_tree('source')
226
        wt.commit('A', allow_pointless=True, rev_id='A')
227
        wt.branch.repository.sign_revision('A',
228
            bzrlib.gpg.LoopbackGPGStrategy(None))
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
229
        old_signature = wt.branch.repository.get_signature_text('A')
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.
230
        try:
231
            old_format = bzrdir.BzrDirFormat.get_default_format()
232
            # This gives metadir branches something they can convert to.
233
            # it would be nice to have a 'latest' vs 'default' concept.
234
            bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
235
            try:
236
                upgrade(wt.basedir)
237
            finally:
238
                bzrdir.BzrDirFormat.set_default_format(old_format)
239
        except errors.UpToDateFormat:
240
            # this is in the most current format already.
241
            return
242
        wt = WorkingTree.open(wt.basedir)
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
243
        new_signature = wt.branch.repository.get_signature_text('A')
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.
244
        self.assertEqual(old_signature, new_signature)
245
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
246
    def test_exposed_versioned_files_are_marked_dirty(self):
247
        repo = self.make_repository('.')
248
        repo.lock_write()
249
        inv = repo.get_inventory_weave()
250
        repo.unlock()
251
        self.assertRaises(errors.OutSideTransaction, inv.add_lines, 'foo', [], [])
252
1624.3.19 by Olaf Conradi
New call get_format_description to give a user-friendly description of a
253
    def test_format_description(self):
254
        repo = self.make_repository('.')
255
        text = repo._format.get_format_description()
256
        self.failUnless(len(text))
257
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
258
259
class TestCaseWithComplexRepository(TestCaseWithRepository):
260
261
    def setUp(self):
262
        super(TestCaseWithComplexRepository, self).setUp()
263
        tree_a = self.make_branch_and_tree('a')
264
        self.bzrdir = tree_a.branch.bzrdir
265
        # add a corrupt inventory 'orphan'
1534.1.34 by Robert Collins
Move missing_revision_ids from Repository to InterRepository, and eliminate the now unused Repository._compatible_formats method.
266
        # this may need some generalising for knits.
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
267
        inv_file = tree_a.branch.repository.control_weaves.get_weave(
268
            'inventory', 
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
269
            tree_a.branch.repository.get_transaction())
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
270
        inv_file.add_lines('orphan', [], [])
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
271
        # add a real revision 'rev1'
272
        tree_a.commit('rev1', rev_id='rev1', allow_pointless=True)
273
        # add a real revision 'rev2' based on rev1
274
        tree_a.commit('rev2', rev_id='rev2', allow_pointless=True)
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
275
        # add a reference to a ghost
276
        tree_a.add_pending_merge('ghost1')
277
        tree_a.commit('rev3', rev_id='rev3', allow_pointless=True)
278
        # add another reference to a ghost, and a second ghost.
279
        tree_a.add_pending_merge('ghost1')
280
        tree_a.add_pending_merge('ghost2')
281
        tree_a.commit('rev4', rev_id='rev4', allow_pointless=True)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
282
283
    def test_all_revision_ids(self):
284
        # all_revision_ids -> all revisions
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
285
        self.assertEqual(['rev1', 'rev2', 'rev3', 'rev4'],
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
286
                         self.bzrdir.open_repository().all_revision_ids())
287
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
288
    def test_get_ancestry_missing_revision(self):
1534.1.34 by Robert Collins
Move missing_revision_ids from Repository to InterRepository, and eliminate the now unused Repository._compatible_formats method.
289
        # get_ancestry(revision that is in some data but not fully installed
290
        # -> NoSuchRevision
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
291
        self.assertRaises(errors.NoSuchRevision,
292
                          self.bzrdir.open_repository().get_ancestry, 'orphan')
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
293
1590.1.1 by Robert Collins
Improve common_ancestor performance.
294
    def test_get_revision_graph(self):
295
        # we can get a mapping of id->parents for the entire revision graph or bits thereof.
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
296
        self.assertEqual({'rev1':[],
297
                          'rev2':['rev1'],
298
                          'rev3':['rev2'],
299
                          'rev4':['rev3'],
300
                          },
1590.1.1 by Robert Collins
Improve common_ancestor performance.
301
                         self.bzrdir.open_repository().get_revision_graph(None))
302
        self.assertEqual({'rev1':[]},
303
                         self.bzrdir.open_repository().get_revision_graph('rev1'))
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
304
        self.assertEqual({'rev1':[],
305
                          'rev2':['rev1']},
1590.1.1 by Robert Collins
Improve common_ancestor performance.
306
                         self.bzrdir.open_repository().get_revision_graph('rev2'))
307
        self.assertRaises(NoSuchRevision,
308
                          self.bzrdir.open_repository().get_revision_graph,
309
                          'orphan')
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
310
        # and ghosts are not mentioned
311
        self.assertEqual({'rev1':[],
312
                          'rev2':['rev1'],
313
                          'rev3':['rev2'],
314
                          },
315
                         self.bzrdir.open_repository().get_revision_graph('rev3'))
316
317
    def test_get_revision_graph_with_ghosts(self):
318
        # we can get a graph object with roots, ghosts, ancestors and
319
        # descendants.
320
        repo = self.bzrdir.open_repository()
321
        graph = repo.get_revision_graph_with_ghosts([])
322
        self.assertEqual(set(['rev1']), graph.roots)
323
        self.assertEqual(set(['ghost1', 'ghost2']), graph.ghosts)
324
        self.assertEqual({'rev1':[],
325
                          'rev2':['rev1'],
326
                          'rev3':['rev2', 'ghost1'],
327
                          'rev4':['rev3', 'ghost1', 'ghost2'],
328
                          },
329
                          graph.get_ancestors())
330
        self.assertEqual({'ghost1':{'rev3':1, 'rev4':1},
331
                          'ghost2':{'rev4':1},
332
                          'rev1':{'rev2':1},
333
                          'rev2':{'rev3':1},
334
                          'rev3':{'rev4':1},
335
                          'rev4':{},
336
                          },
337
                          graph.get_descendants())
1590.1.1 by Robert Collins
Improve common_ancestor performance.
338
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
339
340
class TestCaseWithCorruptRepository(TestCaseWithRepository):
341
342
    def setUp(self):
343
        super(TestCaseWithCorruptRepository, self).setUp()
344
        # a inventory with no parents and the revision has parents..
345
        # i.e. a ghost.
346
        repo = self.make_repository('inventory_with_unnecessary_ghost')
347
        inv = bzrlib.tree.EmptyTree().inventory
348
        sha1 = repo.add_inventory('ghost', inv, [])
349
        rev = bzrlib.revision.Revision(timestamp=0,
350
                                       timezone=None,
351
                                       committer="Foo Bar <foo@example.com>",
352
                                       message="Message",
353
                                       inventory_sha1=sha1,
354
                                       revision_id='ghost')
355
        rev.parent_ids = ['the_ghost']
356
        repo.add_revision('ghost', rev)
357
         
358
        sha1 = repo.add_inventory('the_ghost', inv, [])
359
        rev = bzrlib.revision.Revision(timestamp=0,
360
                                       timezone=None,
361
                                       committer="Foo Bar <foo@example.com>",
362
                                       message="Message",
363
                                       inventory_sha1=sha1,
364
                                       revision_id='the_ghost')
365
        rev.parent_ids = []
366
        repo.add_revision('the_ghost', rev)
367
        # check its setup usefully
368
        inv_weave = repo.get_inventory_weave()
1563.2.30 by Robert Collins
Remove all but fetch references to revision_store, making the repository references that are weave specific use the RevisionTextStore.text_store attribute.
369
        self.assertEqual(['ghost'], inv_weave.get_ancestry(['ghost']))
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
370
1594.2.10 by Robert Collins
Teach knit fetching and branching to only duplicate relevant data avoiding unnecessary reconciles.
371
    def test_corrupt_revision_access_asserts_if_reported_wrong(self):
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
372
        repo = repository.Repository.open('inventory_with_unnecessary_ghost')
1594.2.10 by Robert Collins
Teach knit fetching and branching to only duplicate relevant data avoiding unnecessary reconciles.
373
        reported_wrong = False
374
        try:
375
            if repo.get_ancestry('ghost') != [None, 'the_ghost', 'ghost']:
376
                reported_wrong = True
377
        except errors.CorruptRepository:
378
            # caught the bad data:
379
            return
380
        if not reported_wrong:
381
            return
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
382
        self.assertRaises(errors.CorruptRepository, repo.get_revision, 'ghost')
383
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
384
    def test_corrupt_revision_get_revision_reconcile(self):
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
385
        repo = repository.Repository.open('inventory_with_unnecessary_ghost')
386
        repo.get_revision_reconcile('ghost')