~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2009-04-08 13:13:30 UTC
  • mfrom: (4202.4.3 348459-wrong-annotation)
  • mto: This revision was merged to the branch mainline in revision 4273.
  • Revision ID: v.ladeuil+lp@free.fr-20090408131330-mx1hq45oarrxia2z
Allows external annotation tie-breakers

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()
107
100
        self.assertEquals(out,
108
101
                          'Using saved push location: %s\n'
109
 
                          % urlutils.local_path_from_url(path))
 
102
                          % local_path_from_url(path))
110
103
        self.assertEqual(err,
111
104
                         'All changes applied successfully.\n'
112
105
                         'Pushed up to revision 2.\n')
113
106
        self.assertEqual(path,
114
107
                         branch_b.bzrdir.root_transport.base)
115
108
        # test explicit --remember
116
 
        self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
 
109
        self.run_bzr('push ../branch_c --remember')
117
110
        self.assertEquals(branch_a.get_push_location(),
118
111
                          branch_c.bzrdir.root_transport.base)
119
112
 
123
116
        out, err = self.run_bzr('push pushed-location')
124
117
        self.assertEqual('', out)
125
118
        self.assertEqual('Created new branch.\n', err)
126
 
        b2 = branch.Branch.open('pushed-location')
 
119
        b2 = Branch.open('pushed-location')
127
120
        self.assertEndsWith(b2.base, 'pushed-location/')
128
121
 
129
 
    def test_push_no_tree(self):
130
 
        # bzr push --no-tree of a branch with working trees
131
 
        b = self.make_branch_and_tree('push-from')
132
 
        self.build_tree(['push-from/file'])
133
 
        b.add('file')
134
 
        b.commit('commit 1')
135
 
        out, err = self.run_bzr('push --no-tree -d push-from push-to')
136
 
        self.assertEqual('', out)
137
 
        self.assertEqual('Created new branch.\n', err)
138
 
        self.assertPathDoesNotExist('push-to/file')
139
 
 
140
122
    def test_push_new_branch_revision_count(self):
141
123
        # bzr push of a branch with revisions to a new location
142
124
        # should print the number of revisions equal to the length of the
145
127
        self.build_tree(['tree/file'])
146
128
        t.add('file')
147
129
        t.commit('commit 1')
148
 
        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('..')
149
133
        self.assertEqual('', out)
150
134
        self.assertEqual('Created new branch.\n', err)
151
135
 
152
136
    def test_push_only_pushes_history(self):
153
137
        # Knit branches should only push the history for the current revision.
154
 
        format = bzrdir.BzrDirMetaFormat1()
155
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
138
        format = BzrDirMetaFormat1()
 
139
        format.repository_format = RepositoryFormatKnit1()
156
140
        shared_repo = self.make_repository('repo', format=format, shared=True)
157
141
        shared_repo.set_make_working_trees(True)
158
142
 
159
143
        def make_shared_tree(path):
160
144
            shared_repo.bzrdir.root_transport.mkdir(path)
161
145
            shared_repo.bzrdir.create_branch_convenience('repo/' + path)
162
 
            return workingtree.WorkingTree.open('repo/' + path)
 
146
            return WorkingTree.open('repo/' + path)
163
147
        tree_a = make_shared_tree('a')
164
148
        self.build_tree(['repo/a/file'])
165
149
        tree_a.add('file')
180
164
 
181
165
        # Now that we have a repository with shared files, make sure
182
166
        # that things aren't copied out by a 'push'
183
 
        self.run_bzr('push ../../push-b', working_dir='repo/b')
184
 
        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')
185
170
        pushed_repo = pushed_tree.branch.repository
186
171
        self.assertFalse(pushed_repo.has_revision('a-1'))
187
172
        self.assertFalse(pushed_repo.has_revision('a-2'))
189
174
 
190
175
    def test_push_funky_id(self):
191
176
        t = self.make_branch_and_tree('tree')
192
 
        self.build_tree(['tree/filename'])
 
177
        os.chdir('tree')
 
178
        self.build_tree(['filename'])
193
179
        t.add('filename', 'funky-chars<>%&;"\'')
194
180
        t.commit('commit filename')
195
 
        self.run_bzr('push -d tree new-tree')
 
181
        self.run_bzr('push ../new-tree')
196
182
 
197
183
    def test_push_dash_d(self):
198
184
        t = self.make_branch_and_tree('from')
199
185
        t.commit(allow_pointless=True,
200
186
                message='first commit')
201
187
        self.run_bzr('push -d from to-one')
202
 
        self.assertPathExists('to-one')
 
188
        self.failUnlessExists('to-one')
203
189
        self.run_bzr('push -d %s %s'
204
190
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
205
 
        self.assertPathExists('to-two')
206
 
 
207
 
    def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
208
 
        # See https://bugs.launchpad.net/bzr/+bug/465517
209
 
        target_repo = self.make_repository('target')
210
 
        source = self.make_branch_builder('source')
211
 
        source.start_series()
212
 
        source.build_snapshot('A', None, [
213
 
            ('add', ('', 'root-id', 'directory', None))])
214
 
        source.build_snapshot('B', ['A'], [])
215
 
        source.build_snapshot('C', ['A'], [])
216
 
        source.finish_series()
217
 
        self.run_bzr('push target -d source')
218
 
        self.addCleanup(target_repo.lock_read().unlock)
219
 
        # We should have pushed 'C', but not 'B', since it isn't in the
220
 
        # ancestry
221
 
        self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
 
191
        self.failUnlessExists('to-two')
222
192
 
223
193
    def test_push_smart_non_stacked_streaming_acceptance(self):
224
194
        self.setup_smart_server_with_call_log()
231
201
        # being too low. If rpc_count increases, more network roundtrips have
232
202
        # become necessary for this use case. Please do not adjust this number
233
203
        # upwards without agreement from bzr's network support maintainers.
234
 
        self.assertLength(9, self.hpss_calls)
 
204
        self.assertLength(20, self.hpss_calls)
235
205
 
236
206
    def test_push_smart_stacked_streaming_acceptance(self):
237
207
        self.setup_smart_server_with_call_log()
247
217
        # being too low. If rpc_count increases, more network roundtrips have
248
218
        # become necessary for this use case. Please do not adjust this number
249
219
        # upwards without agreement from bzr's network support maintainers.
250
 
        self.assertLength(13, self.hpss_calls)
251
 
        remote = branch.Branch.open('public')
 
220
        self.assertLength(42, self.hpss_calls)
 
221
        remote = Branch.open('public')
252
222
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
253
223
 
254
 
    def test_push_smart_tags_streaming_acceptance(self):
255
 
        self.setup_smart_server_with_call_log()
256
 
        t = self.make_branch_and_tree('from')
257
 
        rev_id = t.commit(allow_pointless=True, message='first commit')
258
 
        t.branch.tags.set_tag('new-tag', rev_id)
259
 
        self.reset_smart_call_log()
260
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
261
 
        # This figure represent the amount of work to perform this use case. It
262
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
263
 
        # being too low. If rpc_count increases, more network roundtrips have
264
 
        # become necessary for this use case. Please do not adjust this number
265
 
        # upwards without agreement from bzr's network support maintainers.
266
 
        self.assertLength(11, self.hpss_calls)
267
 
 
268
 
    def test_push_smart_incremental_acceptance(self):
269
 
        self.setup_smart_server_with_call_log()
270
 
        t = self.make_branch_and_tree('from')
271
 
        rev_id1 = t.commit(allow_pointless=True, message='first commit')
272
 
        rev_id2 = t.commit(allow_pointless=True, message='second commit')
273
 
        self.run_bzr(
274
 
            ['push', self.get_url('to-one'), '-r1'], working_dir='from')
275
 
        self.reset_smart_call_log()
276
 
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
277
 
        # This figure represent the amount of work to perform this use case. It
278
 
        # is entirely ok to reduce this number if a test fails due to rpc_count
279
 
        # being too low. If rpc_count increases, more network roundtrips have
280
 
        # become necessary for this use case. Please do not adjust this number
281
 
        # upwards without agreement from bzr's network support maintainers.
282
 
        self.assertLength(11, self.hpss_calls)
283
 
 
284
 
    def test_push_smart_with_default_stacking_url_path_segment(self):
285
 
        # If the default stacked-on location is a path element then branches
286
 
        # we push there over the smart server are stacked and their
287
 
        # stacked_on_url is that exact path segment. Added to nail bug 385132.
288
 
        self.setup_smart_server_with_call_log()
289
 
        self.make_branch('stack-on', format='1.9')
290
 
        self.make_bzrdir('.').get_config().set_default_stack_on(
291
 
            '/stack-on')
292
 
        self.make_branch('from', format='1.9')
293
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
294
 
        b = branch.Branch.open(self.get_url('to'))
295
 
        self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
296
 
 
297
 
    def test_push_smart_with_default_stacking_relative_path(self):
298
 
        # If the default stacked-on location is a relative path then branches
299
 
        # we push there over the smart server are stacked and their
300
 
        # stacked_on_url is a relative path. 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('stack-on')
304
 
        self.make_branch('from', format='1.9')
305
 
        out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
306
 
        b = branch.Branch.open(self.get_url('to'))
307
 
        self.assertEqual('../stack-on', b.get_stacked_on_url())
308
 
 
309
224
    def create_simple_tree(self):
310
225
        tree = self.make_branch_and_tree('tree')
311
226
        self.build_tree(['tree/a'])
322
237
                           working_dir='tree')
323
238
        self.run_bzr('push ../new/tree --create-prefix',
324
239
                     working_dir='tree')
325
 
        new_tree = workingtree.WorkingTree.open('new/tree')
 
240
        new_tree = WorkingTree.open('new/tree')
326
241
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
327
 
        self.assertPathExists('new/tree/a')
 
242
        self.failUnlessExists('new/tree/a')
328
243
 
329
244
    def test_push_use_existing(self):
330
245
        """'bzr push --use-existing-dir' can push into an existing dir.
342
257
        self.run_bzr('push --use-existing-dir ../target',
343
258
                     working_dir='tree')
344
259
 
345
 
        new_tree = workingtree.WorkingTree.open('target')
 
260
        new_tree = WorkingTree.open('target')
346
261
        self.assertEqual(tree.last_revision(), new_tree.last_revision())
347
262
        # The push should have created target/a
348
 
        self.assertPathExists('target/a')
349
 
 
350
 
    def test_push_use_existing_into_empty_bzrdir(self):
351
 
        """'bzr push --use-existing-dir' into a dir with an empty .bzr dir
352
 
        fails.
353
 
        """
354
 
        tree = self.create_simple_tree()
355
 
        self.build_tree(['target/', 'target/.bzr/'])
356
 
        self.run_bzr_error(
357
 
            ['Target directory ../target already contains a .bzr directory, '
358
 
             'but it is not valid.'],
359
 
            'push ../target --use-existing-dir', working_dir='tree')
 
263
        self.failUnlessExists('target/a')
360
264
 
361
265
    def test_push_onto_repo(self):
362
266
        """We should be able to 'bzr push' into an existing bzrdir."""
369
273
        # Pushing onto an existing bzrdir will create a repository and
370
274
        # branch as needed, but will only create a working tree if there was
371
275
        # no BzrDir before.
372
 
        self.assertRaises(errors.NoWorkingTree,
373
 
                          workingtree.WorkingTree.open, 'repo')
374
 
        new_branch = branch.Branch.open('repo')
 
276
        self.assertRaises(errors.NoWorkingTree, WorkingTree.open, 'repo')
 
277
        new_branch = Branch.open('repo')
375
278
        self.assertEqual(tree.last_revision(), new_branch.last_revision())
376
279
 
377
280
    def test_push_onto_just_bzrdir(self):
396
299
 
397
300
        self.run_bzr('push -r1 ../to', working_dir='from')
398
301
 
399
 
        tree_to = workingtree.WorkingTree.open('to')
 
302
        tree_to = WorkingTree.open('to')
400
303
        repo_to = tree_to.branch.repository
401
304
        self.assertTrue(repo_to.has_revision('from-1'))
402
305
        self.assertFalse(repo_to.has_revision('from-2'))
403
306
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
404
307
 
405
308
        self.run_bzr_error(
406
 
            ['bzr: ERROR: bzr push --revision '
407
 
             'takes exactly one revision identifier\n'],
 
309
            "bzr: ERROR: bzr push --revision takes one value.\n",
408
310
            'push -r0..2 ../to', working_dir='from')
409
311
 
410
312
    def create_trunk_and_feature_branch(self):
423
325
 
424
326
    def assertPublished(self, branch_revid, stacked_on):
425
327
        """Assert that the branch 'published' has been published correctly."""
426
 
        published_branch = branch.Branch.open('published')
 
328
        published_branch = Branch.open('published')
427
329
        # The published branch refers to the mainline
428
330
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
429
331
        # and the branch's work was pushed
451
353
        self.assertEqual('', out)
452
354
        self.assertEqual('Created new stacked branch referring to %s.\n' %
453
355
            trunk_tree.branch.base, err)
454
 
        self.assertPublished(branch_tree.last_revision(),
455
 
                             trunk_tree.branch.base)
 
356
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
456
357
 
457
358
    def test_push_new_branch_stacked_uses_parent_public(self):
458
359
        """Pushing a new branch with --stacked creates a stacked branch."""
459
360
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
460
361
        # the trunk is published on a web server
461
 
        self.transport_readonly_server = http_server.HttpServer
 
362
        self.transport_readonly_server = HttpServer
462
363
        trunk_public = self.make_branch('public_trunk', format='1.9')
463
364
        trunk_public.pull(trunk_tree.branch)
464
365
        trunk_public_url = self.get_readonly_url('public_trunk')
496
397
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
497
398
        self.make_branch('from', format='pack-0.92')
498
399
        out, err = self.run_bzr('push -d from to')
499
 
        b = branch.Branch.open('to')
500
 
        self.assertEqual('../stack_on', b.get_stacked_on_url())
 
400
        branch = Branch.open('to')
 
401
        self.assertEqual('../stack_on', branch.get_stacked_on_url())
501
402
 
502
403
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
503
404
        self.make_branch('stack_on', format='pack-0.92')
504
405
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
505
406
        self.make_branch('from', format='pack-0.92')
506
407
        out, err = self.run_bzr('push -d from to')
507
 
        b = branch.Branch.open('to')
508
 
        self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
 
408
        branch = Branch.open('to')
 
409
        self.assertRaises(errors.UnstackableBranchFormat,
 
410
            branch.get_stacked_on_url)
509
411
 
510
412
    def test_push_doesnt_create_broken_branch(self):
511
413
        """Pushing a new standalone branch works even when there's a default
552
454
        # subsequent log is accurate
553
455
        self.assertNotContainsRe(out, 'rev1')
554
456
 
555
 
    def test_push_from_subdir(self):
556
 
        t = self.make_branch_and_tree('tree')
557
 
        self.build_tree(['tree/dir/', 'tree/dir/file'])
558
 
        t.add('dir', 'dir/file')
559
 
        t.commit('r1')
560
 
        out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
561
 
        self.assertEqual('', out)
562
 
        self.assertEqual('Created new branch.\n', err)
563
 
 
564
 
 
565
 
class RedirectingMemoryTransport(memory.MemoryTransport):
 
457
 
 
458
class RedirectingMemoryTransport(MemoryTransport):
566
459
 
567
460
    def mkdir(self, relpath, mode=None):
 
461
        from bzrlib.trace import mutter
 
462
        mutter('cwd: %r, rel: %r, abs: %r' % (self._cwd, relpath, abspath))
568
463
        if self._cwd == '/source/':
569
464
            raise errors.RedirectRequested(self.abspath(relpath),
570
465
                                           self.abspath('../target'),
577
472
            return super(RedirectingMemoryTransport, self).mkdir(
578
473
                relpath, mode)
579
474
 
580
 
    def get(self, relpath):
581
 
        if self.clone(relpath)._cwd == '/infinite-loop/':
582
 
            raise errors.RedirectRequested(self.abspath(relpath),
583
 
                                           self.abspath('../infinite-loop'),
584
 
                                           is_permanent=True)
585
 
        else:
586
 
            return super(RedirectingMemoryTransport, self).get(relpath)
587
 
 
588
475
    def _redirected_to(self, source, target):
589
476
        # We do accept redirections
590
477
        return transport.get_transport(target)
591
478
 
592
479
 
593
 
class RedirectingMemoryServer(memory.MemoryServer):
 
480
class RedirectingMemoryServer(MemoryServer):
594
481
 
595
 
    def start_server(self):
 
482
    def setUp(self):
596
483
        self._dirs = {'/': None}
597
484
        self._files = {}
598
485
        self._locks = {}
606
493
        result._locks = self._locks
607
494
        return result
608
495
 
609
 
    def stop_server(self):
 
496
    def tearDown(self):
610
497
        transport.unregister_transport(self._scheme, self._memory_factory)
611
498
 
612
499
 
613
 
class TestPushRedirect(tests.TestCaseWithTransport):
 
500
class TestPushRedirect(ExternalBase):
614
501
 
615
502
    def setUp(self):
616
 
        tests.TestCaseWithTransport.setUp(self)
 
503
        ExternalBase.setUp(self)
617
504
        self.memory_server = RedirectingMemoryServer()
618
 
        self.start_server(self.memory_server)
 
505
        self.memory_server.setUp()
 
506
        self.addCleanup(self.memory_server.tearDown)
 
507
 
619
508
        # Make the branch and tree that we'll be pushing.
620
509
        t = self.make_branch_and_tree('tree')
621
510
        self.build_tree(['tree/file'])
631
520
        destination_url = self.memory_server.get_url() + 'source'
632
521
        self.run_bzr(['push', '-d', 'tree', destination_url])
633
522
 
634
 
        local_revision = branch.Branch.open('tree').last_revision()
635
 
        remote_revision = branch.Branch.open(
 
523
        local_revision = Branch.open('tree').last_revision()
 
524
        remote_revision = Branch.open(
636
525
            self.memory_server.get_url() + 'target').last_revision()
637
526
        self.assertEqual(remote_revision, local_revision)
638
527
 
646
535
             % re.escape(destination_url)],
647
536
            ['push', '-d', 'tree', destination_url], retcode=3)
648
537
        self.assertEqual('', out)
649
 
 
650
 
 
651
 
class TestPushStrictMixin(object):
652
 
 
653
 
    def make_local_branch_and_tree(self):
654
 
        self.tree = self.make_branch_and_tree('local')
655
 
        self.build_tree_contents([('local/file', 'initial')])
656
 
        self.tree.add('file')
657
 
        self.tree.commit('adding file', rev_id='added')
658
 
        self.build_tree_contents([('local/file', 'modified')])
659
 
        self.tree.commit('modify file', rev_id='modified')
660
 
 
661
 
    def set_config_push_strict(self, value):
662
 
        # set config var (any of bazaar.conf, locations.conf, branch.conf
663
 
        # should do)
664
 
        conf = self.tree.branch.get_config()
665
 
        conf.set_user_option('push_strict', value)
666
 
 
667
 
    _default_command = ['push', '../to']
668
 
    _default_wd = 'local'
669
 
    _default_errors = ['Working tree ".*/local/" has uncommitted '
670
 
                       'changes \(See bzr status\)\.',]
671
 
    _default_additional_error = 'Use --no-strict to force the push.\n'
672
 
    _default_additional_warning = 'Uncommitted changes will not be pushed.'
673
 
 
674
 
 
675
 
    def assertPushFails(self, args):
676
 
        out, err = self.run_bzr_error(self._default_errors,
677
 
                                      self._default_command + args,
678
 
                                      working_dir=self._default_wd, retcode=3)
679
 
        self.assertContainsRe(err, self._default_additional_error)
680
 
 
681
 
    def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
682
 
        if with_warning:
683
 
            error_regexes = self._default_errors
684
 
        else:
685
 
            error_regexes = []
686
 
        out, err = self.run_bzr(self._default_command + args,
687
 
                                working_dir=self._default_wd,
688
 
                                error_regexes=error_regexes)
689
 
        if with_warning:
690
 
            self.assertContainsRe(err, self._default_additional_warning)
691
 
        else:
692
 
            self.assertNotContainsRe(err, self._default_additional_warning)
693
 
        branch_from = branch.Branch.open(self._default_wd)
694
 
        if revid_to_push is None:
695
 
            revid_to_push = branch_from.last_revision()
696
 
        branch_to = branch.Branch.open('to')
697
 
        repo_to = branch_to.repository
698
 
        self.assertTrue(repo_to.has_revision(revid_to_push))
699
 
        self.assertEqual(revid_to_push, branch_to.last_revision())
700
 
 
701
 
 
702
 
 
703
 
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
704
 
                                   TestPushStrictMixin):
705
 
 
706
 
    def setUp(self):
707
 
        super(TestPushStrictWithoutChanges, self).setUp()
708
 
        self.make_local_branch_and_tree()
709
 
 
710
 
    def test_push_default(self):
711
 
        self.assertPushSucceeds([])
712
 
 
713
 
    def test_push_strict(self):
714
 
        self.assertPushSucceeds(['--strict'])
715
 
 
716
 
    def test_push_no_strict(self):
717
 
        self.assertPushSucceeds(['--no-strict'])
718
 
 
719
 
    def test_push_config_var_strict(self):
720
 
        self.set_config_push_strict('true')
721
 
        self.assertPushSucceeds([])
722
 
 
723
 
    def test_push_config_var_no_strict(self):
724
 
        self.set_config_push_strict('false')
725
 
        self.assertPushSucceeds([])
726
 
 
727
 
 
728
 
strict_push_change_scenarios = [
729
 
    ('uncommitted',
730
 
        dict(_changes_type= '_uncommitted_changes')),
731
 
    ('pending-merges',
732
 
        dict(_changes_type= '_pending_merges')),
733
 
    ('out-of-sync-trees',
734
 
        dict(_changes_type= '_out_of_sync_trees')),
735
 
    ]
736
 
 
737
 
 
738
 
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
739
 
                                TestPushStrictMixin):
740
 
 
741
 
    scenarios = strict_push_change_scenarios 
742
 
    _changes_type = None # Set by load_tests
743
 
 
744
 
    def setUp(self):
745
 
        super(TestPushStrictWithChanges, self).setUp()
746
 
        # Apply the changes defined in load_tests: one of _uncommitted_changes,
747
 
        # _pending_merges or _out_of_sync_trees
748
 
        getattr(self, self._changes_type)()
749
 
 
750
 
    def _uncommitted_changes(self):
751
 
        self.make_local_branch_and_tree()
752
 
        # Make a change without committing it
753
 
        self.build_tree_contents([('local/file', 'in progress')])
754
 
 
755
 
    def _pending_merges(self):
756
 
        self.make_local_branch_and_tree()
757
 
        # Create 'other' branch containing a new file
758
 
        other_bzrdir = self.tree.bzrdir.sprout('other')
759
 
        other_tree = other_bzrdir.open_workingtree()
760
 
        self.build_tree_contents([('other/other-file', 'other')])
761
 
        other_tree.add('other-file')
762
 
        other_tree.commit('other commit', rev_id='other')
763
 
        # Merge and revert, leaving a pending merge
764
 
        self.tree.merge_from_branch(other_tree.branch)
765
 
        self.tree.revert(filenames=['other-file'], backups=False)
766
 
 
767
 
    def _out_of_sync_trees(self):
768
 
        self.make_local_branch_and_tree()
769
 
        self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
770
 
        # Make a change and commit it
771
 
        self.build_tree_contents([('local/file', 'modified in local')])
772
 
        self.tree.commit('modify file', rev_id='modified-in-local')
773
 
        # Exercise commands from the checkout directory
774
 
        self._default_wd = 'checkout'
775
 
        self._default_errors = ["Working tree is out of date, please run"
776
 
                                " 'bzr update'\.",]
777
 
 
778
 
    def test_push_default(self):
779
 
        self.assertPushSucceeds([], with_warning=True)
780
 
 
781
 
    def test_push_with_revision(self):
782
 
        self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
783
 
 
784
 
    def test_push_no_strict(self):
785
 
        self.assertPushSucceeds(['--no-strict'])
786
 
 
787
 
    def test_push_strict_with_changes(self):
788
 
        self.assertPushFails(['--strict'])
789
 
 
790
 
    def test_push_respect_config_var_strict(self):
791
 
        self.set_config_push_strict('true')
792
 
        self.assertPushFails([])
793
 
 
794
 
    def test_push_bogus_config_var_ignored(self):
795
 
        self.set_config_push_strict("I don't want you to be strict")
796
 
        self.assertPushSucceeds([], with_warning=True)
797
 
 
798
 
    def test_push_no_strict_command_line_override_config(self):
799
 
        self.set_config_push_strict('yES')
800
 
        self.assertPushFails([])
801
 
        self.assertPushSucceeds(['--no-strict'])
802
 
 
803
 
    def test_push_strict_command_line_override_config(self):
804
 
        self.set_config_push_strict('oFF')
805
 
        self.assertPushFails(['--strict'])
806
 
        self.assertPushSucceeds([])
807
 
 
808
 
 
809
 
class TestPushForeign(tests.TestCaseWithTransport):
810
 
 
811
 
    def setUp(self):
812
 
        super(TestPushForeign, self).setUp()
813
 
        test_foreign.register_dummy_foreign_for_test(self)
814
 
 
815
 
    def make_dummy_builder(self, relpath):
816
 
        builder = self.make_branch_builder(
817
 
            relpath, format=test_foreign.DummyForeignVcsDirFormat())
818
 
        builder.build_snapshot('revid', None,
819
 
            [('add', ('', 'TREE_ROOT', 'directory', None)),
820
 
             ('add', ('foo', 'fooid', 'file', 'bar'))])
821
 
        return builder
822
 
 
823
 
    def test_no_roundtripping(self):
824
 
        target_branch = self.make_dummy_builder('dp').get_branch()
825
 
        source_tree = self.make_branch_and_tree("dc")
826
 
        output, error = self.run_bzr("push -d dc dp", retcode=3)
827
 
        self.assertEquals("", output)
828
 
        self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
829
 
            " push to dummy. You may want to use dpush instead.\n")