/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_branch.py

  • Committer: Robert Collins
  • Date: 2010-05-11 08:44:59 UTC
  • mfrom: (5221 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100511084459-pb0uinna9zs3wu59
Merge trunk - resolve conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
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
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for the Branch facility that are not interface  tests.
18
18
 
19
 
For interface tests see tests/branch_implementations/*.py.
 
19
For interface tests see tests/per_branch/*.py.
20
20
 
21
21
For concrete class tests see this file, and for meta-branch tests
22
22
also see this file.
23
23
"""
24
24
 
25
 
from StringIO import StringIO
26
 
 
27
 
import bzrlib.branch
28
 
from bzrlib.branch import (BzrBranch5, 
29
 
                           BzrBranchFormat5)
30
 
import bzrlib.bzrdir as bzrdir
31
 
from bzrlib.bzrdir import (BzrDirMetaFormat1, BzrDirMeta1, 
32
 
                           BzrDir, BzrDirFormat)
33
 
from bzrlib.errors import (NotBranchError,
34
 
                           UnknownFormatError,
35
 
                           UnsupportedFormatError,
36
 
                           )
37
 
 
38
 
from bzrlib.tests import TestCase, TestCaseWithTransport
39
 
from bzrlib.transport import get_transport
40
 
 
41
 
class TestDefaultFormat(TestCase):
 
25
from cStringIO import StringIO
 
26
 
 
27
from bzrlib import (
 
28
    branch as _mod_branch,
 
29
    bzrdir,
 
30
    config,
 
31
    errors,
 
32
    tests,
 
33
    trace,
 
34
    transport,
 
35
    urlutils,
 
36
    )
 
37
 
 
38
 
 
39
class TestDefaultFormat(tests.TestCase):
 
40
 
 
41
    def test_default_format(self):
 
42
        # update this if you change the default branch format
 
43
        self.assertIsInstance(_mod_branch.BranchFormat.get_default_format(),
 
44
                _mod_branch.BzrBranchFormat7)
 
45
 
 
46
    def test_default_format_is_same_as_bzrdir_default(self):
 
47
        # XXX: it might be nice if there was only one place the default was
 
48
        # set, but at the moment that's not true -- mbp 20070814 --
 
49
        # https://bugs.launchpad.net/bzr/+bug/132376
 
50
        self.assertEqual(
 
51
            _mod_branch.BranchFormat.get_default_format(),
 
52
            bzrdir.BzrDirFormat.get_default_format().get_branch_format())
42
53
 
43
54
    def test_get_set_default_format(self):
44
 
        old_format = bzrlib.branch.BranchFormat.get_default_format()
45
 
        # default is 5
46
 
        self.assertTrue(isinstance(old_format, bzrlib.branch.BzrBranchFormat5))
47
 
        bzrlib.branch.BranchFormat.set_default_format(SampleBranchFormat())
 
55
        # set the format and then set it back again
 
56
        old_format = _mod_branch.BranchFormat.get_default_format()
 
57
        _mod_branch.BranchFormat.set_default_format(SampleBranchFormat())
48
58
        try:
49
59
            # the default branch format is used by the meta dir format
50
60
            # which is not the default bzrdir format at this point
51
 
            dir = BzrDirMetaFormat1().initialize('memory:///')
 
61
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
52
62
            result = dir.create_branch()
53
63
            self.assertEqual(result, 'A branch')
54
64
        finally:
55
 
            bzrlib.branch.BranchFormat.set_default_format(old_format)
56
 
        self.assertEqual(old_format, bzrlib.branch.BranchFormat.get_default_format())
57
 
 
58
 
 
59
 
class TestBranchFormat5(TestCaseWithTransport):
 
65
            _mod_branch.BranchFormat.set_default_format(old_format)
 
66
        self.assertEqual(old_format,
 
67
                         _mod_branch.BranchFormat.get_default_format())
 
68
 
 
69
 
 
70
class TestBranchFormat5(tests.TestCaseWithTransport):
60
71
    """Tests specific to branch format 5"""
61
72
 
62
73
    def test_branch_format_5_uses_lockdir(self):
63
74
        url = self.get_url()
64
 
        bzrdir = BzrDirMetaFormat1().initialize(url)
65
 
        bzrdir.create_repository()
66
 
        branch = bzrdir.create_branch()
 
75
        bdir = bzrdir.BzrDirMetaFormat1().initialize(url)
 
76
        bdir.create_repository()
 
77
        branch = bdir.create_branch()
67
78
        t = self.get_transport()
68
79
        self.log("branch instance is %r" % branch)
69
 
        self.assert_(isinstance(branch, BzrBranch5))
 
80
        self.assert_(isinstance(branch, _mod_branch.BzrBranch5))
70
81
        self.assertIsDirectory('.', t)
71
82
        self.assertIsDirectory('.bzr/branch', t)
72
83
        self.assertIsDirectory('.bzr/branch/lock', t)
73
84
        branch.lock_write()
 
85
        self.addCleanup(branch.unlock)
 
86
        self.assertIsDirectory('.bzr/branch/lock/held', t)
 
87
 
 
88
    def test_set_push_location(self):
 
89
        from bzrlib.config import (locations_config_filename,
 
90
                                   ensure_config_dir_exists)
 
91
        ensure_config_dir_exists()
 
92
        fn = locations_config_filename()
 
93
        # write correct newlines to locations.conf
 
94
        # by default ConfigObj uses native line-endings for new files
 
95
        # but uses already existing line-endings if file is not empty
 
96
        f = open(fn, 'wb')
74
97
        try:
75
 
            self.assertIsDirectory('.bzr/branch/lock/held', t)
 
98
            f.write('# comment\n')
76
99
        finally:
77
 
            branch.unlock()
78
 
 
79
 
 
80
 
class TestBranchEscaping(TestCaseWithTransport):
81
 
    """Test a branch can be correctly stored and used on a vfat-like transport
82
 
    
83
 
    Makes sure we have proper escaping of invalid characters, etc.
84
 
 
85
 
    It'd be better to test all operations on the FakeVFATTransportDecorator,
86
 
    but working trees go straight to the os not through the Transport layer.
87
 
    Therefore we build some history first in the regular way and then 
88
 
    check it's safe to access for vfat.
89
 
    """
90
 
 
91
 
    FOO_ID = 'foo<:>ID'
92
 
    REV_ID = 'revid-1'
93
 
 
94
 
    def setUp(self):
95
 
        super(TestBranchEscaping, self).setUp()
96
 
        from bzrlib.repository import RepositoryFormatKnit1
97
 
        bzrdir = BzrDirMetaFormat1().initialize(self.get_url())
98
 
        repo = RepositoryFormatKnit1().initialize(bzrdir)
99
 
        branch = bzrdir.create_branch()
100
 
        wt = bzrdir.create_workingtree()
101
 
        self.build_tree_contents([("foo", "contents of foo")])
102
 
        # add file with id containing wierd characters
103
 
        wt.add(['foo'], [self.FOO_ID])
104
 
        wt.commit('this is my new commit', rev_id=self.REV_ID)
105
 
 
106
 
    def test_branch_on_vfat(self):
107
 
        from bzrlib.transport.fakevfat import FakeVFATTransportDecorator
108
 
        # now access over vfat; should be safe
109
 
        transport = FakeVFATTransportDecorator('vfat+' + self.get_url())
110
 
        bzrdir, junk = BzrDir.open_containing_from_transport(transport)
111
 
        branch = bzrdir.open_branch()
112
 
        revtree = branch.repository.revision_tree(self.REV_ID)
113
 
        contents = revtree.get_file_text(self.FOO_ID)
114
 
        self.assertEqual(contents, 'contents of foo')
115
 
 
116
 
 
117
 
class SampleBranchFormat(bzrlib.branch.BranchFormat):
 
100
            f.close()
 
101
 
 
102
        branch = self.make_branch('.', format='knit')
 
103
        branch.set_push_location('foo')
 
104
        local_path = urlutils.local_path_from_url(branch.base[:-1])
 
105
        self.assertFileEqual("# comment\n"
 
106
                             "[%s]\n"
 
107
                             "push_location = foo\n"
 
108
                             "push_location:policy = norecurse\n" % local_path,
 
109
                             fn)
 
110
 
 
111
    # TODO RBC 20051029 test getting a push location from a branch in a
 
112
    # recursive section - that is, it appends the branch name.
 
113
 
 
114
 
 
115
class SampleBranchFormat(_mod_branch.BranchFormat):
118
116
    """A sample format
119
117
 
120
 
    this format is initializable, unsupported to aid in testing the 
 
118
    this format is initializable, unsupported to aid in testing the
121
119
    open and open_downlevel routines.
122
120
    """
123
121
 
125
123
        """See BzrBranchFormat.get_format_string()."""
126
124
        return "Sample branch format."
127
125
 
128
 
    def initialize(self, a_bzrdir):
 
126
    def initialize(self, a_bzrdir, name=None):
129
127
        """Format 4 branches cannot be created."""
130
 
        t = a_bzrdir.get_branch_transport(self)
 
128
        t = a_bzrdir.get_branch_transport(self, name=name)
131
129
        t.put_bytes('format', self.get_format_string())
132
130
        return 'A branch'
133
131
 
134
132
    def is_supported(self):
135
133
        return False
136
134
 
137
 
    def open(self, transport, _found=False):
 
135
    def open(self, transport, name=None, _found=False, ignore_fallbacks=False):
138
136
        return "opened branch."
139
137
 
140
138
 
141
 
class TestBzrBranchFormat(TestCaseWithTransport):
 
139
class TestBzrBranchFormat(tests.TestCaseWithTransport):
142
140
    """Tests for the BzrBranchFormat facility."""
143
141
 
144
142
    def test_find_format(self):
145
143
        # is the right format object found for a branch?
146
144
        # create a branch with a few known format objects.
147
 
        # this is not quite the same as 
 
145
        # this is not quite the same as
148
146
        self.build_tree(["foo/", "bar/"])
149
147
        def check_format(format, url):
150
148
            dir = format._matchingbzrdir.initialize(url)
151
149
            dir.create_repository()
152
150
            format.initialize(dir)
153
 
            found_format = bzrlib.branch.BranchFormat.find_format(dir)
 
151
            found_format = _mod_branch.BranchFormat.find_format(dir)
154
152
            self.failUnless(isinstance(found_format, format.__class__))
155
 
        check_format(bzrlib.branch.BzrBranchFormat5(), "bar")
156
 
        
 
153
        check_format(_mod_branch.BzrBranchFormat5(), "bar")
 
154
 
157
155
    def test_find_format_not_branch(self):
158
156
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
159
 
        self.assertRaises(NotBranchError,
160
 
                          bzrlib.branch.BranchFormat.find_format,
 
157
        self.assertRaises(errors.NotBranchError,
 
158
                          _mod_branch.BranchFormat.find_format,
161
159
                          dir)
162
160
 
163
161
    def test_find_format_unknown_format(self):
164
162
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
165
163
        SampleBranchFormat().initialize(dir)
166
 
        self.assertRaises(UnknownFormatError,
167
 
                          bzrlib.branch.BranchFormat.find_format,
 
164
        self.assertRaises(errors.UnknownFormatError,
 
165
                          _mod_branch.BranchFormat.find_format,
168
166
                          dir)
169
167
 
170
168
    def test_register_unregister_format(self):
174
172
        # make a branch
175
173
        format.initialize(dir)
176
174
        # register a format for it.
177
 
        bzrlib.branch.BranchFormat.register_format(format)
 
175
        _mod_branch.BranchFormat.register_format(format)
178
176
        # which branch.Open will refuse (not supported)
179
 
        self.assertRaises(UnsupportedFormatError, bzrlib.branch.Branch.open, self.get_url())
 
177
        self.assertRaises(errors.UnsupportedFormatError,
 
178
                          _mod_branch.Branch.open, self.get_url())
 
179
        self.make_branch_and_tree('foo')
180
180
        # but open_downlevel will work
181
 
        self.assertEqual(format.open(dir), bzrdir.BzrDir.open(self.get_url()).open_branch(unsupported=True))
 
181
        self.assertEqual(
 
182
            format.open(dir),
 
183
            bzrdir.BzrDir.open(self.get_url()).open_branch(unsupported=True))
182
184
        # unregister the format
183
 
        bzrlib.branch.BranchFormat.unregister_format(format)
184
 
 
185
 
 
186
 
class TestBranchReference(TestCaseWithTransport):
 
185
        _mod_branch.BranchFormat.unregister_format(format)
 
186
        self.make_branch_and_tree('bar')
 
187
 
 
188
 
 
189
class TestBranch67(object):
 
190
    """Common tests for both branch 6 and 7 which are mostly the same."""
 
191
 
 
192
    def get_format_name(self):
 
193
        raise NotImplementedError(self.get_format_name)
 
194
 
 
195
    def get_format_name_subtree(self):
 
196
        raise NotImplementedError(self.get_format_name)
 
197
 
 
198
    def get_class(self):
 
199
        raise NotImplementedError(self.get_class)
 
200
 
 
201
    def test_creation(self):
 
202
        format = bzrdir.BzrDirMetaFormat1()
 
203
        format.set_branch_format(_mod_branch.BzrBranchFormat6())
 
204
        branch = self.make_branch('a', format=format)
 
205
        self.assertIsInstance(branch, self.get_class())
 
206
        branch = self.make_branch('b', format=self.get_format_name())
 
207
        self.assertIsInstance(branch, self.get_class())
 
208
        branch = _mod_branch.Branch.open('a')
 
209
        self.assertIsInstance(branch, self.get_class())
 
210
 
 
211
    def test_layout(self):
 
212
        branch = self.make_branch('a', format=self.get_format_name())
 
213
        self.failUnlessExists('a/.bzr/branch/last-revision')
 
214
        self.failIfExists('a/.bzr/branch/revision-history')
 
215
        self.failIfExists('a/.bzr/branch/references')
 
216
 
 
217
    def test_config(self):
 
218
        """Ensure that all configuration data is stored in the branch"""
 
219
        branch = self.make_branch('a', format=self.get_format_name())
 
220
        branch.set_parent('http://bazaar-vcs.org')
 
221
        self.failIfExists('a/.bzr/branch/parent')
 
222
        self.assertEqual('http://bazaar-vcs.org', branch.get_parent())
 
223
        branch.set_push_location('sftp://bazaar-vcs.org')
 
224
        config = branch.get_config()._get_branch_data_config()
 
225
        self.assertEqual('sftp://bazaar-vcs.org',
 
226
                         config.get_user_option('push_location'))
 
227
        branch.set_bound_location('ftp://bazaar-vcs.org')
 
228
        self.failIfExists('a/.bzr/branch/bound')
 
229
        self.assertEqual('ftp://bazaar-vcs.org', branch.get_bound_location())
 
230
 
 
231
    def test_set_revision_history(self):
 
232
        builder = self.make_branch_builder('.', format=self.get_format_name())
 
233
        builder.build_snapshot('foo', None,
 
234
            [('add', ('', None, 'directory', None))],
 
235
            message='foo')
 
236
        builder.build_snapshot('bar', None, [], message='bar')
 
237
        branch = builder.get_branch()
 
238
        branch.lock_write()
 
239
        self.addCleanup(branch.unlock)
 
240
        branch.set_revision_history(['foo', 'bar'])
 
241
        branch.set_revision_history(['foo'])
 
242
        self.assertRaises(errors.NotLefthandHistory,
 
243
                          branch.set_revision_history, ['bar'])
 
244
 
 
245
    def do_checkout_test(self, lightweight=False):
 
246
        tree = self.make_branch_and_tree('source',
 
247
            format=self.get_format_name_subtree())
 
248
        subtree = self.make_branch_and_tree('source/subtree',
 
249
            format=self.get_format_name_subtree())
 
250
        subsubtree = self.make_branch_and_tree('source/subtree/subsubtree',
 
251
            format=self.get_format_name_subtree())
 
252
        self.build_tree(['source/subtree/file',
 
253
                         'source/subtree/subsubtree/file'])
 
254
        subsubtree.add('file')
 
255
        subtree.add('file')
 
256
        subtree.add_reference(subsubtree)
 
257
        tree.add_reference(subtree)
 
258
        tree.commit('a revision')
 
259
        subtree.commit('a subtree file')
 
260
        subsubtree.commit('a subsubtree file')
 
261
        tree.branch.create_checkout('target', lightweight=lightweight)
 
262
        self.failUnlessExists('target')
 
263
        self.failUnlessExists('target/subtree')
 
264
        self.failUnlessExists('target/subtree/file')
 
265
        self.failUnlessExists('target/subtree/subsubtree/file')
 
266
        subbranch = _mod_branch.Branch.open('target/subtree/subsubtree')
 
267
        if lightweight:
 
268
            self.assertEndsWith(subbranch.base, 'source/subtree/subsubtree/')
 
269
        else:
 
270
            self.assertEndsWith(subbranch.base, 'target/subtree/subsubtree/')
 
271
 
 
272
    def test_checkout_with_references(self):
 
273
        self.do_checkout_test()
 
274
 
 
275
    def test_light_checkout_with_references(self):
 
276
        self.do_checkout_test(lightweight=True)
 
277
 
 
278
    def test_set_push(self):
 
279
        branch = self.make_branch('source', format=self.get_format_name())
 
280
        branch.get_config().set_user_option('push_location', 'old',
 
281
            store=config.STORE_LOCATION)
 
282
        warnings = []
 
283
        def warning(*args):
 
284
            warnings.append(args[0] % args[1:])
 
285
        _warning = trace.warning
 
286
        trace.warning = warning
 
287
        try:
 
288
            branch.set_push_location('new')
 
289
        finally:
 
290
            trace.warning = _warning
 
291
        self.assertEqual(warnings[0], 'Value "new" is masked by "old" from '
 
292
                         'locations.conf')
 
293
 
 
294
 
 
295
class TestBranch6(TestBranch67, tests.TestCaseWithTransport):
 
296
 
 
297
    def get_class(self):
 
298
        return _mod_branch.BzrBranch6
 
299
 
 
300
    def get_format_name(self):
 
301
        return "dirstate-tags"
 
302
 
 
303
    def get_format_name_subtree(self):
 
304
        return "dirstate-with-subtree"
 
305
 
 
306
    def test_set_stacked_on_url_errors(self):
 
307
        branch = self.make_branch('a', format=self.get_format_name())
 
308
        self.assertRaises(errors.UnstackableBranchFormat,
 
309
            branch.set_stacked_on_url, None)
 
310
 
 
311
    def test_default_stacked_location(self):
 
312
        branch = self.make_branch('a', format=self.get_format_name())
 
313
        self.assertRaises(errors.UnstackableBranchFormat, branch.get_stacked_on_url)
 
314
 
 
315
 
 
316
class TestBranch7(TestBranch67, tests.TestCaseWithTransport):
 
317
 
 
318
    def get_class(self):
 
319
        return _mod_branch.BzrBranch7
 
320
 
 
321
    def get_format_name(self):
 
322
        return "1.9"
 
323
 
 
324
    def get_format_name_subtree(self):
 
325
        return "development-subtree"
 
326
 
 
327
    def test_set_stacked_on_url_unstackable_repo(self):
 
328
        repo = self.make_repository('a', format='dirstate-tags')
 
329
        control = repo.bzrdir
 
330
        branch = _mod_branch.BzrBranchFormat7().initialize(control)
 
331
        target = self.make_branch('b')
 
332
        self.assertRaises(errors.UnstackableRepositoryFormat,
 
333
            branch.set_stacked_on_url, target.base)
 
334
 
 
335
    def test_clone_stacked_on_unstackable_repo(self):
 
336
        repo = self.make_repository('a', format='dirstate-tags')
 
337
        control = repo.bzrdir
 
338
        branch = _mod_branch.BzrBranchFormat7().initialize(control)
 
339
        # Calling clone should not raise UnstackableRepositoryFormat.
 
340
        cloned_bzrdir = control.clone('cloned')
 
341
 
 
342
    def _test_default_stacked_location(self):
 
343
        branch = self.make_branch('a', format=self.get_format_name())
 
344
        self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
 
345
 
 
346
    def test_stack_and_unstack(self):
 
347
        branch = self.make_branch('a', format=self.get_format_name())
 
348
        target = self.make_branch_and_tree('b', format=self.get_format_name())
 
349
        branch.set_stacked_on_url(target.branch.base)
 
350
        self.assertEqual(target.branch.base, branch.get_stacked_on_url())
 
351
        revid = target.commit('foo')
 
352
        self.assertTrue(branch.repository.has_revision(revid))
 
353
        branch.set_stacked_on_url(None)
 
354
        self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
 
355
        self.assertFalse(branch.repository.has_revision(revid))
 
356
 
 
357
    def test_open_opens_stacked_reference(self):
 
358
        branch = self.make_branch('a', format=self.get_format_name())
 
359
        target = self.make_branch_and_tree('b', format=self.get_format_name())
 
360
        branch.set_stacked_on_url(target.branch.base)
 
361
        branch = branch.bzrdir.open_branch()
 
362
        revid = target.commit('foo')
 
363
        self.assertTrue(branch.repository.has_revision(revid))
 
364
 
 
365
 
 
366
class BzrBranch8(tests.TestCaseWithTransport):
 
367
 
 
368
    def make_branch(self, location, format=None):
 
369
        if format is None:
 
370
            format = bzrdir.format_registry.make_bzrdir('1.9')
 
371
            format.set_branch_format(_mod_branch.BzrBranchFormat8())
 
372
        return tests.TestCaseWithTransport.make_branch(
 
373
            self, location, format=format)
 
374
 
 
375
    def create_branch_with_reference(self):
 
376
        branch = self.make_branch('branch')
 
377
        branch._set_all_reference_info({'file-id': ('path', 'location')})
 
378
        return branch
 
379
 
 
380
    @staticmethod
 
381
    def instrument_branch(branch, gets):
 
382
        old_get = branch._transport.get
 
383
        def get(*args, **kwargs):
 
384
            gets.append((args, kwargs))
 
385
            return old_get(*args, **kwargs)
 
386
        branch._transport.get = get
 
387
 
 
388
    def test_reference_info_caching_read_locked(self):
 
389
        gets = []
 
390
        branch = self.create_branch_with_reference()
 
391
        branch.lock_read()
 
392
        self.addCleanup(branch.unlock)
 
393
        self.instrument_branch(branch, gets)
 
394
        branch.get_reference_info('file-id')
 
395
        branch.get_reference_info('file-id')
 
396
        self.assertEqual(1, len(gets))
 
397
 
 
398
    def test_reference_info_caching_read_unlocked(self):
 
399
        gets = []
 
400
        branch = self.create_branch_with_reference()
 
401
        self.instrument_branch(branch, gets)
 
402
        branch.get_reference_info('file-id')
 
403
        branch.get_reference_info('file-id')
 
404
        self.assertEqual(2, len(gets))
 
405
 
 
406
    def test_reference_info_caching_write_locked(self):
 
407
        gets = []
 
408
        branch = self.make_branch('branch')
 
409
        branch.lock_write()
 
410
        self.instrument_branch(branch, gets)
 
411
        self.addCleanup(branch.unlock)
 
412
        branch._set_all_reference_info({'file-id': ('path2', 'location2')})
 
413
        path, location = branch.get_reference_info('file-id')
 
414
        self.assertEqual(0, len(gets))
 
415
        self.assertEqual('path2', path)
 
416
        self.assertEqual('location2', location)
 
417
 
 
418
    def test_reference_info_caches_cleared(self):
 
419
        branch = self.make_branch('branch')
 
420
        branch.lock_write()
 
421
        branch.set_reference_info('file-id', 'path2', 'location2')
 
422
        branch.unlock()
 
423
        doppelganger = _mod_branch.Branch.open('branch')
 
424
        doppelganger.set_reference_info('file-id', 'path3', 'location3')
 
425
        self.assertEqual(('path3', 'location3'),
 
426
                         branch.get_reference_info('file-id'))
 
427
 
 
428
class TestBranchReference(tests.TestCaseWithTransport):
187
429
    """Tests for the branch reference facility."""
188
430
 
189
431
    def test_create_open_reference(self):
190
432
        bzrdirformat = bzrdir.BzrDirMetaFormat1()
191
 
        t = get_transport(self.get_url('.'))
 
433
        t = transport.get_transport(self.get_url('.'))
192
434
        t.mkdir('repo')
193
435
        dir = bzrdirformat.initialize(self.get_url('repo'))
194
436
        dir.create_repository()
195
437
        target_branch = dir.create_branch()
196
438
        t.mkdir('branch')
197
439
        branch_dir = bzrdirformat.initialize(self.get_url('branch'))
198
 
        made_branch = bzrlib.branch.BranchReferenceFormat().initialize(branch_dir, target_branch)
 
440
        made_branch = _mod_branch.BranchReferenceFormat().initialize(
 
441
            branch_dir, target_branch=target_branch)
199
442
        self.assertEqual(made_branch.base, target_branch.base)
200
443
        opened_branch = branch_dir.open_branch()
201
444
        self.assertEqual(opened_branch.base, target_branch.base)
 
445
 
 
446
    def test_get_reference(self):
 
447
        """For a BranchReference, get_reference should reutrn the location."""
 
448
        branch = self.make_branch('target')
 
449
        checkout = branch.create_checkout('checkout', lightweight=True)
 
450
        reference_url = branch.bzrdir.root_transport.abspath('') + '/'
 
451
        # if the api for create_checkout changes to return different checkout types
 
452
        # then this file read will fail.
 
453
        self.assertFileEqual(reference_url, 'checkout/.bzr/branch/location')
 
454
        self.assertEqual(reference_url,
 
455
            _mod_branch.BranchReferenceFormat().get_reference(checkout.bzrdir))
 
456
 
 
457
 
 
458
class TestHooks(tests.TestCaseWithTransport):
 
459
 
 
460
    def test_constructor(self):
 
461
        """Check that creating a BranchHooks instance has the right defaults."""
 
462
        hooks = _mod_branch.BranchHooks()
 
463
        self.assertTrue("set_rh" in hooks, "set_rh not in %s" % hooks)
 
464
        self.assertTrue("post_push" in hooks, "post_push not in %s" % hooks)
 
465
        self.assertTrue("post_commit" in hooks, "post_commit not in %s" % hooks)
 
466
        self.assertTrue("pre_commit" in hooks, "pre_commit not in %s" % hooks)
 
467
        self.assertTrue("post_pull" in hooks, "post_pull not in %s" % hooks)
 
468
        self.assertTrue("post_uncommit" in hooks,
 
469
                        "post_uncommit not in %s" % hooks)
 
470
        self.assertTrue("post_change_branch_tip" in hooks,
 
471
                        "post_change_branch_tip not in %s" % hooks)
 
472
        self.assertTrue("post_branch_init" in hooks,
 
473
                        "post_branch_init not in %s" % hooks)
 
474
        self.assertTrue("post_switch" in hooks,
 
475
                        "post_switch not in %s" % hooks)
 
476
 
 
477
    def test_installed_hooks_are_BranchHooks(self):
 
478
        """The installed hooks object should be a BranchHooks."""
 
479
        # the installed hooks are saved in self._preserved_hooks.
 
480
        self.assertIsInstance(self._preserved_hooks[_mod_branch.Branch][1],
 
481
                              _mod_branch.BranchHooks)
 
482
 
 
483
    def test_post_branch_init_hook(self):
 
484
        calls = []
 
485
        _mod_branch.Branch.hooks.install_named_hook('post_branch_init',
 
486
            calls.append, None)
 
487
        self.assertLength(0, calls)
 
488
        branch = self.make_branch('a')
 
489
        self.assertLength(1, calls)
 
490
        params = calls[0]
 
491
        self.assertIsInstance(params, _mod_branch.BranchInitHookParams)
 
492
        self.assertTrue(hasattr(params, 'bzrdir'))
 
493
        self.assertTrue(hasattr(params, 'branch'))
 
494
 
 
495
    def test_post_switch_hook(self):
 
496
        from bzrlib import switch
 
497
        calls = []
 
498
        _mod_branch.Branch.hooks.install_named_hook('post_switch',
 
499
            calls.append, None)
 
500
        tree = self.make_branch_and_tree('branch-1')
 
501
        self.build_tree(['branch-1/file-1'])
 
502
        tree.add('file-1')
 
503
        tree.commit('rev1')
 
504
        to_branch = tree.bzrdir.sprout('branch-2').open_branch()
 
505
        self.build_tree(['branch-1/file-2'])
 
506
        tree.add('file-2')
 
507
        tree.remove('file-1')
 
508
        tree.commit('rev2')
 
509
        checkout = tree.branch.create_checkout('checkout')
 
510
        self.assertLength(0, calls)
 
511
        switch.switch(checkout.bzrdir, to_branch)
 
512
        self.assertLength(1, calls)
 
513
        params = calls[0]
 
514
        self.assertIsInstance(params, _mod_branch.SwitchHookParams)
 
515
        self.assertTrue(hasattr(params, 'to_branch'))
 
516
        self.assertTrue(hasattr(params, 'revision_id'))
 
517
 
 
518
 
 
519
class TestBranchOptions(tests.TestCaseWithTransport):
 
520
 
 
521
    def setUp(self):
 
522
        super(TestBranchOptions, self).setUp()
 
523
        self.branch = self.make_branch('.')
 
524
        self.config = self.branch.get_config()
 
525
 
 
526
    def check_append_revisions_only(self, expected_value, value=None):
 
527
        """Set append_revisions_only in config and check its interpretation."""
 
528
        if value is not None:
 
529
            self.config.set_user_option('append_revisions_only', value)
 
530
        self.assertEqual(expected_value,
 
531
                         self.branch._get_append_revisions_only())
 
532
 
 
533
    def test_valid_append_revisions_only(self):
 
534
        self.assertEquals(None,
 
535
                          self.config.get_user_option('append_revisions_only'))
 
536
        self.check_append_revisions_only(None)
 
537
        self.check_append_revisions_only(False, 'False')
 
538
        self.check_append_revisions_only(True, 'True')
 
539
        # The following values will cause compatibility problems on projects
 
540
        # using older bzr versions (<2.2) but are accepted
 
541
        self.check_append_revisions_only(False, 'false')
 
542
        self.check_append_revisions_only(True, 'true')
 
543
 
 
544
    def test_invalid_append_revisions_only(self):
 
545
        """Ensure warning is noted on invalid settings"""
 
546
        self.warnings = []
 
547
        def warning(*args):
 
548
            self.warnings.append(args[0] % args[1:])
 
549
        self.overrideAttr(trace, 'warning', warning)
 
550
        self.check_append_revisions_only(None, 'not-a-bool')
 
551
        self.assertLength(1, self.warnings)
 
552
        self.assertEqual(
 
553
            'Value "not-a-bool" is not a boolean for "append_revisions_only"',
 
554
            self.warnings[0])
 
555
 
 
556
 
 
557
class TestPullResult(tests.TestCase):
 
558
 
 
559
    def test_pull_result_to_int(self):
 
560
        # to support old code, the pull result can be used as an int
 
561
        r = _mod_branch.PullResult()
 
562
        r.old_revno = 10
 
563
        r.new_revno = 20
 
564
        # this usage of results is not recommended for new code (because it
 
565
        # doesn't describe very well what happened), but for api stability
 
566
        # it's still supported
 
567
        a = "%d revisions pulled" % r
 
568
        self.assertEqual(a, "10 revisions pulled")
 
569
 
 
570
    def test_report_changed(self):
 
571
        r = _mod_branch.PullResult()
 
572
        r.old_revid = "old-revid"
 
573
        r.old_revno = 10
 
574
        r.new_revid = "new-revid"
 
575
        r.new_revno = 20
 
576
        f = StringIO()
 
577
        r.report(f)
 
578
        self.assertEqual("Now on revision 20.\n", f.getvalue())
 
579
 
 
580
    def test_report_unchanged(self):
 
581
        r = _mod_branch.PullResult()
 
582
        r.old_revid = "same-revid"
 
583
        r.new_revid = "same-revid"
 
584
        f = StringIO()
 
585
        r.report(f)
 
586
        self.assertEqual("No revisions to pull.\n", f.getvalue())
 
587
 
 
588
 
 
589
class _StubLockable(object):
 
590
    """Helper for TestRunWithWriteLockedTarget."""
 
591
 
 
592
    def __init__(self, calls, unlock_exc=None):
 
593
        self.calls = calls
 
594
        self.unlock_exc = unlock_exc
 
595
 
 
596
    def lock_write(self):
 
597
        self.calls.append('lock_write')
 
598
 
 
599
    def unlock(self):
 
600
        self.calls.append('unlock')
 
601
        if self.unlock_exc is not None:
 
602
            raise self.unlock_exc
 
603
 
 
604
 
 
605
class _ErrorFromCallable(Exception):
 
606
    """Helper for TestRunWithWriteLockedTarget."""
 
607
 
 
608
 
 
609
class _ErrorFromUnlock(Exception):
 
610
    """Helper for TestRunWithWriteLockedTarget."""
 
611
 
 
612
 
 
613
class TestRunWithWriteLockedTarget(tests.TestCase):
 
614
    """Tests for _run_with_write_locked_target."""
 
615
 
 
616
    def setUp(self):
 
617
        tests.TestCase.setUp(self)
 
618
        self._calls = []
 
619
 
 
620
    def func_that_returns_ok(self):
 
621
        self._calls.append('func called')
 
622
        return 'ok'
 
623
 
 
624
    def func_that_raises(self):
 
625
        self._calls.append('func called')
 
626
        raise _ErrorFromCallable()
 
627
 
 
628
    def test_success_unlocks(self):
 
629
        lockable = _StubLockable(self._calls)
 
630
        result = _mod_branch._run_with_write_locked_target(
 
631
            lockable, self.func_that_returns_ok)
 
632
        self.assertEqual('ok', result)
 
633
        self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)
 
634
 
 
635
    def test_exception_unlocks_and_propagates(self):
 
636
        lockable = _StubLockable(self._calls)
 
637
        self.assertRaises(_ErrorFromCallable,
 
638
                          _mod_branch._run_with_write_locked_target,
 
639
                          lockable, self.func_that_raises)
 
640
        self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)
 
641
 
 
642
    def test_callable_succeeds_but_error_during_unlock(self):
 
643
        lockable = _StubLockable(self._calls, unlock_exc=_ErrorFromUnlock())
 
644
        self.assertRaises(_ErrorFromUnlock,
 
645
                          _mod_branch._run_with_write_locked_target,
 
646
                          lockable, self.func_that_returns_ok)
 
647
        self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)
 
648
 
 
649
    def test_error_during_unlock_does_not_mask_original_error(self):
 
650
        lockable = _StubLockable(self._calls, unlock_exc=_ErrorFromUnlock())
 
651
        self.assertRaises(_ErrorFromCallable,
 
652
                          _mod_branch._run_with_write_locked_target,
 
653
                          lockable, self.func_that_raises)
 
654
        self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)
 
655
 
 
656