~bzr-pqm/bzr/bzr.dev

1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
1
# Copyright (C) 2005, 2006 Canonical Ltd
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
2
# Authors:  Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
from cStringIO import StringIO
19
import os
20
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
21
from bzrlib import (
22
    branch,
23
    bzrdir,
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
24
    conflicts,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
25
    errors,
2922.2.1 by John Arbash Meinel
Add failing tests exposing part of bug #114615
26
    osutils,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
27
    revision as _mod_revision,
28
    ui,
29
    uncommit,
30
    workingtree,
31
    )
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
32
from bzrlib.errors import (NotBranchError, NotVersionedError, 
33
                           UnsupportedOperation)
2949.5.1 by Alexander Belchenko
selftest: use SymlinkFeature instead of TestSkipped where appropriate
34
from bzrlib.osutils import pathjoin, getcwd
35
from bzrlib.tests import TestCase
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
36
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
37
from bzrlib.trace import mutter
38
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
39
                                WorkingTree)
40
41
42
class CapturingUIFactory(ui.UIFactory):
43
    """A UI Factory for testing - capture the updates made through it."""
44
45
    def __init__(self):
46
        super(CapturingUIFactory, self).__init__()
47
        self._calls = []
48
        self.depth = 0
49
50
    def clear(self):
51
        """See progress.ProgressBar.clear()."""
52
53
    def clear_term(self):
54
        """See progress.ProgressBar.clear_term()."""
55
56
    def finished(self):
57
        """See progress.ProgressBar.finished()."""
58
        self.depth -= 1
59
60
    def note(self, fmt_string, *args, **kwargs):
61
        """See progress.ProgressBar.note()."""
62
63
    def progress_bar(self):
64
        return self
65
    
66
    def nested_progress_bar(self):
67
        self.depth += 1
68
        return self
69
70
    def update(self, message, count=None, total=None):
71
        """See progress.ProgressBar.update()."""
72
        if self.depth == 1:
2531.1.3 by Ian Clatworthy
Fix whitespace and improve tests to cover actual progress messages
73
            self._calls.append(("update", count, total, message))
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
74
75
76
class TestCapturingUI(TestCase):
77
78
    def test_nested_ignore_depth_beyond_one(self):
79
        # we only want to capture the first level out progress, not
80
        # want sub-components might do. So we have nested bars ignored.
81
        factory = CapturingUIFactory()
82
        pb1 = factory.nested_progress_bar()
83
        pb1.update('foo', 0, 1)
84
        pb2 = factory.nested_progress_bar()
85
        pb2.update('foo', 0, 1)
86
        pb2.finished()
87
        pb1.finished()
2531.1.3 by Ian Clatworthy
Fix whitespace and improve tests to cover actual progress messages
88
        self.assertEqual([("update", 0, 1, 'foo')], factory._calls)
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
89
90
91
class TestCommit(TestCaseWithWorkingTree):
92
2922.2.1 by John Arbash Meinel
Add failing tests exposing part of bug #114615
93
    def test_autodelete_renamed(self):
94
        tree_a = self.make_branch_and_tree('a')
95
        self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2'])
96
        tree_a.add(['dir', 'dir/f1', 'dir/f2'], ['dir-id', 'f1-id', 'f2-id'])
97
        rev_id1 = tree_a.commit('init')
98
        # Start off by renaming entries,
99
        # but then actually auto delete the whole tree
100
        # https://bugs.launchpad.net/bzr/+bug/114615
101
        tree_a.rename_one('dir/f1', 'dir/a')
102
        tree_a.rename_one('dir/f2', 'dir/z')
103
        osutils.rmtree('a/dir')
104
        tree_a.commit('autoremoved')
105
106
        tree_a.lock_read()
107
        try:
108
            root_id = tree_a.inventory.root.file_id
109
            paths = [(path, ie.file_id)
110
                     for path, ie in tree_a.iter_entries_by_dir()]
111
        finally:
112
            tree_a.unlock()
113
        # The only paths left should be the root
114
        self.assertEqual([('', root_id)], paths)
115
2922.2.3 by John Arbash Meinel
Add a test which shows why the previous fix is broken.
116
    def test_no_autodelete_renamed_away(self):
117
        tree_a = self.make_branch_and_tree('a')
118
        self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2', 'a/dir2/'])
119
        tree_a.add(['dir', 'dir/f1', 'dir/f2', 'dir2'],
120
                   ['dir-id', 'f1-id', 'f2-id', 'dir2-id'])
121
        rev_id1 = tree_a.commit('init')
122
        # Rename one entry out of this directory
123
        tree_a.rename_one('dir/f1', 'dir2/a')
124
        osutils.rmtree('a/dir')
125
        tree_a.commit('autoremoved')
126
127
        tree_a.lock_read()
128
        try:
129
            root_id = tree_a.inventory.root.file_id
130
            paths = [(path, ie.file_id)
131
                     for path, ie in tree_a.iter_entries_by_dir()]
132
        finally:
133
            tree_a.unlock()
134
        # The only paths left should be the root
135
        self.assertEqual([('', root_id), ('dir2', 'dir2-id'),
136
                          ('dir2/a', 'f1-id'),
137
                         ], paths)
138
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
139
    def test_no_autodelete_alternate_renamed(self):
140
        # Test for bug #114615
141
        tree_a = self.make_branch_and_tree('A')
142
        self.build_tree(['A/a/', 'A/a/m', 'A/a/n'])
143
        tree_a.add(['a', 'a/m', 'a/n'], ['a-id', 'm-id', 'n-id'])
144
        tree_a.commit('init')
145
146
        tree_a.lock_read()
147
        try:
148
            root_id = tree_a.inventory.root.file_id
149
        finally:
150
            tree_a.unlock()
151
152
        tree_b = tree_a.bzrdir.sprout('B').open_workingtree()
153
        self.build_tree(['B/xyz/'])
154
        tree_b.add(['xyz'], ['xyz-id'])
155
        tree_b.rename_one('a/m', 'xyz/m')
156
        osutils.rmtree('B/a')
157
        tree_b.commit('delete in B')
158
159
        paths = [(path, ie.file_id)
160
                 for path, ie in tree_b.iter_entries_by_dir()]
161
        self.assertEqual([('', root_id),
162
                          ('xyz', 'xyz-id'),
163
                          ('xyz/m', 'm-id'),
164
                         ], paths)
165
166
        self.build_tree_contents([('A/a/n', 'new contents for n\n')])
167
        tree_a.commit('change n in A')
168
169
        # Merging from A should introduce conflicts because 'n' was modified
170
        # and removed, so 'a' needs to be restored.
171
        num_conflicts = tree_b.merge_from_branch(tree_a.branch)
172
        self.assertEqual(3, num_conflicts)
173
        paths = [(path, ie.file_id)
174
                 for path, ie in tree_b.iter_entries_by_dir()]
175
        self.assertEqual([('', root_id),
176
                          ('a', 'a-id'),
177
                          ('xyz', 'xyz-id'),
178
                          ('a/n.OTHER', 'n-id'),
179
                          ('xyz/m', 'm-id'),
180
                         ], paths)
181
        osutils.rmtree('B/a')
182
        try:
183
            # bzr resolve --all
184
            tree_b.set_conflicts(conflicts.ConflictList())
185
        except errors.UnsupportedOperation:
186
            # On WT2, set_conflicts is unsupported, but the rmtree has the same
187
            # effect.
188
            pass
189
        tree_b.commit('autoremove a, without touching xyz/m')
190
        paths = [(path, ie.file_id)
191
                 for path, ie in tree_b.iter_entries_by_dir()]
192
        self.assertEqual([('', root_id),
193
                          ('xyz', 'xyz-id'),
194
                          ('xyz/m', 'm-id'),
195
                         ], paths)
196
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
197
    def test_commit_sets_last_revision(self):
198
        tree = self.make_branch_and_tree('tree')
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
199
        committed_id = tree.commit('foo', rev_id='foo')
1908.7.6 by Robert Collins
Deprecate WorkingTree.last_revision.
200
        self.assertEqual(['foo'], tree.get_parent_ids())
1773.1.1 by Robert Collins
Teach WorkingTree.commit to return the committed revision id.
201
        # the commit should have returned the same id we asked for.
202
        self.assertEqual('foo', committed_id)
203
204
    def test_commit_returns_revision_id(self):
205
        tree = self.make_branch_and_tree('.')
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
206
        committed_id = tree.commit('message')
1773.1.1 by Robert Collins
Teach WorkingTree.commit to return the committed revision id.
207
        self.assertTrue(tree.branch.repository.has_revision(committed_id))
208
        self.assertNotEqual(None, committed_id)
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
209
210
    def test_commit_local_unbound(self):
211
        # using the library api to do a local commit on unbound branches is 
212
        # also an error
213
        tree = self.make_branch_and_tree('tree')
214
        self.assertRaises(errors.LocalRequiresBoundBranch,
215
                          tree.commit,
216
                          'foo',
217
                          local=True)
2374.2.1 by John Arbash Meinel
(broken) merge a test case showing that commiting a merge of a kind change fails.
218
219
    def test_commit_merged_kind_change(self):
220
        """Test merging a kind change.
221
222
        Test making a kind change in a working tree, and then merging that
223
        from another. When committed it should commit the new kind.
224
        """
225
        wt = self.make_branch_and_tree('.')
226
        self.build_tree(['a'])
227
        wt.add(['a'])
228
        wt.commit('commit one')
229
        wt2 = wt.bzrdir.sprout('to').open_workingtree()
230
        os.remove('a')
231
        os.mkdir('a')
232
        wt.commit('changed kind')
233
        wt2.merge_from_branch(wt.branch)
234
        wt2.commit('merged kind change')
235
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
236
    def test_local_commit_ignores_master(self):
237
        # a --local commit does not require access to the master branch
238
        # at all, or even for it to exist.
239
        # we test this by setting up a bound branch and then corrupting
240
        # the master.
241
        master = self.make_branch('master')
242
        tree = self.make_branch_and_tree('tree')
243
        try:
244
            tree.branch.bind(master)
245
        except errors.UpgradeRequired:
246
            # older format.
247
            return
1955.3.14 by John Arbash Meinel
Correctly fix the workingtree put() test fixes
248
        master.bzrdir.transport.put_bytes('branch-format', 'garbage')
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
249
        del master
250
        # check its corrupted.
251
        self.assertRaises(errors.UnknownFormatError,
252
                          bzrdir.BzrDir.open,
253
                          'master')
254
        tree.commit('foo', rev_id='foo', local=True)
255
 
256
    def test_local_commit_does_not_push_to_master(self):
257
        # a --local commit does not require access to the master branch
258
        # at all, or even for it to exist.
259
        # we test that even when its available it does not push to it.
260
        master = self.make_branch('master')
261
        tree = self.make_branch_and_tree('tree')
262
        try:
263
            tree.branch.bind(master)
264
        except errors.UpgradeRequired:
265
            # older format.
266
            return
267
        tree.commit('foo', rev_id='foo', local=True)
268
        self.failIf(master.repository.has_revision('foo'))
2598.5.7 by Aaron Bentley
Updates from review
269
        self.assertEqual(_mod_revision.NULL_REVISION,
270
                         (_mod_revision.ensure_null(master.last_revision())))
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
271
272
    def test_record_initial_ghost(self):
273
        """The working tree needs to record ghosts during commit."""
274
        wt = self.make_branch_and_tree('.')
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
275
        wt.set_parent_ids(['non:existent@rev--ision--0--2'],
276
            allow_leftmost_as_ghost=True)
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
277
        rev_id = wt.commit('commit against a ghost first parent.')
278
        rev = wt.branch.repository.get_revision(rev_id)
279
        self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
280
        # parent_sha1s is not populated now, WTF. rbc 20051003
281
        self.assertEqual(len(rev.parent_sha1s), 0)
282
283
    def test_record_two_ghosts(self):
284
        """The working tree should preserve all the parents during commit."""
285
        wt = self.make_branch_and_tree('.')
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
286
        wt.set_parent_ids([
287
                'foo@azkhazan-123123-abcabc',
288
                'wibble@fofof--20050401--1928390812',
289
            ],
290
            allow_leftmost_as_ghost=True)
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
291
        rev_id = wt.commit("commit from ghost base with one merge")
292
        # the revision should have been committed with two parents
293
        rev = wt.branch.repository.get_revision(rev_id)
294
        self.assertEqual(['foo@azkhazan-123123-abcabc',
295
            'wibble@fofof--20050401--1928390812'],
296
            rev.parent_ids)
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
297
1988.3.1 by Robert Collins
Add test case to ensure that the working tree inventory and disk state is correctly update when commit is removing directory entries.
298
    def test_commit_deleted_subtree_and_files_updates_workingtree(self):
299
        """The working trees inventory may be adjusted by commit."""
300
        wt = self.make_branch_and_tree('.')
301
        wt.lock_write()
302
        self.build_tree(['a', 'b/', 'b/c', 'd'])
303
        wt.add(['a', 'b', 'b/c', 'd'], ['a-id', 'b-id', 'c-id', 'd-id'])
304
        this_dir = self.get_transport()
305
        this_dir.delete_tree('b')
306
        this_dir.delete('d')
307
        # now we have a tree with a through d in the inventory, but only
308
        # a present on disk. After commit b-id, c-id and d-id should be
309
        # missing from the inventory, within the same tree transaction.
310
        wt.commit('commit stuff')
311
        self.assertTrue(wt.has_id('a-id'))
312
        self.assertFalse(wt.has_or_had_id('b-id'))
313
        self.assertFalse(wt.has_or_had_id('c-id'))
314
        self.assertFalse(wt.has_or_had_id('d-id'))
315
        self.assertTrue(wt.has_filename('a'))
316
        self.assertFalse(wt.has_filename('b'))
317
        self.assertFalse(wt.has_filename('b/c'))
318
        self.assertFalse(wt.has_filename('d'))
319
        wt.unlock()
320
        # the changes should have persisted to disk - reopen the workingtree
321
        # to be sure.
322
        wt = wt.bzrdir.open_workingtree()
323
        wt.lock_read()
324
        self.assertTrue(wt.has_id('a-id'))
325
        self.assertFalse(wt.has_or_had_id('b-id'))
326
        self.assertFalse(wt.has_or_had_id('c-id'))
327
        self.assertFalse(wt.has_or_had_id('d-id'))
328
        self.assertTrue(wt.has_filename('a'))
329
        self.assertFalse(wt.has_filename('b'))
330
        self.assertFalse(wt.has_filename('b/c'))
331
        self.assertFalse(wt.has_filename('d'))
332
        wt.unlock()
1731.2.4 by Aaron Bentley
Ensure subsume works with Knit2 repos
333
2363.2.2 by John Arbash Meinel
Simplify the test even further....
334
    def test_commit_deleted_subtree_with_removed(self):
2363.2.1 by John Arbash Meinel
(broken) Add a simplified test which exposes the bug.
335
        wt = self.make_branch_and_tree('.')
336
        self.build_tree(['a', 'b/', 'b/c', 'd'])
337
        wt.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
338
        wt.commit('first')
2363.2.2 by John Arbash Meinel
Simplify the test even further....
339
        wt.remove('b/c')
2363.2.1 by John Arbash Meinel
(broken) Add a simplified test which exposes the bug.
340
        this_dir = self.get_transport()
341
        this_dir.delete_tree('b')
342
        wt.lock_write()
343
        wt.commit('commit deleted rename')
344
        self.assertTrue(wt.has_id('a-id'))
345
        self.assertFalse(wt.has_or_had_id('b-id'))
346
        self.assertFalse(wt.has_or_had_id('c-id'))
347
        self.assertTrue(wt.has_filename('a'))
348
        self.assertFalse(wt.has_filename('b'))
349
        self.assertFalse(wt.has_filename('b/c'))
350
        wt.unlock()
351
1731.2.4 by Aaron Bentley
Ensure subsume works with Knit2 repos
352
    def test_commit_move_new(self):
353
        wt = self.make_branch_and_tree('first')
354
        wt.commit('first')
355
        wt2 = wt.bzrdir.sprout('second').open_workingtree()
356
        self.build_tree(['second/name1'])
357
        wt2.add('name1', 'name1-id')
358
        wt2.commit('second')
359
        wt.merge_from_branch(wt2.branch)
360
        wt.rename_one('name1', 'name2')
361
        wt.commit('third')
362
        wt.path2id('name1-id')
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
363
364
    def test_nested_commit(self):
365
        """Commit in multiply-nested trees"""
366
        tree = self.make_branch_and_tree('.')
367
        if not tree.supports_tree_reference():
368
            # inapplicable test.
369
            return
370
        subtree = self.make_branch_and_tree('subtree')
371
        subsubtree = self.make_branch_and_tree('subtree/subtree')
372
        subtree.add(['subtree'])
373
        tree.add(['subtree'])
374
        # use allow_pointless=False to ensure that the deepest tree, which
375
        # has no commits made to it, does not get a pointless commit.
376
        rev_id = tree.commit('added reference', allow_pointless=False)
377
        tree.lock_read()
378
        self.addCleanup(tree.unlock)
379
        # the deepest subtree has not changed, so no commit should take place.
2598.5.10 by Aaron Bentley
Return NULL_REVISION instead of None for the null revision
380
        self.assertEqual('null:', subsubtree.last_revision())
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
381
        # the intermediate tree should have committed a pointer to the current
382
        # subtree revision.
383
        sub_basis = subtree.basis_tree()
384
        sub_basis.lock_read()
385
        self.addCleanup(sub_basis.unlock)
386
        self.assertEqual(subsubtree.last_revision(),
2255.2.227 by Robert Collins
Make all test_commit tests pass.
387
            sub_basis.get_reference_revision(sub_basis.path2id('subtree')))
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
388
        # the intermediate tree has changed, so should have had a commit
389
        # take place.
390
        self.assertNotEqual(None, subtree.last_revision())
391
        # the outer tree should have committed a pointer to the current
392
        # subtree revision.
393
        basis = tree.basis_tree()
394
        basis.lock_read()
395
        self.addCleanup(basis.unlock)
396
        self.assertEqual(subtree.last_revision(),
2255.2.226 by Robert Collins
Get merge_nested finally working: change nested tree iterators to take file_ids, and ensure the right branch is connected to in the merge logic. May not be suitable for shared repositories yet.
397
            basis.get_reference_revision(basis.path2id('subtree')))
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
398
        # the outer tree must have have changed too.
399
        self.assertNotEqual(None, rev_id)
1988.3.1 by Robert Collins
Add test case to ensure that the working tree inventory and disk state is correctly update when commit is removing directory entries.
400
        
2255.2.220 by Robert Collins
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.
401
    def test_nested_commit_second_commit_detects_changes(self):
402
        """Commit with a nested tree picks up the correct child revid."""
403
        tree = self.make_branch_and_tree('.')
404
        if not tree.supports_tree_reference():
405
            # inapplicable test.
406
            return
407
        subtree = self.make_branch_and_tree('subtree')
408
        tree.add(['subtree'])
409
        self.build_tree(['subtree/file'])
410
        subtree.add(['file'], ['file-id'])
411
        rev_id = tree.commit('added reference', allow_pointless=False)
412
        child_revid = subtree.last_revision()
413
        # now change the child tree
414
        self.build_tree_contents([('subtree/file', 'new-content')])
415
        # and commit in the parent should commit the child and grab its revid,
416
        # we test with allow_pointless=False here so that we are simulating
417
        # what users will see.
418
        rev_id2 = tree.commit('changed subtree only', allow_pointless=False)
419
        # the child tree has changed, so should have had a commit
420
        # take place.
421
        self.assertNotEqual(None, subtree.last_revision())
422
        self.assertNotEqual(child_revid, subtree.last_revision())
423
        # the outer tree should have committed a pointer to the current
424
        # subtree revision.
425
        basis = tree.basis_tree()
426
        basis.lock_read()
427
        self.addCleanup(basis.unlock)
428
        self.assertEqual(subtree.last_revision(),
2255.2.226 by Robert Collins
Get merge_nested finally working: change nested tree iterators to take file_ids, and ensure the right branch is connected to in the merge logic. May not be suitable for shared repositories yet.
429
            basis.get_reference_revision(basis.path2id('subtree')))
2255.2.220 by Robert Collins
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.
430
        self.assertNotEqual(rev_id, rev_id2)
431
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
432
    def test_nested_pointless_commits_are_pointless(self):
433
        tree = self.make_branch_and_tree('.')
434
        if not tree.supports_tree_reference():
435
            # inapplicable test.
436
            return
437
        subtree = self.make_branch_and_tree('subtree')
438
        tree.add(['subtree'])
439
        # record the reference.
440
        rev_id = tree.commit('added reference')
441
        child_revid = subtree.last_revision()
442
        # now do a no-op commit with allow_pointless=False
443
        self.assertRaises(errors.PointlessCommit, tree.commit, '',
444
            allow_pointless=False)
445
        self.assertEqual(child_revid, subtree.last_revision())
446
        self.assertEqual(rev_id, tree.last_revision())
447
1988.3.1 by Robert Collins
Add test case to ensure that the working tree inventory and disk state is correctly update when commit is removing directory entries.
448
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
449
class TestCommitProgress(TestCaseWithWorkingTree):
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
450
    
451
    def restoreDefaults(self):
452
        ui.ui_factory = self.old_ui_factory
453
454
    def test_commit_progress_steps(self):
455
        # during commit we one progress update for every entry in the 
456
        # inventory, and then one for the inventory, and one for the
457
        # inventory, and one for the revision insertions.
458
        # first we need a test commit to do. Lets setup a branch with 
459
        # 3 files, and alter one in a selected-file commit. This exercises
460
        # a number of cases quickly. We should also test things like 
461
        # selective commits which excludes newly added files.
462
        tree = self.make_branch_and_tree('.')
463
        self.build_tree(['a', 'b', 'c'])
464
        tree.add(['a', 'b', 'c'])
465
        tree.commit('first post')
466
        f = file('b', 'wt')
467
        f.write('new content')
468
        f.close()
469
        # set a progress bar that captures the calls so we can see what is 
470
        # emitted
471
        self.old_ui_factory = ui.ui_factory
472
        self.addCleanup(self.restoreDefaults)
473
        factory = CapturingUIFactory()
474
        ui.ui_factory = factory
475
        # TODO RBC 20060421 it would be nice to merge the reporter output
476
        # into the factory for this test - just make the test ui factory
477
        # pun as a reporter. Then we can check the ordering is right.
478
        tree.commit('second post', specific_files=['b'])
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
479
        # 5 steps, the first of which is reported 2 times, once per dir
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
480
        self.assertEqual(
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
481
            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
482
             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
483
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
484
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
485
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
486
             ('update', 5, 5, 'Running post_commit hooks - Stage')],
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
487
            factory._calls
488
           )
2553.1.2 by Robert Collins
Show hook names during commit.
489
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
490
    def test_commit_progress_shows_post_hook_names(self):
2553.1.2 by Robert Collins
Show hook names during commit.
491
        tree = self.make_branch_and_tree('.')
492
        # set a progress bar that captures the calls so we can see what is 
493
        # emitted
494
        self.old_ui_factory = ui.ui_factory
495
        self.addCleanup(self.restoreDefaults)
496
        factory = CapturingUIFactory()
497
        ui.ui_factory = factory
498
        def a_hook(_, _2, _3, _4, _5, _6):
499
            pass
500
        branch.Branch.hooks.install_hook('post_commit', a_hook)
501
        branch.Branch.hooks.name_hook(a_hook, 'hook name')
502
        tree.commit('first post')
503
        self.assertEqual(
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
504
            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
505
             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
506
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
507
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
508
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
509
             ('update', 5, 5, 'Running post_commit hooks - Stage'),
510
             ('update', 5, 5, 'Running post_commit hooks [hook name] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
511
             ],
512
            factory._calls
513
           )
514
515
    def test_commit_progress_shows_pre_hook_names(self):
516
        tree = self.make_branch_and_tree('.')
517
        # set a progress bar that captures the calls so we can see what is 
518
        # emitted
519
        self.old_ui_factory = ui.ui_factory
520
        self.addCleanup(self.restoreDefaults)
521
        factory = CapturingUIFactory()
522
        ui.ui_factory = factory
2659.3.3 by NamNguyen
Changed ``pre_commit`` hook signature.
523
        def a_hook(_, _2, _3, _4, _5, _6, _7, _8):
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
524
            pass
525
        branch.Branch.hooks.install_hook('pre_commit', a_hook)
526
        branch.Branch.hooks.name_hook(a_hook, 'hook name')
527
        tree.commit('first post')
528
        self.assertEqual(
529
            [('update', 1, 5, 'Collecting changes [Directory 0] - Stage'),
530
             ('update', 1, 5, 'Collecting changes [Directory 1] - Stage'),
531
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
532
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
533
             ('update', 3, 5, 'Running pre_commit hooks [hook name] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
534
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
535
             ('update', 5, 5, 'Running post_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
536
             ],
537
            factory._calls
538
           )