~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2007-07-18 20:30:14 UTC
  • mto: This revision was merged to the branch mainline in revision 2643.
  • Revision ID: john@arbash-meinel.com-20070718203014-u8gpbqn5z9ftx1tu
Lot's of fixes from Martin's comments.
Fix signed/unsigned character issues
Add lots of comments to help understand the code
Add tests for proper Unicode handling (we should abort if we get a Unicode string,
and we should correctly handle utf-8 strings)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 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
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
"""Black-box tests for bzr push."""
19
19
 
20
20
import os
21
 
import re
22
21
 
23
22
from bzrlib import (
24
23
    errors,
25
 
    transport,
26
24
    urlutils,
27
25
    )
28
26
from bzrlib.branch import Branch
29
27
from bzrlib.bzrdir import BzrDirMetaFormat1
30
28
from bzrlib.osutils import abspath
31
29
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
32
 
from bzrlib.smart import client, server
33
30
from bzrlib.tests.blackbox import ExternalBase
34
 
from bzrlib.tests.http_server import HttpServer
35
 
from bzrlib.transport.memory import MemoryServer, MemoryTransport
36
31
from bzrlib.uncommit import uncommit
37
32
from bzrlib.urlutils import local_path_from_url
38
33
from bzrlib.workingtree import WorkingTree
40
35
 
41
36
class TestPush(ExternalBase):
42
37
 
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
 
 
53
38
    def test_push_remember(self):
54
39
        """Push changes from one branch to another and test push location."""
55
40
        transport = self.get_transport()
98
83
        out, err = self.run_bzr('push')
99
84
        path = branch_a.get_push_location()
100
85
        self.assertEquals(out,
101
 
                          'Using saved push location: %s\n'
 
86
                          'Using saved location: %s\n' 
 
87
                          'Pushed up to revision 2.\n'
102
88
                          % local_path_from_url(path))
103
89
        self.assertEqual(err,
104
 
                         'All changes applied successfully.\n'
105
 
                         'Pushed up to revision 2.\n')
 
90
                         'All changes applied successfully.\n')
106
91
        self.assertEqual(path,
107
92
                         branch_b.bzrdir.root_transport.base)
108
93
        # test explicit --remember
109
94
        self.run_bzr('push ../branch_c --remember')
110
95
        self.assertEquals(branch_a.get_push_location(),
111
96
                          branch_c.bzrdir.root_transport.base)
112
 
 
 
97
    
113
98
    def test_push_without_tree(self):
114
99
        # bzr push from a branch that does not have a checkout should work.
115
100
        b = self.make_branch('.')
120
105
        self.assertEndsWith(b2.base, 'pushed-location/')
121
106
 
122
107
    def test_push_new_branch_revision_count(self):
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
 
108
        # bzr push of a branch with revisions to a new location 
 
109
        # should print the number of revisions equal to the length of the 
125
110
        # local branch.
126
111
        t = self.make_branch_and_tree('tree')
127
112
        self.build_tree(['tree/file'])
186
171
                message='first commit')
187
172
        self.run_bzr('push -d from to-one')
188
173
        self.failUnlessExists('to-one')
189
 
        self.run_bzr('push -d %s %s'
 
174
        self.run_bzr('push -d %s %s' 
190
175
            % tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
191
176
        self.failUnlessExists('to-two')
192
177
 
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(42, self.hpss_calls)
221
 
        remote = Branch.open('public')
222
 
        self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
223
 
 
224
178
    def create_simple_tree(self):
225
179
        tree = self.make_branch_and_tree('tree')
226
180
        self.build_tree(['tree/a'])
290
244
        self.run_bzr_error(['At ../dir you have a valid .bzr control'],
291
245
                'push ../dir',
292
246
                working_dir='tree')
293
 
 
294
 
    def test_push_with_revisionspec(self):
295
 
        """We should be able to push a revision older than the tip."""
296
 
        tree_from = self.make_branch_and_tree('from')
297
 
        tree_from.commit("One.", rev_id="from-1")
298
 
        tree_from.commit("Two.", rev_id="from-2")
299
 
 
300
 
        self.run_bzr('push -r1 ../to', working_dir='from')
301
 
 
302
 
        tree_to = WorkingTree.open('to')
303
 
        repo_to = tree_to.branch.repository
304
 
        self.assertTrue(repo_to.has_revision('from-1'))
305
 
        self.assertFalse(repo_to.has_revision('from-2'))
306
 
        self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
307
 
 
308
 
        self.run_bzr_error(
309
 
            "bzr: ERROR: bzr push --revision takes one value.\n",
310
 
            'push -r0..2 ../to', working_dir='from')
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
 
 
457
 
 
458
 
class RedirectingMemoryTransport(MemoryTransport):
459
 
 
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)
471
 
        else:
472
 
            return super(RedirectingMemoryTransport, self).mkdir(
473
 
                relpath, mode)
474
 
 
475
 
    def _redirected_to(self, source, target):
476
 
        # We do accept redirections
477
 
        return transport.get_transport(target)
478
 
 
479
 
 
480
 
class RedirectingMemoryServer(MemoryServer):
481
 
 
482
 
    def setUp(self):
483
 
        self._dirs = {'/': None}
484
 
        self._files = {}
485
 
        self._locks = {}
486
 
        self._scheme = 'redirecting-memory+%s:///' % id(self)
487
 
        transport.register_transport(self._scheme, self._memory_factory)
488
 
 
489
 
    def _memory_factory(self, url):
490
 
        result = RedirectingMemoryTransport(url)
491
 
        result._dirs = self._dirs
492
 
        result._files = self._files
493
 
        result._locks = self._locks
494
 
        return result
495
 
 
496
 
    def tearDown(self):
497
 
        transport.unregister_transport(self._scheme, self._memory_factory)
498
 
 
499
 
 
500
 
class TestPushRedirect(ExternalBase):
501
 
 
502
 
    def setUp(self):
503
 
        ExternalBase.setUp(self)
504
 
        self.memory_server = RedirectingMemoryServer()
505
 
        self.memory_server.setUp()
506
 
        self.addCleanup(self.memory_server.tearDown)
507
 
 
508
 
        # Make the branch and tree that we'll be pushing.
509
 
        t = self.make_branch_and_tree('tree')
510
 
        self.build_tree(['tree/file'])
511
 
        t.add('file')
512
 
        t.commit('commit 1')
513
 
 
514
 
    def test_push_redirects_on_mkdir(self):
515
 
        """If the push requires a mkdir, push respects redirect requests.
516
 
 
517
 
        This is added primarily to handle lp:/ URI support, so that users can
518
 
        push to new branches by specifying lp:/ URIs.
519
 
        """
520
 
        destination_url = self.memory_server.get_url() + 'source'
521
 
        self.run_bzr(['push', '-d', 'tree', destination_url])
522
 
 
523
 
        local_revision = Branch.open('tree').last_revision()
524
 
        remote_revision = Branch.open(
525
 
            self.memory_server.get_url() + 'target').last_revision()
526
 
        self.assertEqual(remote_revision, local_revision)
527
 
 
528
 
    def test_push_gracefully_handles_too_many_redirects(self):
529
 
        """Push fails gracefully if the mkdir generates a large number of
530
 
        redirects.
531
 
        """
532
 
        destination_url = self.memory_server.get_url() + 'infinite-loop'
533
 
        out, err = self.run_bzr_error(
534
 
            ['Too many redirections trying to make %s\\.\n'
535
 
             % re.escape(destination_url)],
536
 
            ['push', '-d', 'tree', destination_url], retcode=3)
537
 
        self.assertEqual('', out)