~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2010-10-26 08:08:23 UTC
  • mfrom: (5514.1.1 665100-content-type)
  • mto: This revision was merged to the branch mainline in revision 5516.
  • Revision ID: v.ladeuil+lp@free.fr-20101026080823-3wggo03b7cpn9908
Correctly set the Content-Type header when POSTing http requests

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) 2006-2010 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
 
# 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
17
16
 
18
17
 
19
18
"""Black-box tests for bzr push."""
20
19
 
21
 
import os
22
 
 
23
 
import bzrlib
24
 
from bzrlib.branch import Branch
25
 
from bzrlib.bzrdir import BzrDirMetaFormat1
26
 
from bzrlib.osutils import abspath
27
 
from bzrlib.repository import RepositoryFormatKnit1
28
 
from bzrlib.tests.blackbox import ExternalBase
29
 
from bzrlib.uncommit import uncommit
30
 
from bzrlib.urlutils import local_path_from_url
31
 
from bzrlib.workingtree import WorkingTree
32
 
 
33
 
 
34
 
class TestPush(ExternalBase):
 
20
import re
 
21
 
 
22
from bzrlib import (
 
23
    branch,
 
24
    bzrdir,
 
25
    errors,
 
26
    osutils,
 
27
    tests,
 
28
    transport,
 
29
    uncommit,
 
30
    urlutils,
 
31
    workingtree
 
32
    )
 
33
from bzrlib.repofmt import knitrepo
 
34
from bzrlib.tests import (
 
35
    blackbox,
 
36
    http_server,
 
37
    test_foreign,
 
38
    test_server,
 
39
    )
 
40
from bzrlib.transport import memory
 
41
 
 
42
 
 
43
def load_tests(standard_tests, module, loader):
 
44
    """Multiply tests for the push command."""
 
45
    result = loader.suiteClass()
 
46
 
 
47
    # one for each king of change
 
48
    changes_tests, remaining_tests = tests.split_suite_by_condition(
 
49
        standard_tests, tests.condition_isinstance((
 
50
                TestPushStrictWithChanges,
 
51
                )))
 
52
    changes_scenarios = [
 
53
        ('uncommitted',
 
54
         dict(_changes_type= '_uncommitted_changes')),
 
55
        ('pending-merges',
 
56
         dict(_changes_type= '_pending_merges')),
 
57
        ('out-of-sync-trees',
 
58
         dict(_changes_type= '_out_of_sync_trees')),
 
59
        ]
 
60
    tests.multiply_tests(changes_tests, changes_scenarios, result)
 
61
    # No parametrization for the remaining tests
 
62
    result.addTests(remaining_tests)
 
63
 
 
64
    return result
 
65
 
 
66
 
 
67
class TestPush(tests.TestCaseWithTransport):
 
68
 
 
69
    def test_push_error_on_vfs_http(self):
 
70
        """ pushing a branch to a HTTP server fails cleanly. """
 
71
        # the trunk is published on a web server
 
72
        self.transport_readonly_server = http_server.HttpServer
 
73
        self.make_branch('source')
 
74
        public_url = self.get_readonly_url('target')
 
75
        self.run_bzr_error(['http does not support mkdir'],
 
76
                           ['push', public_url],
 
77
                           working_dir='source')
35
78
 
36
79
    def test_push_remember(self):
37
80
        """Push changes from one branch to another and test push location."""
55
98
        self.assertEqual(None, branch_b.get_push_location())
56
99
 
57
100
        # test push for failure without push location set
58
 
        os.chdir('branch_a')
59
 
        out = self.runbzr('push', retcode=3)
 
101
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
60
102
        self.assertEquals(out,
61
103
                ('','bzr: ERROR: No push location known or specified.\n'))
62
104
 
63
105
        # test not remembered if cannot actually push
64
 
        self.run_bzr('push', '../path/which/doesnt/exist', retcode=3)
65
 
        out = self.run_bzr('push', retcode=3)
 
106
        self.run_bzr('push path/which/doesnt/exist',
 
107
                     working_dir='branch_a', retcode=3)
 
108
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
66
109
        self.assertEquals(
67
110
                ('', 'bzr: ERROR: No push location known or specified.\n'),
68
111
                out)
69
112
 
70
113
        # test implicit --remember when no push location set, push fails
71
 
        out = self.run_bzr('push', '../branch_b', retcode=3)
 
114
        out = self.run_bzr('push ../branch_b',
 
115
                           working_dir='branch_a', retcode=3)
72
116
        self.assertEquals(out,
73
117
                ('','bzr: ERROR: These branches have diverged.  '
74
 
                    'Try a merge then push with overwrite.\n'))
75
 
        self.assertEquals(abspath(branch_a.get_push_location()),
76
 
                          abspath(branch_b.bzrdir.root_transport.base))
 
118
                 'See "bzr help diverged-branches" for more information.\n'))
 
119
        self.assertEquals(osutils.abspath(branch_a.get_push_location()),
 
120
                          osutils.abspath(branch_b.bzrdir.root_transport.base))
77
121
 
78
122
        # test implicit --remember after resolving previous failure
79
 
        uncommit(branch=branch_b, tree=tree_b)
 
123
        uncommit.uncommit(branch=branch_b, tree=tree_b)
80
124
        transport.delete('branch_b/c')
81
 
        out = self.run_bzr('push')
 
125
        out, err = self.run_bzr('push', working_dir='branch_a')
82
126
        path = branch_a.get_push_location()
83
 
        self.assertEquals(('Using saved location: %s\n' 
84
 
                           % (local_path_from_url(path),)
85
 
                          , 'All changes applied successfully.\n'
86
 
                            '1 revision(s) pushed.\n'), out)
 
127
        self.assertEquals(out,
 
128
                          'Using saved push location: %s\n'
 
129
                          % urlutils.local_path_from_url(path))
 
130
        self.assertEqual(err,
 
131
                         'All changes applied successfully.\n'
 
132
                         'Pushed up to revision 2.\n')
87
133
        self.assertEqual(path,
88
134
                         branch_b.bzrdir.root_transport.base)
89
135
        # test explicit --remember
90
 
        self.run_bzr('push', '../branch_c', '--remember')
 
136
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
91
137
        self.assertEquals(branch_a.get_push_location(),
92
138
                          branch_c.bzrdir.root_transport.base)
93
 
    
 
139
 
94
140
    def test_push_without_tree(self):
95
141
        # bzr push from a branch that does not have a checkout should work.
96
142
        b = self.make_branch('.')
97
 
        out, err = self.run_bzr('push', 'pushed-location')
 
143
        out, err = self.run_bzr('push pushed-location')
98
144
        self.assertEqual('', out)
99
 
        self.assertEqual('0 revision(s) pushed.\n', err)
100
 
        b2 = bzrlib.branch.Branch.open('pushed-location')
 
145
        self.assertEqual('Created new branch.\n', err)
 
146
        b2 = branch.Branch.open('pushed-location')
101
147
        self.assertEndsWith(b2.base, 'pushed-location/')
102
148
 
 
149
    def test_push_no_tree(self):
 
150
        # bzr push --no-tree of a branch with working trees
 
151
        b = self.make_branch_and_tree('push-from')
 
152
        self.build_tree(['push-from/file'])
 
153
        b.add('file')
 
154
        b.commit('commit 1')
 
155
        out, err = self.run_bzr('push --no-tree -d push-from push-to')
 
156
        self.assertEqual('', out)
 
157
        self.assertEqual('Created new branch.\n', err)
 
158
        self.failIfExists('push-to/file')
 
159
 
103
160
    def test_push_new_branch_revision_count(self):
104
 
        # bzr push of a branch with revisions to a new location 
105
 
        # should print the number of revisions equal to the length of the 
 
161
        # bzr push of a branch with revisions to a new location
 
162
        # should print the number of revisions equal to the length of the
106
163
        # local branch.
107
164
        t = self.make_branch_and_tree('tree')
108
165
        self.build_tree(['tree/file'])
109
166
        t.add('file')
110
167
        t.commit('commit 1')
111
 
        os.chdir('tree')
112
 
        out, err = self.run_bzr('push', 'pushed-to')
113
 
        os.chdir('..')
 
168
        out, err = self.run_bzr('push -d tree pushed-to')
114
169
        self.assertEqual('', out)
115
 
        self.assertEqual('1 revision(s) pushed.\n', err)
 
170
        self.assertEqual('Created new branch.\n', err)
116
171
 
117
172
    def test_push_only_pushes_history(self):
118
173
        # Knit branches should only push the history for the current revision.
119
 
        format = BzrDirMetaFormat1()
120
 
        format.repository_format = RepositoryFormatKnit1()
 
174
        format = bzrdir.BzrDirMetaFormat1()
 
175
        format.repository_format = knitrepo.RepositoryFormatKnit1()
121
176
        shared_repo = self.make_repository('repo', format=format, shared=True)
122
177
        shared_repo.set_make_working_trees(True)
123
178
 
124
179
        def make_shared_tree(path):
125
180
            shared_repo.bzrdir.root_transport.mkdir(path)
126
181
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
127
 
            return WorkingTree.open('repo/' + path)
 
182
            return workingtree.WorkingTree.open('repo/' + path)
128
183
        tree_a = make_shared_tree('a')
129
184
        self.build_tree(['repo/a/file'])
130
185
        tree_a.add('file')
145
200
 
146
201
        # Now that we have a repository with shared files, make sure
147
202
        # that things aren't copied out by a 'push'
148
 
        os.chdir('repo/b')
149
 
        self.run_bzr('push', '../../push-b')
150
 
        pushed_tree = WorkingTree.open('../../push-b')
 
203
        self.run_bzr('push ../../push-b', working_dir='repo/b')
 
204
        pushed_tree = workingtree.WorkingTree.open('push-b')
151
205
        pushed_repo = pushed_tree.branch.repository
152
206
        self.assertFalse(pushed_repo.has_revision('a-1'))
153
207
        self.assertFalse(pushed_repo.has_revision('a-2'))
155
209
 
156
210
    def test_push_funky_id(self):
157
211
        t = self.make_branch_and_tree('tree')
158
 
        os.chdir('tree')
159
 
        self.build_tree(['filename'])
 
212
        self.build_tree(['tree/filename'])
160
213
        t.add('filename', 'funky-chars<>%&;"\'')
161
214
        t.commit('commit filename')
162
 
        self.run_bzr('push', '../new-tree')
 
215
        self.run_bzr('push -d tree new-tree')
 
216
 
 
217
    def test_push_dash_d(self):
 
218
        t = self.make_branch_and_tree('from')
 
219
        t.commit(allow_pointless=True,
 
220
                message='first commit')
 
221
        self.run_bzr('push -d from to-one')
 
222
        self.failUnlessExists('to-one')
 
223
        self.run_bzr('push -d %s %s'
 
224
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
 
225
        self.failUnlessExists('to-two')
 
226
 
 
227
    def test_push_smart_non_stacked_streaming_acceptance(self):
 
228
        self.setup_smart_server_with_call_log()
 
229
        t = self.make_branch_and_tree('from')
 
230
        t.commit(allow_pointless=True, message='first commit')
 
231
        self.reset_smart_call_log()
 
232
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
233
        # This figure represent the amount of work to perform this use case. It
 
234
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
235
        # being too low. If rpc_count increases, more network roundtrips have
 
236
        # become necessary for this use case. Please do not adjust this number
 
237
        # upwards without agreement from bzr's network support maintainers.
 
238
        self.assertLength(9, self.hpss_calls)
 
239
 
 
240
    def test_push_smart_stacked_streaming_acceptance(self):
 
241
        self.setup_smart_server_with_call_log()
 
242
        parent = self.make_branch_and_tree('parent', format='1.9')
 
243
        parent.commit(message='first commit')
 
244
        local = parent.bzrdir.sprout('local').open_workingtree()
 
245
        local.commit(message='local commit')
 
246
        self.reset_smart_call_log()
 
247
        self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
 
248
            self.get_url('public')], working_dir='local')
 
249
        # This figure represent the amount of work to perform this use case. It
 
250
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
251
        # being too low. If rpc_count increases, more network roundtrips have
 
252
        # become necessary for this use case. Please do not adjust this number
 
253
        # upwards without agreement from bzr's network support maintainers.
 
254
        self.assertLength(14, self.hpss_calls)
 
255
        remote = branch.Branch.open('public')
 
256
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
 
257
 
 
258
    def test_push_smart_tags_streaming_acceptance(self):
 
259
        self.setup_smart_server_with_call_log()
 
260
        t = self.make_branch_and_tree('from')
 
261
        rev_id = t.commit(allow_pointless=True, message='first commit')
 
262
        t.branch.tags.set_tag('new-tag', rev_id)
 
263
        self.reset_smart_call_log()
 
264
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
265
        # This figure represent the amount of work to perform this use case. It
 
266
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
267
        # being too low. If rpc_count increases, more network roundtrips have
 
268
        # become necessary for this use case. Please do not adjust this number
 
269
        # upwards without agreement from bzr's network support maintainers.
 
270
        self.assertLength(11, self.hpss_calls)
 
271
 
 
272
    def test_push_smart_incremental_acceptance(self):
 
273
        self.setup_smart_server_with_call_log()
 
274
        t = self.make_branch_and_tree('from')
 
275
        rev_id1 = t.commit(allow_pointless=True, message='first commit')
 
276
        rev_id2 = t.commit(allow_pointless=True, message='second commit')
 
277
        self.run_bzr(
 
278
            ['push', self.get_url('to-one'), '-r1'], working_dir='from')
 
279
        self.reset_smart_call_log()
 
280
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
281
        # This figure represent the amount of work to perform this use case. It
 
282
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
283
        # being too low. If rpc_count increases, more network roundtrips have
 
284
        # become necessary for this use case. Please do not adjust this number
 
285
        # upwards without agreement from bzr's network support maintainers.
 
286
        self.assertLength(11, self.hpss_calls)
 
287
 
 
288
    def test_push_smart_with_default_stacking_url_path_segment(self):
 
289
        # If the default stacked-on location is a path element then branches
 
290
        # we push there over the smart server are stacked and their
 
291
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
 
292
        self.setup_smart_server_with_call_log()
 
293
        self.make_branch('stack-on', format='1.9')
 
294
        self.make_bzrdir('.').get_config().set_default_stack_on(
 
295
            '/stack-on')
 
296
        self.make_branch('from', format='1.9')
 
297
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
 
298
        b = branch.Branch.open(self.get_url('to'))
 
299
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
 
300
 
 
301
    def test_push_smart_with_default_stacking_relative_path(self):
 
302
        # If the default stacked-on location is a relative path then branches
 
303
        # we push there over the smart server are stacked and their
 
304
        # stacked_on_url is a relative path. Added to nail bug 385132.
 
305
        self.setup_smart_server_with_call_log()
 
306
        self.make_branch('stack-on', format='1.9')
 
307
        self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
 
308
        self.make_branch('from', format='1.9')
 
309
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
 
310
        b = branch.Branch.open(self.get_url('to'))
 
311
        self.assertEqual('../stack-on', b.get_stacked_on_url())
 
312
 
 
313
    def create_simple_tree(self):
 
314
        tree = self.make_branch_and_tree('tree')
 
315
        self.build_tree(['tree/a'])
 
316
        tree.add(['a'], ['a-id'])
 
317
        tree.commit('one', rev_id='r1')
 
318
        return tree
 
319
 
 
320
    def test_push_create_prefix(self):
 
321
        """'bzr push --create-prefix' will create leading directories."""
 
322
        tree = self.create_simple_tree()
 
323
 
 
324
        self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
 
325
                           'push ../new/tree',
 
326
                           working_dir='tree')
 
327
        self.run_bzr('push ../new/tree --create-prefix',
 
328
                     working_dir='tree')
 
329
        new_tree = workingtree.WorkingTree.open('new/tree')
 
330
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
331
        self.failUnlessExists('new/tree/a')
 
332
 
 
333
    def test_push_use_existing(self):
 
334
        """'bzr push --use-existing-dir' can push into an existing dir.
 
335
 
 
336
        By default, 'bzr push' will not use an existing, non-versioned dir.
 
337
        """
 
338
        tree = self.create_simple_tree()
 
339
        self.build_tree(['target/'])
 
340
 
 
341
        self.run_bzr_error(['Target directory ../target already exists',
 
342
                            'Supply --use-existing-dir',
 
343
                           ],
 
344
                           'push ../target', working_dir='tree')
 
345
 
 
346
        self.run_bzr('push --use-existing-dir ../target',
 
347
                     working_dir='tree')
 
348
 
 
349
        new_tree = workingtree.WorkingTree.open('target')
 
350
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
 
351
        # The push should have created target/a
 
352
        self.failUnlessExists('target/a')
 
353
 
 
354
    def test_push_use_existing_into_empty_bzrdir(self):
 
355
        """'bzr push --use-existing-dir' into a dir with an empty .bzr dir
 
356
        fails.
 
357
        """
 
358
        tree = self.create_simple_tree()
 
359
        self.build_tree(['target/', 'target/.bzr/'])
 
360
        self.run_bzr_error(
 
361
            ['Target directory ../target already contains a .bzr directory, '
 
362
             'but it is not valid.'],
 
363
            'push ../target --use-existing-dir', working_dir='tree')
 
364
 
 
365
    def test_push_onto_repo(self):
 
366
        """We should be able to 'bzr push' into an existing bzrdir."""
 
367
        tree = self.create_simple_tree()
 
368
        repo = self.make_repository('repo', shared=True)
 
369
 
 
370
        self.run_bzr('push ../repo',
 
371
                     working_dir='tree')
 
372
 
 
373
        # Pushing onto an existing bzrdir will create a repository and
 
374
        # branch as needed, but will only create a working tree if there was
 
375
        # no BzrDir before.
 
376
        self.assertRaises(errors.NoWorkingTree,
 
377
                          workingtree.WorkingTree.open, 'repo')
 
378
        new_branch = branch.Branch.open('repo')
 
379
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
 
380
 
 
381
    def test_push_onto_just_bzrdir(self):
 
382
        """We don't handle when the target is just a bzrdir.
 
383
 
 
384
        Because you shouldn't be able to create *just* a bzrdir in the wild.
 
385
        """
 
386
        # TODO: jam 20070109 Maybe it would be better to create the repository
 
387
        #       if at this point
 
388
        tree = self.create_simple_tree()
 
389
        a_bzrdir = self.make_bzrdir('dir')
 
390
 
 
391
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
 
392
                'push ../dir',
 
393
                working_dir='tree')
 
394
 
 
395
    def test_push_with_revisionspec(self):
 
396
        """We should be able to push a revision older than the tip."""
 
397
        tree_from = self.make_branch_and_tree('from')
 
398
        tree_from.commit("One.", rev_id="from-1")
 
399
        tree_from.commit("Two.", rev_id="from-2")
 
400
 
 
401
        self.run_bzr('push -r1 ../to', working_dir='from')
 
402
 
 
403
        tree_to = workingtree.WorkingTree.open('to')
 
404
        repo_to = tree_to.branch.repository
 
405
        self.assertTrue(repo_to.has_revision('from-1'))
 
406
        self.assertFalse(repo_to.has_revision('from-2'))
 
407
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
 
408
 
 
409
        self.run_bzr_error(
 
410
            ['bzr: ERROR: bzr push --revision '
 
411
             'takes exactly one revision identifier\n'],
 
412
            'push -r0..2 ../to', working_dir='from')
 
413
 
 
414
    def create_trunk_and_feature_branch(self):
 
415
        # We have a mainline
 
416
        trunk_tree = self.make_branch_and_tree('target',
 
417
            format='1.9')
 
418
        trunk_tree.commit('mainline')
 
419
        # and a branch from it
 
420
        branch_tree = self.make_branch_and_tree('branch',
 
421
            format='1.9')
 
422
        branch_tree.pull(trunk_tree.branch)
 
423
        branch_tree.branch.set_parent(trunk_tree.branch.base)
 
424
        # with some work on it
 
425
        branch_tree.commit('moar work plz')
 
426
        return trunk_tree, branch_tree
 
427
 
 
428
    def assertPublished(self, branch_revid, stacked_on):
 
429
        """Assert that the branch 'published' has been published correctly."""
 
430
        published_branch = branch.Branch.open('published')
 
431
        # The published branch refers to the mainline
 
432
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
 
433
        # and the branch's work was pushed
 
434
        self.assertTrue(published_branch.repository.has_revision(branch_revid))
 
435
 
 
436
    def test_push_new_branch_stacked_on(self):
 
437
        """Pushing a new branch with --stacked-on creates a stacked branch."""
 
438
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
439
        # we publish branch_tree with a reference to the mainline.
 
440
        out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
 
441
            self.get_url('published')], working_dir='branch')
 
442
        self.assertEqual('', out)
 
443
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
444
            trunk_tree.branch.base, err)
 
445
        self.assertPublished(branch_tree.last_revision(),
 
446
            trunk_tree.branch.base)
 
447
 
 
448
    def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
 
449
        """When the parent has no public url the parent is used as-is."""
 
450
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
451
        # now we do a stacked push, which should determine the public location
 
452
        # for us.
 
453
        out, err = self.run_bzr(['push', '--stacked',
 
454
            self.get_url('published')], working_dir='branch')
 
455
        self.assertEqual('', out)
 
456
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
457
            trunk_tree.branch.base, err)
 
458
        self.assertPublished(branch_tree.last_revision(),
 
459
                             trunk_tree.branch.base)
 
460
 
 
461
    def test_push_new_branch_stacked_uses_parent_public(self):
 
462
        """Pushing a new branch with --stacked creates a stacked branch."""
 
463
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
464
        # the trunk is published on a web server
 
465
        self.transport_readonly_server = http_server.HttpServer
 
466
        trunk_public = self.make_branch('public_trunk', format='1.9')
 
467
        trunk_public.pull(trunk_tree.branch)
 
468
        trunk_public_url = self.get_readonly_url('public_trunk')
 
469
        trunk_tree.branch.set_public_branch(trunk_public_url)
 
470
        # now we do a stacked push, which should determine the public location
 
471
        # for us.
 
472
        out, err = self.run_bzr(['push', '--stacked',
 
473
            self.get_url('published')], working_dir='branch')
 
474
        self.assertEqual('', out)
 
475
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
476
            trunk_public_url, err)
 
477
        self.assertPublished(branch_tree.last_revision(), trunk_public_url)
 
478
 
 
479
    def test_push_new_branch_stacked_no_parent(self):
 
480
        """Pushing with --stacked and no parent branch errors."""
 
481
        branch = self.make_branch_and_tree('branch', format='1.9')
 
482
        # now we do a stacked push, which should fail as the place to refer too
 
483
        # cannot be determined.
 
484
        out, err = self.run_bzr_error(
 
485
            ['Could not determine branch to refer to\\.'], ['push', '--stacked',
 
486
            self.get_url('published')], working_dir='branch')
 
487
        self.assertEqual('', out)
 
488
        self.assertFalse(self.get_transport('published').has('.'))
 
489
 
 
490
    def test_push_notifies_default_stacking(self):
 
491
        self.make_branch('stack_on', format='1.6')
 
492
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
493
        self.make_branch('from', format='1.6')
 
494
        out, err = self.run_bzr('push -d from to')
 
495
        self.assertContainsRe(err,
 
496
                              'Using default stacking branch stack_on at .*')
 
497
 
 
498
    def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
 
499
        self.make_branch('stack_on', format='1.6')
 
500
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
501
        self.make_branch('from', format='pack-0.92')
 
502
        out, err = self.run_bzr('push -d from to')
 
503
        b = branch.Branch.open('to')
 
504
        self.assertEqual('../stack_on', b.get_stacked_on_url())
 
505
 
 
506
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
 
507
        self.make_branch('stack_on', format='pack-0.92')
 
508
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
509
        self.make_branch('from', format='pack-0.92')
 
510
        out, err = self.run_bzr('push -d from to')
 
511
        b = branch.Branch.open('to')
 
512
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
 
513
 
 
514
    def test_push_doesnt_create_broken_branch(self):
 
515
        """Pushing a new standalone branch works even when there's a default
 
516
        stacking policy at the destination.
 
517
 
 
518
        The new branch will preserve the repo format (even if it isn't the
 
519
        default for the branch), and will be stacked when the repo format
 
520
        allows (which means that the branch format isn't necessarly preserved).
 
521
        """
 
522
        self.make_repository('repo', shared=True, format='1.6')
 
523
        builder = self.make_branch_builder('repo/local', format='pack-0.92')
 
524
        builder.start_series()
 
525
        builder.build_snapshot('rev-1', None, [
 
526
            ('add', ('', 'root-id', 'directory', '')),
 
527
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
 
528
        builder.build_snapshot('rev-2', ['rev-1'], [])
 
529
        builder.build_snapshot('rev-3', ['rev-2'],
 
530
            [('modify', ('f-id', 'new-content\n'))])
 
531
        builder.finish_series()
 
532
        branch = builder.get_branch()
 
533
        # Push rev-1 to "trunk", so that we can stack on it.
 
534
        self.run_bzr('push -d repo/local trunk -r 1')
 
535
        # Set a default stacking policy so that new branches will automatically
 
536
        # stack on trunk.
 
537
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
 
538
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
 
539
        out, err = self.run_bzr('push -d repo/local remote -r 2')
 
540
        self.assertContainsRe(
 
541
            err, 'Using default stacking branch trunk at .*')
 
542
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
 
543
        # fulltext record for f-id @ rev-1, then this will fail.
 
544
        out, err = self.run_bzr('push -d repo/local remote -r 3')
 
545
 
 
546
    def test_push_verbose_shows_log(self):
 
547
        tree = self.make_branch_and_tree('source')
 
548
        tree.commit('rev1')
 
549
        out, err = self.run_bzr('push -v -d source target')
 
550
        # initial push contains log
 
551
        self.assertContainsRe(out, 'rev1')
 
552
        tree.commit('rev2')
 
553
        out, err = self.run_bzr('push -v -d source target')
 
554
        # subsequent push contains log
 
555
        self.assertContainsRe(out, 'rev2')
 
556
        # subsequent log is accurate
 
557
        self.assertNotContainsRe(out, 'rev1')
 
558
 
 
559
    def test_push_from_subdir(self):
 
560
        t = self.make_branch_and_tree('tree')
 
561
        self.build_tree(['tree/dir/', 'tree/dir/file'])
 
562
        t.add('dir', 'dir/file')
 
563
        t.commit('r1')
 
564
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
 
565
        self.assertEqual('', out)
 
566
        self.assertEqual('Created new branch.\n', err)
 
567
 
 
568
 
 
569
class RedirectingMemoryTransport(memory.MemoryTransport):
 
570
 
 
571
    def mkdir(self, relpath, mode=None):
 
572
        if self._cwd == '/source/':
 
573
            raise errors.RedirectRequested(self.abspath(relpath),
 
574
                                           self.abspath('../target'),
 
575
                                           is_permanent=True)
 
576
        elif self._cwd == '/infinite-loop/':
 
577
            raise errors.RedirectRequested(self.abspath(relpath),
 
578
                                           self.abspath('../infinite-loop'),
 
579
                                           is_permanent=True)
 
580
        else:
 
581
            return super(RedirectingMemoryTransport, self).mkdir(
 
582
                relpath, mode)
 
583
 
 
584
    def get(self, relpath):
 
585
        if self.clone(relpath)._cwd == '/infinite-loop/':
 
586
            raise errors.RedirectRequested(self.abspath(relpath),
 
587
                                           self.abspath('../infinite-loop'),
 
588
                                           is_permanent=True)
 
589
        else:
 
590
            return super(RedirectingMemoryTransport, self).get(relpath)
 
591
 
 
592
    def _redirected_to(self, source, target):
 
593
        # We do accept redirections
 
594
        return transport.get_transport(target)
 
595
 
 
596
 
 
597
class RedirectingMemoryServer(memory.MemoryServer):
 
598
 
 
599
    def start_server(self):
 
600
        self._dirs = {'/': None}
 
601
        self._files = {}
 
602
        self._locks = {}
 
603
        self._scheme = 'redirecting-memory+%s:///' % id(self)
 
604
        transport.register_transport(self._scheme, self._memory_factory)
 
605
 
 
606
    def _memory_factory(self, url):
 
607
        result = RedirectingMemoryTransport(url)
 
608
        result._dirs = self._dirs
 
609
        result._files = self._files
 
610
        result._locks = self._locks
 
611
        return result
 
612
 
 
613
    def stop_server(self):
 
614
        transport.unregister_transport(self._scheme, self._memory_factory)
 
615
 
 
616
 
 
617
class TestPushRedirect(tests.TestCaseWithTransport):
 
618
 
 
619
    def setUp(self):
 
620
        tests.TestCaseWithTransport.setUp(self)
 
621
        self.memory_server = RedirectingMemoryServer()
 
622
        self.start_server(self.memory_server)
 
623
        # Make the branch and tree that we'll be pushing.
 
624
        t = self.make_branch_and_tree('tree')
 
625
        self.build_tree(['tree/file'])
 
626
        t.add('file')
 
627
        t.commit('commit 1')
 
628
 
 
629
    def test_push_redirects_on_mkdir(self):
 
630
        """If the push requires a mkdir, push respects redirect requests.
 
631
 
 
632
        This is added primarily to handle lp:/ URI support, so that users can
 
633
        push to new branches by specifying lp:/ URIs.
 
634
        """
 
635
        destination_url = self.memory_server.get_url() + 'source'
 
636
        self.run_bzr(['push', '-d', 'tree', destination_url])
 
637
 
 
638
        local_revision = branch.Branch.open('tree').last_revision()
 
639
        remote_revision = branch.Branch.open(
 
640
            self.memory_server.get_url() + 'target').last_revision()
 
641
        self.assertEqual(remote_revision, local_revision)
 
642
 
 
643
    def test_push_gracefully_handles_too_many_redirects(self):
 
644
        """Push fails gracefully if the mkdir generates a large number of
 
645
        redirects.
 
646
        """
 
647
        destination_url = self.memory_server.get_url() + 'infinite-loop'
 
648
        out, err = self.run_bzr_error(
 
649
            ['Too many redirections trying to make %s\\.\n'
 
650
             % re.escape(destination_url)],
 
651
            ['push', '-d', 'tree', destination_url], retcode=3)
 
652
        self.assertEqual('', out)
 
653
 
 
654
 
 
655
class TestPushStrictMixin(object):
 
656
 
 
657
    def make_local_branch_and_tree(self):
 
658
        self.tree = self.make_branch_and_tree('local')
 
659
        self.build_tree_contents([('local/file', 'initial')])
 
660
        self.tree.add('file')
 
661
        self.tree.commit('adding file', rev_id='added')
 
662
        self.build_tree_contents([('local/file', 'modified')])
 
663
        self.tree.commit('modify file', rev_id='modified')
 
664
 
 
665
    def set_config_push_strict(self, value):
 
666
        # set config var (any of bazaar.conf, locations.conf, branch.conf
 
667
        # should do)
 
668
        conf = self.tree.branch.get_config()
 
669
        conf.set_user_option('push_strict', value)
 
670
 
 
671
    _default_command = ['push', '../to']
 
672
    _default_wd = 'local'
 
673
    _default_errors = ['Working tree ".*/local/" has uncommitted '
 
674
                       'changes \(See bzr status\)\.',]
 
675
    _default_additional_error = 'Use --no-strict to force the push.\n'
 
676
    _default_additional_warning = 'Uncommitted changes will not be pushed.'
 
677
 
 
678
 
 
679
    def assertPushFails(self, args):
 
680
        out, err = self.run_bzr_error(self._default_errors,
 
681
                                      self._default_command + args,
 
682
                                      working_dir=self._default_wd, retcode=3)
 
683
        self.assertContainsRe(err, self._default_additional_error)
 
684
 
 
685
    def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
 
686
        if with_warning:
 
687
            error_regexes = self._default_errors
 
688
        else:
 
689
            error_regexes = []
 
690
        out, err = self.run_bzr(self._default_command + args,
 
691
                                working_dir=self._default_wd,
 
692
                                error_regexes=error_regexes)
 
693
        if with_warning:
 
694
            self.assertContainsRe(err, self._default_additional_warning)
 
695
        else:
 
696
            self.assertNotContainsRe(err, self._default_additional_warning)
 
697
        branch_from = branch.Branch.open(self._default_wd)
 
698
        if revid_to_push is None:
 
699
            revid_to_push = branch_from.last_revision()
 
700
        branch_to = branch.Branch.open('to')
 
701
        repo_to = branch_to.repository
 
702
        self.assertTrue(repo_to.has_revision(revid_to_push))
 
703
        self.assertEqual(revid_to_push, branch_to.last_revision())
 
704
 
 
705
 
 
706
 
 
707
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
 
708
                                   TestPushStrictMixin):
 
709
 
 
710
    def setUp(self):
 
711
        super(TestPushStrictWithoutChanges, self).setUp()
 
712
        self.make_local_branch_and_tree()
 
713
 
 
714
    def test_push_default(self):
 
715
        self.assertPushSucceeds([])
 
716
 
 
717
    def test_push_strict(self):
 
718
        self.assertPushSucceeds(['--strict'])
 
719
 
 
720
    def test_push_no_strict(self):
 
721
        self.assertPushSucceeds(['--no-strict'])
 
722
 
 
723
    def test_push_config_var_strict(self):
 
724
        self.set_config_push_strict('true')
 
725
        self.assertPushSucceeds([])
 
726
 
 
727
    def test_push_config_var_no_strict(self):
 
728
        self.set_config_push_strict('false')
 
729
        self.assertPushSucceeds([])
 
730
 
 
731
 
 
732
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
 
733
                                TestPushStrictMixin):
 
734
 
 
735
    _changes_type = None # Set by load_tests
 
736
 
 
737
    def setUp(self):
 
738
        super(TestPushStrictWithChanges, self).setUp()
 
739
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
 
740
        # _pending_merges or _out_of_sync_trees
 
741
        getattr(self, self._changes_type)()
 
742
 
 
743
    def _uncommitted_changes(self):
 
744
        self.make_local_branch_and_tree()
 
745
        # Make a change without committing it
 
746
        self.build_tree_contents([('local/file', 'in progress')])
 
747
 
 
748
    def _pending_merges(self):
 
749
        self.make_local_branch_and_tree()
 
750
        # Create 'other' branch containing a new file
 
751
        other_bzrdir = self.tree.bzrdir.sprout('other')
 
752
        other_tree = other_bzrdir.open_workingtree()
 
753
        self.build_tree_contents([('other/other-file', 'other')])
 
754
        other_tree.add('other-file')
 
755
        other_tree.commit('other commit', rev_id='other')
 
756
        # Merge and revert, leaving a pending merge
 
757
        self.tree.merge_from_branch(other_tree.branch)
 
758
        self.tree.revert(filenames=['other-file'], backups=False)
 
759
 
 
760
    def _out_of_sync_trees(self):
 
761
        self.make_local_branch_and_tree()
 
762
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
 
763
        # Make a change and commit it
 
764
        self.build_tree_contents([('local/file', 'modified in local')])
 
765
        self.tree.commit('modify file', rev_id='modified-in-local')
 
766
        # Exercise commands from the checkout directory
 
767
        self._default_wd = 'checkout'
 
768
        self._default_errors = ["Working tree is out of date, please run"
 
769
                                " 'bzr update'\.",]
 
770
 
 
771
    def test_push_default(self):
 
772
        self.assertPushSucceeds([], with_warning=True)
 
773
 
 
774
    def test_push_with_revision(self):
 
775
        self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
 
776
 
 
777
    def test_push_no_strict(self):
 
778
        self.assertPushSucceeds(['--no-strict'])
 
779
 
 
780
    def test_push_strict_with_changes(self):
 
781
        self.assertPushFails(['--strict'])
 
782
 
 
783
    def test_push_respect_config_var_strict(self):
 
784
        self.set_config_push_strict('true')
 
785
        self.assertPushFails([])
 
786
 
 
787
    def test_push_bogus_config_var_ignored(self):
 
788
        self.set_config_push_strict("I don't want you to be strict")
 
789
        self.assertPushSucceeds([], with_warning=True)
 
790
 
 
791
    def test_push_no_strict_command_line_override_config(self):
 
792
        self.set_config_push_strict('yES')
 
793
        self.assertPushFails([])
 
794
        self.assertPushSucceeds(['--no-strict'])
 
795
 
 
796
    def test_push_strict_command_line_override_config(self):
 
797
        self.set_config_push_strict('oFF')
 
798
        self.assertPushFails(['--strict'])
 
799
        self.assertPushSucceeds([])
 
800
 
 
801
 
 
802
class TestPushForeign(tests.TestCaseWithTransport):
 
803
 
 
804
    def setUp(self):
 
805
        super(TestPushForeign, self).setUp()
 
806
        test_foreign.register_dummy_foreign_for_test(self)
 
807
 
 
808
    def make_dummy_builder(self, relpath):
 
809
        builder = self.make_branch_builder(
 
810
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
 
811
        builder.build_snapshot('revid', None,
 
812
            [('add', ('', 'TREE_ROOT', 'directory', None)),
 
813
             ('add', ('foo', 'fooid', 'file', 'bar'))])
 
814
        return builder
 
815
 
 
816
    def test_no_roundtripping(self):
 
817
        target_branch = self.make_dummy_builder('dp').get_branch()
 
818
        source_tree = self.make_branch_and_tree("dc")
 
819
        output, error = self.run_bzr("push -d dc dp", retcode=3)
 
820
        self.assertEquals("", output)
 
821
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
 
822
            " push to dummy. You may want to use dpush instead.\n")