~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Andrew Bennetts
  • Date: 2008-09-05 10:48:03 UTC
  • mto: This revision was merged to the branch mainline in revision 3693.
  • Revision ID: andrew.bennetts@canonical.com-20080905104803-6g72dz6wcldosfs2
Remove monkey-patching of branch._ensure_real from test_remote.py.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
# -*- coding: utf-8 -*-
3
 
 
 
1
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
 
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
6
5
# the Free Software Foundation; either version 2 of the License, or
7
6
# (at your option) any later version.
8
 
 
 
7
#
9
8
# This program is distributed in the hope that it will be useful,
10
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
11
# GNU General Public License for more details.
13
 
 
 
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
18
"""Black-box tests for bzr push."""
20
19
 
21
20
import os
 
21
import re
22
22
 
23
 
import bzrlib
 
23
from bzrlib import (
 
24
    errors,
 
25
    urlutils,
 
26
    )
24
27
from bzrlib.branch import Branch
25
28
from bzrlib.bzrdir import BzrDirMetaFormat1
26
29
from bzrlib.osutils import abspath
27
 
from bzrlib.repository import RepositoryFormatKnit1
 
30
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
28
31
from bzrlib.tests.blackbox import ExternalBase
 
32
from bzrlib.tests.http_server import HttpServer
 
33
from bzrlib.transport import register_transport, unregister_transport
 
34
from bzrlib.transport.memory import MemoryServer, MemoryTransport
29
35
from bzrlib.uncommit import uncommit
 
36
from bzrlib.urlutils import local_path_from_url
30
37
from bzrlib.workingtree import WorkingTree
31
38
 
32
39
 
52
59
        tree_b.commit('commit c')
53
60
        # initial push location must be empty
54
61
        self.assertEqual(None, branch_b.get_push_location())
 
62
 
55
63
        # test push for failure without push location set
56
64
        os.chdir('branch_a')
57
 
        out = self.runbzr('push', retcode=3)
 
65
        out = self.run_bzr('push', retcode=3)
58
66
        self.assertEquals(out,
59
67
                ('','bzr: ERROR: No push location known or specified.\n'))
 
68
 
 
69
        # test not remembered if cannot actually push
 
70
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
71
        out = self.run_bzr('push', retcode=3)
 
72
        self.assertEquals(
 
73
                ('', 'bzr: ERROR: No push location known or specified.\n'),
 
74
                out)
 
75
 
60
76
        # test implicit --remember when no push location set, push fails
61
 
        out = self.runbzr('push ../branch_b', retcode=3)
 
77
        out = self.run_bzr('push ../branch_b', retcode=3)
62
78
        self.assertEquals(out,
63
79
                ('','bzr: ERROR: These branches have diverged.  '
64
 
                    'Try a merge then push with overwrite.\n'))
 
80
                    'Try using "merge" and then "push".\n'))
65
81
        self.assertEquals(abspath(branch_a.get_push_location()),
66
82
                          abspath(branch_b.bzrdir.root_transport.base))
 
83
 
67
84
        # test implicit --remember after resolving previous failure
68
85
        uncommit(branch=branch_b, tree=tree_b)
69
86
        transport.delete('branch_b/c')
70
 
        self.runbzr('push')
71
 
        self.assertEquals(abspath(branch_a.get_push_location()),
72
 
                          abspath(branch_b.bzrdir.root_transport.base))
 
87
        out, err = self.run_bzr('push')
 
88
        path = branch_a.get_push_location()
 
89
        self.assertEquals(out,
 
90
                          'Using saved push location: %s\n' 
 
91
                          'Pushed up to revision 2.\n'
 
92
                          % local_path_from_url(path))
 
93
        self.assertEqual(err,
 
94
                         'All changes applied successfully.\n')
 
95
        self.assertEqual(path,
 
96
                         branch_b.bzrdir.root_transport.base)
73
97
        # test explicit --remember
74
 
        self.runbzr('push ../branch_c --remember')
75
 
        self.assertEquals(abspath(branch_a.get_push_location()),
76
 
                          abspath(branch_c.bzrdir.root_transport.base))
 
98
        self.run_bzr('push ../branch_c --remember')
 
99
        self.assertEquals(branch_a.get_push_location(),
 
100
                          branch_c.bzrdir.root_transport.base)
77
101
    
78
102
    def test_push_without_tree(self):
79
103
        # bzr push from a branch that does not have a checkout should work.
80
104
        b = self.make_branch('.')
81
 
        out, err = self.run_bzr('push', 'pushed-location')
 
105
        out, err = self.run_bzr('push pushed-location')
82
106
        self.assertEqual('', out)
83
 
        self.assertEqual('0 revision(s) pushed.\n', err)
84
 
        b2 = bzrlib.branch.Branch.open('pushed-location')
 
107
        self.assertEqual('Created new branch.\n', err)
 
108
        b2 = Branch.open('pushed-location')
85
109
        self.assertEndsWith(b2.base, 'pushed-location/')
86
110
 
87
111
    def test_push_new_branch_revision_count(self):
93
117
        t.add('file')
94
118
        t.commit('commit 1')
95
119
        os.chdir('tree')
96
 
        out, err = self.run_bzr('push', 'pushed-to')
 
120
        out, err = self.run_bzr('push pushed-to')
97
121
        os.chdir('..')
98
122
        self.assertEqual('', out)
99
 
        self.assertEqual('1 revision(s) pushed.\n', err)
 
123
        self.assertEqual('Created new branch.\n', err)
100
124
 
101
125
    def test_push_only_pushes_history(self):
102
126
        # Knit branches should only push the history for the current revision.
130
154
        # Now that we have a repository with shared files, make sure
131
155
        # that things aren't copied out by a 'push'
132
156
        os.chdir('repo/b')
133
 
        self.run_bzr('push', '../../push-b')
 
157
        self.run_bzr('push ../../push-b')
134
158
        pushed_tree = WorkingTree.open('../../push-b')
135
159
        pushed_repo = pushed_tree.branch.repository
136
160
        self.assertFalse(pushed_repo.has_revision('a-1'))
137
161
        self.assertFalse(pushed_repo.has_revision('a-2'))
138
162
        self.assertTrue(pushed_repo.has_revision('b-1'))
139
163
 
 
164
    def test_push_funky_id(self):
 
165
        t = self.make_branch_and_tree('tree')
 
166
        os.chdir('tree')
 
167
        self.build_tree(['filename'])
 
168
        t.add('filename', 'funky-chars<>%&;"\'')
 
169
        t.commit('commit filename')
 
170
        self.run_bzr('push ../new-tree')
 
171
 
 
172
    def test_push_dash_d(self):
 
173
        t = self.make_branch_and_tree('from')
 
174
        t.commit(allow_pointless=True,
 
175
                message='first commit')
 
176
        self.run_bzr('push -d from to-one')
 
177
        self.failUnlessExists('to-one')
 
178
        self.run_bzr('push -d %s %s' 
 
179
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
 
180
        self.failUnlessExists('to-two')
 
181
 
 
182
    def create_simple_tree(self):
 
183
        tree = self.make_branch_and_tree('tree')
 
184
        self.build_tree(['tree/a'])
 
185
        tree.add(['a'], ['a-id'])
 
186
        tree.commit('one', rev_id='r1')
 
187
        return tree
 
188
 
 
189
    def test_push_create_prefix(self):
 
190
        """'bzr push --create-prefix' will create leading directories."""
 
191
        tree = self.create_simple_tree()
 
192
 
 
193
        self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
 
194
                           'push ../new/tree',
 
195
                           working_dir='tree')
 
196
        self.run_bzr('push ../new/tree --create-prefix',
 
197
                     working_dir='tree')
 
198
        new_tree = WorkingTree.open('new/tree')
 
199
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
200
        self.failUnlessExists('new/tree/a')
 
201
 
 
202
    def test_push_use_existing(self):
 
203
        """'bzr push --use-existing-dir' can push into an existing dir.
 
204
 
 
205
        By default, 'bzr push' will not use an existing, non-versioned dir.
 
206
        """
 
207
        tree = self.create_simple_tree()
 
208
        self.build_tree(['target/'])
 
209
 
 
210
        self.run_bzr_error(['Target directory ../target already exists',
 
211
                            'Supply --use-existing-dir',
 
212
                           ],
 
213
                           'push ../target', working_dir='tree')
 
214
 
 
215
        self.run_bzr('push --use-existing-dir ../target',
 
216
                     working_dir='tree')
 
217
 
 
218
        new_tree = WorkingTree.open('target')
 
219
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
220
        # The push should have created target/a
 
221
        self.failUnlessExists('target/a')
 
222
 
 
223
    def test_push_onto_repo(self):
 
224
        """We should be able to 'bzr push' into an existing bzrdir."""
 
225
        tree = self.create_simple_tree()
 
226
        repo = self.make_repository('repo', shared=True)
 
227
 
 
228
        self.run_bzr('push ../repo',
 
229
                     working_dir='tree')
 
230
 
 
231
        # Pushing onto an existing bzrdir will create a repository and
 
232
        # branch as needed, but will only create a working tree if there was
 
233
        # no BzrDir before.
 
234
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
235
        new_branch = Branch.open('repo')
 
236
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
 
237
 
 
238
    def test_push_onto_just_bzrdir(self):
 
239
        """We don't handle when the target is just a bzrdir.
 
240
 
 
241
        Because you shouldn't be able to create *just* a bzrdir in the wild.
 
242
        """
 
243
        # TODO: jam 20070109 Maybe it would be better to create the repository
 
244
        #       if at this point
 
245
        tree = self.create_simple_tree()
 
246
        a_bzrdir = self.make_bzrdir('dir')
 
247
 
 
248
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
 
249
                'push ../dir',
 
250
                working_dir='tree')
 
251
 
 
252
    def test_push_with_revisionspec(self):
 
253
        """We should be able to push a revision older than the tip."""
 
254
        tree_from = self.make_branch_and_tree('from')
 
255
        tree_from.commit("One.", rev_id="from-1")
 
256
        tree_from.commit("Two.", rev_id="from-2")
 
257
 
 
258
        self.run_bzr('push -r1 ../to', working_dir='from')
 
259
 
 
260
        tree_to = WorkingTree.open('to')
 
261
        repo_to = tree_to.branch.repository
 
262
        self.assertTrue(repo_to.has_revision('from-1'))
 
263
        self.assertFalse(repo_to.has_revision('from-2'))
 
264
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
 
265
 
 
266
        self.run_bzr_error(
 
267
            "bzr: ERROR: bzr push --revision takes one value.\n",
 
268
            'push -r0..2 ../to', working_dir='from')
 
269
 
 
270
    def create_trunk_and_feature_branch(self):
 
271
        # We have a mainline
 
272
        trunk_tree = self.make_branch_and_tree('target',
 
273
            format='development')
 
274
        trunk_tree.commit('mainline')
 
275
        # and a branch from it
 
276
        branch_tree = self.make_branch_and_tree('branch',
 
277
            format='development')
 
278
        branch_tree.pull(trunk_tree.branch)
 
279
        branch_tree.branch.set_parent(trunk_tree.branch.base)
 
280
        # with some work on it
 
281
        branch_tree.commit('moar work plz')
 
282
        return trunk_tree, branch_tree
 
283
 
 
284
    def assertPublished(self, branch_revid, stacked_on):
 
285
        """Assert that the branch 'published' has been published correctly."""
 
286
        published_branch = Branch.open('published')
 
287
        # The published branch refers to the mainline
 
288
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
 
289
        # and the branch's work was pushed
 
290
        self.assertTrue(published_branch.repository.has_revision(branch_revid))
 
291
 
 
292
    def test_push_new_branch_stacked_on(self):
 
293
        """Pushing a new branch with --stacked-on creates a stacked branch."""
 
294
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
295
        # we publish branch_tree with a reference to the mainline.
 
296
        out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
 
297
            self.get_url('published')], working_dir='branch')
 
298
        self.assertEqual('', out)
 
299
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
300
            trunk_tree.branch.base, err)
 
301
        self.assertPublished(branch_tree.last_revision(),
 
302
            trunk_tree.branch.base)
 
303
 
 
304
    def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
 
305
        """When the parent has no public url the parent is used as-is."""
 
306
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
307
        # now we do a stacked push, which should determine the public location
 
308
        # for us.
 
309
        out, err = self.run_bzr(['push', '--stacked',
 
310
            self.get_url('published')], working_dir='branch')
 
311
        self.assertEqual('', out)
 
312
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
313
            trunk_tree.branch.base, err)
 
314
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
 
315
 
 
316
    def test_push_new_branch_stacked_uses_parent_public(self):
 
317
        """Pushing a new branch with --stacked creates a stacked branch."""
 
318
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
319
        # the trunk is published on a web server
 
320
        self.transport_readonly_server = HttpServer
 
321
        trunk_public = self.make_branch('public_trunk', format='development')
 
322
        trunk_public.pull(trunk_tree.branch)
 
323
        trunk_public_url = self.get_readonly_url('public_trunk')
 
324
        trunk_tree.branch.set_public_branch(trunk_public_url)
 
325
        # now we do a stacked push, which should determine the public location
 
326
        # for us.
 
327
        out, err = self.run_bzr(['push', '--stacked',
 
328
            self.get_url('published')], working_dir='branch')
 
329
        self.assertEqual('', out)
 
330
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
331
            trunk_public_url, err)
 
332
        self.assertPublished(branch_tree.last_revision(), trunk_public_url)
 
333
 
 
334
    def test_push_new_branch_stacked_no_parent(self):
 
335
        """Pushing with --stacked and no parent branch errors."""
 
336
        branch = self.make_branch_and_tree('branch', format='development')
 
337
        # now we do a stacked push, which should fail as the place to refer too
 
338
        # cannot be determined.
 
339
        out, err = self.run_bzr_error(
 
340
            ['Could not determine branch to refer to\\.'], ['push', '--stacked',
 
341
            self.get_url('published')], working_dir='branch')
 
342
        self.assertEqual('', out)
 
343
        self.assertFalse(self.get_transport('published').has('.'))
 
344
 
 
345
    def test_push_notifies_default_stacking(self):
 
346
        self.make_branch('stack_on', format='development1')
 
347
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
348
        self.make_branch('from', format='development1')
 
349
        out, err = self.run_bzr('push -d from to')
 
350
        self.assertContainsRe(err,
 
351
                              'Using default stacking branch stack_on at .*')
 
352
 
 
353
 
 
354
class RedirectingMemoryTransport(MemoryTransport):
 
355
 
 
356
    def mkdir(self, path, mode=None):
 
357
        path = self.abspath(path)[len(self._scheme):]
 
358
        if path == '/source':
 
359
            raise errors.RedirectRequested(
 
360
                path, self._scheme + '/target', is_permanent=True)
 
361
        elif path == '/infinite-loop':
 
362
            raise errors.RedirectRequested(
 
363
                path, self._scheme + '/infinite-loop', is_permanent=True)
 
364
        else:
 
365
            return super(RedirectingMemoryTransport, self).mkdir(
 
366
                path, mode)
 
367
 
 
368
 
 
369
class RedirectingMemoryServer(MemoryServer):
 
370
 
 
371
    def setUp(self):
 
372
        self._dirs = {'/': None}
 
373
        self._files = {}
 
374
        self._locks = {}
 
375
        self._scheme = 'redirecting-memory+%s:///' % id(self)
 
376
        register_transport(self._scheme, self._memory_factory)
 
377
 
 
378
    def _memory_factory(self, url):
 
379
        result = RedirectingMemoryTransport(url)
 
380
        result._dirs = self._dirs
 
381
        result._files = self._files
 
382
        result._locks = self._locks
 
383
        return result
 
384
 
 
385
    def tearDown(self):
 
386
        unregister_transport(self._scheme, self._memory_factory)
 
387
 
 
388
 
 
389
class TestPushRedirect(ExternalBase):
 
390
 
 
391
    def setUp(self):
 
392
        ExternalBase.setUp(self)
 
393
        self.memory_server = RedirectingMemoryServer()
 
394
        self.memory_server.setUp()
 
395
        self.addCleanup(self.memory_server.tearDown)
 
396
 
 
397
        # Make the branch and tree that we'll be pushing.
 
398
        t = self.make_branch_and_tree('tree')
 
399
        self.build_tree(['tree/file'])
 
400
        t.add('file')
 
401
        t.commit('commit 1')
 
402
 
 
403
    def test_push_redirects_on_mkdir(self):
 
404
        """If the push requires a mkdir, push respects redirect requests.
 
405
 
 
406
        This is added primarily to handle lp:/ URI support, so that users can
 
407
        push to new branches by specifying lp:/ URIs.
 
408
        """
 
409
        os.chdir('tree')
 
410
        destination_url = self.memory_server.get_url() + 'source'
 
411
        self.run_bzr('push %s' % destination_url)
 
412
        os.chdir('..')
 
413
 
 
414
        local_revision = Branch.open('tree').last_revision()
 
415
        remote_revision = Branch.open(
 
416
            self.memory_server.get_url() + 'target').last_revision()
 
417
        self.assertEqual(remote_revision, local_revision)
 
418
 
 
419
    def test_push_gracefully_handles_too_many_redirects(self):
 
420
        """Push fails gracefully if the mkdir generates a large number of
 
421
        redirects.
 
422
        """
 
423
        os.chdir('tree')
 
424
        destination_url = self.memory_server.get_url() + 'infinite-loop'
 
425
        out, err = self.run_bzr_error(
 
426
            ['Too many redirections trying to make %s\\.\n'
 
427
             % re.escape(destination_url)],
 
428
            'push %s' % destination_url, retcode=3)
 
429
        os.chdir('..')
 
430
        self.assertEqual('', out)