~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Jelmer Vernooij
  • Date: 2009-06-09 00:59:51 UTC
  • mto: (4443.1.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 4444.
  • Revision ID: jelmer@samba.org-20090609005951-apv900cdk35o2ygh
Move squashing of XML-invalid characters to XMLSerializer.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
 
18
18
"""Black-box tests for bzr push."""
19
19
 
 
20
import os
20
21
import re
21
22
 
22
23
from bzrlib import (
23
 
    branch,
24
 
    bzrdir,
25
24
    errors,
26
 
    osutils,
27
 
    tests,
28
25
    transport,
29
 
    uncommit,
30
26
    urlutils,
31
 
    workingtree
32
 
    )
33
 
from bzrlib.repofmt import knitrepo
34
 
from bzrlib.tests import (
35
 
    blackbox,
36
 
    http_server,
37
 
    scenarios,
38
 
    test_foreign,
39
 
    test_server,
40
 
    )
41
 
from bzrlib.transport import memory
42
 
 
43
 
 
44
 
load_tests = scenarios.load_tests_apply_scenarios
45
 
 
46
 
 
47
 
class TestPush(tests.TestCaseWithTransport):
 
27
    )
 
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
 
39
 
 
40
 
 
41
class TestPush(ExternalBase):
48
42
 
49
43
    def test_push_error_on_vfs_http(self):
50
44
        """ pushing a branch to a HTTP server fails cleanly. """
51
45
        # the trunk is published on a web server
52
 
        self.transport_readonly_server = http_server.HttpServer
 
46
        self.transport_readonly_server = HttpServer
53
47
        self.make_branch('source')
54
48
        public_url = self.get_readonly_url('target')
55
49
        self.run_bzr_error(['http does not support mkdir'],
78
72
        self.assertEqual(None, branch_b.get_push_location())
79
73
 
80
74
        # test push for failure without push location set
81
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
75
        os.chdir('branch_a')
 
76
        out = self.run_bzr('push', retcode=3)
82
77
        self.assertEquals(out,
83
78
                ('','bzr: ERROR: No push location known or specified.\n'))
84
79
 
85
80
        # test not remembered if cannot actually push
86
 
        self.run_bzr('push path/which/doesnt/exist',
87
 
                     working_dir='branch_a', retcode=3)
88
 
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
 
81
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
 
82
        out = self.run_bzr('push', retcode=3)
89
83
        self.assertEquals(
90
84
                ('', 'bzr: ERROR: No push location known or specified.\n'),
91
85
                out)
92
86
 
93
87
        # test implicit --remember when no push location set, push fails
94
 
        out = self.run_bzr('push ../branch_b',
95
 
                           working_dir='branch_a', retcode=3)
 
88
        out = self.run_bzr('push ../branch_b', retcode=3)
96
89
        self.assertEquals(out,
97
90
                ('','bzr: ERROR: These branches have diverged.  '
98
 
                 'See "bzr help diverged-branches" for more information.\n'))
99
 
        self.assertEquals(osutils.abspath(branch_a.get_push_location()),
100
 
                          osutils.abspath(branch_b.bzrdir.root_transport.base))
 
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))
101
94
 
102
95
        # test implicit --remember after resolving previous failure
103
 
        uncommit.uncommit(branch=branch_b, tree=tree_b)
 
96
        uncommit(branch=branch_b, tree=tree_b)
104
97
        transport.delete('branch_b/c')
105
 
        out, err = self.run_bzr('push', working_dir='branch_a')
 
98
        out, err = self.run_bzr('push')
106
99
        path = branch_a.get_push_location()
 
100
        self.assertEquals(out,
 
101
                          'Using saved push location: %s\n'
 
102
                          % local_path_from_url(path))
107
103
        self.assertEqual(err,
108
 
                         'Using saved push location: %s\n'
109
104
                         'All changes applied successfully.\n'
110
 
                         'Pushed up to revision 2.\n'
111
 
                         % urlutils.local_path_from_url(path))
 
105
                         'Pushed up to revision 2.\n')
112
106
        self.assertEqual(path,
113
107
                         branch_b.bzrdir.root_transport.base)
114
108
        # test explicit --remember
115
 
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
109
        self.run_bzr('push ../branch_c --remember')
116
110
        self.assertEquals(branch_a.get_push_location(),
117
111
                          branch_c.bzrdir.root_transport.base)
118
112
 
122
116
        out, err = self.run_bzr('push pushed-location')
123
117
        self.assertEqual('', out)
124
118
        self.assertEqual('Created new branch.\n', err)
125
 
        b2 = branch.Branch.open('pushed-location')
 
119
        b2 = Branch.open('pushed-location')
126
120
        self.assertEndsWith(b2.base, 'pushed-location/')
127
121
 
128
 
    def test_push_no_tree(self):
129
 
        # bzr push --no-tree of a branch with working trees
130
 
        b = self.make_branch_and_tree('push-from')
131
 
        self.build_tree(['push-from/file'])
132
 
        b.add('file')
133
 
        b.commit('commit 1')
134
 
        out, err = self.run_bzr('push --no-tree -d push-from push-to')
135
 
        self.assertEqual('', out)
136
 
        self.assertEqual('Created new branch.\n', err)
137
 
        self.assertPathDoesNotExist('push-to/file')
138
 
 
139
122
    def test_push_new_branch_revision_count(self):
140
123
        # bzr push of a branch with revisions to a new location
141
124
        # should print the number of revisions equal to the length of the
144
127
        self.build_tree(['tree/file'])
145
128
        t.add('file')
146
129
        t.commit('commit 1')
147
 
        out, err = self.run_bzr('push -d tree pushed-to')
 
130
        os.chdir('tree')
 
131
        out, err = self.run_bzr('push pushed-to')
 
132
        os.chdir('..')
148
133
        self.assertEqual('', out)
149
134
        self.assertEqual('Created new branch.\n', err)
150
135
 
151
 
    def test_push_quiet(self):
152
 
        # test that using -q makes output quiet
153
 
        t = self.make_branch_and_tree('tree')
154
 
        self.build_tree(['tree/file'])
155
 
        t.add('file')
156
 
        t.commit('commit 1')
157
 
        self.run_bzr('push -d tree pushed-to')
158
 
        path = t.branch.get_push_location()
159
 
        out, err = self.run_bzr('push', working_dir="tree")
160
 
        self.assertEqual('Using saved push location: %s\nNo new revisions to push.\n' % urlutils.local_path_from_url(path), err)
161
 
        out, err = self.run_bzr('push -q', working_dir="tree")
162
 
        self.assertEqual('', out)
163
 
        self.assertEqual('', err)
164
 
 
165
136
    def test_push_only_pushes_history(self):
166
137
        # Knit branches should only push the history for the current revision.
167
 
        format = bzrdir.BzrDirMetaFormat1()
168
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
138
        format = BzrDirMetaFormat1()
 
139
        format.repository_format = RepositoryFormatKnit1()
169
140
        shared_repo = self.make_repository('repo', format=format, shared=True)
170
141
        shared_repo.set_make_working_trees(True)
171
142
 
172
143
        def make_shared_tree(path):
173
144
            shared_repo.bzrdir.root_transport.mkdir(path)
174
145
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
175
 
            return workingtree.WorkingTree.open('repo/' + path)
 
146
            return WorkingTree.open('repo/' + path)
176
147
        tree_a = make_shared_tree('a')
177
148
        self.build_tree(['repo/a/file'])
178
149
        tree_a.add('file')
193
164
 
194
165
        # Now that we have a repository with shared files, make sure
195
166
        # that things aren't copied out by a 'push'
196
 
        self.run_bzr('push ../../push-b', working_dir='repo/b')
197
 
        pushed_tree = workingtree.WorkingTree.open('push-b')
 
167
        os.chdir('repo/b')
 
168
        self.run_bzr('push ../../push-b')
 
169
        pushed_tree = WorkingTree.open('../../push-b')
198
170
        pushed_repo = pushed_tree.branch.repository
199
171
        self.assertFalse(pushed_repo.has_revision('a-1'))
200
172
        self.assertFalse(pushed_repo.has_revision('a-2'))
202
174
 
203
175
    def test_push_funky_id(self):
204
176
        t = self.make_branch_and_tree('tree')
205
 
        self.build_tree(['tree/filename'])
 
177
        os.chdir('tree')
 
178
        self.build_tree(['filename'])
206
179
        t.add('filename', 'funky-chars<>%&;"\'')
207
180
        t.commit('commit filename')
208
 
        self.run_bzr('push -d tree new-tree')
 
181
        self.run_bzr('push ../new-tree')
209
182
 
210
183
    def test_push_dash_d(self):
211
184
        t = self.make_branch_and_tree('from')
212
185
        t.commit(allow_pointless=True,
213
186
                message='first commit')
214
187
        self.run_bzr('push -d from to-one')
215
 
        self.assertPathExists('to-one')
 
188
        self.failUnlessExists('to-one')
216
189
        self.run_bzr('push -d %s %s'
217
190
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
218
 
        self.assertPathExists('to-two')
219
 
 
220
 
    def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
221
 
        # See https://bugs.launchpad.net/bzr/+bug/465517
222
 
        target_repo = self.make_repository('target')
223
 
        source = self.make_branch_builder('source')
224
 
        source.start_series()
225
 
        source.build_snapshot('A', None, [
226
 
            ('add', ('', 'root-id', 'directory', None))])
227
 
        source.build_snapshot('B', ['A'], [])
228
 
        source.build_snapshot('C', ['A'], [])
229
 
        source.finish_series()
230
 
        self.run_bzr('push target -d source')
231
 
        self.addCleanup(target_repo.lock_read().unlock)
232
 
        # We should have pushed 'C', but not 'B', since it isn't in the
233
 
        # ancestry
234
 
        self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
 
191
        self.failUnlessExists('to-two')
235
192
 
236
193
    def test_push_smart_non_stacked_streaming_acceptance(self):
237
194
        self.setup_smart_server_with_call_log()
260
217
        # being too low. If rpc_count increases, more network roundtrips have
261
218
        # become necessary for this use case. Please do not adjust this number
262
219
        # upwards without agreement from bzr's network support maintainers.
263
 
        self.assertLength(13, self.hpss_calls)
264
 
        remote = branch.Branch.open('public')
 
220
        self.assertLength(14, self.hpss_calls)
 
221
        remote = Branch.open('public')
265
222
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
266
223
 
267
 
    def test_push_smart_tags_streaming_acceptance(self):
268
 
        self.setup_smart_server_with_call_log()
269
 
        t = self.make_branch_and_tree('from')
270
 
        rev_id = t.commit(allow_pointless=True, message='first commit')
271
 
        t.branch.tags.set_tag('new-tag', rev_id)
272
 
        self.reset_smart_call_log()
273
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
274
 
        # This figure represent the amount of work to perform this use case. It
275
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
276
 
        # being too low. If rpc_count increases, more network roundtrips have
277
 
        # become necessary for this use case. Please do not adjust this number
278
 
        # upwards without agreement from bzr's network support maintainers.
279
 
        self.assertLength(11, self.hpss_calls)
280
 
 
281
 
    def test_push_smart_incremental_acceptance(self):
282
 
        self.setup_smart_server_with_call_log()
283
 
        t = self.make_branch_and_tree('from')
284
 
        rev_id1 = t.commit(allow_pointless=True, message='first commit')
285
 
        rev_id2 = t.commit(allow_pointless=True, message='second commit')
286
 
        self.run_bzr(
287
 
            ['push', self.get_url('to-one'), '-r1'], working_dir='from')
288
 
        self.reset_smart_call_log()
289
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
290
 
        # This figure represent the amount of work to perform this use case. It
291
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
292
 
        # being too low. If rpc_count increases, more network roundtrips have
293
 
        # become necessary for this use case. Please do not adjust this number
294
 
        # upwards without agreement from bzr's network support maintainers.
295
 
        self.assertLength(11, self.hpss_calls)
296
 
 
297
 
    def test_push_smart_with_default_stacking_url_path_segment(self):
298
 
        # If the default stacked-on location is a path element then branches
299
 
        # we push there over the smart server are stacked and their
300
 
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
301
 
        self.setup_smart_server_with_call_log()
302
 
        self.make_branch('stack-on', format='1.9')
303
 
        self.make_bzrdir('.').get_config().set_default_stack_on(
304
 
            '/stack-on')
305
 
        self.make_branch('from', format='1.9')
306
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
307
 
        b = branch.Branch.open(self.get_url('to'))
308
 
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
309
 
 
310
 
    def test_push_smart_with_default_stacking_relative_path(self):
311
 
        # If the default stacked-on location is a relative path then branches
312
 
        # we push there over the smart server are stacked and their
313
 
        # stacked_on_url is a relative path. Added to nail bug 385132.
314
 
        self.setup_smart_server_with_call_log()
315
 
        self.make_branch('stack-on', format='1.9')
316
 
        self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
317
 
        self.make_branch('from', format='1.9')
318
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
319
 
        b = branch.Branch.open(self.get_url('to'))
320
 
        self.assertEqual('../stack-on', b.get_stacked_on_url())
321
 
 
322
224
    def create_simple_tree(self):
323
225
        tree = self.make_branch_and_tree('tree')
324
226
        self.build_tree(['tree/a'])
335
237
                           working_dir='tree')
336
238
        self.run_bzr('push ../new/tree --create-prefix',
337
239
                     working_dir='tree')
338
 
        new_tree = workingtree.WorkingTree.open('new/tree')
 
240
        new_tree = WorkingTree.open('new/tree')
339
241
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
340
 
        self.assertPathExists('new/tree/a')
 
242
        self.failUnlessExists('new/tree/a')
341
243
 
342
244
    def test_push_use_existing(self):
343
245
        """'bzr push --use-existing-dir' can push into an existing dir.
355
257
        self.run_bzr('push --use-existing-dir ../target',
356
258
                     working_dir='tree')
357
259
 
358
 
        new_tree = workingtree.WorkingTree.open('target')
 
260
        new_tree = WorkingTree.open('target')
359
261
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
360
262
        # The push should have created target/a
361
 
        self.assertPathExists('target/a')
362
 
 
363
 
    def test_push_use_existing_into_empty_bzrdir(self):
364
 
        """'bzr push --use-existing-dir' into a dir with an empty .bzr dir
365
 
        fails.
366
 
        """
367
 
        tree = self.create_simple_tree()
368
 
        self.build_tree(['target/', 'target/.bzr/'])
369
 
        self.run_bzr_error(
370
 
            ['Target directory ../target already contains a .bzr directory, '
371
 
             'but it is not valid.'],
372
 
            'push ../target --use-existing-dir', working_dir='tree')
 
263
        self.failUnlessExists('target/a')
373
264
 
374
265
    def test_push_onto_repo(self):
375
266
        """We should be able to 'bzr push' into an existing bzrdir."""
382
273
        # Pushing onto an existing bzrdir will create a repository and
383
274
        # branch as needed, but will only create a working tree if there was
384
275
        # no BzrDir before.
385
 
        self.assertRaises(errors.NoWorkingTree,
386
 
                          workingtree.WorkingTree.open, 'repo')
387
 
        new_branch = branch.Branch.open('repo')
 
276
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
277
        new_branch = Branch.open('repo')
388
278
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
389
279
 
390
280
    def test_push_onto_just_bzrdir(self):
409
299
 
410
300
        self.run_bzr('push -r1 ../to', working_dir='from')
411
301
 
412
 
        tree_to = workingtree.WorkingTree.open('to')
 
302
        tree_to = WorkingTree.open('to')
413
303
        repo_to = tree_to.branch.repository
414
304
        self.assertTrue(repo_to.has_revision('from-1'))
415
305
        self.assertFalse(repo_to.has_revision('from-2'))
436
326
 
437
327
    def assertPublished(self, branch_revid, stacked_on):
438
328
        """Assert that the branch 'published' has been published correctly."""
439
 
        published_branch = branch.Branch.open('published')
 
329
        published_branch = Branch.open('published')
440
330
        # The published branch refers to the mainline
441
331
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
442
332
        # and the branch's work was pushed
464
354
        self.assertEqual('', out)
465
355
        self.assertEqual('Created new stacked branch referring to %s.\n' %
466
356
            trunk_tree.branch.base, err)
467
 
        self.assertPublished(branch_tree.last_revision(),
468
 
                             trunk_tree.branch.base)
 
357
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
469
358
 
470
359
    def test_push_new_branch_stacked_uses_parent_public(self):
471
360
        """Pushing a new branch with --stacked creates a stacked branch."""
472
361
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
473
362
        # the trunk is published on a web server
474
 
        self.transport_readonly_server = http_server.HttpServer
 
363
        self.transport_readonly_server = HttpServer
475
364
        trunk_public = self.make_branch('public_trunk', format='1.9')
476
365
        trunk_public.pull(trunk_tree.branch)
477
366
        trunk_public_url = self.get_readonly_url('public_trunk')
509
398
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
510
399
        self.make_branch('from', format='pack-0.92')
511
400
        out, err = self.run_bzr('push -d from to')
512
 
        b = branch.Branch.open('to')
513
 
        self.assertEqual('../stack_on', b.get_stacked_on_url())
 
401
        branch = Branch.open('to')
 
402
        self.assertEqual('../stack_on', branch.get_stacked_on_url())
514
403
 
515
404
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
516
405
        self.make_branch('stack_on', format='pack-0.92')
517
406
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
518
407
        self.make_branch('from', format='pack-0.92')
519
408
        out, err = self.run_bzr('push -d from to')
520
 
        b = branch.Branch.open('to')
521
 
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
 
409
        branch = Branch.open('to')
 
410
        self.assertRaises(errors.UnstackableBranchFormat,
 
411
            branch.get_stacked_on_url)
522
412
 
523
413
    def test_push_doesnt_create_broken_branch(self):
524
414
        """Pushing a new standalone branch works even when there's a default
565
455
        # subsequent log is accurate
566
456
        self.assertNotContainsRe(out, 'rev1')
567
457
 
568
 
    def test_push_from_subdir(self):
569
 
        t = self.make_branch_and_tree('tree')
570
 
        self.build_tree(['tree/dir/', 'tree/dir/file'])
571
 
        t.add('dir', 'dir/file')
572
 
        t.commit('r1')
573
 
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
574
 
        self.assertEqual('', out)
575
 
        self.assertEqual('Created new branch.\n', err)
576
 
 
577
 
 
578
 
class RedirectingMemoryTransport(memory.MemoryTransport):
 
458
 
 
459
class RedirectingMemoryTransport(MemoryTransport):
579
460
 
580
461
    def mkdir(self, relpath, mode=None):
 
462
        from bzrlib.trace import mutter
 
463
        mutter('cwd: %r, rel: %r, abs: %r' % (self._cwd, relpath, abspath))
581
464
        if self._cwd == '/source/':
582
465
            raise errors.RedirectRequested(self.abspath(relpath),
583
466
                                           self.abspath('../target'),
590
473
            return super(RedirectingMemoryTransport, self).mkdir(
591
474
                relpath, mode)
592
475
 
593
 
    def get(self, relpath):
594
 
        if self.clone(relpath)._cwd == '/infinite-loop/':
595
 
            raise errors.RedirectRequested(self.abspath(relpath),
596
 
                                           self.abspath('../infinite-loop'),
597
 
                                           is_permanent=True)
598
 
        else:
599
 
            return super(RedirectingMemoryTransport, self).get(relpath)
600
 
 
601
476
    def _redirected_to(self, source, target):
602
477
        # We do accept redirections
603
478
        return transport.get_transport(target)
604
479
 
605
480
 
606
 
class RedirectingMemoryServer(memory.MemoryServer):
 
481
class RedirectingMemoryServer(MemoryServer):
607
482
 
608
 
    def start_server(self):
 
483
    def setUp(self):
609
484
        self._dirs = {'/': None}
610
485
        self._files = {}
611
486
        self._locks = {}
619
494
        result._locks = self._locks
620
495
        return result
621
496
 
622
 
    def stop_server(self):
 
497
    def tearDown(self):
623
498
        transport.unregister_transport(self._scheme, self._memory_factory)
624
499
 
625
500
 
626
 
class TestPushRedirect(tests.TestCaseWithTransport):
 
501
class TestPushRedirect(ExternalBase):
627
502
 
628
503
    def setUp(self):
629
 
        tests.TestCaseWithTransport.setUp(self)
 
504
        ExternalBase.setUp(self)
630
505
        self.memory_server = RedirectingMemoryServer()
631
 
        self.start_server(self.memory_server)
 
506
        self.memory_server.setUp()
 
507
        self.addCleanup(self.memory_server.tearDown)
 
508
 
632
509
        # Make the branch and tree that we'll be pushing.
633
510
        t = self.make_branch_and_tree('tree')
634
511
        self.build_tree(['tree/file'])
644
521
        destination_url = self.memory_server.get_url() + 'source'
645
522
        self.run_bzr(['push', '-d', 'tree', destination_url])
646
523
 
647
 
        local_revision = branch.Branch.open('tree').last_revision()
648
 
        remote_revision = branch.Branch.open(
 
524
        local_revision = Branch.open('tree').last_revision()
 
525
        remote_revision = Branch.open(
649
526
            self.memory_server.get_url() + 'target').last_revision()
650
527
        self.assertEqual(remote_revision, local_revision)
651
528
 
659
536
             % re.escape(destination_url)],
660
537
            ['push', '-d', 'tree', destination_url], retcode=3)
661
538
        self.assertEqual('', out)
662
 
 
663
 
 
664
 
class TestPushStrictMixin(object):
665
 
 
666
 
    def make_local_branch_and_tree(self):
667
 
        self.tree = self.make_branch_and_tree('local')
668
 
        self.build_tree_contents([('local/file', 'initial')])
669
 
        self.tree.add('file')
670
 
        self.tree.commit('adding file', rev_id='added')
671
 
        self.build_tree_contents([('local/file', 'modified')])
672
 
        self.tree.commit('modify file', rev_id='modified')
673
 
 
674
 
    def set_config_push_strict(self, value):
675
 
        # set config var (any of bazaar.conf, locations.conf, branch.conf
676
 
        # should do)
677
 
        conf = self.tree.branch.get_config()
678
 
        conf.set_user_option('push_strict', value)
679
 
 
680
 
    _default_command = ['push', '../to']
681
 
    _default_wd = 'local'
682
 
    _default_errors = ['Working tree ".*/local/" has uncommitted '
683
 
                       'changes \(See bzr status\)\.',]
684
 
    _default_additional_error = 'Use --no-strict to force the push.\n'
685
 
    _default_additional_warning = 'Uncommitted changes will not be pushed.'
686
 
 
687
 
 
688
 
    def assertPushFails(self, args):
689
 
        out, err = self.run_bzr_error(self._default_errors,
690
 
                                      self._default_command + args,
691
 
                                      working_dir=self._default_wd, retcode=3)
692
 
        self.assertContainsRe(err, self._default_additional_error)
693
 
 
694
 
    def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
695
 
        if with_warning:
696
 
            error_regexes = self._default_errors
697
 
        else:
698
 
            error_regexes = []
699
 
        out, err = self.run_bzr(self._default_command + args,
700
 
                                working_dir=self._default_wd,
701
 
                                error_regexes=error_regexes)
702
 
        if with_warning:
703
 
            self.assertContainsRe(err, self._default_additional_warning)
704
 
        else:
705
 
            self.assertNotContainsRe(err, self._default_additional_warning)
706
 
        branch_from = branch.Branch.open(self._default_wd)
707
 
        if revid_to_push is None:
708
 
            revid_to_push = branch_from.last_revision()
709
 
        branch_to = branch.Branch.open('to')
710
 
        repo_to = branch_to.repository
711
 
        self.assertTrue(repo_to.has_revision(revid_to_push))
712
 
        self.assertEqual(revid_to_push, branch_to.last_revision())
713
 
 
714
 
 
715
 
 
716
 
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
717
 
                                   TestPushStrictMixin):
718
 
 
719
 
    def setUp(self):
720
 
        super(TestPushStrictWithoutChanges, self).setUp()
721
 
        self.make_local_branch_and_tree()
722
 
 
723
 
    def test_push_default(self):
724
 
        self.assertPushSucceeds([])
725
 
 
726
 
    def test_push_strict(self):
727
 
        self.assertPushSucceeds(['--strict'])
728
 
 
729
 
    def test_push_no_strict(self):
730
 
        self.assertPushSucceeds(['--no-strict'])
731
 
 
732
 
    def test_push_config_var_strict(self):
733
 
        self.set_config_push_strict('true')
734
 
        self.assertPushSucceeds([])
735
 
 
736
 
    def test_push_config_var_no_strict(self):
737
 
        self.set_config_push_strict('false')
738
 
        self.assertPushSucceeds([])
739
 
 
740
 
 
741
 
strict_push_change_scenarios = [
742
 
    ('uncommitted',
743
 
        dict(_changes_type= '_uncommitted_changes')),
744
 
    ('pending-merges',
745
 
        dict(_changes_type= '_pending_merges')),
746
 
    ('out-of-sync-trees',
747
 
        dict(_changes_type= '_out_of_sync_trees')),
748
 
    ]
749
 
 
750
 
 
751
 
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
752
 
                                TestPushStrictMixin):
753
 
 
754
 
    scenarios = strict_push_change_scenarios 
755
 
    _changes_type = None # Set by load_tests
756
 
 
757
 
    def setUp(self):
758
 
        super(TestPushStrictWithChanges, self).setUp()
759
 
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
760
 
        # _pending_merges or _out_of_sync_trees
761
 
        getattr(self, self._changes_type)()
762
 
 
763
 
    def _uncommitted_changes(self):
764
 
        self.make_local_branch_and_tree()
765
 
        # Make a change without committing it
766
 
        self.build_tree_contents([('local/file', 'in progress')])
767
 
 
768
 
    def _pending_merges(self):
769
 
        self.make_local_branch_and_tree()
770
 
        # Create 'other' branch containing a new file
771
 
        other_bzrdir = self.tree.bzrdir.sprout('other')
772
 
        other_tree = other_bzrdir.open_workingtree()
773
 
        self.build_tree_contents([('other/other-file', 'other')])
774
 
        other_tree.add('other-file')
775
 
        other_tree.commit('other commit', rev_id='other')
776
 
        # Merge and revert, leaving a pending merge
777
 
        self.tree.merge_from_branch(other_tree.branch)
778
 
        self.tree.revert(filenames=['other-file'], backups=False)
779
 
 
780
 
    def _out_of_sync_trees(self):
781
 
        self.make_local_branch_and_tree()
782
 
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
783
 
        # Make a change and commit it
784
 
        self.build_tree_contents([('local/file', 'modified in local')])
785
 
        self.tree.commit('modify file', rev_id='modified-in-local')
786
 
        # Exercise commands from the checkout directory
787
 
        self._default_wd = 'checkout'
788
 
        self._default_errors = ["Working tree is out of date, please run"
789
 
                                " 'bzr update'\.",]
790
 
 
791
 
    def test_push_default(self):
792
 
        self.assertPushSucceeds([], with_warning=True)
793
 
 
794
 
    def test_push_with_revision(self):
795
 
        self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
796
 
 
797
 
    def test_push_no_strict(self):
798
 
        self.assertPushSucceeds(['--no-strict'])
799
 
 
800
 
    def test_push_strict_with_changes(self):
801
 
        self.assertPushFails(['--strict'])
802
 
 
803
 
    def test_push_respect_config_var_strict(self):
804
 
        self.set_config_push_strict('true')
805
 
        self.assertPushFails([])
806
 
 
807
 
    def test_push_bogus_config_var_ignored(self):
808
 
        self.set_config_push_strict("I don't want you to be strict")
809
 
        self.assertPushSucceeds([], with_warning=True)
810
 
 
811
 
    def test_push_no_strict_command_line_override_config(self):
812
 
        self.set_config_push_strict('yES')
813
 
        self.assertPushFails([])
814
 
        self.assertPushSucceeds(['--no-strict'])
815
 
 
816
 
    def test_push_strict_command_line_override_config(self):
817
 
        self.set_config_push_strict('oFF')
818
 
        self.assertPushFails(['--strict'])
819
 
        self.assertPushSucceeds([])
820
 
 
821
 
 
822
 
class TestPushForeign(tests.TestCaseWithTransport):
823
 
 
824
 
    def setUp(self):
825
 
        super(TestPushForeign, self).setUp()
826
 
        test_foreign.register_dummy_foreign_for_test(self)
827
 
 
828
 
    def make_dummy_builder(self, relpath):
829
 
        builder = self.make_branch_builder(
830
 
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
831
 
        builder.build_snapshot('revid', None,
832
 
            [('add', ('', 'TREE_ROOT', 'directory', None)),
833
 
             ('add', ('foo', 'fooid', 'file', 'bar'))])
834
 
        return builder
835
 
 
836
 
    def test_no_roundtripping(self):
837
 
        target_branch = self.make_dummy_builder('dp').get_branch()
838
 
        source_tree = self.make_branch_and_tree("dc")
839
 
        output, error = self.run_bzr("push -d dc dp", retcode=3)
840
 
        self.assertEquals("", output)
841
 
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
842
 
            " push to dummy. You may want to use dpush instead.\n")