~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2009-03-24 05:21:02 UTC
  • mfrom: (4192 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4202.
  • Revision ID: mbp@sourcefrog.net-20090324052102-8kk087b32tep3d9h
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 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
22
22
 
23
23
from bzrlib import (
24
24
    errors,
 
25
    transport,
25
26
    urlutils,
26
27
    )
27
28
from bzrlib.branch import Branch
28
29
from bzrlib.bzrdir import BzrDirMetaFormat1
29
30
from bzrlib.osutils import abspath
30
31
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
 
32
from bzrlib.smart import client, server
31
33
from bzrlib.tests.blackbox import ExternalBase
32
 
from bzrlib.transport import register_transport, unregister_transport
 
34
from bzrlib.tests.http_server import HttpServer
33
35
from bzrlib.transport.memory import MemoryServer, MemoryTransport
34
36
from bzrlib.uncommit import uncommit
35
37
from bzrlib.urlutils import local_path_from_url
38
40
 
39
41
class TestPush(ExternalBase):
40
42
 
 
43
    def test_push_error_on_vfs_http(self):
 
44
        """ pushing a branch to a HTTP server fails cleanly. """
 
45
        # the trunk is published on a web server
 
46
        self.transport_readonly_server = HttpServer
 
47
        self.make_branch('source')
 
48
        public_url = self.get_readonly_url('target')
 
49
        self.run_bzr_error(['http does not support mkdir'],
 
50
                           ['push', public_url],
 
51
                           working_dir='source')
 
52
 
41
53
    def test_push_remember(self):
42
54
        """Push changes from one branch to another and test push location."""
43
55
        transport = self.get_transport()
86
98
        out, err = self.run_bzr('push')
87
99
        path = branch_a.get_push_location()
88
100
        self.assertEquals(out,
89
 
                          'Using saved location: %s\n' 
90
 
                          'Pushed up to revision 2.\n'
 
101
                          'Using saved push location: %s\n'
91
102
                          % local_path_from_url(path))
92
103
        self.assertEqual(err,
93
 
                         'All changes applied successfully.\n')
 
104
                         'All changes applied successfully.\n'
 
105
                         'Pushed up to revision 2.\n')
94
106
        self.assertEqual(path,
95
107
                         branch_b.bzrdir.root_transport.base)
96
108
        # test explicit --remember
97
109
        self.run_bzr('push ../branch_c --remember')
98
110
        self.assertEquals(branch_a.get_push_location(),
99
111
                          branch_c.bzrdir.root_transport.base)
100
 
    
 
112
 
101
113
    def test_push_without_tree(self):
102
114
        # bzr push from a branch that does not have a checkout should work.
103
115
        b = self.make_branch('.')
108
120
        self.assertEndsWith(b2.base, 'pushed-location/')
109
121
 
110
122
    def test_push_new_branch_revision_count(self):
111
 
        # bzr push of a branch with revisions to a new location 
112
 
        # should print the number of revisions equal to the length of the 
 
123
        # bzr push of a branch with revisions to a new location
 
124
        # should print the number of revisions equal to the length of the
113
125
        # local branch.
114
126
        t = self.make_branch_and_tree('tree')
115
127
        self.build_tree(['tree/file'])
174
186
                message='first commit')
175
187
        self.run_bzr('push -d from to-one')
176
188
        self.failUnlessExists('to-one')
177
 
        self.run_bzr('push -d %s %s' 
 
189
        self.run_bzr('push -d %s %s'
178
190
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
179
191
        self.failUnlessExists('to-two')
180
192
 
 
193
    def test_push_smart_non_stacked_streaming_acceptance(self):
 
194
        self.setup_smart_server_with_call_log()
 
195
        t = self.make_branch_and_tree('from')
 
196
        t.commit(allow_pointless=True, message='first commit')
 
197
        self.reset_smart_call_log()
 
198
        self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
 
199
        # This figure represent the amount of work to perform this use case. It
 
200
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
201
        # being too low. If rpc_count increases, more network roundtrips have
 
202
        # become necessary for this use case. Please do not adjust this number
 
203
        # upwards without agreement from bzr's network support maintainers.
 
204
        self.assertLength(20, self.hpss_calls)
 
205
 
 
206
    def test_push_smart_stacked_streaming_acceptance(self):
 
207
        self.setup_smart_server_with_call_log()
 
208
        parent = self.make_branch_and_tree('parent', format='1.9')
 
209
        parent.commit(message='first commit')
 
210
        local = parent.bzrdir.sprout('local').open_workingtree()
 
211
        local.commit(message='local commit')
 
212
        self.reset_smart_call_log()
 
213
        self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
 
214
            self.get_url('public')], working_dir='local')
 
215
        # This figure represent the amount of work to perform this use case. It
 
216
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
217
        # being too low. If rpc_count increases, more network roundtrips have
 
218
        # become necessary for this use case. Please do not adjust this number
 
219
        # upwards without agreement from bzr's network support maintainers.
 
220
        self.assertLength(56, self.hpss_calls)
 
221
        remote = Branch.open('public')
 
222
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
 
223
 
181
224
    def create_simple_tree(self):
182
225
        tree = self.make_branch_and_tree('tree')
183
226
        self.build_tree(['tree/a'])
266
309
            "bzr: ERROR: bzr push --revision takes one value.\n",
267
310
            'push -r0..2 ../to', working_dir='from')
268
311
 
 
312
    def create_trunk_and_feature_branch(self):
 
313
        # We have a mainline
 
314
        trunk_tree = self.make_branch_and_tree('target',
 
315
            format='development')
 
316
        trunk_tree.commit('mainline')
 
317
        # and a branch from it
 
318
        branch_tree = self.make_branch_and_tree('branch',
 
319
            format='development')
 
320
        branch_tree.pull(trunk_tree.branch)
 
321
        branch_tree.branch.set_parent(trunk_tree.branch.base)
 
322
        # with some work on it
 
323
        branch_tree.commit('moar work plz')
 
324
        return trunk_tree, branch_tree
 
325
 
 
326
    def assertPublished(self, branch_revid, stacked_on):
 
327
        """Assert that the branch 'published' has been published correctly."""
 
328
        published_branch = Branch.open('published')
 
329
        # The published branch refers to the mainline
 
330
        self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
 
331
        # and the branch's work was pushed
 
332
        self.assertTrue(published_branch.repository.has_revision(branch_revid))
 
333
 
 
334
    def test_push_new_branch_stacked_on(self):
 
335
        """Pushing a new branch with --stacked-on creates a stacked branch."""
 
336
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
337
        # we publish branch_tree with a reference to the mainline.
 
338
        out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
 
339
            self.get_url('published')], working_dir='branch')
 
340
        self.assertEqual('', out)
 
341
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
342
            trunk_tree.branch.base, err)
 
343
        self.assertPublished(branch_tree.last_revision(),
 
344
            trunk_tree.branch.base)
 
345
 
 
346
    def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
 
347
        """When the parent has no public url the parent is used as-is."""
 
348
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
349
        # now we do a stacked push, which should determine the public location
 
350
        # for us.
 
351
        out, err = self.run_bzr(['push', '--stacked',
 
352
            self.get_url('published')], working_dir='branch')
 
353
        self.assertEqual('', out)
 
354
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
355
            trunk_tree.branch.base, err)
 
356
        self.assertPublished(branch_tree.last_revision(), trunk_tree.branch.base)
 
357
 
 
358
    def test_push_new_branch_stacked_uses_parent_public(self):
 
359
        """Pushing a new branch with --stacked creates a stacked branch."""
 
360
        trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
 
361
        # the trunk is published on a web server
 
362
        self.transport_readonly_server = HttpServer
 
363
        trunk_public = self.make_branch('public_trunk', format='development')
 
364
        trunk_public.pull(trunk_tree.branch)
 
365
        trunk_public_url = self.get_readonly_url('public_trunk')
 
366
        trunk_tree.branch.set_public_branch(trunk_public_url)
 
367
        # now we do a stacked push, which should determine the public location
 
368
        # for us.
 
369
        out, err = self.run_bzr(['push', '--stacked',
 
370
            self.get_url('published')], working_dir='branch')
 
371
        self.assertEqual('', out)
 
372
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
373
            trunk_public_url, err)
 
374
        self.assertPublished(branch_tree.last_revision(), trunk_public_url)
 
375
 
 
376
    def test_push_new_branch_stacked_no_parent(self):
 
377
        """Pushing with --stacked and no parent branch errors."""
 
378
        branch = self.make_branch_and_tree('branch', format='development')
 
379
        # now we do a stacked push, which should fail as the place to refer too
 
380
        # cannot be determined.
 
381
        out, err = self.run_bzr_error(
 
382
            ['Could not determine branch to refer to\\.'], ['push', '--stacked',
 
383
            self.get_url('published')], working_dir='branch')
 
384
        self.assertEqual('', out)
 
385
        self.assertFalse(self.get_transport('published').has('.'))
 
386
 
 
387
    def test_push_notifies_default_stacking(self):
 
388
        self.make_branch('stack_on', format='1.6')
 
389
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
390
        self.make_branch('from', format='1.6')
 
391
        out, err = self.run_bzr('push -d from to')
 
392
        self.assertContainsRe(err,
 
393
                              'Using default stacking branch stack_on at .*')
 
394
 
 
395
    def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
 
396
        self.make_branch('stack_on', format='1.6')
 
397
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
398
        self.make_branch('from', format='pack-0.92')
 
399
        out, err = self.run_bzr('push -d from to')
 
400
        branch = Branch.open('to')
 
401
        self.assertEqual('../stack_on', branch.get_stacked_on_url())
 
402
 
 
403
    def test_push_does_not_change_format_with_default_if_target_cannot(self):
 
404
        self.make_branch('stack_on', format='pack-0.92')
 
405
        self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
 
406
        self.make_branch('from', format='pack-0.92')
 
407
        out, err = self.run_bzr('push -d from to')
 
408
        branch = Branch.open('to')
 
409
        self.assertRaises(errors.UnstackableBranchFormat,
 
410
            branch.get_stacked_on_url)
 
411
 
 
412
    def test_push_doesnt_create_broken_branch(self):
 
413
        """Pushing a new standalone branch works even when there's a default
 
414
        stacking policy at the destination.
 
415
 
 
416
        The new branch will preserve the repo format (even if it isn't the
 
417
        default for the branch), and will be stacked when the repo format
 
418
        allows (which means that the branch format isn't necessarly preserved).
 
419
        """
 
420
        self.make_repository('repo', shared=True, format='1.6')
 
421
        builder = self.make_branch_builder('repo/local', format='pack-0.92')
 
422
        builder.start_series()
 
423
        builder.build_snapshot('rev-1', None, [
 
424
            ('add', ('', 'root-id', 'directory', '')),
 
425
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
 
426
        builder.build_snapshot('rev-2', ['rev-1'], [])
 
427
        builder.build_snapshot('rev-3', ['rev-2'],
 
428
            [('modify', ('f-id', 'new-content\n'))])
 
429
        builder.finish_series()
 
430
        branch = builder.get_branch()
 
431
        # Push rev-1 to "trunk", so that we can stack on it.
 
432
        self.run_bzr('push -d repo/local trunk -r 1')
 
433
        # Set a default stacking policy so that new branches will automatically
 
434
        # stack on trunk.
 
435
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
 
436
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
 
437
        out, err = self.run_bzr('push -d repo/local remote -r 2')
 
438
        self.assertContainsRe(
 
439
            err, 'Using default stacking branch trunk at .*')
 
440
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
 
441
        # fulltext record for f-id @ rev-1, then this will fail.
 
442
        out, err = self.run_bzr('push -d repo/local remote -r 3')
 
443
 
 
444
    def test_push_verbose_shows_log(self):
 
445
        tree = self.make_branch_and_tree('source')
 
446
        tree.commit('rev1')
 
447
        out, err = self.run_bzr('push -v -d source target')
 
448
        # initial push contains log
 
449
        self.assertContainsRe(out, 'rev1')
 
450
        tree.commit('rev2')
 
451
        out, err = self.run_bzr('push -v -d source target')
 
452
        # subsequent push contains log
 
453
        self.assertContainsRe(out, 'rev2')
 
454
        # subsequent log is accurate
 
455
        self.assertNotContainsRe(out, 'rev1')
 
456
 
269
457
 
270
458
class RedirectingMemoryTransport(MemoryTransport):
271
459
 
272
 
    def mkdir(self, path, mode=None):
273
 
        path = self.abspath(path)[len(self._scheme):]
274
 
        if path == '/source':
275
 
            raise errors.RedirectRequested(
276
 
                path, self._scheme + '/target', is_permanent=True)
277
 
        elif path == '/infinite-loop':
278
 
            raise errors.RedirectRequested(
279
 
                path, self._scheme + '/infinite-loop', is_permanent=True)
 
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))
 
463
        if self._cwd == '/source/':
 
464
            raise errors.RedirectRequested(self.abspath(relpath),
 
465
                                           self.abspath('../target'),
 
466
                                           is_permanent=True)
 
467
        elif self._cwd == '/infinite-loop/':
 
468
            raise errors.RedirectRequested(self.abspath(relpath),
 
469
                                           self.abspath('../infinite-loop'),
 
470
                                           is_permanent=True)
280
471
        else:
281
472
            return super(RedirectingMemoryTransport, self).mkdir(
282
 
                path, mode)
 
473
                relpath, mode)
 
474
 
 
475
    def _redirected_to(self, source, target):
 
476
        # We do accept redirections
 
477
        return transport.get_transport(target)
283
478
 
284
479
 
285
480
class RedirectingMemoryServer(MemoryServer):
289
484
        self._files = {}
290
485
        self._locks = {}
291
486
        self._scheme = 'redirecting-memory+%s:///' % id(self)
292
 
        register_transport(self._scheme, self._memory_factory)
 
487
        transport.register_transport(self._scheme, self._memory_factory)
293
488
 
294
489
    def _memory_factory(self, url):
295
490
        result = RedirectingMemoryTransport(url)
299
494
        return result
300
495
 
301
496
    def tearDown(self):
302
 
        unregister_transport(self._scheme, self._memory_factory)
 
497
        transport.unregister_transport(self._scheme, self._memory_factory)
303
498
 
304
499
 
305
500
class TestPushRedirect(ExternalBase):
322
517
        This is added primarily to handle lp:/ URI support, so that users can
323
518
        push to new branches by specifying lp:/ URIs.
324
519
        """
325
 
        os.chdir('tree')
326
520
        destination_url = self.memory_server.get_url() + 'source'
327
 
        self.run_bzr('push %s' % destination_url)
328
 
        os.chdir('..')
 
521
        self.run_bzr(['push', '-d', 'tree', destination_url])
329
522
 
330
523
        local_revision = Branch.open('tree').last_revision()
331
524
        remote_revision = Branch.open(
336
529
        """Push fails gracefully if the mkdir generates a large number of
337
530
        redirects.
338
531
        """
339
 
        os.chdir('tree')
340
532
        destination_url = self.memory_server.get_url() + 'infinite-loop'
341
533
        out, err = self.run_bzr_error(
342
534
            ['Too many redirections trying to make %s\\.\n'
343
535
             % re.escape(destination_url)],
344
 
            'push %s' % destination_url, retcode=3)
345
 
        os.chdir('..')
 
536
            ['push', '-d', 'tree', destination_url], retcode=3)
346
537
        self.assertEqual('', out)