13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
"""Black-box tests for bzr push."""
22
23
from bzrlib import (
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):
64
def test_push_error_on_vfs_http(self):
65
""" pushing a branch to a HTTP server fails cleanly. """
66
# the trunk is published on a web server
67
self.transport_readonly_server = http_server.HttpServer
68
self.make_branch('source')
69
public_url = self.get_readonly_url('target')
70
self.run_bzr_error(['http does not support mkdir'],
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.tests.http_server import HttpServer
33
from bzrlib.transport import register_transport, unregister_transport
34
from bzrlib.transport.memory import MemoryServer, MemoryTransport
35
from bzrlib.uncommit import uncommit
36
from bzrlib.urlutils import local_path_from_url
37
from bzrlib.workingtree import WorkingTree
40
class TestPush(ExternalBase):
74
42
def test_push_remember(self):
75
43
"""Push changes from one branch to another and test push location."""
93
61
self.assertEqual(None, branch_b.get_push_location())
95
63
# test push for failure without push location set
96
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
65
out = self.run_bzr('push', retcode=3)
97
66
self.assertEquals(out,
98
67
('','bzr: ERROR: No push location known or specified.\n'))
100
69
# test not remembered if cannot actually push
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)
70
self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
71
out = self.run_bzr('push', retcode=3)
104
72
self.assertEquals(
105
73
('', 'bzr: ERROR: No push location known or specified.\n'),
108
76
# test implicit --remember when no push location set, push fails
109
out = self.run_bzr('push ../branch_b',
110
working_dir='branch_a', retcode=3)
77
out = self.run_bzr('push ../branch_b', retcode=3)
111
78
self.assertEquals(out,
112
79
('','bzr: ERROR: These branches have diverged. '
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))
80
'Try using "merge" and then "push".\n'))
81
self.assertEquals(abspath(branch_a.get_push_location()),
82
abspath(branch_b.bzrdir.root_transport.base))
117
84
# test implicit --remember after resolving previous failure
118
uncommit.uncommit(branch=branch_b, tree=tree_b)
85
uncommit(branch=branch_b, tree=tree_b)
119
86
transport.delete('branch_b/c')
120
out, err = self.run_bzr('push', working_dir='branch_a')
87
out, err = self.run_bzr('push')
121
88
path = branch_a.get_push_location()
122
89
self.assertEquals(out,
123
'Using saved push location: %s\n'
124
% urlutils.local_path_from_url(path))
90
'Using saved location: %s\n'
91
'Pushed up to revision 2.\n'
92
% local_path_from_url(path))
125
93
self.assertEqual(err,
126
'All changes applied successfully.\n'
127
'Pushed up to revision 2.\n')
94
'All changes applied successfully.\n')
128
95
self.assertEqual(path,
129
96
branch_b.bzrdir.root_transport.base)
130
97
# test explicit --remember
131
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
98
self.run_bzr('push ../branch_c --remember')
132
99
self.assertEquals(branch_a.get_push_location(),
133
100
branch_c.bzrdir.root_transport.base)
135
102
def test_push_without_tree(self):
136
103
# bzr push from a branch that does not have a checkout should work.
137
104
b = self.make_branch('.')
138
105
out, err = self.run_bzr('push pushed-location')
139
106
self.assertEqual('', out)
140
107
self.assertEqual('Created new branch.\n', err)
141
b2 = branch.Branch.open('pushed-location')
108
b2 = Branch.open('pushed-location')
142
109
self.assertEndsWith(b2.base, 'pushed-location/')
144
111
def test_push_new_branch_revision_count(self):
145
# bzr push of a branch with revisions to a new location
146
# should print the number of revisions equal to the length of the
112
# bzr push of a branch with revisions to a new location
113
# should print the number of revisions equal to the length of the
148
115
t = self.make_branch_and_tree('tree')
149
116
self.build_tree(['tree/file'])
151
118
t.commit('commit 1')
152
out, err = self.run_bzr('push -d tree pushed-to')
120
out, err = self.run_bzr('push pushed-to')
153
122
self.assertEqual('', out)
154
123
self.assertEqual('Created new branch.\n', err)
156
125
def test_push_only_pushes_history(self):
157
126
# Knit branches should only push the history for the current revision.
158
format = bzrdir.BzrDirMetaFormat1()
159
format.repository_format = knitrepo.RepositoryFormatKnit1()
127
format = BzrDirMetaFormat1()
128
format.repository_format = RepositoryFormatKnit1()
160
129
shared_repo = self.make_repository('repo', format=format, shared=True)
161
130
shared_repo.set_make_working_trees(True)
163
132
def make_shared_tree(path):
164
133
shared_repo.bzrdir.root_transport.mkdir(path)
165
134
shared_repo.bzrdir.create_branch_convenience('repo/' + path)
166
return workingtree.WorkingTree.open('repo/' + path)
135
return WorkingTree.open('repo/' + path)
167
136
tree_a = make_shared_tree('a')
168
137
self.build_tree(['repo/a/file'])
169
138
tree_a.add('file')
204
175
message='first commit')
205
176
self.run_bzr('push -d from to-one')
206
177
self.failUnlessExists('to-one')
207
self.run_bzr('push -d %s %s'
178
self.run_bzr('push -d %s %s'
208
179
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
209
180
self.failUnlessExists('to-two')
211
def test_push_smart_non_stacked_streaming_acceptance(self):
212
self.setup_smart_server_with_call_log()
213
t = self.make_branch_and_tree('from')
214
t.commit(allow_pointless=True, message='first commit')
215
self.reset_smart_call_log()
216
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
217
# This figure represent the amount of work to perform this use case. It
218
# is entirely ok to reduce this number if a test fails due to rpc_count
219
# being too low. If rpc_count increases, more network roundtrips have
220
# become necessary for this use case. Please do not adjust this number
221
# upwards without agreement from bzr's network support maintainers.
222
self.assertLength(9, self.hpss_calls)
224
def test_push_smart_stacked_streaming_acceptance(self):
225
self.setup_smart_server_with_call_log()
226
parent = self.make_branch_and_tree('parent', format='1.9')
227
parent.commit(message='first commit')
228
local = parent.bzrdir.sprout('local').open_workingtree()
229
local.commit(message='local commit')
230
self.reset_smart_call_log()
231
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
232
self.get_url('public')], working_dir='local')
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(14, self.hpss_calls)
239
remote = branch.Branch.open('public')
240
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
242
def test_push_smart_tags_streaming_acceptance(self):
243
self.setup_smart_server_with_call_log()
244
t = self.make_branch_and_tree('from')
245
rev_id = t.commit(allow_pointless=True, message='first commit')
246
t.branch.tags.set_tag('new-tag', rev_id)
247
self.reset_smart_call_log()
248
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
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(11, self.hpss_calls)
256
def test_push_smart_with_default_stacking_url_path_segment(self):
257
# If the default stacked-on location is a path element then branches
258
# we push there over the smart server are stacked and their
259
# stacked_on_url is that exact path segment. Added to nail bug 385132.
260
self.setup_smart_server_with_call_log()
261
self.make_branch('stack-on', format='1.9')
262
self.make_bzrdir('.').get_config().set_default_stack_on(
264
self.make_branch('from', format='1.9')
265
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
266
b = branch.Branch.open(self.get_url('to'))
267
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
269
def test_push_smart_with_default_stacking_relative_path(self):
270
# If the default stacked-on location is a relative path then branches
271
# we push there over the smart server are stacked and their
272
# stacked_on_url is a relative path. Added to nail bug 385132.
273
self.setup_smart_server_with_call_log()
274
self.make_branch('stack-on', format='1.9')
275
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
276
self.make_branch('from', format='1.9')
277
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
278
b = branch.Branch.open(self.get_url('to'))
279
self.assertEqual('../stack-on', b.get_stacked_on_url())
281
182
def create_simple_tree(self):
282
183
tree = self.make_branch_and_tree('tree')
283
184
self.build_tree(['tree/a'])
445
343
self.assertFalse(self.get_transport('published').has('.'))
447
345
def test_push_notifies_default_stacking(self):
448
self.make_branch('stack_on', format='1.6')
346
self.make_branch('stack_on', format='development1')
449
347
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
450
self.make_branch('from', format='1.6')
348
self.make_branch('from', format='development1')
451
349
out, err = self.run_bzr('push -d from to')
452
350
self.assertContainsRe(err,
453
351
'Using default stacking branch stack_on at .*')
455
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
456
self.make_branch('stack_on', format='1.6')
457
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
458
self.make_branch('from', format='pack-0.92')
459
out, err = self.run_bzr('push -d from to')
460
b = branch.Branch.open('to')
461
self.assertEqual('../stack_on', b.get_stacked_on_url())
463
def test_push_does_not_change_format_with_default_if_target_cannot(self):
464
self.make_branch('stack_on', format='pack-0.92')
465
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
466
self.make_branch('from', format='pack-0.92')
467
out, err = self.run_bzr('push -d from to')
468
b = branch.Branch.open('to')
469
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
471
def test_push_doesnt_create_broken_branch(self):
472
"""Pushing a new standalone branch works even when there's a default
473
stacking policy at the destination.
475
The new branch will preserve the repo format (even if it isn't the
476
default for the branch), and will be stacked when the repo format
477
allows (which means that the branch format isn't necessarly preserved).
479
self.make_repository('repo', shared=True, format='1.6')
480
builder = self.make_branch_builder('repo/local', format='pack-0.92')
481
builder.start_series()
482
builder.build_snapshot('rev-1', None, [
483
('add', ('', 'root-id', 'directory', '')),
484
('add', ('filename', 'f-id', 'file', 'content\n'))])
485
builder.build_snapshot('rev-2', ['rev-1'], [])
486
builder.build_snapshot('rev-3', ['rev-2'],
487
[('modify', ('f-id', 'new-content\n'))])
488
builder.finish_series()
489
branch = builder.get_branch()
490
# Push rev-1 to "trunk", so that we can stack on it.
491
self.run_bzr('push -d repo/local trunk -r 1')
492
# Set a default stacking policy so that new branches will automatically
494
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
495
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
496
out, err = self.run_bzr('push -d repo/local remote -r 2')
497
self.assertContainsRe(
498
err, 'Using default stacking branch trunk at .*')
499
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
500
# fulltext record for f-id @ rev-1, then this will fail.
501
out, err = self.run_bzr('push -d repo/local remote -r 3')
503
def test_push_verbose_shows_log(self):
504
tree = self.make_branch_and_tree('source')
506
out, err = self.run_bzr('push -v -d source target')
507
# initial push contains log
508
self.assertContainsRe(out, 'rev1')
510
out, err = self.run_bzr('push -v -d source target')
511
# subsequent push contains log
512
self.assertContainsRe(out, 'rev2')
513
# subsequent log is accurate
514
self.assertNotContainsRe(out, 'rev1')
516
def test_push_from_subdir(self):
517
t = self.make_branch_and_tree('tree')
518
self.build_tree(['tree/dir/', 'tree/dir/file'])
519
t.add('dir', 'dir/file')
521
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
522
self.assertEqual('', out)
523
self.assertEqual('Created new branch.\n', err)
526
class RedirectingMemoryTransport(memory.MemoryTransport):
528
def mkdir(self, relpath, mode=None):
529
if self._cwd == '/source/':
530
raise errors.RedirectRequested(self.abspath(relpath),
531
self.abspath('../target'),
533
elif self._cwd == '/infinite-loop/':
534
raise errors.RedirectRequested(self.abspath(relpath),
535
self.abspath('../infinite-loop'),
354
class RedirectingMemoryTransport(MemoryTransport):
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)
538
365
return super(RedirectingMemoryTransport, self).mkdir(
541
def get(self, relpath):
542
if self.clone(relpath)._cwd == '/infinite-loop/':
543
raise errors.RedirectRequested(self.abspath(relpath),
544
self.abspath('../infinite-loop'),
547
return super(RedirectingMemoryTransport, self).get(relpath)
549
def _redirected_to(self, source, target):
550
# We do accept redirections
551
return transport.get_transport(target)
554
class RedirectingMemoryServer(memory.MemoryServer):
369
class RedirectingMemoryServer(MemoryServer):
557
372
self._dirs = {'/': None}
560
375
self._scheme = 'redirecting-memory+%s:///' % id(self)
561
transport.register_transport(self._scheme, self._memory_factory)
376
register_transport(self._scheme, self._memory_factory)
563
378
def _memory_factory(self, url):
564
379
result = RedirectingMemoryTransport(url)
603
420
"""Push fails gracefully if the mkdir generates a large number of
606
424
destination_url = self.memory_server.get_url() + 'infinite-loop'
607
425
out, err = self.run_bzr_error(
608
426
['Too many redirections trying to make %s\\.\n'
609
427
% re.escape(destination_url)],
610
['push', '-d', 'tree', destination_url], retcode=3)
428
'push %s' % destination_url, retcode=3)
611
430
self.assertEqual('', out)
614
class TestPushStrictMixin(object):
616
def make_local_branch_and_tree(self):
617
self.tree = self.make_branch_and_tree('local')
618
self.build_tree_contents([('local/file', 'initial')])
619
self.tree.add('file')
620
self.tree.commit('adding file', rev_id='added')
621
self.build_tree_contents([('local/file', 'modified')])
622
self.tree.commit('modify file', rev_id='modified')
624
def set_config_push_strict(self, value):
625
# set config var (any of bazaar.conf, locations.conf, branch.conf
627
conf = self.tree.branch.get_config()
628
conf.set_user_option('push_strict', value)
630
_default_command = ['push', '../to']
631
_default_wd = 'local'
632
_default_errors = ['Working tree ".*/local/" has uncommitted '
633
'changes \(See bzr status\)\.',]
634
_default_pushed_revid = 'modified'
636
def assertPushFails(self, args):
637
self.run_bzr_error(self._default_errors, self._default_command + args,
638
working_dir=self._default_wd, retcode=3)
640
def assertPushSucceeds(self, args, pushed_revid=None):
641
self.run_bzr(self._default_command + args,
642
working_dir=self._default_wd)
643
if pushed_revid is None:
644
pushed_revid = self._default_pushed_revid
645
tree_to = workingtree.WorkingTree.open('to')
646
repo_to = tree_to.branch.repository
647
self.assertTrue(repo_to.has_revision(pushed_revid))
648
self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
652
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
653
TestPushStrictMixin):
656
super(TestPushStrictWithoutChanges, self).setUp()
657
self.make_local_branch_and_tree()
659
def test_push_default(self):
660
self.assertPushSucceeds([])
662
def test_push_strict(self):
663
self.assertPushSucceeds(['--strict'])
665
def test_push_no_strict(self):
666
self.assertPushSucceeds(['--no-strict'])
668
def test_push_config_var_strict(self):
669
self.set_config_push_strict('true')
670
self.assertPushSucceeds([])
672
def test_push_config_var_no_strict(self):
673
self.set_config_push_strict('false')
674
self.assertPushSucceeds([])
677
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
678
TestPushStrictMixin):
680
_changes_type = None # Set by load_tests
683
super(TestPushStrictWithChanges, self).setUp()
684
getattr(self, self._changes_type)()
686
def _uncommitted_changes(self):
687
self.make_local_branch_and_tree()
688
# Make a change without committing it
689
self.build_tree_contents([('local/file', 'in progress')])
691
def _pending_merges(self):
692
self.make_local_branch_and_tree()
693
# Create 'other' branch containing a new file
694
other_bzrdir = self.tree.bzrdir.sprout('other')
695
other_tree = other_bzrdir.open_workingtree()
696
self.build_tree_contents([('other/other-file', 'other')])
697
other_tree.add('other-file')
698
other_tree.commit('other commit', rev_id='other')
699
# Merge and revert, leaving a pending merge
700
self.tree.merge_from_branch(other_tree.branch)
701
self.tree.revert(filenames=['other-file'], backups=False)
703
def _out_of_sync_trees(self):
704
self.make_local_branch_and_tree()
705
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
706
# Make a change and commit it
707
self.build_tree_contents([('local/file', 'modified in local')])
708
self.tree.commit('modify file', rev_id='modified-in-local')
709
# Exercise commands from the checkout directory
710
self._default_wd = 'checkout'
711
self._default_errors = ["Working tree is out of date, please run"
713
self._default_pushed_revid = 'modified-in-local'
715
def test_push_default(self):
716
self.assertPushFails([])
718
def test_push_with_revision(self):
719
self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
721
def test_push_no_strict(self):
722
self.assertPushSucceeds(['--no-strict'])
724
def test_push_strict_with_changes(self):
725
self.assertPushFails(['--strict'])
727
def test_push_respect_config_var_strict(self):
728
self.set_config_push_strict('true')
729
self.assertPushFails([])
731
def test_push_bogus_config_var_ignored(self):
732
self.set_config_push_strict("I don't want you to be strict")
733
self.assertPushFails([])
735
def test_push_no_strict_command_line_override_config(self):
736
self.set_config_push_strict('yES')
737
self.assertPushFails([])
738
self.assertPushSucceeds(['--no-strict'])
740
def test_push_strict_command_line_override_config(self):
741
self.set_config_push_strict('oFF')
742
self.assertPushFails(['--strict'])
743
self.assertPushSucceeds([])