~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-07 16:05:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060607160527-2b3649154d0e2e84
more code cleanup

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005, 2006 by 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 import (
22
 
    errors,
23
 
    lockdir,
24
 
    osutils,
25
 
    tests,
26
 
    )
 
21
from bzrlib.tests import TestCaseWithTransport
27
22
from bzrlib.branch import Branch
28
23
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
 
24
from bzrlib.workingtree import WorkingTree
29
25
from bzrlib.commit import Commit, NullCommitReporter
30
26
from bzrlib.config import BranchConfig
31
27
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
32
28
                           LockContention)
33
 
from bzrlib.tests import TestCaseWithTransport
34
 
from bzrlib.workingtree import WorkingTree
35
29
 
36
30
 
37
31
# TODO: Test commit with some added, and added-but-missing files
223
217
        wt.move(['hello'], 'a')
224
218
        r2 = 'test@rev-2'
225
219
        wt.commit('two', rev_id=r2, allow_pointless=False)
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()
 
220
        self.check_inventory_shape(wt.read_working_inventory(),
 
221
                                   ['a', 'a/hello', 'b'])
232
222
 
233
223
        wt.move(['b'], 'a')
234
224
        r3 = 'test@rev-3'
235
225
        wt.commit('three', rev_id=r3, allow_pointless=False)
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()
 
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'])
244
230
 
245
231
        wt.move(['a/hello'], 'a/b')
246
232
        r4 = 'test@rev-4'
247
233
        wt.commit('four', rev_id=r4, allow_pointless=False)
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()
 
234
        self.check_inventory_shape(wt.read_working_inventory(),
 
235
                                   ['a', 'a/b/hello', 'a/b'])
254
236
 
255
237
        inv = b.repository.get_revision_inventory(r4)
256
238
        eq(inv['hello-id'].revision, r4)
257
239
        eq(inv['a-id'].revision, r1)
258
240
        eq(inv['b-id'].revision, r3)
259
 
 
 
241
        
260
242
    def test_removed_commit(self):
261
243
        """Commit with a removed file"""
262
244
        wt = self.make_branch_and_tree('.')
357
339
                                                      allow_pointless=True,
358
340
                                                      rev_id='B',
359
341
                                                      working_tree=wt)
360
 
            def sign(text):
361
 
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
362
 
            self.assertEqual(sign(Testament.from_revision(branch.repository,
363
 
                             'B').as_short_text()),
 
342
            self.assertEqual(Testament.from_revision(branch.repository,
 
343
                             'B').as_short_text(),
364
344
                             branch.repository.get_signature_text('B'))
365
345
        finally:
366
346
            bzrlib.gpg.GPGStrategy = oldstrategy
427
407
        bound = master.sprout('bound')
428
408
        wt = bound.open_workingtree()
429
409
        wt.branch.set_bound_location(os.path.realpath('master'))
430
 
 
431
 
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
432
410
        master_branch.lock_write()
433
411
        try:
434
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
435
412
            self.assertRaises(LockContention, wt.commit, 'silly')
436
413
        finally:
437
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
438
414
            master_branch.unlock()
439
415
 
440
416
    def test_commit_bound_merge(self):
460
436
        # do a merge into the bound branch from other, and then change the
461
437
        # content file locally to force a new revision (rather than using the
462
438
        # revision from other). This forces extra processing in commit.
463
 
        bound_tree.merge_from_branch(other_tree.branch)
 
439
        self.merge(other_tree.branch, bound_tree)
464
440
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
465
441
 
466
442
        # before #34959 was fixed, this failed with 'revision not present in
514
490
            other_tree.commit('modify all sample files and dirs.')
515
491
        finally:
516
492
            other_tree.unlock()
517
 
        this_tree.merge_from_branch(other_tree.branch)
 
493
        self.merge(other_tree.branch, this_tree)
518
494
        reporter = CapturingReporter()
519
495
        this_tree.commit('do the commit', reporter=reporter)
520
496
        self.assertEqual([
521
 
            ('change', 'unchanged', ''),
522
497
            ('change', 'unchanged', 'dirtoleave'),
523
498
            ('change', 'unchanged', 'filetoleave'),
524
499
            ('change', 'modified', 'filetomodify'),
532
507
            ('deleted', 'filetoremove'),
533
508
            ],
534
509
            reporter.calls)
535
 
 
536
 
    def test_commit_removals_respects_filespec(self):
537
 
        """Commit respects the specified_files for removals."""
538
 
        tree = self.make_branch_and_tree('.')
539
 
        self.build_tree(['a', 'b'])
540
 
        tree.add(['a', 'b'])
541
 
        tree.commit('added a, b')
542
 
        tree.remove(['a', 'b'])
543
 
        tree.commit('removed a', specific_files='a')
544
 
        basis = tree.basis_tree()
545
 
        tree.lock_read()
546
 
        try:
547
 
            self.assertIs(None, basis.path2id('a'))
548
 
            self.assertFalse(basis.path2id('b') is None)
549
 
        finally:
550
 
            tree.unlock()
551
 
 
552
 
    def test_commit_saves_1ms_timestamp(self):
553
 
        """Passing in a timestamp is saved with 1ms resolution"""
554
 
        tree = self.make_branch_and_tree('.')
555
 
        self.build_tree(['a'])
556
 
        tree.add('a')
557
 
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
558
 
                    rev_id='a1')
559
 
 
560
 
        rev = tree.branch.repository.get_revision('a1')
561
 
        self.assertEqual(1153248633.419, rev.timestamp)
562
 
 
563
 
    def test_commit_has_1ms_resolution(self):
564
 
        """Allowing commit to generate the timestamp also has 1ms resolution"""
565
 
        tree = self.make_branch_and_tree('.')
566
 
        self.build_tree(['a'])
567
 
        tree.add('a')
568
 
        tree.commit('added a', rev_id='a1')
569
 
 
570
 
        rev = tree.branch.repository.get_revision('a1')
571
 
        timestamp = rev.timestamp
572
 
        timestamp_1ms = round(timestamp, 3)
573
 
        self.assertEqual(timestamp_1ms, timestamp)
574
 
 
575
 
    def assertBasisTreeKind(self, kind, tree, file_id):
576
 
        basis = tree.basis_tree()
577
 
        basis.lock_read()
578
 
        try:
579
 
            self.assertEqual(kind, basis.kind(file_id))
580
 
        finally:
581
 
            basis.unlock()
582
 
 
583
 
    def test_commit_kind_changes(self):
584
 
        if not osutils.has_symlinks():
585
 
            raise tests.TestSkipped('Test requires symlink support')
586
 
        tree = self.make_branch_and_tree('.')
587
 
        os.symlink('target', 'name')
588
 
        tree.add('name', 'a-file-id')
589
 
        tree.commit('Added a symlink')
590
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
591
 
 
592
 
        os.unlink('name')
593
 
        self.build_tree(['name'])
594
 
        tree.commit('Changed symlink to file')
595
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
596
 
 
597
 
        os.unlink('name')
598
 
        os.symlink('target', 'name')
599
 
        tree.commit('file to symlink')
600
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
601
 
 
602
 
        os.unlink('name')
603
 
        os.mkdir('name')
604
 
        tree.commit('symlink to directory')
605
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
606
 
 
607
 
        os.rmdir('name')
608
 
        os.symlink('target', 'name')
609
 
        tree.commit('directory to symlink')
610
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
611
 
 
612
 
        # prepare for directory <-> file tests
613
 
        os.unlink('name')
614
 
        os.mkdir('name')
615
 
        tree.commit('symlink to directory')
616
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
617
 
 
618
 
        os.rmdir('name')
619
 
        self.build_tree(['name'])
620
 
        tree.commit('Changed directory to file')
621
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
622
 
 
623
 
        os.unlink('name')
624
 
        os.mkdir('name')
625
 
        tree.commit('file to directory')
626
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
627
 
 
628
 
    def test_commit_unversioned_specified(self):
629
 
        """Commit should raise if specified files isn't in basis or worktree"""
630
 
        tree = self.make_branch_and_tree('.')
631
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
632
 
                          'message', specific_files=['bogus'])
633
 
 
634
 
    class Callback(object):
635
 
        
636
 
        def __init__(self, message, testcase):
637
 
            self.called = False
638
 
            self.message = message
639
 
            self.testcase = testcase
640
 
 
641
 
        def __call__(self, commit_obj):
642
 
            self.called = True
643
 
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
644
 
            return self.message
645
 
 
646
 
    def test_commit_callback(self):
647
 
        """Commit should invoke a callback to get the message"""
648
 
 
649
 
        tree = self.make_branch_and_tree('.')
650
 
        try:
651
 
            tree.commit()
652
 
        except Exception, e:
653
 
            self.assertTrue(isinstance(e, BzrError))
654
 
            self.assertEqual('The message or message_callback keyword'
655
 
                             ' parameter is required for commit().', str(e))
656
 
        else:
657
 
            self.fail('exception not raised')
658
 
        cb = self.Callback(u'commit 1', self)
659
 
        tree.commit(message_callback=cb)
660
 
        self.assertTrue(cb.called)
661
 
        repository = tree.branch.repository
662
 
        message = repository.get_revision(tree.last_revision()).message
663
 
        self.assertEqual('commit 1', message)
664
 
 
665
 
    def test_no_callback_pointless(self):
666
 
        """Callback should not be invoked for pointless commit"""
667
 
        tree = self.make_branch_and_tree('.')
668
 
        cb = self.Callback(u'commit 2', self)
669
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
670
 
                          allow_pointless=False)
671
 
        self.assertFalse(cb.called)
672
 
 
673
 
    def test_no_callback_netfailure(self):
674
 
        """Callback should not be invoked if connectivity fails"""
675
 
        tree = self.make_branch_and_tree('.')
676
 
        cb = self.Callback(u'commit 2', self)
677
 
        repository = tree.branch.repository
678
 
        # simulate network failure
679
 
        def raise_(self, arg, arg2):
680
 
            raise errors.NoSuchFile('foo')
681
 
        repository.add_inventory = raise_
682
 
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
683
 
        self.assertFalse(cb.called)
684
 
 
685
 
    def test_selected_file_merge_commit(self):
686
 
        """Ensure the correct error is raised"""
687
 
        tree = self.make_branch_and_tree('foo')
688
 
        # pending merge would turn into a left parent
689
 
        tree.commit('commit 1')
690
 
        tree.add_parent_tree_id('example')
691
 
        self.build_tree(['foo/bar', 'foo/baz'])
692
 
        tree.add(['bar', 'baz'])
693
 
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
694
 
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
695
 
        self.assertEqual(['bar', 'baz'], err.files)
696
 
        self.assertEqual('Selected-file commit of merges is not supported'
697
 
                         ' yet: files bar, baz', str(err))