~bzr-pqm/bzr/bzr.dev

1666.1.19 by Robert Collins
Introduce a progress bar during commit.
1
# (C) 2005,2006 Canonical Ltd
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
21
import bzrlib
22
import bzrlib.branch
23
from bzrlib.branch import Branch
24
import bzrlib.bzrdir as bzrdir
25
from bzrlib.bzrdir import BzrDir
26
import bzrlib.errors as errors
27
from bzrlib.errors import (NotBranchError, NotVersionedError, 
28
                           UnsupportedOperation)
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.tests import TestSkipped, TestCase
31
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
32
from bzrlib.trace import mutter
33
import bzrlib.ui as ui
34
import bzrlib.workingtree as workingtree
35
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
36
                                WorkingTree)
37
38
39
class CapturingUIFactory(ui.UIFactory):
40
    """A UI Factory for testing - capture the updates made through it."""
41
42
    def __init__(self):
43
        super(CapturingUIFactory, self).__init__()
44
        self._calls = []
45
        self.depth = 0
46
47
    def clear(self):
48
        """See progress.ProgressBar.clear()."""
49
50
    def clear_term(self):
51
        """See progress.ProgressBar.clear_term()."""
52
53
    def finished(self):
54
        """See progress.ProgressBar.finished()."""
55
        self.depth -= 1
56
57
    def note(self, fmt_string, *args, **kwargs):
58
        """See progress.ProgressBar.note()."""
59
60
    def progress_bar(self):
61
        return self
62
    
63
    def nested_progress_bar(self):
64
        self.depth += 1
65
        return self
66
67
    def update(self, message, count=None, total=None):
68
        """See progress.ProgressBar.update()."""
69
        if self.depth == 1:
70
            self._calls.append(("update", count, total))
71
72
73
class TestCapturingUI(TestCase):
74
75
    def test_nested_ignore_depth_beyond_one(self):
76
        # we only want to capture the first level out progress, not
77
        # want sub-components might do. So we have nested bars ignored.
78
        factory = CapturingUIFactory()
79
        pb1 = factory.nested_progress_bar()
80
        pb1.update('foo', 0, 1)
81
        pb2 = factory.nested_progress_bar()
82
        pb2.update('foo', 0, 1)
83
        pb2.finished()
84
        pb1.finished()
85
        self.assertEqual([("update", 0, 1)], factory._calls)
86
87
88
class TestCommit(TestCaseWithWorkingTree):
89
90
    def test_commit_sets_last_revision(self):
91
        tree = self.make_branch_and_tree('tree')
92
        tree.commit('foo', rev_id='foo', allow_pointless=True)
93
        self.assertEqual('foo', tree.last_revision())
94
95
    def test_commit_local_unbound(self):
96
        # using the library api to do a local commit on unbound branches is 
97
        # also an error
98
        tree = self.make_branch_and_tree('tree')
99
        self.assertRaises(errors.LocalRequiresBoundBranch,
100
                          tree.commit,
101
                          'foo',
102
                          local=True)
103
 
104
    def test_local_commit_ignores_master(self):
105
        # a --local commit does not require access to the master branch
106
        # at all, or even for it to exist.
107
        # we test this by setting up a bound branch and then corrupting
108
        # the master.
109
        master = self.make_branch('master')
110
        tree = self.make_branch_and_tree('tree')
111
        try:
112
            tree.branch.bind(master)
113
        except errors.UpgradeRequired:
114
            # older format.
115
            return
116
        master.bzrdir.transport.put('branch-format', StringIO('garbage'))
117
        del master
118
        # check its corrupted.
119
        self.assertRaises(errors.UnknownFormatError,
120
                          bzrdir.BzrDir.open,
121
                          'master')
122
        tree.commit('foo', rev_id='foo', local=True)
123
 
124
    def test_local_commit_does_not_push_to_master(self):
125
        # a --local commit does not require access to the master branch
126
        # at all, or even for it to exist.
127
        # we test that even when its available it does not push to it.
128
        master = self.make_branch('master')
129
        tree = self.make_branch_and_tree('tree')
130
        try:
131
            tree.branch.bind(master)
132
        except errors.UpgradeRequired:
133
            # older format.
134
            return
135
        tree.commit('foo', rev_id='foo', local=True)
136
        self.failIf(master.repository.has_revision('foo'))
137
        self.assertEqual(None, master.last_revision())
138
        
139
140
class TestCommmitProgress(TestCaseWithWorkingTree):
141
    
142
    def restoreDefaults(self):
143
        ui.ui_factory = self.old_ui_factory
144
145
    def test_commit_progress_steps(self):
146
        # during commit we one progress update for every entry in the 
147
        # inventory, and then one for the inventory, and one for the
148
        # inventory, and one for the revision insertions.
149
        # first we need a test commit to do. Lets setup a branch with 
150
        # 3 files, and alter one in a selected-file commit. This exercises
151
        # a number of cases quickly. We should also test things like 
152
        # selective commits which excludes newly added files.
153
        tree = self.make_branch_and_tree('.')
154
        self.build_tree(['a', 'b', 'c'])
155
        tree.add(['a', 'b', 'c'])
156
        tree.commit('first post')
157
        f = file('b', 'wt')
158
        f.write('new content')
159
        f.close()
160
        # set a progress bar that captures the calls so we can see what is 
161
        # emitted
162
        self.old_ui_factory = ui.ui_factory
163
        self.addCleanup(self.restoreDefaults)
164
        factory = CapturingUIFactory()
165
        ui.ui_factory = factory
166
        # TODO RBC 20060421 it would be nice to merge the reporter output
167
        # into the factory for this test - just make the test ui factory
168
        # pun as a reporter. Then we can check the ordering is right.
169
        tree.commit('second post', specific_files=['b'])
170
        # 11 steps: 1 for rev, 1 for inventory, 1 for finishing. 2 for root
171
        # and 3 for basis files, and 3 for new inventory files.
172
        self.assertEqual(
173
            [("update", 0, 10),
174
             ("update", 1, 10),
175
             ("update", 2, 10),
176
             ("update", 3, 10),
177
             ("update", 4, 10),
178
             ("update", 5, 10),
179
             ("update", 6, 10),
180
             ("update", 7, 10),
181
             ("update", 8, 10),
182
             ("update", 9, 10),
183
             ("update", 10, 10)],
184
            factory._calls
185
           )