1# Copyright (C) 2010, 2011, 2012, 2016 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17"""Tests for bzr directories that support colocated branches."""
18
19from breezy.branch import Branch
20from breezy.controldir import BranchReferenceLoop
21from breezy import (
22    branchbuilder,
23    errors,
24    tests,
25    urlutils,
26    )
27from breezy.tests import (
28    per_controldir,
29    )
30from breezy.tests.features import (
31    UnicodeFilenameFeature,
32    )
33
34
35class TestColocatedBranchSupport(per_controldir.TestCaseWithControlDir):
36
37    def create_branch(self, bzrdir, name=None):
38        branch = bzrdir.create_branch(name=name)
39        # Create a commit on the branch, just because some formats
40        # have nascent branches that don't hit disk.
41        bb = branchbuilder.BranchBuilder(branch=branch)
42        bb.build_commit()
43        return branch
44
45    def test_destroy_colocated_branch(self):
46        branch = self.make_branch('branch')
47        bzrdir = branch.controldir
48        colo_branch = self.create_branch(bzrdir, 'colo')
49        try:
50            bzrdir.destroy_branch("colo")
51        except (errors.UnsupportedOperation, errors.TransportNotPossible):
52            raise tests.TestNotApplicable(
53                'Format does not support destroying branch')
54        self.assertRaises(errors.NotBranchError, bzrdir.open_branch,
55                          "colo")
56
57    def test_create_colo_branch(self):
58        # a bzrdir can construct a branch and repository for itself.
59        if not self.bzrdir_format.is_supported():
60            # unsupported formats are not loopback testable
61            # because the default open will not open them and
62            # they may not be initializable.
63            raise tests.TestNotApplicable('Control dir format not supported')
64        t = self.get_transport()
65        try:
66            made_control = self.bzrdir_format.initialize(t.base)
67        except errors.UninitializableFormat:
68            raise tests.TestNotApplicable(
69                'Control dir does not support creating new branches.')
70        made_control.create_repository()
71        made_branch = made_control.create_branch("colo")
72        self.assertIsInstance(made_branch, Branch)
73        self.assertEqual("colo", made_branch.name)
74        self.assertEqual(made_control, made_branch.controldir)
75
76    def test_open_by_url(self):
77        # a bzrdir can construct a branch and repository for itself.
78        if not self.bzrdir_format.is_supported():
79            # unsupported formats are not loopback testable
80            # because the default open will not open them and
81            # they may not be initializable.
82            raise tests.TestNotApplicable('Control dir format not supported')
83        t = self.get_transport()
84        try:
85            made_control = self.bzrdir_format.initialize(t.base)
86        except errors.UninitializableFormat:
87            raise tests.TestNotApplicable(
88                'Control dir does not support creating new branches.')
89        made_control.create_repository()
90        made_branch = self.create_branch(made_control, name="colo")
91        other_branch = self.create_branch(made_control, name="othercolo")
92        self.assertIsInstance(made_branch, Branch)
93        self.assertEqual(made_control, made_branch.controldir)
94        self.assertNotEqual(made_branch.user_url, other_branch.user_url)
95        self.assertNotEqual(made_branch.control_url, other_branch.control_url)
96        re_made_branch = Branch.open(made_branch.user_url)
97        self.assertEqual(re_made_branch.name, "colo")
98        self.assertEqual(made_branch.control_url, re_made_branch.control_url)
99        self.assertEqual(made_branch.user_url, re_made_branch.user_url)
100
101    def test_sprout_into_colocated(self):
102        # a bzrdir can construct a branch and repository for itself.
103        if not self.bzrdir_format.is_supported():
104            # unsupported formats are not loopback testable
105            # because the default open will not open them and
106            # they may not be initializable.
107            raise tests.TestNotApplicable('Control dir format not supported')
108        from_tree = self.make_branch_and_tree('from')
109        revid = from_tree.commit("rev1")
110        try:
111            other_branch = self.make_branch("to")
112        except errors.UninitializableFormat:
113            raise tests.TestNotApplicable(
114                'Control dir does not support creating new branches.')
115        to_dir = from_tree.controldir.sprout(
116            urlutils.join_segment_parameters(
117                other_branch.user_url, {"branch": "target"}))
118        to_branch = to_dir.open_branch(name="target")
119        self.assertEqual(revid, to_branch.last_revision())
120
121    def test_sprout_into_colocated_leaves_workingtree(self):
122        # a bzrdir can construct a branch and repository for itself.
123        if not self.bzrdir_format.is_supported():
124            # unsupported formats are not loopback testable
125            # because the default open will not open them and
126            # they may not be initializable.
127            raise tests.TestNotApplicable('Control dir format not supported')
128        if not self.bzrdir_format.supports_workingtrees:
129            raise tests.TestNotApplicable(
130                'Control dir format does not support working trees')
131        from_tree = self.make_branch_and_tree('from')
132        self.build_tree_contents([('from/foo', 'contents')])
133        from_tree.add(['foo'])
134        revid1 = from_tree.commit("rev1")
135        self.build_tree_contents([('from/foo', 'new contents')])
136        revid2 = from_tree.commit("rev2")
137        try:
138            other_branch = self.make_branch_and_tree("to")
139        except errors.UninitializableFormat:
140            raise tests.TestNotApplicable(
141                'Control dir does not support creating new branches.')
142
143        result = other_branch.controldir.push_branch(
144            from_tree.branch, revision_id=revid1)
145        self.assertTrue(result.workingtree_updated)
146        self.assertFileEqual('contents', 'to/foo')
147
148        from_tree.controldir.sprout(
149            urlutils.join_segment_parameters(
150                other_branch.user_url, {"branch": "target"}),
151            revision_id=revid2)
152        active_branch = other_branch.controldir.open_branch(name="")
153        self.assertEqual(revid1, active_branch.last_revision())
154        to_branch = other_branch.controldir.open_branch(name="target")
155        self.assertEqual(revid2, to_branch.last_revision())
156        self.assertFileEqual('contents', 'to/foo')
157
158    def test_unicode(self):
159        self.requireFeature(UnicodeFilenameFeature)
160        if not self.bzrdir_format.is_supported():
161            # unsupported formats are not loopback testable
162            # because the default open will not open them and
163            # they may not be initializable.
164            raise tests.TestNotApplicable('Control dir format not supported')
165        t = self.get_transport()
166        try:
167            made_control = self.bzrdir_format.initialize(t.base)
168        except errors.UninitializableFormat:
169            raise tests.TestNotApplicable(
170                'Control dir does not support creating new branches.')
171        made_control.create_repository()
172        made_branch = self.create_branch(made_control, name=u"col\xe9")
173        self.assertIn(
174            u"col\xe9", [b.name for b in made_control.list_branches()])
175        made_branch = Branch.open(made_branch.user_url)
176        self.assertEqual(u"col\xe9", made_branch.name)
177        made_control.destroy_branch(u"col\xe9")
178
179    def test_get_branches(self):
180        repo = self.make_repository('branch-1')
181        self.assertNotIn('foo', list(repo.controldir.get_branches()))
182        target_branch = self.create_branch(repo.controldir, name='foo')
183        self.assertIn('foo', list(repo.controldir.get_branches()))
184        self.assertEqual(target_branch.base,
185                         repo.controldir.get_branches()['foo'].base)
186
187    def test_branch_names(self):
188        repo = self.make_repository('branch-1')
189        target_branch = self.create_branch(repo.controldir, name='foo')
190        self.assertIn('foo', repo.controldir.branch_names())
191
192    def test_branch_name_with_slash(self):
193        repo = self.make_repository('branch-1')
194        try:
195            target_branch = self.create_branch(repo.controldir, name='foo/bar')
196        except errors.InvalidBranchName:
197            raise tests.TestNotApplicable(
198                "format does not support branches with / in their name")
199        self.assertIn('foo/bar', list(repo.controldir.get_branches()))
200        self.assertEqual(
201            target_branch.base, repo.controldir.open_branch(name='foo/bar').base)
202
203    def test_branch_reference(self):
204        referenced = self.make_branch('referenced')
205        repo = self.make_repository('repo')
206        try:
207            repo.controldir.set_branch_reference(referenced, name='foo')
208        except errors.IncompatibleFormat:
209            raise tests.TestNotApplicable(
210                'Control dir does not support creating branch references.')
211        self.assertEqual(referenced.user_url,
212                         repo.controldir.get_branch_reference('foo'))
213
214    def test_branch_reference_loop(self):
215        repo = self.make_repository('repo')
216        to_branch = self.create_branch(repo.controldir, name='somebranch')
217        try:
218            self.assertRaises(
219                BranchReferenceLoop,
220                repo.controldir.set_branch_reference,
221                to_branch, name='somebranch')
222        except errors.IncompatibleFormat:
223            raise tests.TestNotApplicable(
224                'Control dir does not support creating branch references.')
225