~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_push.py

  • Committer: Robert Collins
  • Date: 2008-04-06 23:37:06 UTC
  • mto: This revision was merged to the branch mainline in revision 3340.
  • Revision ID: robertc@robertcollins.net-20080406233706-3md3w1c651a0pndm
Fix ReST table formatting in doc/developers/plugin-api.txt.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
"""Black-box tests for bzr push."""
 
19
 
 
20
import os
 
21
import re
 
22
 
 
23
from bzrlib import (
 
24
    errors,
 
25
    urlutils,
 
26
    )
 
27
from bzrlib.branch import Branch
 
28
from bzrlib.bzrdir import BzrDirMetaFormat1
 
29
from bzrlib.osutils import abspath
 
30
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
 
31
from bzrlib.tests.blackbox import ExternalBase
 
32
from bzrlib.transport import register_transport, unregister_transport
 
33
from bzrlib.transport.memory import MemoryServer, MemoryTransport
 
34
from bzrlib.uncommit import uncommit
 
35
from bzrlib.urlutils import local_path_from_url
 
36
from bzrlib.workingtree import WorkingTree
 
37
 
 
38
 
 
39
class TestPush(ExternalBase):
 
40
 
 
41
    def test_push_remember(self):
 
42
        """Push changes from one branch to another and test push location."""
 
43
        transport = self.get_transport()
 
44
        tree_a = self.make_branch_and_tree('branch_a')
 
45
        branch_a = tree_a.branch
 
46
        self.build_tree(['branch_a/a'])
 
47
        tree_a.add('a')
 
48
        tree_a.commit('commit a')
 
49
        tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
 
50
        branch_b = tree_b.branch
 
51
        tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
 
52
        branch_c = tree_c.branch
 
53
        self.build_tree(['branch_a/b'])
 
54
        tree_a.add('b')
 
55
        tree_a.commit('commit b')
 
56
        self.build_tree(['branch_b/c'])
 
57
        tree_b.add('c')
 
58
        tree_b.commit('commit c')
 
59
        # initial push location must be empty
 
60
        self.assertEqual(None, branch_b.get_push_location())
 
61
 
 
62
        # test push for failure without push location set
 
63
        os.chdir('branch_a')
 
64
        out = self.run_bzr('push', retcode=3)
 
65
        self.assertEquals(out,
 
66
                ('','bzr: ERROR: No push location known or specified.\n'))
 
67
 
 
68
        # test not remembered if cannot actually push
 
69
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
70
        out = self.run_bzr('push', retcode=3)
 
71
        self.assertEquals(
 
72
                ('', 'bzr: ERROR: No push location known or specified.\n'),
 
73
                out)
 
74
 
 
75
        # test implicit --remember when no push location set, push fails
 
76
        out = self.run_bzr('push ../branch_b', retcode=3)
 
77
        self.assertEquals(out,
 
78
                ('','bzr: ERROR: These branches have diverged.  '
 
79
                    'Try using "merge" and then "push".\n'))
 
80
        self.assertEquals(abspath(branch_a.get_push_location()),
 
81
                          abspath(branch_b.bzrdir.root_transport.base))
 
82
 
 
83
        # test implicit --remember after resolving previous failure
 
84
        uncommit(branch=branch_b, tree=tree_b)
 
85
        transport.delete('branch_b/c')
 
86
        out, err = self.run_bzr('push')
 
87
        path = branch_a.get_push_location()
 
88
        self.assertEquals(out,
 
89
                          'Using saved location: %s\n' 
 
90
                          'Pushed up to revision 2.\n'
 
91
                          % local_path_from_url(path))
 
92
        self.assertEqual(err,
 
93
                         'All changes applied successfully.\n')
 
94
        self.assertEqual(path,
 
95
                         branch_b.bzrdir.root_transport.base)
 
96
        # test explicit --remember
 
97
        self.run_bzr('push ../branch_c --remember')
 
98
        self.assertEquals(branch_a.get_push_location(),
 
99
                          branch_c.bzrdir.root_transport.base)
 
100
    
 
101
    def test_push_without_tree(self):
 
102
        # bzr push from a branch that does not have a checkout should work.
 
103
        b = self.make_branch('.')
 
104
        out, err = self.run_bzr('push pushed-location')
 
105
        self.assertEqual('', out)
 
106
        self.assertEqual('Created new branch.\n', err)
 
107
        b2 = Branch.open('pushed-location')
 
108
        self.assertEndsWith(b2.base, 'pushed-location/')
 
109
 
 
110
    def test_push_new_branch_revision_count(self):
 
111
        # bzr push of a branch with revisions to a new location 
 
112
        # should print the number of revisions equal to the length of the 
 
113
        # local branch.
 
114
        t = self.make_branch_and_tree('tree')
 
115
        self.build_tree(['tree/file'])
 
116
        t.add('file')
 
117
        t.commit('commit 1')
 
118
        os.chdir('tree')
 
119
        out, err = self.run_bzr('push pushed-to')
 
120
        os.chdir('..')
 
121
        self.assertEqual('', out)
 
122
        self.assertEqual('Created new branch.\n', err)
 
123
 
 
124
    def test_push_only_pushes_history(self):
 
125
        # Knit branches should only push the history for the current revision.
 
126
        format = BzrDirMetaFormat1()
 
127
        format.repository_format = RepositoryFormatKnit1()
 
128
        shared_repo = self.make_repository('repo', format=format, shared=True)
 
129
        shared_repo.set_make_working_trees(True)
 
130
 
 
131
        def make_shared_tree(path):
 
132
            shared_repo.bzrdir.root_transport.mkdir(path)
 
133
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
 
134
            return WorkingTree.open('repo/' + path)
 
135
        tree_a = make_shared_tree('a')
 
136
        self.build_tree(['repo/a/file'])
 
137
        tree_a.add('file')
 
138
        tree_a.commit('commit a-1', rev_id='a-1')
 
139
        f = open('repo/a/file', 'ab')
 
140
        f.write('more stuff\n')
 
141
        f.close()
 
142
        tree_a.commit('commit a-2', rev_id='a-2')
 
143
 
 
144
        tree_b = make_shared_tree('b')
 
145
        self.build_tree(['repo/b/file'])
 
146
        tree_b.add('file')
 
147
        tree_b.commit('commit b-1', rev_id='b-1')
 
148
 
 
149
        self.assertTrue(shared_repo.has_revision('a-1'))
 
150
        self.assertTrue(shared_repo.has_revision('a-2'))
 
151
        self.assertTrue(shared_repo.has_revision('b-1'))
 
152
 
 
153
        # Now that we have a repository with shared files, make sure
 
154
        # that things aren't copied out by a 'push'
 
155
        os.chdir('repo/b')
 
156
        self.run_bzr('push ../../push-b')
 
157
        pushed_tree = WorkingTree.open('../../push-b')
 
158
        pushed_repo = pushed_tree.branch.repository
 
159
        self.assertFalse(pushed_repo.has_revision('a-1'))
 
160
        self.assertFalse(pushed_repo.has_revision('a-2'))
 
161
        self.assertTrue(pushed_repo.has_revision('b-1'))
 
162
 
 
163
    def test_push_funky_id(self):
 
164
        t = self.make_branch_and_tree('tree')
 
165
        os.chdir('tree')
 
166
        self.build_tree(['filename'])
 
167
        t.add('filename', 'funky-chars<>%&;"\'')
 
168
        t.commit('commit filename')
 
169
        self.run_bzr('push ../new-tree')
 
170
 
 
171
    def test_push_dash_d(self):
 
172
        t = self.make_branch_and_tree('from')
 
173
        t.commit(allow_pointless=True,
 
174
                message='first commit')
 
175
        self.run_bzr('push -d from to-one')
 
176
        self.failUnlessExists('to-one')
 
177
        self.run_bzr('push -d %s %s' 
 
178
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
 
179
        self.failUnlessExists('to-two')
 
180
 
 
181
    def create_simple_tree(self):
 
182
        tree = self.make_branch_and_tree('tree')
 
183
        self.build_tree(['tree/a'])
 
184
        tree.add(['a'], ['a-id'])
 
185
        tree.commit('one', rev_id='r1')
 
186
        return tree
 
187
 
 
188
    def test_push_create_prefix(self):
 
189
        """'bzr push --create-prefix' will create leading directories."""
 
190
        tree = self.create_simple_tree()
 
191
 
 
192
        self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
 
193
                           'push ../new/tree',
 
194
                           working_dir='tree')
 
195
        self.run_bzr('push ../new/tree --create-prefix',
 
196
                     working_dir='tree')
 
197
        new_tree = WorkingTree.open('new/tree')
 
198
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
199
        self.failUnlessExists('new/tree/a')
 
200
 
 
201
    def test_push_use_existing(self):
 
202
        """'bzr push --use-existing-dir' can push into an existing dir.
 
203
 
 
204
        By default, 'bzr push' will not use an existing, non-versioned dir.
 
205
        """
 
206
        tree = self.create_simple_tree()
 
207
        self.build_tree(['target/'])
 
208
 
 
209
        self.run_bzr_error(['Target directory ../target already exists',
 
210
                            'Supply --use-existing-dir',
 
211
                           ],
 
212
                           'push ../target', working_dir='tree')
 
213
 
 
214
        self.run_bzr('push --use-existing-dir ../target',
 
215
                     working_dir='tree')
 
216
 
 
217
        new_tree = WorkingTree.open('target')
 
218
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
219
        # The push should have created target/a
 
220
        self.failUnlessExists('target/a')
 
221
 
 
222
    def test_push_onto_repo(self):
 
223
        """We should be able to 'bzr push' into an existing bzrdir."""
 
224
        tree = self.create_simple_tree()
 
225
        repo = self.make_repository('repo', shared=True)
 
226
 
 
227
        self.run_bzr('push ../repo',
 
228
                     working_dir='tree')
 
229
 
 
230
        # Pushing onto an existing bzrdir will create a repository and
 
231
        # branch as needed, but will only create a working tree if there was
 
232
        # no BzrDir before.
 
233
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
234
        new_branch = Branch.open('repo')
 
235
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
 
236
 
 
237
    def test_push_onto_just_bzrdir(self):
 
238
        """We don't handle when the target is just a bzrdir.
 
239
 
 
240
        Because you shouldn't be able to create *just* a bzrdir in the wild.
 
241
        """
 
242
        # TODO: jam 20070109 Maybe it would be better to create the repository
 
243
        #       if at this point
 
244
        tree = self.create_simple_tree()
 
245
        a_bzrdir = self.make_bzrdir('dir')
 
246
 
 
247
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
 
248
                'push ../dir',
 
249
                working_dir='tree')
 
250
 
 
251
 
 
252
class RedirectingMemoryTransport(MemoryTransport):
 
253
 
 
254
    def mkdir(self, path, mode=None):
 
255
        path = self.abspath(path)[len(self._scheme):]
 
256
        if path == '/source':
 
257
            raise errors.RedirectRequested(
 
258
                path, self._scheme + '/target', is_permanent=True)
 
259
        elif path == '/infinite-loop':
 
260
            raise errors.RedirectRequested(
 
261
                path, self._scheme + '/infinite-loop', is_permanent=True)
 
262
        else:
 
263
            return super(RedirectingMemoryTransport, self).mkdir(
 
264
                path, mode)
 
265
 
 
266
 
 
267
class RedirectingMemoryServer(MemoryServer):
 
268
 
 
269
    def setUp(self):
 
270
        self._dirs = {'/': None}
 
271
        self._files = {}
 
272
        self._locks = {}
 
273
        self._scheme = 'redirecting-memory+%s:///' % id(self)
 
274
        register_transport(self._scheme, self._memory_factory)
 
275
 
 
276
    def _memory_factory(self, url):
 
277
        result = RedirectingMemoryTransport(url)
 
278
        result._dirs = self._dirs
 
279
        result._files = self._files
 
280
        result._locks = self._locks
 
281
        return result
 
282
 
 
283
    def tearDown(self):
 
284
        unregister_transport(self._scheme, self._memory_factory)
 
285
 
 
286
 
 
287
class TestPushRedirect(ExternalBase):
 
288
 
 
289
    def setUp(self):
 
290
        ExternalBase.setUp(self)
 
291
        self.memory_server = RedirectingMemoryServer()
 
292
        self.memory_server.setUp()
 
293
        self.addCleanup(self.memory_server.tearDown)
 
294
 
 
295
        # Make the branch and tree that we'll be pushing.
 
296
        t = self.make_branch_and_tree('tree')
 
297
        self.build_tree(['tree/file'])
 
298
        t.add('file')
 
299
        t.commit('commit 1')
 
300
 
 
301
    def test_push_redirects_on_mkdir(self):
 
302
        """If the push requires a mkdir, push respects redirect requests.
 
303
 
 
304
        This is added primarily to handle lp:/ URI support, so that users can
 
305
        push to new branches by specifying lp:/ URIs.
 
306
        """
 
307
        os.chdir('tree')
 
308
        destination_url = self.memory_server.get_url() + 'source'
 
309
        self.run_bzr('push %s' % destination_url)
 
310
        os.chdir('..')
 
311
 
 
312
        local_revision = Branch.open('tree').last_revision()
 
313
        remote_revision = Branch.open(
 
314
            self.memory_server.get_url() + 'target').last_revision()
 
315
        self.assertEqual(remote_revision, local_revision)
 
316
 
 
317
    def test_push_gracefully_handles_too_many_redirects(self):
 
318
        """Push fails gracefully if the mkdir generates a large number of
 
319
        redirects.
 
320
        """
 
321
        os.chdir('tree')
 
322
        destination_url = self.memory_server.get_url() + 'infinite-loop'
 
323
        out, err = self.run_bzr_error(
 
324
            ['Too many redirections trying to make %s\\.\n'
 
325
             % re.escape(destination_url)],
 
326
            'push %s' % destination_url, retcode=3)
 
327
        os.chdir('..')
 
328
        self.assertEqual('', out)