~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Robert Collins
  • Date: 2007-03-08 04:06:06 UTC
  • mfrom: (2323.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 2442.
  • Revision ID: robertc@robertcollins.net-20070308040606-84gsniv56huiyjt4
Merge bzr.dev.

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
217
223
        wt.move(['hello'], 'a')
218
224
        r2 = 'test@rev-2'
219
225
        wt.commit('two', rev_id=r2, allow_pointless=False)
220
 
        self.check_inventory_shape(wt.read_working_inventory(),
221
 
                                   ['a', 'a/hello', 'b'])
 
226
        wt.lock_read()
 
227
        try:
 
228
            self.check_inventory_shape(wt.read_working_inventory(),
 
229
                                       ['a', 'a/hello', 'b'])
 
230
        finally:
 
231
            wt.unlock()
222
232
 
223
233
        wt.move(['b'], 'a')
224
234
        r3 = 'test@rev-3'
225
235
        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'])
 
236
        wt.lock_read()
 
237
        try:
 
238
            self.check_inventory_shape(wt.read_working_inventory(),
 
239
                                       ['a', 'a/hello', 'a/b'])
 
240
            self.check_inventory_shape(b.repository.get_revision_inventory(r3),
 
241
                                       ['a', 'a/hello', 'a/b'])
 
242
        finally:
 
243
            wt.unlock()
230
244
 
231
245
        wt.move(['a/hello'], 'a/b')
232
246
        r4 = 'test@rev-4'
233
247
        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'])
 
248
        wt.lock_read()
 
249
        try:
 
250
            self.check_inventory_shape(wt.read_working_inventory(),
 
251
                                       ['a', 'a/b/hello', 'a/b'])
 
252
        finally:
 
253
            wt.unlock()
236
254
 
237
255
        inv = b.repository.get_revision_inventory(r4)
238
256
        eq(inv['hello-id'].revision, r4)
239
257
        eq(inv['a-id'].revision, r1)
240
258
        eq(inv['b-id'].revision, r3)
241
 
        
 
259
 
242
260
    def test_removed_commit(self):
243
261
        """Commit with a removed file"""
244
262
        wt = self.make_branch_and_tree('.')
407
425
        bound = master.sprout('bound')
408
426
        wt = bound.open_workingtree()
409
427
        wt.branch.set_bound_location(os.path.realpath('master'))
 
428
 
 
429
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
410
430
        master_branch.lock_write()
411
431
        try:
 
432
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
412
433
            self.assertRaises(LockContention, wt.commit, 'silly')
413
434
        finally:
 
435
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
414
436
            master_branch.unlock()
415
437
 
416
438
    def test_commit_bound_merge(self):
436
458
        # do a merge into the bound branch from other, and then change the
437
459
        # content file locally to force a new revision (rather than using the
438
460
        # revision from other). This forces extra processing in commit.
439
 
        self.merge(other_tree.branch, bound_tree)
 
461
        bound_tree.merge_from_branch(other_tree.branch)
440
462
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
441
463
 
442
464
        # before #34959 was fixed, this failed with 'revision not present in
490
512
            other_tree.commit('modify all sample files and dirs.')
491
513
        finally:
492
514
            other_tree.unlock()
493
 
        self.merge(other_tree.branch, this_tree)
 
515
        this_tree.merge_from_branch(other_tree.branch)
494
516
        reporter = CapturingReporter()
495
517
        this_tree.commit('do the commit', reporter=reporter)
496
518
        self.assertEqual([
 
519
            ('change', 'unchanged', ''),
497
520
            ('change', 'unchanged', 'dirtoleave'),
498
521
            ('change', 'unchanged', 'filetoleave'),
499
522
            ('change', 'modified', 'filetomodify'),
507
530
            ('deleted', 'filetoremove'),
508
531
            ],
509
532
            reporter.calls)
 
533
 
 
534
    def test_commit_removals_respects_filespec(self):
 
535
        """Commit respects the specified_files for removals."""
 
536
        tree = self.make_branch_and_tree('.')
 
537
        self.build_tree(['a', 'b'])
 
538
        tree.add(['a', 'b'])
 
539
        tree.commit('added a, b')
 
540
        tree.remove(['a', 'b'])
 
541
        tree.commit('removed a', specific_files='a')
 
542
        basis = tree.basis_tree()
 
543
        tree.lock_read()
 
544
        try:
 
545
            self.assertIs(None, basis.path2id('a'))
 
546
            self.assertFalse(basis.path2id('b') is None)
 
547
        finally:
 
548
            tree.unlock()
 
549
 
 
550
    def test_commit_saves_1ms_timestamp(self):
 
551
        """Passing in a timestamp is saved with 1ms resolution"""
 
552
        tree = self.make_branch_and_tree('.')
 
553
        self.build_tree(['a'])
 
554
        tree.add('a')
 
555
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
 
556
                    rev_id='a1')
 
557
 
 
558
        rev = tree.branch.repository.get_revision('a1')
 
559
        self.assertEqual(1153248633.419, rev.timestamp)
 
560
 
 
561
    def test_commit_has_1ms_resolution(self):
 
562
        """Allowing commit to generate the timestamp also has 1ms resolution"""
 
563
        tree = self.make_branch_and_tree('.')
 
564
        self.build_tree(['a'])
 
565
        tree.add('a')
 
566
        tree.commit('added a', rev_id='a1')
 
567
 
 
568
        rev = tree.branch.repository.get_revision('a1')
 
569
        timestamp = rev.timestamp
 
570
        timestamp_1ms = round(timestamp, 3)
 
571
        self.assertEqual(timestamp_1ms, timestamp)
 
572
 
 
573
    def assertBasisTreeKind(self, kind, tree, file_id):
 
574
        basis = tree.basis_tree()
 
575
        basis.lock_read()
 
576
        try:
 
577
            self.assertEqual(kind, basis.kind(file_id))
 
578
        finally:
 
579
            basis.unlock()
 
580
 
 
581
    def test_commit_kind_changes(self):
 
582
        if not osutils.has_symlinks():
 
583
            raise tests.TestSkipped('Test requires symlink support')
 
584
        tree = self.make_branch_and_tree('.')
 
585
        os.symlink('target', 'name')
 
586
        tree.add('name', 'a-file-id')
 
587
        tree.commit('Added a symlink')
 
588
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
589
 
 
590
        os.unlink('name')
 
591
        self.build_tree(['name'])
 
592
        tree.commit('Changed symlink to file')
 
593
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
594
 
 
595
        os.unlink('name')
 
596
        os.symlink('target', 'name')
 
597
        tree.commit('file to symlink')
 
598
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
599
 
 
600
        os.unlink('name')
 
601
        os.mkdir('name')
 
602
        tree.commit('symlink to directory')
 
603
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
604
 
 
605
        os.rmdir('name')
 
606
        os.symlink('target', 'name')
 
607
        tree.commit('directory to symlink')
 
608
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
609
 
 
610
        # prepare for directory <-> file tests
 
611
        os.unlink('name')
 
612
        os.mkdir('name')
 
613
        tree.commit('symlink to directory')
 
614
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
615
 
 
616
        os.rmdir('name')
 
617
        self.build_tree(['name'])
 
618
        tree.commit('Changed directory to file')
 
619
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
620
 
 
621
        os.unlink('name')
 
622
        os.mkdir('name')
 
623
        tree.commit('file to directory')
 
624
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
625
 
 
626
    def test_commit_unversioned_specified(self):
 
627
        """Commit should raise if specified files isn't in basis or worktree"""
 
628
        tree = self.make_branch_and_tree('.')
 
629
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
 
630
                          'message', specific_files=['bogus'])
 
631
 
 
632
    class Callback(object):
 
633
        
 
634
        def __init__(self, message, testcase):
 
635
            self.called = False
 
636
            self.message = message
 
637
            self.testcase = testcase
 
638
 
 
639
        def __call__(self, commit_obj):
 
640
            self.called = True
 
641
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
 
642
            return self.message
 
643
 
 
644
    def test_commit_callback(self):
 
645
        """Commit should invoke a callback to get the message"""
 
646
 
 
647
        tree = self.make_branch_and_tree('.')
 
648
        try:
 
649
            tree.commit()
 
650
        except Exception, e:
 
651
            self.assertTrue(isinstance(e, BzrError))
 
652
            self.assertEqual('The message or message_callback keyword'
 
653
                             ' parameter is required for commit().', str(e))
 
654
        else:
 
655
            self.fail('exception not raised')
 
656
        cb = self.Callback(u'commit 1', self)
 
657
        tree.commit(message_callback=cb)
 
658
        self.assertTrue(cb.called)
 
659
        repository = tree.branch.repository
 
660
        message = repository.get_revision(tree.last_revision()).message
 
661
        self.assertEqual('commit 1', message)
 
662
 
 
663
    def test_no_callback_pointless(self):
 
664
        """Callback should not be invoked for pointless commit"""
 
665
        tree = self.make_branch_and_tree('.')
 
666
        cb = self.Callback(u'commit 2', self)
 
667
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
 
668
                          allow_pointless=False)
 
669
        self.assertFalse(cb.called)
 
670
 
 
671
    def test_no_callback_netfailure(self):
 
672
        """Callback should not be invoked if connectivity fails"""
 
673
        tree = self.make_branch_and_tree('.')
 
674
        cb = self.Callback(u'commit 2', self)
 
675
        repository = tree.branch.repository
 
676
        # simulate network failure
 
677
        def raise_(self, arg, arg2):
 
678
            raise errors.NoSuchFile('foo')
 
679
        repository.add_inventory = raise_
 
680
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
 
681
        self.assertFalse(cb.called)