1
# Copyright (C) 2005, 2008, 2009 Canonical Ltd
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.
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.
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
17
"""Tests for the bzrlib ui
21
from StringIO import StringIO
27
import bzrlib.errors as errors
28
from bzrlib.progress import (
33
from bzrlib.symbol_versioning import (
36
from bzrlib.tests import (
41
from bzrlib.tests.test_progress import _TTYStringIO
42
from bzrlib.ui import (
46
from bzrlib.ui.text import TextUIFactory
49
class UITests(TestCase):
51
def test_silent_factory(self):
52
ui = SilentUIFactory()
54
self.assertEqual(None,
55
self.apply_redirected(None, stdout, stdout,
57
self.assertEqual('', stdout.getvalue())
58
self.assertEqual(None,
59
self.apply_redirected(None, stdout, stdout,
61
u'Hello\u1234 %(user)s',
63
self.assertEqual('', stdout.getvalue())
65
def test_text_factory_ascii_password(self):
66
ui = TestUIFactory(stdin='secret\n', stdout=StringIOWrapper())
67
pb = ui.nested_progress_bar()
69
self.assertEqual('secret',
70
self.apply_redirected(ui.stdin, ui.stdout,
73
# ': ' is appended to prompt
74
self.assertEqual(': ', ui.stdout.getvalue())
75
# stdin should be empty
76
self.assertEqual('', ui.stdin.readline())
80
def test_text_factory_utf8_password(self):
81
"""Test an utf8 password.
83
We can't predict what encoding users will have for stdin, so we force
84
it to utf8 to test that we transport the password correctly.
86
ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
87
stdout=StringIOWrapper())
88
ui.stdin.encoding = 'utf8'
89
ui.stdout.encoding = ui.stdin.encoding
90
pb = ui.nested_progress_bar()
92
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stdout,
94
u'Hello \u1234 %(user)s',
96
# We use StringIO objects, we need to decode them
97
self.assertEqual(u'baz\u1234', password.decode('utf8'))
98
self.assertEqual(u'Hello \u1234 some\u1234: ',
99
ui.stdout.getvalue().decode('utf8'))
100
# stdin should be empty
101
self.assertEqual('', ui.stdin.readline())
105
def test_progress_note(self):
108
ui_factory = TextUIFactory(stdin=StringIO(''),
111
pb = ui_factory.nested_progress_bar()
113
result = pb.note('t')
114
self.assertEqual(None, result)
115
self.assertEqual("t\n", stdout.getvalue())
116
# Since there was no update() call, there should be no clear() call
117
self.failIf(re.search(r'^\r {10,}\r$',
118
stderr.getvalue()) is not None,
119
'We cleared the stderr without anything to put there')
123
def test_progress_note_clears(self):
126
# The PQM redirects the output to a file, so it
127
# defaults to creating a Dots progress bar. we
128
# need to force it to believe we are a TTY
129
ui_factory = TextUIFactory(
131
stdout=stdout, stderr=stderr)
132
pb = ui_factory.nested_progress_bar()
134
# Create a progress update that isn't throttled
136
result = pb.note('t')
137
self.assertEqual(None, result)
138
self.assertEqual("t\n", stdout.getvalue())
139
# the exact contents will depend on the terminal width and we don't
140
# care about that right now - but you're probably running it on at
141
# least a 10-character wide terminal :)
142
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
146
def test_progress_nested(self):
147
# test factory based nested and popping.
148
ui = TextUIFactory(None, None, None)
149
pb1 = ui.nested_progress_bar()
150
pb2 = ui.nested_progress_bar()
151
# You do get a warning if the outermost progress bar wasn't finished
152
# first - it's not clear if this is really useful or if it should just
153
# become orphaned -- mbp 20090120
154
warnings, _ = self.callCatchWarnings(pb1.finished)
155
if len(warnings) != 1:
156
self.fail("unexpected warnings: %r" % (warnings,))
160
def test_progress_stack(self):
161
# test the progress bar stack which the default text factory
165
# make a stack, which accepts parameters like a pb.
166
stack = self.applyDeprecated(
167
deprecated_in((1, 12, 0)),
169
to_file=stderr, to_messages_file=stdout)
171
self.assertFalse(getattr(stack, 'note', False))
172
pb1 = stack.get_nested()
173
pb2 = stack.get_nested()
174
warnings, _ = self.callCatchWarnings(pb1.finished)
175
self.assertEqual(len(warnings), 1)
178
# the text ui factory never actually removes the stack once its setup.
179
# we need to be able to nest again correctly from here.
180
pb1 = stack.get_nested()
181
pb2 = stack.get_nested()
182
warnings, _ = self.callCatchWarnings(pb1.finished)
183
self.assertEqual(len(warnings), 1)
187
def assert_get_bool_acceptance_of_user_input(self, factory):
188
factory.stdin = StringIO("y\nyes with garbage\n"
189
"yes\nn\nnot an answer\n"
191
factory.stdout = StringIO()
192
# there is no output from the base factory
193
self.assertEqual(True, factory.get_boolean(""))
194
self.assertEqual(True, factory.get_boolean(""))
195
self.assertEqual(False, factory.get_boolean(""))
196
self.assertEqual(False, factory.get_boolean(""))
197
self.assertEqual("foo\n", factory.stdin.read())
198
# stdin should be empty
199
self.assertEqual('', factory.stdin.readline())
201
def test_silent_ui_getbool(self):
202
factory = SilentUIFactory()
203
self.assert_get_bool_acceptance_of_user_input(factory)
205
def test_silent_factory_prompts_silently(self):
206
factory = SilentUIFactory()
208
factory.stdin = StringIO("y\n")
209
self.assertEqual(True,
210
self.apply_redirected(None, stdout, stdout,
211
factory.get_boolean, "foo"))
212
self.assertEqual("", stdout.getvalue())
213
# stdin should be empty
214
self.assertEqual('', factory.stdin.readline())
216
def test_text_ui_getbool(self):
217
factory = TextUIFactory(None, None, None)
218
self.assert_get_bool_acceptance_of_user_input(factory)
220
def test_text_factory_prompts_and_clears(self):
221
# a get_boolean call should clear the pb before prompting
223
factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
224
pb = factory.nested_progress_bar()
226
pb.show_spinner = False
227
pb.show_count = False
228
pb.update("foo", 0, 1)
229
self.assertEqual(True,
230
self.apply_redirected(None, factory.stdout,
234
output = out.getvalue()
235
self.assertContainsRe(factory.stdout.getvalue(),
237
self.assertContainsRe(factory.stdout.getvalue(),
238
r"what do you want\? \[y/n\]: what do you want\? \[y/n\]: ")
239
# stdin should have been totally consumed
240
self.assertEqual('', factory.stdin.readline())
242
def test_text_tick_after_update(self):
243
ui_factory = TextUIFactory(stdout=StringIO(), stderr=StringIO())
244
pb = ui_factory.nested_progress_bar()
246
pb.update('task', 0, 3)
247
# Reset the clock, so that it actually tries to repaint itself
248
ui_factory._progress_view._last_repaint = time.time() - 1.0