~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Martin Pool
  • Date: 2006-03-22 18:03:25 UTC
  • mto: This revision was merged to the branch mainline in revision 1626.
  • Revision ID: mbp@sourcefrog.net-20060322180325-eff9250dcb85c390
Add missing selftest modules to setup.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
import os
 
19
 
 
20
import bzrlib
 
21
from bzrlib.tests import TestCaseWithTransport
 
22
from bzrlib.branch import Branch
 
23
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
 
24
from bzrlib.workingtree import WorkingTree
 
25
from bzrlib.commit import Commit
 
26
from bzrlib.config import BranchConfig
 
27
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
28
                           LockContention)
 
29
 
 
30
 
 
31
# TODO: Test commit with some added, and added-but-missing files
 
32
 
 
33
class MustSignConfig(BranchConfig):
 
34
 
 
35
    def signature_needed(self):
 
36
        return True
 
37
 
 
38
    def gpg_signing_command(self):
 
39
        return ['cat', '-']
 
40
 
 
41
 
 
42
class BranchWithHooks(BranchConfig):
 
43
 
 
44
    def post_commit(self):
 
45
        return "bzrlib.ahook bzrlib.ahook"
 
46
 
 
47
 
 
48
class TestCommit(TestCaseWithTransport):
 
49
 
 
50
    def test_simple_commit(self):
 
51
        """Commit and check two versions of a single file."""
 
52
        wt = self.make_branch_and_tree('.')
 
53
        b = wt.branch
 
54
        file('hello', 'w').write('hello world')
 
55
        wt.add('hello')
 
56
        wt.commit(message='add hello')
 
57
        file_id = wt.path2id('hello')
 
58
 
 
59
        file('hello', 'w').write('version 2')
 
60
        wt.commit(message='commit 2')
 
61
 
 
62
        eq = self.assertEquals
 
63
        eq(b.revno(), 2)
 
64
        rh = b.revision_history()
 
65
        rev = b.repository.get_revision(rh[0])
 
66
        eq(rev.message, 'add hello')
 
67
 
 
68
        tree1 = b.repository.revision_tree(rh[0])
 
69
        text = tree1.get_file_text(file_id)
 
70
        eq(text, 'hello world')
 
71
 
 
72
        tree2 = b.repository.revision_tree(rh[1])
 
73
        eq(tree2.get_file_text(file_id), 'version 2')
 
74
 
 
75
    def test_delete_commit(self):
 
76
        """Test a commit with a deleted file"""
 
77
        wt = self.make_branch_and_tree('.')
 
78
        b = wt.branch
 
79
        file('hello', 'w').write('hello world')
 
80
        wt.add(['hello'], ['hello-id'])
 
81
        wt.commit(message='add hello')
 
82
 
 
83
        os.remove('hello')
 
84
        wt.commit('removed hello', rev_id='rev2')
 
85
 
 
86
        tree = b.repository.revision_tree('rev2')
 
87
        self.assertFalse(tree.has_id('hello-id'))
 
88
 
 
89
    def test_pointless_commit(self):
 
90
        """Commit refuses unless there are changes or it's forced."""
 
91
        wt = self.make_branch_and_tree('.')
 
92
        b = wt.branch
 
93
        file('hello', 'w').write('hello')
 
94
        wt.add(['hello'])
 
95
        wt.commit(message='add hello')
 
96
        self.assertEquals(b.revno(), 1)
 
97
        self.assertRaises(PointlessCommit,
 
98
                          wt.commit,
 
99
                          message='fails',
 
100
                          allow_pointless=False)
 
101
        self.assertEquals(b.revno(), 1)
 
102
        
 
103
    def test_commit_empty(self):
 
104
        """Commiting an empty tree works."""
 
105
        wt = self.make_branch_and_tree('.')
 
106
        b = wt.branch
 
107
        wt.commit(message='empty tree', allow_pointless=True)
 
108
        self.assertRaises(PointlessCommit,
 
109
                          wt.commit,
 
110
                          message='empty tree',
 
111
                          allow_pointless=False)
 
112
        wt.commit(message='empty tree', allow_pointless=True)
 
113
        self.assertEquals(b.revno(), 2)
 
114
 
 
115
    def test_selective_delete(self):
 
116
        """Selective commit in tree with deletions"""
 
117
        wt = self.make_branch_and_tree('.')
 
118
        b = wt.branch
 
119
        file('hello', 'w').write('hello')
 
120
        file('buongia', 'w').write('buongia')
 
121
        wt.add(['hello', 'buongia'],
 
122
              ['hello-id', 'buongia-id'])
 
123
        wt.commit(message='add files',
 
124
                 rev_id='test@rev-1')
 
125
        
 
126
        os.remove('hello')
 
127
        file('buongia', 'w').write('new text')
 
128
        wt.commit(message='update text',
 
129
                 specific_files=['buongia'],
 
130
                 allow_pointless=False,
 
131
                 rev_id='test@rev-2')
 
132
 
 
133
        wt.commit(message='remove hello',
 
134
                 specific_files=['hello'],
 
135
                 allow_pointless=False,
 
136
                 rev_id='test@rev-3')
 
137
 
 
138
        eq = self.assertEquals
 
139
        eq(b.revno(), 3)
 
140
 
 
141
        tree2 = b.repository.revision_tree('test@rev-2')
 
142
        self.assertTrue(tree2.has_filename('hello'))
 
143
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
 
144
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
 
145
        
 
146
        tree3 = b.repository.revision_tree('test@rev-3')
 
147
        self.assertFalse(tree3.has_filename('hello'))
 
148
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
 
149
 
 
150
    def test_commit_rename(self):
 
151
        """Test commit of a revision where a file is renamed."""
 
152
        tree = self.make_branch_and_tree('.')
 
153
        b = tree.branch
 
154
        self.build_tree(['hello'], line_endings='binary')
 
155
        tree.add(['hello'], ['hello-id'])
 
156
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
157
 
 
158
        tree.rename_one('hello', 'fruity')
 
159
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
160
 
 
161
        eq = self.assertEquals
 
162
        tree1 = b.repository.revision_tree('test@rev-1')
 
163
        eq(tree1.id2path('hello-id'), 'hello')
 
164
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
 
165
        self.assertFalse(tree1.has_filename('fruity'))
 
166
        self.check_inventory_shape(tree1.inventory, ['hello'])
 
167
        ie = tree1.inventory['hello-id']
 
168
        eq(ie.revision, 'test@rev-1')
 
169
 
 
170
        tree2 = b.repository.revision_tree('test@rev-2')
 
171
        eq(tree2.id2path('hello-id'), 'fruity')
 
172
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
 
173
        self.check_inventory_shape(tree2.inventory, ['fruity'])
 
174
        ie = tree2.inventory['hello-id']
 
175
        eq(ie.revision, 'test@rev-2')
 
176
 
 
177
    def test_reused_rev_id(self):
 
178
        """Test that a revision id cannot be reused in a branch"""
 
179
        wt = self.make_branch_and_tree('.')
 
180
        b = wt.branch
 
181
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
 
182
        self.assertRaises(Exception,
 
183
                          wt.commit,
 
184
                          message='reused id',
 
185
                          rev_id='test@rev-1',
 
186
                          allow_pointless=True)
 
187
 
 
188
    def test_commit_move(self):
 
189
        """Test commit of revisions with moved files and directories"""
 
190
        eq = self.assertEquals
 
191
        wt = self.make_branch_and_tree('.')
 
192
        b = wt.branch
 
193
        r1 = 'test@rev-1'
 
194
        self.build_tree(['hello', 'a/', 'b/'])
 
195
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
 
196
        wt.commit('initial', rev_id=r1, allow_pointless=False)
 
197
        wt.move(['hello'], 'a')
 
198
        r2 = 'test@rev-2'
 
199
        wt.commit('two', rev_id=r2, allow_pointless=False)
 
200
        self.check_inventory_shape(wt.read_working_inventory(),
 
201
                                   ['a', 'a/hello', 'b'])
 
202
 
 
203
        wt.move(['b'], 'a')
 
204
        r3 = 'test@rev-3'
 
205
        wt.commit('three', rev_id=r3, allow_pointless=False)
 
206
        self.check_inventory_shape(wt.read_working_inventory(),
 
207
                                   ['a', 'a/hello', 'a/b'])
 
208
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
 
209
                                   ['a', 'a/hello', 'a/b'])
 
210
 
 
211
        wt.move(['a/hello'], 'a/b')
 
212
        r4 = 'test@rev-4'
 
213
        wt.commit('four', rev_id=r4, allow_pointless=False)
 
214
        self.check_inventory_shape(wt.read_working_inventory(),
 
215
                                   ['a', 'a/b/hello', 'a/b'])
 
216
 
 
217
        inv = b.repository.get_revision_inventory(r4)
 
218
        eq(inv['hello-id'].revision, r4)
 
219
        eq(inv['a-id'].revision, r1)
 
220
        eq(inv['b-id'].revision, r3)
 
221
        
 
222
    def test_removed_commit(self):
 
223
        """Commit with a removed file"""
 
224
        wt = self.make_branch_and_tree('.')
 
225
        b = wt.branch
 
226
        file('hello', 'w').write('hello world')
 
227
        wt.add(['hello'], ['hello-id'])
 
228
        wt.commit(message='add hello')
 
229
        wt.remove('hello')
 
230
        wt.commit('removed hello', rev_id='rev2')
 
231
 
 
232
        tree = b.repository.revision_tree('rev2')
 
233
        self.assertFalse(tree.has_id('hello-id'))
 
234
 
 
235
    def test_committed_ancestry(self):
 
236
        """Test commit appends revisions to ancestry."""
 
237
        wt = self.make_branch_and_tree('.')
 
238
        b = wt.branch
 
239
        rev_ids = []
 
240
        for i in range(4):
 
241
            file('hello', 'w').write((str(i) * 4) + '\n')
 
242
            if i == 0:
 
243
                wt.add(['hello'], ['hello-id'])
 
244
            rev_id = 'test@rev-%d' % (i+1)
 
245
            rev_ids.append(rev_id)
 
246
            wt.commit(message='rev %d' % (i+1),
 
247
                     rev_id=rev_id)
 
248
        eq = self.assertEquals
 
249
        eq(b.revision_history(), rev_ids)
 
250
        for i in range(4):
 
251
            anc = b.repository.get_ancestry(rev_ids[i])
 
252
            eq(anc, [None] + rev_ids[:i+1])
 
253
 
 
254
    def test_commit_new_subdir_child_selective(self):
 
255
        wt = self.make_branch_and_tree('.')
 
256
        b = wt.branch
 
257
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
 
258
        wt.add(['dir', 'dir/file1', 'dir/file2'],
 
259
              ['dirid', 'file1id', 'file2id'])
 
260
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
 
261
        inv = b.repository.get_inventory('1')
 
262
        self.assertEqual('1', inv['dirid'].revision)
 
263
        self.assertEqual('1', inv['file1id'].revision)
 
264
        # FIXME: This should raise a KeyError I think, rbc20051006
 
265
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
 
266
 
 
267
    def test_strict_commit(self):
 
268
        """Try and commit with unknown files and strict = True, should fail."""
 
269
        from bzrlib.errors import StrictCommitFailed
 
270
        wt = self.make_branch_and_tree('.')
 
271
        b = wt.branch
 
272
        file('hello', 'w').write('hello world')
 
273
        wt.add('hello')
 
274
        file('goodbye', 'w').write('goodbye cruel world!')
 
275
        self.assertRaises(StrictCommitFailed, wt.commit,
 
276
            message='add hello but not goodbye', strict=True)
 
277
 
 
278
    def test_strict_commit_without_unknowns(self):
 
279
        """Try and commit with no unknown files and strict = True,
 
280
        should work."""
 
281
        from bzrlib.errors import StrictCommitFailed
 
282
        wt = self.make_branch_and_tree('.')
 
283
        b = wt.branch
 
284
        file('hello', 'w').write('hello world')
 
285
        wt.add('hello')
 
286
        wt.commit(message='add hello', strict=True)
 
287
 
 
288
    def test_nonstrict_commit(self):
 
289
        """Try and commit with unknown files and strict = False, should work."""
 
290
        wt = self.make_branch_and_tree('.')
 
291
        b = wt.branch
 
292
        file('hello', 'w').write('hello world')
 
293
        wt.add('hello')
 
294
        file('goodbye', 'w').write('goodbye cruel world!')
 
295
        wt.commit(message='add hello but not goodbye', strict=False)
 
296
 
 
297
    def test_nonstrict_commit_without_unknowns(self):
 
298
        """Try and commit with no unknown files and strict = False,
 
299
        should work."""
 
300
        wt = self.make_branch_and_tree('.')
 
301
        b = wt.branch
 
302
        file('hello', 'w').write('hello world')
 
303
        wt.add('hello')
 
304
        wt.commit(message='add hello', strict=False)
 
305
 
 
306
    def test_signed_commit(self):
 
307
        import bzrlib.gpg
 
308
        import bzrlib.commit as commit
 
309
        oldstrategy = bzrlib.gpg.GPGStrategy
 
310
        wt = self.make_branch_and_tree('.')
 
311
        branch = wt.branch
 
312
        wt.commit("base", allow_pointless=True, rev_id='A')
 
313
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
314
        try:
 
315
            from bzrlib.testament import Testament
 
316
            # monkey patch gpg signing mechanism
 
317
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
 
318
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
 
319
                                                      allow_pointless=True,
 
320
                                                      rev_id='B',
 
321
                                                      working_tree=wt)
 
322
            self.assertEqual(Testament.from_revision(branch.repository,
 
323
                             'B').as_short_text(),
 
324
                             branch.repository.get_signature_text('B'))
 
325
        finally:
 
326
            bzrlib.gpg.GPGStrategy = oldstrategy
 
327
 
 
328
    def test_commit_failed_signature(self):
 
329
        import bzrlib.gpg
 
330
        import bzrlib.commit as commit
 
331
        oldstrategy = bzrlib.gpg.GPGStrategy
 
332
        wt = self.make_branch_and_tree('.')
 
333
        branch = wt.branch
 
334
        wt.commit("base", allow_pointless=True, rev_id='A')
 
335
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
336
        try:
 
337
            from bzrlib.testament import Testament
 
338
            # monkey patch gpg signing mechanism
 
339
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
 
340
            config = MustSignConfig(branch)
 
341
            self.assertRaises(SigningFailed,
 
342
                              commit.Commit(config=config).commit,
 
343
                              message="base",
 
344
                              allow_pointless=True,
 
345
                              rev_id='B',
 
346
                              working_tree=wt)
 
347
            branch = Branch.open(self.get_url('.'))
 
348
            self.assertEqual(branch.revision_history(), ['A'])
 
349
            self.failIf(branch.repository.has_revision('B'))
 
350
        finally:
 
351
            bzrlib.gpg.GPGStrategy = oldstrategy
 
352
 
 
353
    def test_commit_invokes_hooks(self):
 
354
        import bzrlib.commit as commit
 
355
        wt = self.make_branch_and_tree('.')
 
356
        branch = wt.branch
 
357
        calls = []
 
358
        def called(branch, rev_id):
 
359
            calls.append('called')
 
360
        bzrlib.ahook = called
 
361
        try:
 
362
            config = BranchWithHooks(branch)
 
363
            commit.Commit(config=config).commit(
 
364
                            message = "base",
 
365
                            allow_pointless=True,
 
366
                            rev_id='A', working_tree = wt)
 
367
            self.assertEqual(['called', 'called'], calls)
 
368
        finally:
 
369
            del bzrlib.ahook
 
370
 
 
371
    def test_commit_object_doesnt_set_nick(self):
 
372
        # using the Commit object directly does not set the branch nick.
 
373
        wt = self.make_branch_and_tree('.')
 
374
        c = Commit()
 
375
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
 
376
        self.assertEquals(wt.branch.revno(), 1)
 
377
        self.assertEqual({},
 
378
                         wt.branch.repository.get_revision(
 
379
                            wt.branch.last_revision()).properties)
 
380
 
 
381
    def test_safe_master_lock(self):
 
382
        os.mkdir('master')
 
383
        master = BzrDirMetaFormat1().initialize('master')
 
384
        master.create_repository()
 
385
        master_branch = master.create_branch()
 
386
        master.create_workingtree()
 
387
        bound = master.sprout('bound')
 
388
        wt = bound.open_workingtree()
 
389
        wt.branch.set_bound_location(os.path.realpath('master'))
 
390
        master_branch.lock_write()
 
391
        self.assertRaises(LockContention, wt.commit, 'silly')