18
18
"""Black-box tests for bzr push."""
23
22
from bzrlib import (
28
from bzrlib.branch import Branch
29
from bzrlib.bzrdir import BzrDirMetaFormat1
30
from bzrlib.osutils import abspath
31
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
32
from bzrlib.smart import client, server
33
from bzrlib.tests.blackbox import ExternalBase
34
from bzrlib.tests.http_server import HttpServer
35
from bzrlib.transport.memory import MemoryServer, MemoryTransport
36
from bzrlib.uncommit import uncommit
37
from bzrlib.urlutils import local_path_from_url
38
from bzrlib.workingtree import WorkingTree
41
class TestPush(ExternalBase):
33
from bzrlib.repofmt import knitrepo
34
from bzrlib.tests import http_server
35
from bzrlib.transport import memory
38
def load_tests(standard_tests, module, loader):
39
"""Multiply tests for the push command."""
40
result = loader.suiteClass()
42
# one for each king of change
43
changes_tests, remaining_tests = tests.split_suite_by_condition(
44
standard_tests, tests.condition_isinstance((
45
TestPushStrictWithChanges,
49
dict(_changes_type= '_uncommitted_changes')),
51
dict(_changes_type= '_pending_merges')),
53
dict(_changes_type= '_out_of_sync_trees')),
55
tests.multiply_tests(changes_tests, changes_scenarios, result)
56
# No parametrization for the remaining tests
57
result.addTests(remaining_tests)
62
class TestPush(tests.TestCaseWithTransport):
43
64
def test_push_error_on_vfs_http(self):
44
65
""" pushing a branch to a HTTP server fails cleanly. """
45
66
# the trunk is published on a web server
46
self.transport_readonly_server = HttpServer
67
self.transport_readonly_server = http_server.HttpServer
47
68
self.make_branch('source')
48
69
public_url = self.get_readonly_url('target')
49
70
self.run_bzr_error(['http does not support mkdir'],
72
93
self.assertEqual(None, branch_b.get_push_location())
74
95
# test push for failure without push location set
76
out = self.run_bzr('push', retcode=3)
96
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
77
97
self.assertEquals(out,
78
98
('','bzr: ERROR: No push location known or specified.\n'))
80
100
# test not remembered if cannot actually push
81
self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
82
out = self.run_bzr('push', retcode=3)
101
self.run_bzr('push path/which/doesnt/exist',
102
working_dir='branch_a', retcode=3)
103
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
83
104
self.assertEquals(
84
105
('', 'bzr: ERROR: No push location known or specified.\n'),
87
108
# test implicit --remember when no push location set, push fails
88
out = self.run_bzr('push ../branch_b', retcode=3)
109
out = self.run_bzr('push ../branch_b',
110
working_dir='branch_a', retcode=3)
89
111
self.assertEquals(out,
90
112
('','bzr: ERROR: These branches have diverged. '
91
'Try using "merge" and then "push".\n'))
92
self.assertEquals(abspath(branch_a.get_push_location()),
93
abspath(branch_b.bzrdir.root_transport.base))
113
'See "bzr help diverged-branches" for more information.\n'))
114
self.assertEquals(osutils.abspath(branch_a.get_push_location()),
115
osutils.abspath(branch_b.bzrdir.root_transport.base))
95
117
# test implicit --remember after resolving previous failure
96
uncommit(branch=branch_b, tree=tree_b)
118
uncommit.uncommit(branch=branch_b, tree=tree_b)
97
119
transport.delete('branch_b/c')
98
out, err = self.run_bzr('push')
120
out, err = self.run_bzr('push', working_dir='branch_a')
99
121
path = branch_a.get_push_location()
100
122
self.assertEquals(out,
101
123
'Using saved push location: %s\n'
102
% local_path_from_url(path))
124
% urlutils.local_path_from_url(path))
103
125
self.assertEqual(err,
104
126
'All changes applied successfully.\n'
105
127
'Pushed up to revision 2.\n')
106
128
self.assertEqual(path,
107
129
branch_b.bzrdir.root_transport.base)
108
130
# test explicit --remember
109
self.run_bzr('push ../branch_c --remember')
131
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
110
132
self.assertEquals(branch_a.get_push_location(),
111
133
branch_c.bzrdir.root_transport.base)
127
149
self.build_tree(['tree/file'])
129
151
t.commit('commit 1')
131
out, err = self.run_bzr('push pushed-to')
152
out, err = self.run_bzr('push -d tree pushed-to')
133
153
self.assertEqual('', out)
134
154
self.assertEqual('Created new branch.\n', err)
136
156
def test_push_only_pushes_history(self):
137
157
# Knit branches should only push the history for the current revision.
138
format = BzrDirMetaFormat1()
139
format.repository_format = RepositoryFormatKnit1()
158
format = bzrdir.BzrDirMetaFormat1()
159
format.repository_format = knitrepo.RepositoryFormatKnit1()
140
160
shared_repo = self.make_repository('repo', format=format, shared=True)
141
161
shared_repo.set_make_working_trees(True)
143
163
def make_shared_tree(path):
144
164
shared_repo.bzrdir.root_transport.mkdir(path)
145
165
shared_repo.bzrdir.create_branch_convenience('repo/' + path)
146
return WorkingTree.open('repo/' + path)
166
return workingtree.WorkingTree.open('repo/' + path)
147
167
tree_a = make_shared_tree('a')
148
168
self.build_tree(['repo/a/file'])
149
169
tree_a.add('file')
218
236
# become necessary for this use case. Please do not adjust this number
219
237
# upwards without agreement from bzr's network support maintainers.
220
238
self.assertLength(14, self.hpss_calls)
221
remote = Branch.open('public')
239
remote = branch.Branch.open('public')
222
240
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
242
def test_push_smart_with_default_stacking_url_path_segment(self):
243
# If the default stacked-on location is a path element then branches
244
# we push there over the smart server are stacked and their
245
# stacked_on_url is that exact path segment. Added to nail bug 385132.
246
self.setup_smart_server_with_call_log()
247
self.make_branch('stack-on', format='1.9')
248
self.make_bzrdir('.').get_config().set_default_stack_on(
250
self.make_branch('from', format='1.9')
251
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
252
b = branch.Branch.open(self.get_url('to'))
253
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
255
def test_push_smart_with_default_stacking_relative_path(self):
256
# If the default stacked-on location is a relative path then branches
257
# we push there over the smart server are stacked and their
258
# stacked_on_url is a relative path. Added to nail bug 385132.
259
self.setup_smart_server_with_call_log()
260
self.make_branch('stack-on', format='1.9')
261
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
262
self.make_branch('from', format='1.9')
263
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
264
b = branch.Branch.open(self.get_url('to'))
265
self.assertEqual('../stack-on', b.get_stacked_on_url())
224
267
def create_simple_tree(self):
225
268
tree = self.make_branch_and_tree('tree')
226
269
self.build_tree(['tree/a'])
398
443
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
399
444
self.make_branch('from', format='pack-0.92')
400
445
out, err = self.run_bzr('push -d from to')
401
branch = Branch.open('to')
402
self.assertEqual('../stack_on', branch.get_stacked_on_url())
446
b = branch.Branch.open('to')
447
self.assertEqual('../stack_on', b.get_stacked_on_url())
404
449
def test_push_does_not_change_format_with_default_if_target_cannot(self):
405
450
self.make_branch('stack_on', format='pack-0.92')
406
451
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
407
452
self.make_branch('from', format='pack-0.92')
408
453
out, err = self.run_bzr('push -d from to')
409
branch = Branch.open('to')
410
self.assertRaises(errors.UnstackableBranchFormat,
411
branch.get_stacked_on_url)
454
b = branch.Branch.open('to')
455
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
413
457
def test_push_doesnt_create_broken_branch(self):
414
458
"""Pushing a new standalone branch works even when there's a default
455
499
# subsequent log is accurate
456
500
self.assertNotContainsRe(out, 'rev1')
459
class RedirectingMemoryTransport(MemoryTransport):
502
def test_push_from_subdir(self):
503
t = self.make_branch_and_tree('tree')
504
self.build_tree(['tree/dir/', 'tree/dir/file'])
505
t.add('dir', 'dir/file')
507
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
508
self.assertEqual('', out)
509
self.assertEqual('Created new branch.\n', err)
512
class RedirectingMemoryTransport(memory.MemoryTransport):
461
514
def mkdir(self, relpath, mode=None):
462
515
from bzrlib.trace import mutter
463
mutter('cwd: %r, rel: %r, abs: %r' % (self._cwd, relpath, abspath))
464
516
if self._cwd == '/source/':
465
517
raise errors.RedirectRequested(self.abspath(relpath),
466
518
self.abspath('../target'),
536
588
% re.escape(destination_url)],
537
589
['push', '-d', 'tree', destination_url], retcode=3)
538
590
self.assertEqual('', out)
593
class TestPushStrictMixin(object):
595
def make_local_branch_and_tree(self):
596
self.tree = self.make_branch_and_tree('local')
597
self.build_tree_contents([('local/file', 'initial')])
598
self.tree.add('file')
599
self.tree.commit('adding file', rev_id='added')
600
self.build_tree_contents([('local/file', 'modified')])
601
self.tree.commit('modify file', rev_id='modified')
603
def set_config_push_strict(self, value):
604
# set config var (any of bazaar.conf, locations.conf, branch.conf
606
conf = self.tree.branch.get_config()
607
conf.set_user_option('push_strict', value)
609
_default_command = ['push', '../to']
610
_default_wd = 'local'
611
_default_errors = ['Working tree ".*/local/" has uncommitted '
612
'changes \(See bzr status\)\.',]
613
_default_pushed_revid = 'modified'
615
def assertPushFails(self, args):
616
self.run_bzr_error(self._default_errors, self._default_command + args,
617
working_dir=self._default_wd, retcode=3)
619
def assertPushSucceeds(self, args, pushed_revid=None):
620
self.run_bzr(self._default_command + args,
621
working_dir=self._default_wd)
622
if pushed_revid is None:
623
pushed_revid = self._default_pushed_revid
624
tree_to = workingtree.WorkingTree.open('to')
625
repo_to = tree_to.branch.repository
626
self.assertTrue(repo_to.has_revision(pushed_revid))
627
self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
631
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
632
TestPushStrictMixin):
635
super(TestPushStrictWithoutChanges, self).setUp()
636
self.make_local_branch_and_tree()
638
def test_push_default(self):
639
self.assertPushSucceeds([])
641
def test_push_strict(self):
642
self.assertPushSucceeds(['--strict'])
644
def test_push_no_strict(self):
645
self.assertPushSucceeds(['--no-strict'])
647
def test_push_config_var_strict(self):
648
self.set_config_push_strict('true')
649
self.assertPushSucceeds([])
651
def test_push_config_var_no_strict(self):
652
self.set_config_push_strict('false')
653
self.assertPushSucceeds([])
656
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
657
TestPushStrictMixin):
659
_changes_type = None # Set by load_tests
662
super(TestPushStrictWithChanges, self).setUp()
663
getattr(self, self._changes_type)()
665
def _uncommitted_changes(self):
666
self.make_local_branch_and_tree()
667
# Make a change without committing it
668
self.build_tree_contents([('local/file', 'in progress')])
670
def _pending_merges(self):
671
self.make_local_branch_and_tree()
672
# Create 'other' branch containing a new file
673
other_bzrdir = self.tree.bzrdir.sprout('other')
674
other_tree = other_bzrdir.open_workingtree()
675
self.build_tree_contents([('other/other-file', 'other')])
676
other_tree.add('other-file')
677
other_tree.commit('other commit', rev_id='other')
678
# Merge and revert, leaving a pending merge
679
self.tree.merge_from_branch(other_tree.branch)
680
self.tree.revert(filenames=['other-file'], backups=False)
682
def _out_of_sync_trees(self):
683
self.make_local_branch_and_tree()
684
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
685
# Make a change and commit it
686
self.build_tree_contents([('local/file', 'modified in local')])
687
self.tree.commit('modify file', rev_id='modified-in-local')
688
# Exercise commands from the checkout directory
689
self._default_wd = 'checkout'
690
self._default_errors = ["Working tree is out of date, please run"
692
self._default_pushed_revid = 'modified-in-local'
694
def test_push_default(self):
695
self.assertPushFails([])
697
def test_push_with_revision(self):
698
self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
700
def test_push_no_strict(self):
701
self.assertPushSucceeds(['--no-strict'])
703
def test_push_strict_with_changes(self):
704
self.assertPushFails(['--strict'])
706
def test_push_respect_config_var_strict(self):
707
self.set_config_push_strict('true')
708
self.assertPushFails([])
710
def test_push_bogus_config_var_ignored(self):
711
self.set_config_push_strict("I don't want you to be strict")
712
self.assertPushFails([])
714
def test_push_no_strict_command_line_override_config(self):
715
self.set_config_push_strict('yES')
716
self.assertPushFails([])
717
self.assertPushSucceeds(['--no-strict'])
719
def test_push_strict_command_line_override_config(self):
720
self.set_config_push_strict('oFF')
721
self.assertPushFails(['--strict'])
722
self.assertPushSucceeds([])