~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Andrew Bennetts
  • Date: 2010-01-15 05:30:30 UTC
  • mto: (4973.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4975.
  • Revision ID: andrew.bennetts@canonical.com-20100115053030-1d6qd89pnj8hmb55
Pass kinds (not pairs) to MergeHookParams.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
"""Black-box tests for bzr push."""
19
19
 
20
 
import os
21
20
import re
22
21
 
23
22
from bzrlib import (
 
23
    branch,
 
24
    bzrdir,
24
25
    errors,
 
26
    osutils,
 
27
    tests,
25
28
    transport,
 
29
    uncommit,
26
30
    urlutils,
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.tests.blackbox import ExternalBase
33
 
from bzrlib.tests.http_server import HttpServer
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
38
 
 
39
 
 
40
 
class TestPush(ExternalBase):
 
31
    workingtree
 
32
    )
 
33
from bzrlib.repofmt import knitrepo
 
34
from bzrlib.tests import (
 
35
    blackbox,
 
36
    http_server,
 
37
    test_foreign,
 
38
    )
 
39
from bzrlib.transport import memory
 
40
 
 
41
 
 
42
def load_tests(standard_tests, module, loader):
 
43
    """Multiply tests for the push command."""
 
44
    result = loader.suiteClass()
 
45
 
 
46
    # one for each king of change
 
47
    changes_tests, remaining_tests = tests.split_suite_by_condition(
 
48
        standard_tests, tests.condition_isinstance((
 
49
                TestPushStrictWithChanges,
 
50
                )))
 
51
    changes_scenarios = [
 
52
        ('uncommitted',
 
53
         dict(_changes_type= '_uncommitted_changes')),
 
54
        ('pending-merges',
 
55
         dict(_changes_type= '_pending_merges')),
 
56
        ('out-of-sync-trees',
 
57
         dict(_changes_type= '_out_of_sync_trees')),
 
58
        ]
 
59
    tests.multiply_tests(changes_tests, changes_scenarios, result)
 
60
    # No parametrization for the remaining tests
 
61
    result.addTests(remaining_tests)
 
62
 
 
63
    return result
 
64
 
 
65
 
 
66
class TestPush(tests.TestCaseWithTransport):
 
67
 
 
68
    def test_push_error_on_vfs_http(self):
 
69
        """ pushing a branch to a HTTP server fails cleanly. """
 
70
        # the trunk is published on a web server
 
71
        self.transport_readonly_server = http_server.HttpServer
 
72
        self.make_branch('source')
 
73
        public_url = self.get_readonly_url('target')
 
74
        self.run_bzr_error(['http does not support mkdir'],
 
75
                           ['push', public_url],
 
76
                           working_dir='source')
41
77
 
42
78
    def test_push_remember(self):
43
79
        """Push changes from one branch to another and test push location."""
61
97
        self.assertEqual(None, branch_b.get_push_location())
62
98
 
63
99
        # test push for failure without push location set
64
 
        os.chdir('branch_a')
65
 
        out = self.run_bzr('push', retcode=3)
 
100
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
66
101
        self.assertEquals(out,
67
102
                ('','bzr: ERROR: No push location known or specified.\n'))
68
103
 
69
104
        # test not remembered if cannot actually push
70
 
        self.run_bzr('push ../path/which/doesnt/exist', retcode=3)
71
 
        out = self.run_bzr('push', retcode=3)
 
105
        self.run_bzr('push path/which/doesnt/exist',
 
106
                     working_dir='branch_a', retcode=3)
 
107
        out = self.run_bzr('push', working_dir='branch_a', retcode=3)
72
108
        self.assertEquals(
73
109
                ('', 'bzr: ERROR: No push location known or specified.\n'),
74
110
                out)
75
111
 
76
112
        # test implicit --remember when no push location set, push fails
77
 
        out = self.run_bzr('push ../branch_b', retcode=3)
 
113
        out = self.run_bzr('push ../branch_b',
 
114
                           working_dir='branch_a', retcode=3)
78
115
        self.assertEquals(out,
79
116
                ('','bzr: ERROR: These branches have diverged.  '
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
                 'See "bzr help diverged-branches" for more information.\n'))
 
118
        self.assertEquals(osutils.abspath(branch_a.get_push_location()),
 
119
                          osutils.abspath(branch_b.bzrdir.root_transport.base))
83
120
 
84
121
        # test implicit --remember after resolving previous failure
85
 
        uncommit(branch=branch_b, tree=tree_b)
 
122
        uncommit.uncommit(branch=branch_b, tree=tree_b)
86
123
        transport.delete('branch_b/c')
87
 
        out, err = self.run_bzr('push')
 
124
        out, err = self.run_bzr('push', working_dir='branch_a')
88
125
        path = branch_a.get_push_location()
89
126
        self.assertEquals(out,
90
 
                          'Using saved push location: %s\n' 
91
 
                          'Pushed up to revision 2.\n'
92
 
                          % local_path_from_url(path))
 
127
                          'Using saved push location: %s\n'
 
128
                          % urlutils.local_path_from_url(path))
93
129
        self.assertEqual(err,
94
 
                         'All changes applied successfully.\n')
 
130
                         'All changes applied successfully.\n'
 
131
                         'Pushed up to revision 2.\n')
95
132
        self.assertEqual(path,
96
133
                         branch_b.bzrdir.root_transport.base)
97
134
        # test explicit --remember
98
 
        self.run_bzr('push ../branch_c --remember')
 
135
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
99
136
        self.assertEquals(branch_a.get_push_location(),
100
137
                          branch_c.bzrdir.root_transport.base)
101
 
    
 
138
 
102
139
    def test_push_without_tree(self):
103
140
        # bzr push from a branch that does not have a checkout should work.
104
141
        b = self.make_branch('.')
105
142
        out, err = self.run_bzr('push pushed-location')
106
143
        self.assertEqual('', out)
107
144
        self.assertEqual('Created new branch.\n', err)
108
 
        b2 = Branch.open('pushed-location')
 
145
        b2 = branch.Branch.open('pushed-location')
109
146
        self.assertEndsWith(b2.base, 'pushed-location/')
110
147
 
111
148
    def test_push_new_branch_revision_count(self):
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 
 
149
        # bzr push of a branch with revisions to a new location
 
150
        # should print the number of revisions equal to the length of the
114
151
        # local branch.
115
152
        t = self.make_branch_and_tree('tree')
116
153
        self.build_tree(['tree/file'])
117
154
        t.add('file')
118
155
        t.commit('commit 1')
119
 
        os.chdir('tree')
120
 
        out, err = self.run_bzr('push pushed-to')
121
 
        os.chdir('..')
 
156
        out, err = self.run_bzr('push -d tree pushed-to')
122
157
        self.assertEqual('', out)
123
158
        self.assertEqual('Created new branch.\n', err)
124
159
 
125
160
    def test_push_only_pushes_history(self):
126
161
        # Knit branches should only push the history for the current revision.
127
 
        format = BzrDirMetaFormat1()
128
 
        format.repository_format = RepositoryFormatKnit1()
 
162
        format = bzrdir.BzrDirMetaFormat1()
 
163
        format.repository_format = knitrepo.RepositoryFormatKnit1()
129
164
        shared_repo = self.make_repository('repo', format=format, shared=True)
130
165
        shared_repo.set_make_working_trees(True)
131
166
 
132
167
        def make_shared_tree(path):
133
168
            shared_repo.bzrdir.root_transport.mkdir(path)
134
169
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
135
 
            return WorkingTree.open('repo/' + path)
 
170
            return workingtree.WorkingTree.open('repo/' + path)
136
171
        tree_a = make_shared_tree('a')
137
172
        self.build_tree(['repo/a/file'])
138
173
        tree_a.add('file')
153
188
 
154
189
        # Now that we have a repository with shared files, make sure
155
190
        # that things aren't copied out by a 'push'
156
 
        os.chdir('repo/b')
157
 
        self.run_bzr('push ../../push-b')
158
 
        pushed_tree = WorkingTree.open('../../push-b')
 
191
        self.run_bzr('push ../../push-b', working_dir='repo/b')
 
192
        pushed_tree = workingtree.WorkingTree.open('push-b')
159
193
        pushed_repo = pushed_tree.branch.repository
160
194
        self.assertFalse(pushed_repo.has_revision('a-1'))
161
195
        self.assertFalse(pushed_repo.has_revision('a-2'))
163
197
 
164
198
    def test_push_funky_id(self):
165
199
        t = self.make_branch_and_tree('tree')
166
 
        os.chdir('tree')
167
 
        self.build_tree(['filename'])
 
200
        self.build_tree(['tree/filename'])
168
201
        t.add('filename', 'funky-chars<>%&;"\'')
169
202
        t.commit('commit filename')
170
 
        self.run_bzr('push ../new-tree')
 
203
        self.run_bzr('push -d tree new-tree')
171
204
 
172
205
    def test_push_dash_d(self):
173
206
        t = self.make_branch_and_tree('from')
175
208
                message='first commit')
176
209
        self.run_bzr('push -d from to-one')
177
210
        self.failUnlessExists('to-one')
178
 
        self.run_bzr('push -d %s %s' 
 
211
        self.run_bzr('push -d %s %s'
179
212
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
180
213
        self.failUnlessExists('to-two')
181
214
 
 
215
    def test_push_smart_non_stacked_streaming_acceptance(self):
 
216
        self.setup_smart_server_with_call_log()
 
217
        t = self.make_branch_and_tree('from')
 
218
        t.commit(allow_pointless=True, message='first commit')
 
219
        self.reset_smart_call_log()
 
220
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
221
        # This figure represent the amount of work to perform this use case. It
 
222
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
223
        # being too low. If rpc_count increases, more network roundtrips have
 
224
        # become necessary for this use case. Please do not adjust this number
 
225
        # upwards without agreement from bzr's network support maintainers.
 
226
        self.assertLength(9, self.hpss_calls)
 
227
 
 
228
    def test_push_smart_stacked_streaming_acceptance(self):
 
229
        self.setup_smart_server_with_call_log()
 
230
        parent = self.make_branch_and_tree('parent', format='1.9')
 
231
        parent.commit(message='first commit')
 
232
        local = parent.bzrdir.sprout('local').open_workingtree()
 
233
        local.commit(message='local commit')
 
234
        self.reset_smart_call_log()
 
235
        self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
 
236
            self.get_url('public')], working_dir='local')
 
237
        # This figure represent the amount of work to perform this use case. It
 
238
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
239
        # being too low. If rpc_count increases, more network roundtrips have
 
240
        # become necessary for this use case. Please do not adjust this number
 
241
        # upwards without agreement from bzr's network support maintainers.
 
242
        self.assertLength(14, self.hpss_calls)
 
243
        remote = branch.Branch.open('public')
 
244
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
 
245
 
 
246
    def test_push_smart_tags_streaming_acceptance(self):
 
247
        self.setup_smart_server_with_call_log()
 
248
        t = self.make_branch_and_tree('from')
 
249
        rev_id = t.commit(allow_pointless=True, message='first commit')
 
250
        t.branch.tags.set_tag('new-tag', rev_id)
 
251
        self.reset_smart_call_log()
 
252
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
253
        # This figure represent the amount of work to perform this use case. It
 
254
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
255
        # being too low. If rpc_count increases, more network roundtrips have
 
256
        # become necessary for this use case. Please do not adjust this number
 
257
        # upwards without agreement from bzr's network support maintainers.
 
258
        self.assertLength(11, self.hpss_calls)
 
259
 
 
260
    def test_push_smart_incremental_acceptance(self):
 
261
        self.setup_smart_server_with_call_log()
 
262
        t = self.make_branch_and_tree('from')
 
263
        rev_id1 = t.commit(allow_pointless=True, message='first commit')
 
264
        rev_id2 = t.commit(allow_pointless=True, message='second commit')
 
265
        self.run_bzr(
 
266
            ['push', self.get_url('to-one'), '-r1'], working_dir='from')
 
267
        self.reset_smart_call_log()
 
268
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
269
        # This figure represent the amount of work to perform this use case. It
 
270
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
271
        # being too low. If rpc_count increases, more network roundtrips have
 
272
        # become necessary for this use case. Please do not adjust this number
 
273
        # upwards without agreement from bzr's network support maintainers.
 
274
        self.assertLength(11, self.hpss_calls)
 
275
 
 
276
    def test_push_smart_with_default_stacking_url_path_segment(self):
 
277
        # If the default stacked-on location is a path element then branches
 
278
        # we push there over the smart server are stacked and their
 
279
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
 
280
        self.setup_smart_server_with_call_log()
 
281
        self.make_branch('stack-on', format='1.9')
 
282
        self.make_bzrdir('.').get_config().set_default_stack_on(
 
283
            '/stack-on')
 
284
        self.make_branch('from', format='1.9')
 
285
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
 
286
        b = branch.Branch.open(self.get_url('to'))
 
287
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
 
288
 
 
289
    def test_push_smart_with_default_stacking_relative_path(self):
 
290
        # If the default stacked-on location is a relative path then branches
 
291
        # we push there over the smart server are stacked and their
 
292
        # stacked_on_url is a relative path. Added to nail bug 385132.
 
293
        self.setup_smart_server_with_call_log()
 
294
        self.make_branch('stack-on', format='1.9')
 
295
        self.make_bzrdir('.').get_config().set_default_stack_on('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('../stack-on', b.get_stacked_on_url())
 
300
 
182
301
    def create_simple_tree(self):
183
302
        tree = self.make_branch_and_tree('tree')
184
303
        self.build_tree(['tree/a'])
195
314
                           working_dir='tree')
196
315
        self.run_bzr('push ../new/tree --create-prefix',
197
316
                     working_dir='tree')
198
 
        new_tree = WorkingTree.open('new/tree')
 
317
        new_tree = workingtree.WorkingTree.open('new/tree')
199
318
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
200
319
        self.failUnlessExists('new/tree/a')
201
320
 
215
334
        self.run_bzr('push --use-existing-dir ../target',
216
335
                     working_dir='tree')
217
336
 
218
 
        new_tree = WorkingTree.open('target')
 
337
        new_tree = workingtree.WorkingTree.open('target')
219
338
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
220
339
        # The push should have created target/a
221
340
        self.failUnlessExists('target/a')
222
341
 
 
342
    def test_push_use_existing_into_empty_bzrdir(self):
 
343
        """'bzr push --use-existing-dir' into a dir with an empty .bzr dir
 
344
        fails.
 
345
        """
 
346
        tree = self.create_simple_tree()
 
347
        self.build_tree(['target/', 'target/.bzr/'])
 
348
        self.run_bzr_error(
 
349
            ['Target directory ../target already contains a .bzr directory, '
 
350
             'but it is not valid.'],
 
351
            'push ../target --use-existing-dir', working_dir='tree')
 
352
 
223
353
    def test_push_onto_repo(self):
224
354
        """We should be able to 'bzr push' into an existing bzrdir."""
225
355
        tree = self.create_simple_tree()
231
361
        # Pushing onto an existing bzrdir will create a repository and
232
362
        # branch as needed, but will only create a working tree if there was
233
363
        # no BzrDir before.
234
 
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
235
 
        new_branch = Branch.open('repo')
 
364
        self.assertRaises(errors.NoWorkingTree,
 
365
                          workingtree.WorkingTree.open, 'repo')
 
366
        new_branch = branch.Branch.open('repo')
236
367
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
237
368
 
238
369
    def test_push_onto_just_bzrdir(self):
257
388
 
258
389
        self.run_bzr('push -r1 ../to', working_dir='from')
259
390
 
260
 
        tree_to = WorkingTree.open('to')
 
391
        tree_to = workingtree.WorkingTree.open('to')
261
392
        repo_to = tree_to.branch.repository
262
393
        self.assertTrue(repo_to.has_revision('from-1'))
263
394
        self.assertFalse(repo_to.has_revision('from-2'))
264
395
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
265
396
 
266
397
        self.run_bzr_error(
267
 
            "bzr: ERROR: bzr push --revision takes one value.\n",
 
398
            ['bzr: ERROR: bzr push --revision '
 
399
             'takes exactly one revision identifier\n'],
268
400
            'push -r0..2 ../to', working_dir='from')
269
401
 
270
402
    def create_trunk_and_feature_branch(self):
271
403
        # We have a mainline
272
404
        trunk_tree = self.make_branch_and_tree('target',
273
 
            format='development')
 
405
            format='1.9')
274
406
        trunk_tree.commit('mainline')
275
407
        # and a branch from it
276
408
        branch_tree = self.make_branch_and_tree('branch',
277
 
            format='development')
 
409
            format='1.9')
278
410
        branch_tree.pull(trunk_tree.branch)
279
411
        branch_tree.branch.set_parent(trunk_tree.branch.base)
280
412
        # with some work on it
283
415
 
284
416
    def assertPublished(self, branch_revid, stacked_on):
285
417
        """Assert that the branch 'published' has been published correctly."""
286
 
        published_branch = Branch.open('published')
 
418
        published_branch = branch.Branch.open('published')
287
419
        # The published branch refers to the mainline
288
420
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
289
421
        # and the branch's work was pushed
311
443
        self.assertEqual('', out)
312
444
        self.assertEqual('Created new stacked branch referring to %s.\n' %
313
445
            trunk_tree.branch.base, err)
314
 
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
 
446
        self.assertPublished(branch_tree.last_revision(),
 
447
                             trunk_tree.branch.base)
315
448
 
316
449
    def test_push_new_branch_stacked_uses_parent_public(self):
317
450
        """Pushing a new branch with --stacked creates a stacked branch."""
318
451
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
319
452
        # the trunk is published on a web server
320
 
        self.transport_readonly_server = HttpServer
321
 
        trunk_public = self.make_branch('public_trunk', format='development')
 
453
        self.transport_readonly_server = http_server.HttpServer
 
454
        trunk_public = self.make_branch('public_trunk', format='1.9')
322
455
        trunk_public.pull(trunk_tree.branch)
323
456
        trunk_public_url = self.get_readonly_url('public_trunk')
324
457
        trunk_tree.branch.set_public_branch(trunk_public_url)
333
466
 
334
467
    def test_push_new_branch_stacked_no_parent(self):
335
468
        """Pushing with --stacked and no parent branch errors."""
336
 
        branch = self.make_branch_and_tree('branch', format='development')
 
469
        branch = self.make_branch_and_tree('branch', format='1.9')
337
470
        # now we do a stacked push, which should fail as the place to refer too
338
471
        # cannot be determined.
339
472
        out, err = self.run_bzr_error(
350
483
        self.assertContainsRe(err,
351
484
                              'Using default stacking branch stack_on at .*')
352
485
 
 
486
    def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
 
487
        self.make_branch('stack_on', format='1.6')
 
488
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
489
        self.make_branch('from', format='pack-0.92')
 
490
        out, err = self.run_bzr('push -d from to')
 
491
        b = branch.Branch.open('to')
 
492
        self.assertEqual('../stack_on', b.get_stacked_on_url())
 
493
 
 
494
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
 
495
        self.make_branch('stack_on', format='pack-0.92')
 
496
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
497
        self.make_branch('from', format='pack-0.92')
 
498
        out, err = self.run_bzr('push -d from to')
 
499
        b = branch.Branch.open('to')
 
500
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
 
501
 
353
502
    def test_push_doesnt_create_broken_branch(self):
354
503
        """Pushing a new standalone branch works even when there's a default
355
504
        stacking policy at the destination.
395
544
        # subsequent log is accurate
396
545
        self.assertNotContainsRe(out, 'rev1')
397
546
 
398
 
 
399
 
class RedirectingMemoryTransport(MemoryTransport):
 
547
    def test_push_from_subdir(self):
 
548
        t = self.make_branch_and_tree('tree')
 
549
        self.build_tree(['tree/dir/', 'tree/dir/file'])
 
550
        t.add('dir', 'dir/file')
 
551
        t.commit('r1')
 
552
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
 
553
        self.assertEqual('', out)
 
554
        self.assertEqual('Created new branch.\n', err)
 
555
 
 
556
 
 
557
class RedirectingMemoryTransport(memory.MemoryTransport):
400
558
 
401
559
    def mkdir(self, relpath, mode=None):
402
 
        from bzrlib.trace import mutter
403
 
        mutter('cwd: %r, rel: %r, abs: %r' % (self._cwd, relpath, abspath))
404
560
        if self._cwd == '/source/':
405
561
            raise errors.RedirectRequested(self.abspath(relpath),
406
562
                                           self.abspath('../target'),
413
569
            return super(RedirectingMemoryTransport, self).mkdir(
414
570
                relpath, mode)
415
571
 
 
572
    def get(self, relpath):
 
573
        if self.clone(relpath)._cwd == '/infinite-loop/':
 
574
            raise errors.RedirectRequested(self.abspath(relpath),
 
575
                                           self.abspath('../infinite-loop'),
 
576
                                           is_permanent=True)
 
577
        else:
 
578
            return super(RedirectingMemoryTransport, self).get(relpath)
 
579
 
416
580
    def _redirected_to(self, source, target):
417
581
        # We do accept redirections
418
582
        return transport.get_transport(target)
419
583
 
420
584
 
421
 
class RedirectingMemoryServer(MemoryServer):
 
585
class RedirectingMemoryServer(memory.MemoryServer):
422
586
 
423
 
    def setUp(self):
 
587
    def start_server(self):
424
588
        self._dirs = {'/': None}
425
589
        self._files = {}
426
590
        self._locks = {}
434
598
        result._locks = self._locks
435
599
        return result
436
600
 
437
 
    def tearDown(self):
 
601
    def stop_server(self):
438
602
        transport.unregister_transport(self._scheme, self._memory_factory)
439
603
 
440
604
 
441
 
class TestPushRedirect(ExternalBase):
 
605
class TestPushRedirect(tests.TestCaseWithTransport):
442
606
 
443
607
    def setUp(self):
444
 
        ExternalBase.setUp(self)
 
608
        tests.TestCaseWithTransport.setUp(self)
445
609
        self.memory_server = RedirectingMemoryServer()
446
 
        self.memory_server.setUp()
447
 
        self.addCleanup(self.memory_server.tearDown)
448
 
 
 
610
        self.start_server(self.memory_server)
449
611
        # Make the branch and tree that we'll be pushing.
450
612
        t = self.make_branch_and_tree('tree')
451
613
        self.build_tree(['tree/file'])
461
623
        destination_url = self.memory_server.get_url() + 'source'
462
624
        self.run_bzr(['push', '-d', 'tree', destination_url])
463
625
 
464
 
        local_revision = Branch.open('tree').last_revision()
465
 
        remote_revision = Branch.open(
 
626
        local_revision = branch.Branch.open('tree').last_revision()
 
627
        remote_revision = branch.Branch.open(
466
628
            self.memory_server.get_url() + 'target').last_revision()
467
629
        self.assertEqual(remote_revision, local_revision)
468
630
 
476
638
             % re.escape(destination_url)],
477
639
            ['push', '-d', 'tree', destination_url], retcode=3)
478
640
        self.assertEqual('', out)
 
641
 
 
642
 
 
643
class TestPushStrictMixin(object):
 
644
 
 
645
    def make_local_branch_and_tree(self):
 
646
        self.tree = self.make_branch_and_tree('local')
 
647
        self.build_tree_contents([('local/file', 'initial')])
 
648
        self.tree.add('file')
 
649
        self.tree.commit('adding file', rev_id='added')
 
650
        self.build_tree_contents([('local/file', 'modified')])
 
651
        self.tree.commit('modify file', rev_id='modified')
 
652
 
 
653
    def set_config_push_strict(self, value):
 
654
        # set config var (any of bazaar.conf, locations.conf, branch.conf
 
655
        # should do)
 
656
        conf = self.tree.branch.get_config()
 
657
        conf.set_user_option('push_strict', value)
 
658
 
 
659
    _default_command = ['push', '../to']
 
660
    _default_wd = 'local'
 
661
    _default_errors = ['Working tree ".*/local/" has uncommitted '
 
662
                       'changes \(See bzr status\)\.',]
 
663
    _default_pushed_revid = 'modified'
 
664
 
 
665
    def assertPushFails(self, args):
 
666
        self.run_bzr_error(self._default_errors, self._default_command + args,
 
667
                           working_dir=self._default_wd, retcode=3)
 
668
 
 
669
    def assertPushSucceeds(self, args, pushed_revid=None):
 
670
        self.run_bzr(self._default_command + args,
 
671
                     working_dir=self._default_wd)
 
672
        if pushed_revid is None:
 
673
            pushed_revid = self._default_pushed_revid
 
674
        tree_to = workingtree.WorkingTree.open('to')
 
675
        repo_to = tree_to.branch.repository
 
676
        self.assertTrue(repo_to.has_revision(pushed_revid))
 
677
        self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
 
678
 
 
679
 
 
680
 
 
681
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
 
682
                                   TestPushStrictMixin):
 
683
 
 
684
    def setUp(self):
 
685
        super(TestPushStrictWithoutChanges, self).setUp()
 
686
        self.make_local_branch_and_tree()
 
687
 
 
688
    def test_push_default(self):
 
689
        self.assertPushSucceeds([])
 
690
 
 
691
    def test_push_strict(self):
 
692
        self.assertPushSucceeds(['--strict'])
 
693
 
 
694
    def test_push_no_strict(self):
 
695
        self.assertPushSucceeds(['--no-strict'])
 
696
 
 
697
    def test_push_config_var_strict(self):
 
698
        self.set_config_push_strict('true')
 
699
        self.assertPushSucceeds([])
 
700
 
 
701
    def test_push_config_var_no_strict(self):
 
702
        self.set_config_push_strict('false')
 
703
        self.assertPushSucceeds([])
 
704
 
 
705
 
 
706
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
 
707
                                TestPushStrictMixin):
 
708
 
 
709
    _changes_type = None # Set by load_tests
 
710
 
 
711
    def setUp(self):
 
712
        super(TestPushStrictWithChanges, self).setUp()
 
713
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
 
714
        # _pending_merges or _out_of_sync_trees
 
715
        getattr(self, self._changes_type)()
 
716
 
 
717
    def _uncommitted_changes(self):
 
718
        self.make_local_branch_and_tree()
 
719
        # Make a change without committing it
 
720
        self.build_tree_contents([('local/file', 'in progress')])
 
721
 
 
722
    def _pending_merges(self):
 
723
        self.make_local_branch_and_tree()
 
724
        # Create 'other' branch containing a new file
 
725
        other_bzrdir = self.tree.bzrdir.sprout('other')
 
726
        other_tree = other_bzrdir.open_workingtree()
 
727
        self.build_tree_contents([('other/other-file', 'other')])
 
728
        other_tree.add('other-file')
 
729
        other_tree.commit('other commit', rev_id='other')
 
730
        # Merge and revert, leaving a pending merge
 
731
        self.tree.merge_from_branch(other_tree.branch)
 
732
        self.tree.revert(filenames=['other-file'], backups=False)
 
733
 
 
734
    def _out_of_sync_trees(self):
 
735
        self.make_local_branch_and_tree()
 
736
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
 
737
        # Make a change and commit it
 
738
        self.build_tree_contents([('local/file', 'modified in local')])
 
739
        self.tree.commit('modify file', rev_id='modified-in-local')
 
740
        # Exercise commands from the checkout directory
 
741
        self._default_wd = 'checkout'
 
742
        self._default_errors = ["Working tree is out of date, please run"
 
743
                                " 'bzr update'\.",]
 
744
        self._default_pushed_revid = 'modified-in-local'
 
745
 
 
746
    def test_push_default(self):
 
747
        self.assertPushFails([])
 
748
 
 
749
    def test_push_with_revision(self):
 
750
        self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
 
751
 
 
752
    def test_push_no_strict(self):
 
753
        self.assertPushSucceeds(['--no-strict'])
 
754
 
 
755
    def test_push_strict_with_changes(self):
 
756
        self.assertPushFails(['--strict'])
 
757
 
 
758
    def test_push_respect_config_var_strict(self):
 
759
        self.set_config_push_strict('true')
 
760
        self.assertPushFails([])
 
761
 
 
762
    def test_push_bogus_config_var_ignored(self):
 
763
        self.set_config_push_strict("I don't want you to be strict")
 
764
        self.assertPushFails([])
 
765
 
 
766
    def test_push_no_strict_command_line_override_config(self):
 
767
        self.set_config_push_strict('yES')
 
768
        self.assertPushFails([])
 
769
        self.assertPushSucceeds(['--no-strict'])
 
770
 
 
771
    def test_push_strict_command_line_override_config(self):
 
772
        self.set_config_push_strict('oFF')
 
773
        self.assertPushFails(['--strict'])
 
774
        self.assertPushSucceeds([])
 
775
 
 
776
 
 
777
class TestPushForeign(blackbox.ExternalBase):
 
778
 
 
779
    def setUp(self):
 
780
        super(TestPushForeign, self).setUp()
 
781
        test_foreign.register_dummy_foreign_for_test(self)
 
782
 
 
783
    def make_dummy_builder(self, relpath):
 
784
        builder = self.make_branch_builder(
 
785
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
 
786
        builder.build_snapshot('revid', None,
 
787
            [('add', ('', 'TREE_ROOT', 'directory', None)),
 
788
             ('add', ('foo', 'fooid', 'file', 'bar'))])
 
789
        return builder
 
790
 
 
791
    def test_no_roundtripping(self):
 
792
        target_branch = self.make_dummy_builder('dp').get_branch()
 
793
        source_tree = self.make_branch_and_tree("dc")
 
794
        output, error = self.run_bzr("push -d dc dp", retcode=3)
 
795
        self.assertEquals("", output)
 
796
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
 
797
            " push to dummy. You may want to use dpush instead.\n")