~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Blake Winton
  • Date: 2007-10-16 18:26:12 UTC
  • mto: This revision was merged to the branch mainline in revision 2921.
  • Revision ID: bwinton@latte.ca-20071016182612-e06wjvlzzdw0vwki
Fix test failures

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
import os
19
19
 
20
20
import bzrlib
21
 
from bzrlib.tests import TestCaseWithTransport
 
21
from bzrlib import (
 
22
    errors,
 
23
    lockdir,
 
24
    osutils,
 
25
    tests,
 
26
    )
22
27
from bzrlib.branch import Branch
23
28
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
24
 
from bzrlib.workingtree import WorkingTree
25
29
from bzrlib.commit import Commit, NullCommitReporter
26
30
from bzrlib.config import BranchConfig
27
31
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
28
32
                           LockContention)
 
33
from bzrlib.tests import TestCaseWithTransport
 
34
from bzrlib.workingtree import WorkingTree
29
35
 
30
36
 
31
37
# TODO: Test commit with some added, and added-but-missing files
64
70
    def renamed(self, change, old_path, new_path):
65
71
        self.calls.append(('renamed', change, old_path, new_path))
66
72
 
 
73
    def is_verbose(self):
 
74
        return True
 
75
 
67
76
 
68
77
class TestCommit(TestCaseWithTransport):
69
78
 
217
226
        wt.move(['hello'], 'a')
218
227
        r2 = 'test@rev-2'
219
228
        wt.commit('two', rev_id=r2, allow_pointless=False)
220
 
        self.check_inventory_shape(wt.read_working_inventory(),
221
 
                                   ['a', 'a/hello', 'b'])
 
229
        wt.lock_read()
 
230
        try:
 
231
            self.check_inventory_shape(wt.read_working_inventory(),
 
232
                                       ['a/', 'a/hello', 'b/'])
 
233
        finally:
 
234
            wt.unlock()
222
235
 
223
236
        wt.move(['b'], 'a')
224
237
        r3 = 'test@rev-3'
225
238
        wt.commit('three', rev_id=r3, allow_pointless=False)
226
 
        self.check_inventory_shape(wt.read_working_inventory(),
227
 
                                   ['a', 'a/hello', 'a/b'])
228
 
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
229
 
                                   ['a', 'a/hello', 'a/b'])
 
239
        wt.lock_read()
 
240
        try:
 
241
            self.check_inventory_shape(wt.read_working_inventory(),
 
242
                                       ['a/', 'a/hello', 'a/b/'])
 
243
            self.check_inventory_shape(b.repository.get_revision_inventory(r3),
 
244
                                       ['a/', 'a/hello', 'a/b/'])
 
245
        finally:
 
246
            wt.unlock()
230
247
 
231
248
        wt.move(['a/hello'], 'a/b')
232
249
        r4 = 'test@rev-4'
233
250
        wt.commit('four', rev_id=r4, allow_pointless=False)
234
 
        self.check_inventory_shape(wt.read_working_inventory(),
235
 
                                   ['a', 'a/b/hello', 'a/b'])
 
251
        wt.lock_read()
 
252
        try:
 
253
            self.check_inventory_shape(wt.read_working_inventory(),
 
254
                                       ['a/', 'a/b/hello', 'a/b/'])
 
255
        finally:
 
256
            wt.unlock()
236
257
 
237
258
        inv = b.repository.get_revision_inventory(r4)
238
259
        eq(inv['hello-id'].revision, r4)
239
260
        eq(inv['a-id'].revision, r1)
240
261
        eq(inv['b-id'].revision, r3)
241
 
        
 
262
 
242
263
    def test_removed_commit(self):
243
264
        """Commit with a removed file"""
244
265
        wt = self.make_branch_and_tree('.')
339
360
                                                      allow_pointless=True,
340
361
                                                      rev_id='B',
341
362
                                                      working_tree=wt)
342
 
            self.assertEqual(Testament.from_revision(branch.repository,
343
 
                             'B').as_short_text(),
 
363
            def sign(text):
 
364
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
365
            self.assertEqual(sign(Testament.from_revision(branch.repository,
 
366
                             'B').as_short_text()),
344
367
                             branch.repository.get_signature_text('B'))
345
368
        finally:
346
369
            bzrlib.gpg.GPGStrategy = oldstrategy
407
430
        bound = master.sprout('bound')
408
431
        wt = bound.open_workingtree()
409
432
        wt.branch.set_bound_location(os.path.realpath('master'))
 
433
 
 
434
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
410
435
        master_branch.lock_write()
411
436
        try:
 
437
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
412
438
            self.assertRaises(LockContention, wt.commit, 'silly')
413
439
        finally:
 
440
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
414
441
            master_branch.unlock()
415
442
 
416
443
    def test_commit_bound_merge(self):
436
463
        # do a merge into the bound branch from other, and then change the
437
464
        # content file locally to force a new revision (rather than using the
438
465
        # revision from other). This forces extra processing in commit.
439
 
        self.merge(other_tree.branch, bound_tree)
 
466
        bound_tree.merge_from_branch(other_tree.branch)
440
467
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
441
468
 
442
469
        # before #34959 was fixed, this failed with 'revision not present in
490
517
            other_tree.commit('modify all sample files and dirs.')
491
518
        finally:
492
519
            other_tree.unlock()
493
 
        self.merge(other_tree.branch, this_tree)
 
520
        this_tree.merge_from_branch(other_tree.branch)
494
521
        reporter = CapturingReporter()
495
522
        this_tree.commit('do the commit', reporter=reporter)
496
523
        self.assertEqual([
 
524
            ('change', 'unchanged', ''),
497
525
            ('change', 'unchanged', 'dirtoleave'),
498
526
            ('change', 'unchanged', 'filetoleave'),
499
527
            ('change', 'modified', 'filetomodify'),
500
528
            ('change', 'added', 'newdir'),
501
529
            ('change', 'added', 'newfile'),
502
530
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
 
531
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
503
532
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
504
533
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
505
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
506
534
            ('deleted', 'dirtoremove'),
507
535
            ('deleted', 'filetoremove'),
508
536
            ],
509
537
            reporter.calls)
 
538
 
 
539
    def test_commit_removals_respects_filespec(self):
 
540
        """Commit respects the specified_files for removals."""
 
541
        tree = self.make_branch_and_tree('.')
 
542
        self.build_tree(['a', 'b'])
 
543
        tree.add(['a', 'b'])
 
544
        tree.commit('added a, b')
 
545
        tree.remove(['a', 'b'])
 
546
        tree.commit('removed a', specific_files='a')
 
547
        basis = tree.basis_tree()
 
548
        tree.lock_read()
 
549
        try:
 
550
            self.assertIs(None, basis.path2id('a'))
 
551
            self.assertFalse(basis.path2id('b') is None)
 
552
        finally:
 
553
            tree.unlock()
 
554
 
 
555
    def test_commit_saves_1ms_timestamp(self):
 
556
        """Passing in a timestamp is saved with 1ms resolution"""
 
557
        tree = self.make_branch_and_tree('.')
 
558
        self.build_tree(['a'])
 
559
        tree.add('a')
 
560
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
 
561
                    rev_id='a1')
 
562
 
 
563
        rev = tree.branch.repository.get_revision('a1')
 
564
        self.assertEqual(1153248633.419, rev.timestamp)
 
565
 
 
566
    def test_commit_has_1ms_resolution(self):
 
567
        """Allowing commit to generate the timestamp also has 1ms resolution"""
 
568
        tree = self.make_branch_and_tree('.')
 
569
        self.build_tree(['a'])
 
570
        tree.add('a')
 
571
        tree.commit('added a', rev_id='a1')
 
572
 
 
573
        rev = tree.branch.repository.get_revision('a1')
 
574
        timestamp = rev.timestamp
 
575
        timestamp_1ms = round(timestamp, 3)
 
576
        self.assertEqual(timestamp_1ms, timestamp)
 
577
 
 
578
    def assertBasisTreeKind(self, kind, tree, file_id):
 
579
        basis = tree.basis_tree()
 
580
        basis.lock_read()
 
581
        try:
 
582
            self.assertEqual(kind, basis.kind(file_id))
 
583
        finally:
 
584
            basis.unlock()
 
585
 
 
586
    def test_commit_kind_changes(self):
 
587
        if not osutils.has_symlinks():
 
588
            raise tests.TestSkipped('Test requires symlink support')
 
589
        tree = self.make_branch_and_tree('.')
 
590
        os.symlink('target', 'name')
 
591
        tree.add('name', 'a-file-id')
 
592
        tree.commit('Added a symlink')
 
593
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
594
 
 
595
        os.unlink('name')
 
596
        self.build_tree(['name'])
 
597
        tree.commit('Changed symlink to file')
 
598
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
599
 
 
600
        os.unlink('name')
 
601
        os.symlink('target', 'name')
 
602
        tree.commit('file to symlink')
 
603
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
604
 
 
605
        os.unlink('name')
 
606
        os.mkdir('name')
 
607
        tree.commit('symlink to directory')
 
608
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
609
 
 
610
        os.rmdir('name')
 
611
        os.symlink('target', 'name')
 
612
        tree.commit('directory to symlink')
 
613
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
614
 
 
615
        # prepare for directory <-> file tests
 
616
        os.unlink('name')
 
617
        os.mkdir('name')
 
618
        tree.commit('symlink to directory')
 
619
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
620
 
 
621
        os.rmdir('name')
 
622
        self.build_tree(['name'])
 
623
        tree.commit('Changed directory to file')
 
624
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
625
 
 
626
        os.unlink('name')
 
627
        os.mkdir('name')
 
628
        tree.commit('file to directory')
 
629
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
630
 
 
631
    def test_commit_unversioned_specified(self):
 
632
        """Commit should raise if specified files isn't in basis or worktree"""
 
633
        tree = self.make_branch_and_tree('.')
 
634
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
 
635
                          'message', specific_files=['bogus'])
 
636
 
 
637
    class Callback(object):
 
638
        
 
639
        def __init__(self, message, testcase):
 
640
            self.called = False
 
641
            self.message = message
 
642
            self.testcase = testcase
 
643
 
 
644
        def __call__(self, commit_obj):
 
645
            self.called = True
 
646
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
 
647
            return self.message
 
648
 
 
649
    def test_commit_callback(self):
 
650
        """Commit should invoke a callback to get the message"""
 
651
 
 
652
        tree = self.make_branch_and_tree('.')
 
653
        try:
 
654
            tree.commit()
 
655
        except Exception, e:
 
656
            self.assertTrue(isinstance(e, BzrError))
 
657
            self.assertEqual('The message or message_callback keyword'
 
658
                             ' parameter is required for commit().', str(e))
 
659
        else:
 
660
            self.fail('exception not raised')
 
661
        cb = self.Callback(u'commit 1', self)
 
662
        tree.commit(message_callback=cb)
 
663
        self.assertTrue(cb.called)
 
664
        repository = tree.branch.repository
 
665
        message = repository.get_revision(tree.last_revision()).message
 
666
        self.assertEqual('commit 1', message)
 
667
 
 
668
    def test_no_callback_pointless(self):
 
669
        """Callback should not be invoked for pointless commit"""
 
670
        tree = self.make_branch_and_tree('.')
 
671
        cb = self.Callback(u'commit 2', self)
 
672
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
 
673
                          allow_pointless=False)
 
674
        self.assertFalse(cb.called)
 
675
 
 
676
    def test_no_callback_netfailure(self):
 
677
        """Callback should not be invoked if connectivity fails"""
 
678
        tree = self.make_branch_and_tree('.')
 
679
        cb = self.Callback(u'commit 2', self)
 
680
        repository = tree.branch.repository
 
681
        # simulate network failure
 
682
        def raise_(self, arg, arg2):
 
683
            raise errors.NoSuchFile('foo')
 
684
        repository.add_inventory = raise_
 
685
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
 
686
        self.assertFalse(cb.called)
 
687
 
 
688
    def test_selected_file_merge_commit(self):
 
689
        """Ensure the correct error is raised"""
 
690
        tree = self.make_branch_and_tree('foo')
 
691
        # pending merge would turn into a left parent
 
692
        tree.commit('commit 1')
 
693
        tree.add_parent_tree_id('example')
 
694
        self.build_tree(['foo/bar', 'foo/baz'])
 
695
        tree.add(['bar', 'baz'])
 
696
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
 
697
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
 
698
        self.assertEqual(['bar', 'baz'], err.files)
 
699
        self.assertEqual('Selected-file commit of merges is not supported'
 
700
                         ' yet: files bar, baz', str(err))
 
701
 
 
702
    def test_commit_ordering(self):
 
703
        """Test of corner-case commit ordering error"""
 
704
        tree = self.make_branch_and_tree('.')
 
705
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
706
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
 
707
        tree.commit('setup')
 
708
        self.build_tree(['a/c/d/'])
 
709
        tree.add('a/c/d')
 
710
        tree.rename_one('a/z/x', 'a/c/d/x')
 
711
        tree.commit('test', specific_files=['a/z/y'])
 
712
 
 
713
    def test_commit_no_author(self):
 
714
        """The default kwarg author in MutableTree.commit should not add
 
715
        the 'author' revision property.
 
716
        """
 
717
        tree = self.make_branch_and_tree('foo')
 
718
        rev_id = tree.commit('commit 1')
 
719
        rev = tree.branch.repository.get_revision(rev_id)
 
720
        self.assertFalse('author' in rev.properties)
 
721
 
 
722
    def test_commit_author(self):
 
723
        """Passing a non-empty author kwarg to MutableTree.commit should add
 
724
        the 'author' revision property.
 
725
        """
 
726
        tree = self.make_branch_and_tree('foo')
 
727
        rev_id = tree.commit('commit 1', author='John Doe <jdoe@example.com>')
 
728
        rev = tree.branch.repository.get_revision(rev_id)
 
729
        self.assertEqual('John Doe <jdoe@example.com>',
 
730
                         rev.properties['author'])