~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testbranch.py

  • Committer: Martin Pool
  • Date: 2005-08-01 20:10:24 UTC
  • Revision ID: mbp@sourcefrog.net-20050801201024-51c865a893c759fa
- some cleanup of log code to have less special cases

- still some trouble caused by need for EmptyTree to have a root id

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
2
 
#
 
1
# (C) 2005 Canonical Ltd
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Tests for branch implementations - tests a branch format."""
18
 
 
19
 
from bzrlib import (
20
 
    branch as _mod_branch,
21
 
    bzrdir,
22
 
    delta as _mod_delta,
23
 
    errors,
24
 
    gpg,
25
 
    merge,
26
 
    urlutils,
27
 
    transactions,
28
 
    transport,
29
 
    remote,
30
 
    repository,
31
 
    revision,
32
 
    tests,
33
 
    )
34
 
from bzrlib.symbol_versioning import deprecated_in
35
 
from bzrlib.tests import (
36
 
    http_server,
37
 
    per_branch,
38
 
    )
39
 
from bzrlib.tests.http_server import HttpServer
40
 
from bzrlib.transport import memory
41
 
 
42
 
 
43
 
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
44
 
 
45
 
    def test_branch_format_matches_bzrdir_branch_format(self):
46
 
        bzrdir_branch_format = self.bzrdir_format.get_branch_format()
47
 
        self.assertIs(self.branch_format.__class__,
48
 
                      bzrdir_branch_format.__class__)
49
 
 
50
 
    def test_make_branch_gets_expected_format(self):
51
 
        branch = self.make_branch('.')
52
 
        self.assertIs(self.branch_format.__class__,
53
 
            branch._format.__class__)
54
 
 
55
 
 
56
 
class TestBranch(per_branch.TestCaseWithBranch):
57
 
 
58
 
    def test_create_tree_with_merge(self):
59
 
        tree = self.create_tree_with_merge()
60
 
        tree.lock_read()
61
 
        self.addCleanup(tree.unlock)
62
 
        graph = tree.branch.repository.get_graph()
63
 
        ancestry_graph = graph.get_parent_map(
64
 
            tree.branch.repository.all_revision_ids())
65
 
        self.assertEqual({'rev-1':('null:',),
66
 
                          'rev-2':('rev-1', ),
67
 
                          'rev-1.1.1':('rev-1', ),
68
 
                          'rev-3':('rev-2', 'rev-1.1.1', ),
69
 
                         }, ancestry_graph)
70
 
 
71
 
    def test_revision_ids_are_utf8(self):
72
 
        wt = self.make_branch_and_tree('tree')
73
 
        wt.commit('f', rev_id='rev1')
74
 
        wt.commit('f', rev_id='rev2')
75
 
        wt.commit('f', rev_id='rev3')
76
 
 
77
 
        br = self.get_branch()
78
 
        br.fetch(wt.branch)
79
 
        br.set_revision_history(['rev1', 'rev2', 'rev3'])
80
 
        rh = br.revision_history()
81
 
        self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
82
 
        for revision_id in rh:
83
 
            self.assertIsInstance(revision_id, str)
84
 
        last = br.last_revision()
85
 
        self.assertEqual('rev3', last)
86
 
        self.assertIsInstance(last, str)
87
 
        revno, last = br.last_revision_info()
88
 
        self.assertEqual(3, revno)
89
 
        self.assertEqual('rev3', last)
90
 
        self.assertIsInstance(last, str)
91
 
 
92
 
    def test_fetch_revisions(self):
93
 
        """Test fetch-revision operation."""
94
 
        wt = self.make_branch_and_tree('b1')
95
 
        b1 = wt.branch
96
 
        self.build_tree_contents([('b1/foo', 'hello')])
97
 
        wt.add(['foo'], ['foo-id'])
98
 
        wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
99
 
 
100
 
        b2 = self.make_branch('b2')
101
 
        b2.fetch(b1)
102
 
 
103
 
        rev = b2.repository.get_revision('revision-1')
104
 
        tree = b2.repository.revision_tree('revision-1')
105
 
        tree.lock_read()
106
 
        self.addCleanup(tree.unlock)
107
 
        self.assertEqual(tree.get_file_text('foo-id'), 'hello')
108
 
 
109
 
    def test_get_revision_delta(self):
110
 
        tree_a = self.make_branch_and_tree('a')
111
 
        self.build_tree(['a/foo'])
112
 
        tree_a.add('foo', 'file1')
113
 
        tree_a.commit('rev1', rev_id='rev1')
114
 
        self.build_tree(['a/vla'])
115
 
        tree_a.add('vla', 'file2')
116
 
        tree_a.commit('rev2', rev_id='rev2')
117
 
 
118
 
        delta = tree_a.branch.get_revision_delta(1)
119
 
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
120
 
        self.assertEqual([('foo', 'file1', 'file')], delta.added)
121
 
        delta = tree_a.branch.get_revision_delta(2)
122
 
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
123
 
        self.assertEqual([('vla', 'file2', 'file')], delta.added)
124
 
 
125
 
    def get_unbalanced_tree_pair(self):
126
 
        """Return two branches, a and b, with one file in a."""
127
 
        tree_a = self.make_branch_and_tree('a')
128
 
        self.build_tree_contents([('a/b', 'b')])
129
 
        tree_a.add('b')
130
 
        tree_a.commit("silly commit", rev_id='A')
131
 
 
132
 
        tree_b = self.make_branch_and_tree('b')
133
 
        return tree_a, tree_b
134
 
 
135
 
    def get_balanced_branch_pair(self):
136
 
        """Returns br_a, br_b as with one commit in a, and b has a's stores."""
137
 
        tree_a, tree_b = self.get_unbalanced_tree_pair()
138
 
        tree_b.branch.repository.fetch(tree_a.branch.repository)
139
 
        return tree_a, tree_b
140
 
 
141
 
    def test_clone_partial(self):
142
 
        """Copy only part of the history of a branch."""
143
 
        # TODO: RBC 20060208 test with a revision not on revision-history.
144
 
        #       what should that behaviour be ? Emailed the list.
145
 
        # First, make a branch with two commits.
146
 
        wt_a = self.make_branch_and_tree('a')
147
 
        self.build_tree(['a/one'])
148
 
        wt_a.add(['one'])
149
 
        wt_a.commit('commit one', rev_id='1')
150
 
        self.build_tree(['a/two'])
151
 
        wt_a.add(['two'])
152
 
        wt_a.commit('commit two', rev_id='2')
153
 
        # Now make a copy of the repository.
154
 
        repo_b = self.make_repository('b')
155
 
        wt_a.branch.repository.copy_content_into(repo_b)
156
 
        # wt_a might be a lightweight checkout, so get a hold of the actual
157
 
        # branch (because you can't do a partial clone of a lightweight
158
 
        # checkout).
159
 
        branch = wt_a.branch.bzrdir.open_branch()
160
 
        # Then make a branch where the new repository is, but specify a revision
161
 
        # ID.  The new branch's history will stop at the specified revision.
162
 
        br_b = branch.clone(repo_b.bzrdir, revision_id='1')
163
 
        self.assertEqual('1', br_b.last_revision())
164
 
 
165
 
    def get_parented_branch(self):
166
 
        wt_a = self.make_branch_and_tree('a')
167
 
        self.build_tree(['a/one'])
168
 
        wt_a.add(['one'])
169
 
        wt_a.commit('commit one', rev_id='1')
170
 
 
171
 
        branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
172
 
        self.assertEqual(wt_a.branch.base, branch_b.get_parent())
173
 
        return branch_b
174
 
 
175
 
    def test_clone_branch_nickname(self):
176
 
        # test the nick name is preserved always
177
 
        raise tests.TestSkipped('XXX branch cloning is not yet tested.')
178
 
 
179
 
    def test_clone_branch_parent(self):
180
 
        # test the parent is preserved always
181
 
        branch_b = self.get_parented_branch()
182
 
        repo_c = self.make_repository('c')
183
 
        branch_b.repository.copy_content_into(repo_c)
184
 
        branch_c = branch_b.clone(repo_c.bzrdir)
185
 
        self.assertNotEqual(None, branch_c.get_parent())
186
 
        self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
187
 
 
188
 
        # We can also set a specific parent, and it should be honored
189
 
        random_parent = 'http://bazaar-vcs.org/path/to/branch'
190
 
        branch_b.set_parent(random_parent)
191
 
        repo_d = self.make_repository('d')
192
 
        branch_b.repository.copy_content_into(repo_d)
193
 
        branch_d = branch_b.clone(repo_d.bzrdir)
194
 
        self.assertEqual(random_parent, branch_d.get_parent())
195
 
 
196
 
    def test_submit_branch(self):
197
 
        """Submit location can be queried and set"""
198
 
        branch = self.make_branch('branch')
199
 
        self.assertEqual(branch.get_submit_branch(), None)
200
 
        branch.set_submit_branch('sftp://example.com')
201
 
        self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
202
 
        branch.set_submit_branch('sftp://example.net')
203
 
        self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
204
 
 
205
 
    def test_public_branch(self):
206
 
        """public location can be queried and set"""
207
 
        branch = self.make_branch('branch')
208
 
        self.assertEqual(branch.get_public_branch(), None)
209
 
        branch.set_public_branch('sftp://example.com')
210
 
        self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
211
 
        branch.set_public_branch('sftp://example.net')
212
 
        self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
213
 
        branch.set_public_branch(None)
214
 
        self.assertEqual(branch.get_public_branch(), None)
215
 
 
216
 
    def test_record_initial_ghost(self):
217
 
        """Branches should support having ghosts."""
218
 
        wt = self.make_branch_and_tree('.')
219
 
        wt.set_parent_ids(['non:existent@rev--ision--0--2'],
220
 
            allow_leftmost_as_ghost=True)
221
 
        self.assertEqual(['non:existent@rev--ision--0--2'],
222
 
            wt.get_parent_ids())
223
 
        rev_id = wt.commit('commit against a ghost first parent.')
224
 
        rev = wt.branch.repository.get_revision(rev_id)
225
 
        self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
226
 
        # parent_sha1s is not populated now, WTF. rbc 20051003
227
 
        self.assertEqual(len(rev.parent_sha1s), 0)
228
 
 
229
 
    def test_record_two_ghosts(self):
230
 
        """Recording with all ghosts works."""
231
 
        wt = self.make_branch_and_tree('.')
232
 
        wt.set_parent_ids([
233
 
                'foo@azkhazan-123123-abcabc',
234
 
                'wibble@fofof--20050401--1928390812',
235
 
            ],
236
 
            allow_leftmost_as_ghost=True)
237
 
        rev_id = wt.commit("commit from ghost base with one merge")
238
 
        # the revision should have been committed with two parents
239
 
        rev = wt.branch.repository.get_revision(rev_id)
240
 
        self.assertEqual(['foo@azkhazan-123123-abcabc',
241
 
            'wibble@fofof--20050401--1928390812'],
242
 
            rev.parent_ids)
243
 
 
244
 
    def test_bad_revision(self):
245
 
        self.assertRaises(errors.InvalidRevisionId,
246
 
                          self.get_branch().repository.get_revision,
247
 
                          None)
248
 
 
249
 
# TODO 20051003 RBC:
250
 
# compare the gpg-to-sign info for a commit with a ghost and
251
 
#     an identical tree without a ghost
252
 
# fetch missing should rewrite the TOC of weaves to list newly available parents.
253
 
 
254
 
    def test_sign_existing_revision(self):
255
 
        wt = self.make_branch_and_tree('.')
256
 
        branch = wt.branch
257
 
        wt.commit("base", allow_pointless=True, rev_id='A')
258
 
        from bzrlib.testament import Testament
259
 
        strategy = gpg.LoopbackGPGStrategy(None)
260
 
        branch.repository.lock_write()
261
 
        branch.repository.start_write_group()
262
 
        branch.repository.sign_revision('A', strategy)
263
 
        branch.repository.commit_write_group()
264
 
        branch.repository.unlock()
265
 
        self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
266
 
                         Testament.from_revision(branch.repository,
267
 
                         'A').as_short_text() +
268
 
                         '-----END PSEUDO-SIGNED CONTENT-----\n',
269
 
                         branch.repository.get_signature_text('A'))
270
 
 
271
 
    def test_store_signature(self):
272
 
        wt = self.make_branch_and_tree('.')
273
 
        branch = wt.branch
274
 
        branch.lock_write()
275
 
        try:
276
 
            branch.repository.start_write_group()
277
 
            try:
278
 
                branch.repository.store_revision_signature(
279
 
                    gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
280
 
            except:
281
 
                branch.repository.abort_write_group()
282
 
                raise
283
 
            else:
284
 
                branch.repository.commit_write_group()
285
 
        finally:
286
 
            branch.unlock()
287
 
        # A signature without a revision should not be accessible.
288
 
        self.assertRaises(errors.NoSuchRevision,
289
 
                          branch.repository.has_signature_for_revision_id,
290
 
                          'A')
291
 
        wt.commit("base", allow_pointless=True, rev_id='A')
292
 
        self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
293
 
                         'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
294
 
                         branch.repository.get_signature_text('A'))
295
 
 
296
 
    def test_branch_keeps_signatures(self):
297
 
        wt = self.make_branch_and_tree('source')
298
 
        wt.commit('A', allow_pointless=True, rev_id='A')
299
 
        repo = wt.branch.repository
300
 
        repo.lock_write()
301
 
        repo.start_write_group()
302
 
        repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
303
 
        repo.commit_write_group()
304
 
        repo.unlock()
305
 
        #FIXME: clone should work to urls,
306
 
        # wt.clone should work to disks.
307
 
        self.build_tree(['target/'])
308
 
        d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
309
 
        self.assertEqual(repo.get_signature_text('A'),
310
 
                         d2.open_repository().get_signature_text('A'))
311
 
 
312
 
    def test_missing_revisions(self):
313
 
        t1 = self.make_branch_and_tree('b1')
314
 
        rev1 = t1.commit('one')
315
 
        t2 = t1.bzrdir.sprout('b2').open_workingtree()
316
 
        rev2 = t1.commit('two')
317
 
        rev3 = t1.commit('three')
318
 
 
319
 
        self.assertEqual([rev2, rev3],
320
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
321
 
            t2.branch.missing_revisions, t1.branch))
322
 
 
323
 
        self.assertEqual([],
324
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
325
 
            t2.branch.missing_revisions, t1.branch, stop_revision=1))
326
 
        self.assertEqual([rev2],
327
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
328
 
            t2.branch.missing_revisions, t1.branch, stop_revision=2))
329
 
        self.assertEqual([rev2, rev3],
330
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
331
 
            t2.branch.missing_revisions, t1.branch, stop_revision=3))
332
 
 
333
 
        self.assertRaises(errors.NoSuchRevision,
334
 
            self.applyDeprecated, deprecated_in((1, 6, 0)),
335
 
            t2.branch.missing_revisions, t1.branch, stop_revision=4)
336
 
 
337
 
        rev4 = t2.commit('four')
338
 
        self.assertRaises(errors.DivergedBranches,
339
 
            self.applyDeprecated, deprecated_in((1, 6, 0)),
340
 
            t2.branch.missing_revisions, t1.branch)
341
 
 
342
 
    def test_nicks(self):
343
 
        """Test explicit and implicit branch nicknames.
344
 
 
345
 
        Nicknames are implicitly the name of the branch's directory, unless an
346
 
        explicit nickname is set.  That is, an explicit nickname always
347
 
        overrides the implicit one.
348
 
        """
349
 
        t = transport.get_transport(self.get_url())
350
 
        branch = self.make_branch('bzr.dev')
351
 
        # The nick will be 'bzr.dev', because there is no explicit nick set.
352
 
        self.assertEqual(branch.nick, 'bzr.dev')
353
 
        # Move the branch to a different directory, 'bzr.ab'.  Now that branch
354
 
        # will report its nick as 'bzr.ab'.
355
 
        t.move('bzr.dev', 'bzr.ab')
356
 
        branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
357
 
        self.assertEqual(branch.nick, 'bzr.ab')
358
 
        # Set the branch nick explicitly.  This will ensure there's a branch
359
 
        # config file in the branch.
360
 
        branch.nick = "Aaron's branch"
361
 
        if not isinstance(branch, remote.RemoteBranch):
362
 
            self.failUnless(branch._transport.has("branch.conf"))
363
 
        # Because the nick has been set explicitly, the nick is now always
364
 
        # "Aaron's branch", regardless of directory name.
365
 
        self.assertEqual(branch.nick, "Aaron's branch")
366
 
        t.move('bzr.ab', 'integration')
367
 
        branch = _mod_branch.Branch.open(self.get_url('integration'))
368
 
        self.assertEqual(branch.nick, "Aaron's branch")
369
 
        branch.nick = u"\u1234"
370
 
        self.assertEqual(branch.nick, u"\u1234")
371
 
 
372
 
    def test_commit_nicks(self):
373
 
        """Nicknames are committed to the revision"""
374
 
        wt = self.make_branch_and_tree('bzr.dev')
375
 
        branch = wt.branch
376
 
        branch.nick = "My happy branch"
377
 
        wt.commit('My commit respect da nick.')
378
 
        committed = branch.repository.get_revision(branch.last_revision())
379
 
        self.assertEqual(committed.properties["branch-nick"],
380
 
                         "My happy branch")
381
 
 
382
 
    def test_create_colocated(self):
383
 
        try:
384
 
            repo = self.make_repository('.', shared=True)
385
 
        except errors.IncompatibleFormat:
386
 
            return
387
 
        self.assertEquals(0, len(repo.bzrdir.list_branches()))
388
 
        try:
389
 
            child_branch1 = self.branch_format.initialize(repo.bzrdir, 
390
 
                name='branch1')
391
 
        except (errors.UninitializableFormat, errors.NoColocatedBranchSupport):
392
 
            # branch references are not default init'able and
393
 
            # not all bzrdirs support colocated branches.
394
 
            return
395
 
        self.assertEquals(1, len(repo.bzrdir.list_branches()))
396
 
        self.branch_format.initialize(repo.bzrdir, name='branch2')
397
 
        self.assertEquals(2, len(repo.bzrdir.list_branches()))
398
 
 
399
 
    def test_create_open_branch_uses_repository(self):
400
 
        try:
401
 
            repo = self.make_repository('.', shared=True)
402
 
        except errors.IncompatibleFormat:
403
 
            return
404
 
        child_transport = repo.bzrdir.root_transport.clone('child')
405
 
        child_transport.mkdir('.')
406
 
        child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
407
 
        try:
408
 
            child_branch = self.branch_format.initialize(child_dir)
409
 
        except errors.UninitializableFormat:
410
 
            # branch references are not default init'able.
411
 
            return
412
 
        self.assertEqual(repo.bzrdir.root_transport.base,
413
 
                         child_branch.repository.bzrdir.root_transport.base)
414
 
        child_branch = _mod_branch.Branch.open(self.get_url('child'))
415
 
        self.assertEqual(repo.bzrdir.root_transport.base,
416
 
                         child_branch.repository.bzrdir.root_transport.base)
417
 
 
418
 
    def test_format_description(self):
419
 
        tree = self.make_branch_and_tree('tree')
420
 
        text = tree.branch._format.get_format_description()
421
 
        self.failUnless(len(text))
422
 
 
423
 
    def test_get_commit_builder(self):
424
 
        branch = self.make_branch(".")
425
 
        branch.lock_write()
426
 
        builder = branch.get_commit_builder([])
427
 
        self.assertIsInstance(builder, repository.CommitBuilder)
428
 
        branch.repository.commit_write_group()
429
 
        branch.unlock()
430
 
 
431
 
    def test_generate_revision_history(self):
432
 
        """Create a fake revision history easily."""
433
 
        tree = self.make_branch_and_tree('.')
434
 
        rev1 = tree.commit('foo')
435
 
        orig_history = tree.branch.revision_history()
436
 
        rev2 = tree.commit('bar', allow_pointless=True)
437
 
        tree.branch.generate_revision_history(rev1)
438
 
        self.assertEqual(orig_history, tree.branch.revision_history())
439
 
 
440
 
    def test_generate_revision_history_NULL_REVISION(self):
441
 
        tree = self.make_branch_and_tree('.')
442
 
        rev1 = tree.commit('foo')
443
 
        tree.branch.generate_revision_history(revision.NULL_REVISION)
444
 
        self.assertEqual([], tree.branch.revision_history())
445
 
 
446
 
    def test_create_checkout(self):
447
 
        tree_a = self.make_branch_and_tree('a')
448
 
        branch_a = tree_a.branch
449
 
        checkout_b = branch_a.create_checkout('b')
450
 
        self.assertEqual('null:', checkout_b.last_revision())
451
 
        checkout_b.commit('rev1', rev_id='rev1')
452
 
        self.assertEqual('rev1', branch_a.last_revision())
453
 
        self.assertNotEqual(checkout_b.branch.base, branch_a.base)
454
 
 
455
 
        checkout_c = branch_a.create_checkout('c', lightweight=True)
456
 
        self.assertEqual('rev1', checkout_c.last_revision())
457
 
        checkout_c.commit('rev2', rev_id='rev2')
458
 
        self.assertEqual('rev2', branch_a.last_revision())
459
 
        self.assertEqual(checkout_c.branch.base, branch_a.base)
460
 
 
461
 
        checkout_d = branch_a.create_checkout('d', lightweight=True)
462
 
        self.assertEqual('rev2', checkout_d.last_revision())
463
 
        checkout_e = branch_a.create_checkout('e')
464
 
        self.assertEqual('rev2', checkout_e.last_revision())
465
 
 
466
 
    def test_create_anonymous_lightweight_checkout(self):
467
 
        """A lightweight checkout from a readonly branch should succeed."""
468
 
        tree_a = self.make_branch_and_tree('a')
469
 
        rev_id = tree_a.commit('put some content in the branch')
470
 
        # open the branch via a readonly transport
471
 
        source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
472
 
        # sanity check that the test will be valid
473
 
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
474
 
            source_branch.lock_write)
475
 
        checkout = source_branch.create_checkout('c', lightweight=True)
476
 
        self.assertEqual(rev_id, checkout.last_revision())
477
 
 
478
 
    def test_create_anonymous_heavyweight_checkout(self):
479
 
        """A regular checkout from a readonly branch should succeed."""
480
 
        tree_a = self.make_branch_and_tree('a')
481
 
        rev_id = tree_a.commit('put some content in the branch')
482
 
        # open the branch via a readonly transport
483
 
        source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
484
 
        # sanity check that the test will be valid
485
 
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
486
 
            source_branch.lock_write)
487
 
        checkout = source_branch.create_checkout('c')
488
 
        self.assertEqual(rev_id, checkout.last_revision())
489
 
 
490
 
    def test_set_revision_history(self):
491
 
        tree = self.make_branch_and_tree('a')
492
 
        tree.commit('a commit', rev_id='rev1')
493
 
        br = tree.branch
494
 
        br.set_revision_history(["rev1"])
495
 
        self.assertEquals(br.revision_history(), ["rev1"])
496
 
        br.set_revision_history([])
497
 
        self.assertEquals(br.revision_history(), [])
498
 
 
499
 
 
500
 
class TestBranchFormat(per_branch.TestCaseWithBranch):
501
 
 
502
 
    def test_branch_format_network_name(self):
503
 
        br = self.make_branch('.')
504
 
        format = br._format
505
 
        network_name = format.network_name()
506
 
        self.assertIsInstance(network_name, str)
507
 
        # We want to test that the network_name matches the actual format on
508
 
        # disk. For local branches that means that using network_name as a key
509
 
        # in the registry gives back the same format. For remote branches we
510
 
        # check that the network_name of the RemoteBranchFormat we have locally
511
 
        # matches the actual format present on disk.
512
 
        if isinstance(format, remote.RemoteBranchFormat):
513
 
            br._ensure_real()
514
 
            real_branch = br._real_branch
515
 
            self.assertEqual(real_branch._format.network_name(), network_name)
516
 
        else:
517
 
            registry = _mod_branch.network_format_registry
518
 
            looked_up_format = registry.get(network_name)
519
 
            self.assertEqual(format.__class__, looked_up_format.__class__)
520
 
 
521
 
 
522
 
class ChrootedTests(per_branch.TestCaseWithBranch):
523
 
    """A support class that provides readonly urls outside the local namespace.
524
 
 
525
 
    This is done by checking if self.transport_server is a MemoryServer. if it
526
 
    is then we are chrooted already, if it is not then an HttpServer is used
527
 
    for readonly urls.
528
 
    """
529
 
 
530
 
    def setUp(self):
531
 
        super(ChrootedTests, self).setUp()
532
 
        if not self.vfs_transport_factory == memory.MemoryServer:
533
 
            self.transport_readonly_server = HttpServer
534
 
 
535
 
    def test_open_containing(self):
536
 
        self.assertRaises(errors.NotBranchError,
537
 
                          _mod_branch.Branch.open_containing,
538
 
                          self.get_readonly_url(''))
539
 
        self.assertRaises(errors.NotBranchError,
540
 
                          _mod_branch.Branch.open_containing,
541
 
                          self.get_readonly_url('g/p/q'))
542
 
        branch = self.make_branch('.')
543
 
        branch, relpath = _mod_branch.Branch.open_containing(
544
 
            self.get_readonly_url(''))
545
 
        self.assertEqual('', relpath)
546
 
        branch, relpath = _mod_branch.Branch.open_containing(
547
 
            self.get_readonly_url('g/p/q'))
548
 
        self.assertEqual('g/p/q', relpath)
549
 
 
550
 
 
551
 
class InstrumentedTransaction(object):
552
 
 
553
 
    def finish(self):
554
 
        self.calls.append('finish')
555
 
 
556
 
    def __init__(self):
557
 
        self.calls = []
558
 
 
559
 
 
560
 
class TestDecorator(object):
561
 
 
562
 
    def __init__(self):
563
 
        self._calls = []
564
 
 
565
 
    def lock_read(self):
566
 
        self._calls.append('lr')
567
 
 
568
 
    def lock_write(self):
569
 
        self._calls.append('lw')
570
 
 
571
 
    def unlock(self):
572
 
        self._calls.append('ul')
573
 
 
574
 
    @_mod_branch.needs_read_lock
575
 
    def do_with_read(self):
576
 
        return 1
577
 
 
578
 
    @_mod_branch.needs_read_lock
579
 
    def except_with_read(self):
580
 
        raise RuntimeError
581
 
 
582
 
    @_mod_branch.needs_write_lock
583
 
    def do_with_write(self):
584
 
        return 2
585
 
 
586
 
    @_mod_branch.needs_write_lock
587
 
    def except_with_write(self):
588
 
        raise RuntimeError
589
 
 
590
 
 
591
 
class TestDecorators(tests.TestCase):
592
 
 
593
 
    def test_needs_read_lock(self):
594
 
        branch = TestDecorator()
595
 
        self.assertEqual(1, branch.do_with_read())
596
 
        self.assertEqual(['lr', 'ul'], branch._calls)
597
 
 
598
 
    def test_excepts_in_read_lock(self):
599
 
        branch = TestDecorator()
600
 
        self.assertRaises(RuntimeError, branch.except_with_read)
601
 
        self.assertEqual(['lr', 'ul'], branch._calls)
602
 
 
603
 
    def test_needs_write_lock(self):
604
 
        branch = TestDecorator()
605
 
        self.assertEqual(2, branch.do_with_write())
606
 
        self.assertEqual(['lw', 'ul'], branch._calls)
607
 
 
608
 
    def test_excepts_in_write_lock(self):
609
 
        branch = TestDecorator()
610
 
        self.assertRaises(RuntimeError, branch.except_with_write)
611
 
        self.assertEqual(['lw', 'ul'], branch._calls)
612
 
 
613
 
 
614
 
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
615
 
 
616
 
    def test_get_push_location_unset(self):
617
 
        self.assertEqual(None, self.get_branch().get_push_location())
618
 
 
619
 
    def test_get_push_location_exact(self):
620
 
        from bzrlib.config import (locations_config_filename,
621
 
                                   ensure_config_dir_exists)
622
 
        ensure_config_dir_exists()
623
 
        fn = locations_config_filename()
624
 
        open(fn, 'wt').write(("[%s]\n"
625
 
                                  "push_location=foo\n" %
626
 
                                  self.get_branch().base[:-1]))
627
 
        self.assertEqual("foo", self.get_branch().get_push_location())
628
 
 
629
 
    def test_set_push_location(self):
630
 
        branch = self.get_branch()
631
 
        branch.set_push_location('foo')
632
 
        self.assertEqual('foo', branch.get_push_location())
633
 
 
634
 
 
635
 
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
636
 
 
637
 
    def test_get_child_submit_format_default(self):
638
 
        self.assertEqual(None, self.get_branch().get_child_submit_format())
639
 
 
640
 
    def test_get_child_submit_format(self):
641
 
        branch = self.get_branch()
642
 
        branch.get_config().set_user_option('child_submit_format', '10')
643
 
        branch = self.get_branch()
644
 
        self.assertEqual('10', branch.get_child_submit_format())
645
 
 
646
 
 
647
 
class TestFormat(per_branch.TestCaseWithBranch):
648
 
    """Tests for the format itself."""
649
 
 
650
 
    def test_get_reference(self):
651
 
        """get_reference on all regular branches should return None."""
652
 
        if not self.branch_format.is_supported():
653
 
            # unsupported formats are not loopback testable
654
 
            # because the default open will not open them and
655
 
            # they may not be initializable.
656
 
            return
657
 
        made_branch = self.make_branch('.')
658
 
        self.assertEqual(None,
659
 
            made_branch._format.get_reference(made_branch.bzrdir))
660
 
 
661
 
    def test_set_reference(self):
662
 
        """set_reference on all regular branches should be callable."""
663
 
        if not self.branch_format.is_supported():
664
 
            # unsupported formats are not loopback testable
665
 
            # because the default open will not open them and
666
 
            # they may not be initializable.
667
 
            return
668
 
        this_branch = self.make_branch('this')
669
 
        other_branch = self.make_branch('other')
670
 
        try:
671
 
            this_branch._format.set_reference(this_branch.bzrdir, None,
672
 
                other_branch)
673
 
        except NotImplementedError:
674
 
            # that's ok
675
 
            pass
676
 
        else:
677
 
            ref = this_branch._format.get_reference(this_branch.bzrdir)
678
 
            self.assertEqual(ref, other_branch.base)
679
 
 
680
 
    def test_format_initialize_find_open(self):
681
 
        # loopback test to check the current format initializes to itself.
682
 
        if not self.branch_format.is_supported():
683
 
            # unsupported formats are not loopback testable
684
 
            # because the default open will not open them and
685
 
            # they may not be initializable.
686
 
            return
687
 
        # supported formats must be able to init and open
688
 
        t = transport.get_transport(self.get_url())
689
 
        readonly_t = transport.get_transport(self.get_readonly_url())
690
 
        made_branch = self.make_branch('.')
691
 
        self.failUnless(isinstance(made_branch, _mod_branch.Branch))
692
 
 
693
 
        # find it via bzrdir opening:
694
 
        opened_control = bzrdir.BzrDir.open(readonly_t.base)
695
 
        direct_opened_branch = opened_control.open_branch()
696
 
        self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
697
 
        self.assertEqual(opened_control, direct_opened_branch.bzrdir)
698
 
        self.failUnless(isinstance(direct_opened_branch._format,
699
 
                        self.branch_format.__class__))
700
 
 
701
 
        # find it via Branch.open
702
 
        opened_branch = _mod_branch.Branch.open(readonly_t.base)
703
 
        self.failUnless(isinstance(opened_branch, made_branch.__class__))
704
 
        self.assertEqual(made_branch._format.__class__,
705
 
                         opened_branch._format.__class__)
706
 
        # if it has a unique id string, can we probe for it ?
707
 
        try:
708
 
            self.branch_format.get_format_string()
709
 
        except NotImplementedError:
710
 
            return
711
 
        self.assertEqual(self.branch_format,
712
 
                         opened_control.find_branch_format())
713
 
 
714
 
 
715
 
class TestBound(per_branch.TestCaseWithBranch):
716
 
 
717
 
    def test_bind_unbind(self):
718
 
        branch = self.make_branch('1')
719
 
        branch2 = self.make_branch('2')
720
 
        try:
721
 
            branch.bind(branch2)
722
 
        except errors.UpgradeRequired:
723
 
            raise tests.TestNotApplicable('Format does not support binding')
724
 
        self.assertTrue(branch.unbind())
725
 
        self.assertFalse(branch.unbind())
726
 
        self.assertIs(None, branch.get_bound_location())
727
 
 
728
 
    def test_old_bound_location(self):
729
 
        branch = self.make_branch('branch1')
730
 
        try:
731
 
            self.assertIs(None, branch.get_old_bound_location())
732
 
        except errors.UpgradeRequired:
733
 
            raise tests.TestNotApplicable(
734
 
                    'Format does not store old bound locations')
735
 
        branch2 = self.make_branch('branch2')
736
 
        branch.bind(branch2)
737
 
        self.assertIs(None, branch.get_old_bound_location())
738
 
        branch.unbind()
739
 
        self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
740
 
 
741
 
    def test_bind_diverged(self):
742
 
        tree_a = self.make_branch_and_tree('tree_a')
743
 
        tree_a.commit('rev1a')
744
 
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
745
 
        tree_a.commit('rev2a')
746
 
        tree_b.commit('rev2b')
747
 
        try:
748
 
            tree_b.branch.bind(tree_a.branch)
749
 
        except errors.UpgradeRequired:
750
 
            raise tests.TestNotApplicable('Format does not support binding')
751
 
 
752
 
 
753
 
class TestStrict(per_branch.TestCaseWithBranch):
754
 
 
755
 
    def test_strict_history(self):
756
 
        tree1 = self.make_branch_and_tree('tree1')
757
 
        try:
758
 
            tree1.branch.set_append_revisions_only(True)
759
 
        except errors.UpgradeRequired:
760
 
            raise tests.TestSkipped('Format does not support strict history')
761
 
        tree1.commit('empty commit')
762
 
        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
763
 
        tree2.commit('empty commit 2')
764
 
        tree1.pull(tree2.branch)
765
 
        tree1.commit('empty commit 3')
766
 
        tree2.commit('empty commit 4')
767
 
        self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
768
 
        tree2.merge_from_branch(tree1.branch)
769
 
        tree2.commit('empty commit 5')
770
 
        self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
771
 
                          tree2.branch)
772
 
        tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
773
 
        tree3.merge_from_branch(tree2.branch)
774
 
        tree3.commit('empty commit 6')
775
 
        tree2.pull(tree3.branch)
776
 
 
777
 
 
778
 
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
779
 
 
780
 
    def make_branch_with_fallback(self):
781
 
        fallback = self.make_branch('fallback')
782
 
        if not fallback._format.supports_stacking():
783
 
            raise tests.TestNotApplicable("format does not support stacking")
784
 
        stacked = self.make_branch('stacked')
785
 
        stacked.set_stacked_on_url(fallback.base)
786
 
        return stacked
787
 
 
788
 
    def test_fallbacks_not_opened(self):
789
 
        stacked = self.make_branch_with_fallback()
790
 
        self.get_transport('').rename('fallback', 'moved')
791
 
        reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
792
 
        self.assertEqual([], reopened.repository._fallback_repositories)
793
 
 
794
 
    def test_fallbacks_are_opened(self):
795
 
        stacked = self.make_branch_with_fallback()
796
 
        reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
797
 
        self.assertLength(1, reopened.repository._fallback_repositories)
798
 
 
799
 
 
800
 
class TestReferenceLocation(per_branch.TestCaseWithBranch):
801
 
 
802
 
    def test_reference_parent(self):
803
 
        tree = self.make_branch_and_tree('tree')
804
 
        subtree = self.make_branch_and_tree('tree/subtree')
805
 
        subtree.set_root_id('subtree-id')
806
 
        try:
807
 
            tree.add_reference(subtree)
808
 
        except errors.UnsupportedOperation:
809
 
            raise tests.TestNotApplicable('Tree cannot hold references.')
810
 
        reference_parent = tree.branch.reference_parent('subtree-id',
811
 
                                                        'subtree')
812
 
        self.assertEqual(subtree.branch.base, reference_parent.base)
813
 
 
814
 
    def test_reference_parent_accepts_possible_transports(self):
815
 
        tree = self.make_branch_and_tree('tree')
816
 
        subtree = self.make_branch_and_tree('tree/subtree')
817
 
        subtree.set_root_id('subtree-id')
818
 
        try:
819
 
            tree.add_reference(subtree)
820
 
        except errors.UnsupportedOperation:
821
 
            raise tests.TestNotApplicable('Tree cannot hold references.')
822
 
        reference_parent = tree.branch.reference_parent('subtree-id',
823
 
            'subtree', possible_transports=[subtree.bzrdir.root_transport])
824
 
 
825
 
    def test_get_reference_info(self):
826
 
        branch = self.make_branch('branch')
827
 
        try:
828
 
            path, loc = branch.get_reference_info('file-id')
829
 
        except errors.UnsupportedOperation:
830
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
831
 
        self.assertIs(None, path)
832
 
        self.assertIs(None, loc)
833
 
 
834
 
    def test_set_reference_info(self):
835
 
        branch = self.make_branch('branch')
836
 
        try:
837
 
            branch.set_reference_info('file-id', 'path/to/location',
838
 
                                      'path/to/file')
839
 
        except errors.UnsupportedOperation:
840
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
841
 
 
842
 
    def test_set_get_reference_info(self):
843
 
        branch = self.make_branch('branch')
844
 
        try:
845
 
            branch.set_reference_info('file-id', 'path/to/file',
846
 
                                      'path/to/location')
847
 
        except errors.UnsupportedOperation:
848
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
849
 
        # Create a new instance to ensure storage is permanent
850
 
        branch = _mod_branch.Branch.open('branch')
851
 
        tree_path, branch_location = branch.get_reference_info('file-id')
852
 
        self.assertEqual('path/to/location', branch_location)
853
 
 
854
 
    def test_set_null_reference_info(self):
855
 
        branch = self.make_branch('branch')
856
 
        try:
857
 
            branch.set_reference_info('file-id', 'path/to/file',
858
 
                                      'path/to/location')
859
 
        except errors.UnsupportedOperation:
860
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
861
 
        branch.set_reference_info('file-id', None, None)
862
 
        tree_path, branch_location = branch.get_reference_info('file-id')
863
 
        self.assertIs(None, tree_path)
864
 
        self.assertIs(None, branch_location)
865
 
 
866
 
    def test_set_null_reference_info_when_null(self):
867
 
        branch = self.make_branch('branch')
868
 
        try:
869
 
            tree_path, branch_location = branch.get_reference_info('file-id')
870
 
        except errors.UnsupportedOperation:
871
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
872
 
        self.assertIs(None, tree_path)
873
 
        self.assertIs(None, branch_location)
874
 
        branch.set_reference_info('file-id', None, None)
875
 
 
876
 
    def test_set_null_requires_two_nones(self):
877
 
        branch = self.make_branch('branch')
878
 
        try:
879
 
            e = self.assertRaises(ValueError, branch.set_reference_info,
880
 
                                  'file-id', 'path', None)
881
 
        except errors.UnsupportedOperation:
882
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
883
 
        self.assertEqual('tree_path must be None when branch_location is'
884
 
                         ' None.', str(e))
885
 
        e = self.assertRaises(ValueError, branch.set_reference_info,
886
 
                              'file-id', None, 'location')
887
 
        self.assertEqual('branch_location must be None when tree_path is'
888
 
                         ' None.', str(e))
889
 
 
890
 
    def make_branch_with_reference(self, location, reference_location,
891
 
                                   file_id='file-id'):
892
 
        branch = self.make_branch(location)
893
 
        try:
894
 
            branch.set_reference_info(file_id, 'path/to/file',
895
 
                                      reference_location)
896
 
        except errors.UnsupportedOperation:
897
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
898
 
        return branch
899
 
 
900
 
    def test_reference_parent_from_reference_info_(self):
901
 
        referenced_branch = self.make_branch('reference_branch')
902
 
        branch = self.make_branch_with_reference('branch',
903
 
                                                 referenced_branch.base)
904
 
        parent = branch.reference_parent('file-id', 'path/to/file')
905
 
        self.assertEqual(parent.base, referenced_branch.base)
906
 
 
907
 
    def test_branch_relative_reference_location(self):
908
 
        branch = self.make_branch('branch')
909
 
        try:
910
 
            branch.set_reference_info('file-id', 'path/to/file',
911
 
            '../reference_branch')
912
 
        except errors.UnsupportedOperation:
913
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
914
 
        referenced_branch = self.make_branch('reference_branch')
915
 
        parent = branch.reference_parent('file-id', 'path/to/file')
916
 
        self.assertEqual(parent.base, referenced_branch.base)
917
 
 
918
 
    def test_sprout_copies_reference_location(self):
919
 
        branch = self.make_branch_with_reference('branch', '../reference')
920
 
        new_branch = branch.bzrdir.sprout('new-branch').open_branch()
921
 
        self.assertEqual('../reference',
922
 
                         new_branch.get_reference_info('file-id')[1])
923
 
 
924
 
    def test_clone_copies_reference_location(self):
925
 
        branch = self.make_branch_with_reference('branch', '../reference')
926
 
        new_branch = branch.bzrdir.clone('new-branch').open_branch()
927
 
        self.assertEqual('../reference',
928
 
                         new_branch.get_reference_info('file-id')[1])
929
 
 
930
 
    def test_copied_locations_are_rebased(self):
931
 
        branch = self.make_branch_with_reference('branch', 'reference')
932
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
933
 
        self.assertEqual('../reference',
934
 
                         new_branch.get_reference_info('file-id')[1])
935
 
 
936
 
    def test_update_references_retains_old_references(self):
937
 
        branch = self.make_branch_with_reference('branch', 'reference')
938
 
        new_branch = self.make_branch_with_reference(
939
 
            'new_branch', 'reference', 'file-id2')
940
 
        new_branch.update_references(branch)
941
 
        self.assertEqual('reference',
942
 
                         branch.get_reference_info('file-id')[1])
943
 
 
944
 
    def test_update_references_retains_known_references(self):
945
 
        branch = self.make_branch_with_reference('branch', 'reference')
946
 
        new_branch = self.make_branch_with_reference(
947
 
            'new_branch', 'reference2')
948
 
        new_branch.update_references(branch)
949
 
        self.assertEqual('reference',
950
 
                         branch.get_reference_info('file-id')[1])
951
 
 
952
 
    def test_update_references_skips_known_references(self):
953
 
        branch = self.make_branch_with_reference('branch', 'reference')
954
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
955
 
        new_branch.set_reference_info('file-id', '../foo', '../foo')
956
 
        new_branch.update_references(branch)
957
 
        self.assertEqual('reference',
958
 
                         branch.get_reference_info('file-id')[1])
959
 
 
960
 
    def test_pull_updates_references(self):
961
 
        branch = self.make_branch_with_reference('branch', 'reference')
962
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
963
 
        new_branch.set_reference_info('file-id2', '../foo', '../foo')
964
 
        branch.pull(new_branch)
965
 
        self.assertEqual('foo',
966
 
                         branch.get_reference_info('file-id2')[1])
967
 
 
968
 
    def test_push_updates_references(self):
969
 
        branch = self.make_branch_with_reference('branch', 'reference')
970
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
971
 
        new_branch.set_reference_info('file-id2', '../foo', '../foo')
972
 
        new_branch.push(branch)
973
 
        self.assertEqual('foo',
974
 
                         branch.get_reference_info('file-id2')[1])
975
 
 
976
 
    def test_merge_updates_references(self):
977
 
        branch = self.make_branch_with_reference('branch', 'reference')
978
 
        tree = self.make_branch_and_tree('tree')
979
 
        tree.commit('foo')
980
 
        branch.pull(tree.branch)
981
 
        checkout = branch.create_checkout('checkout', lightweight=True)
982
 
        checkout.commit('bar')
983
 
        tree.lock_write()
984
 
        self.addCleanup(tree.unlock)
985
 
        merger = merge.Merger.from_revision_ids(None, tree,
986
 
                                                branch.last_revision(),
987
 
                                                other_branch=branch)
988
 
        merger.merge_type = merge.Merge3Merger
989
 
        merger.do_merge()
990
 
        self.assertEqual('../branch/reference',
991
 
                         tree.branch.get_reference_info('file-id')[1])
992
 
 
993
 
 
994
 
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
995
 
    """Branch implementations adequately implement ControlComponent."""
996
 
    
997
 
    def test_urls(self):
998
 
        br = self.make_branch('branch')
999
 
        self.assertIsInstance(br.user_url, str)
1000
 
        self.assertEqual(br.user_url, br.user_transport.base)
1001
 
        # for all current bzrdir implementations the user dir must be 
1002
 
        # above the control dir but we might need to relax that?
1003
 
        self.assertEqual(br.control_url.find(br.user_url), 0)
1004
 
        self.assertEqual(br.control_url, br.control_transport.base)
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
from bzrlib.selftest import InTempDir
 
18
 
 
19
 
 
20
 
 
21
class TestAppendRevisions(InTempDir):
 
22
    """Test appending more than one revision"""
 
23
    def runTest(self):
 
24
        from bzrlib.branch import Branch
 
25
        br = Branch(".", init=True)
 
26
        br.append_revision("rev1")
 
27
        self.assertEquals(br.revision_history(), ["rev1",])
 
28
        br.append_revision("rev2", "rev3")
 
29
        self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
 
30
        
 
31
 
 
32
 
 
33
TEST_CLASSES = [
 
34
    TestAppendRevisions,
 
35
    ]