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
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests for the bzrlib ui
21
22
from StringIO import StringIO
26
import bzrlib.errors as errors
27
from bzrlib.progress import TTYProgressBar, ProgressBarStack
28
from bzrlib.tests import TestCase
29
from bzrlib.tests.test_progress import _TTYStringIO
30
from bzrlib.ui import SilentUIFactory
31
from bzrlib.ui.text import TextUIFactory
34
class UITests(TestCase):
36
def test_silent_factory(self):
37
ui = SilentUIFactory()
38
pb = ui.nested_progress_bar()
40
# TODO: Test that there is no output from SilentUIFactory
42
self.assertEquals(ui.get_password(), None)
43
self.assertEquals(ui.get_password(u'Hello There \u1234 %(user)s',
49
def test_text_factory(self):
51
pb = ui.nested_progress_bar()
53
# TODO: Test the output from TextUIFactory, perhaps by overriding sys.stdout
55
# Unfortunately we can't actually test the ui.get_password() because
56
# that would actually prompt the user for a password during the test suite
57
# This has been tested manually with both LANG=en_US.utf-8 and LANG=C
59
# self.assertEquals(ui.get_password(u"%(user)s please type 'bogus'",
64
def test_progress_note(self):
67
ui_factory = TextUIFactory(bar_type=TTYProgressBar)
68
pb = ui_factory.nested_progress_bar()
70
pb.to_messages_file = stdout
71
ui_factory._progress_bar_stack.bottom().to_file = stderr
73
self.assertEqual(None, result)
74
self.assertEqual("t\n", stdout.getvalue())
75
# Since there was no update() call, there should be no clear() call
76
self.failIf(re.search(r'^\r {10,}\r$', stderr.getvalue()) is not None,
77
'We cleared the stderr without anything to put there')
81
def test_progress_note_clears(self):
84
# The PQM redirects the output to a file, so it
85
# defaults to creating a Dots progress bar. we
86
# need to force it to believe we are a TTY
87
ui_factory = TextUIFactory(bar_type=TTYProgressBar)
88
pb = ui_factory.nested_progress_bar()
90
pb.to_messages_file = stdout
91
ui_factory._progress_bar_stack.bottom().to_file = stderr
92
# Create a progress update that isn't throttled
96
self.assertEqual(None, result)
97
self.assertEqual("t\n", stdout.getvalue())
98
# the exact contents will depend on the terminal width and we don't
99
# care about that right now - but you're probably running it on at
100
# least a 10-character wide terminal :)
101
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
105
def test_progress_nested(self):
106
# test factory based nested and popping.
108
pb1 = ui.nested_progress_bar()
109
pb2 = ui.nested_progress_bar()
110
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
114
def test_progress_stack(self):
115
# test the progress bar stack which the default text factory
119
# make a stack, which accepts parameters like a pb.
120
stack = ProgressBarStack(to_file=stderr, to_messages_file=stdout)
122
self.assertFalse(getattr(stack, 'note', False))
123
pb1 = stack.get_nested()
124
pb2 = stack.get_nested()
125
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
128
# the text ui factory never actually removes the stack once its setup.
129
# we need to be able to nest again correctly from here.
130
pb1 = stack.get_nested()
131
pb2 = stack.get_nested()
132
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
136
def test_text_factory_setting_progress_bar(self):
137
# we should be able to choose the progress bar type used.
138
factory = bzrlib.ui.text.TextUIFactory(
139
bar_type=bzrlib.progress.DotsProgressBar)
140
bar = factory.nested_progress_bar()
142
self.assertIsInstance(bar, bzrlib.progress.DotsProgressBar)
144
def test_cli_stdin_is_default_stdin(self):
145
factory = bzrlib.ui.CLIUIFactory()
146
self.assertEqual(sys.stdin, factory.stdin)
148
def assert_get_bool_acceptance_of_user_input(self, factory):
149
factory.stdin = StringIO("y\nyes with garbage\nyes\nn\nnot an answer\nno\nfoo\n")
150
factory.stdout = StringIO()
151
# there is no output from the base factory
152
self.assertEqual(True, factory.get_boolean(""))
153
self.assertEqual(True, factory.get_boolean(""))
154
self.assertEqual(False, factory.get_boolean(""))
155
self.assertEqual(False, factory.get_boolean(""))
24
from testtools.matchers import *
32
from bzrlib.tests import (
36
from bzrlib.ui import text as _mod_ui_text
37
from bzrlib.tests.testui import (
38
ProgressRecordingUIFactory,
42
class TestUIConfiguration(tests.TestCaseWithTransport):
44
def test_output_encoding_configuration(self):
45
enc = fixtures.generate_unicode_encodings().next()
46
config.GlobalConfig().set_user_option('output_encoding',
48
ui = tests.TestUIFactory(stdin=None,
49
stdout=tests.StringIOWrapper(),
50
stderr=tests.StringIOWrapper())
51
output = ui.make_output_stream()
52
self.assertEquals(output.encoding, enc)
55
class TestTextUIFactory(tests.TestCase):
57
def make_test_ui_factory(self, stdin_contents):
58
ui = tests.TestUIFactory(stdin=stdin_contents,
59
stdout=tests.StringIOWrapper(),
60
stderr=tests.StringIOWrapper())
63
def test_text_factory_confirm(self):
64
# turns into reading a regular boolean
65
ui = self.make_test_ui_factory('n\n')
66
self.assertEquals(ui.confirm_action(u'Should %(thing)s pass?',
67
'bzrlib.tests.test_ui.confirmation',
71
def test_text_factory_ascii_password(self):
72
ui = self.make_test_ui_factory('secret\n')
73
pb = ui.nested_progress_bar()
75
self.assertEqual('secret',
76
self.apply_redirected(ui.stdin, ui.stdout,
79
# ': ' is appended to prompt
80
self.assertEqual(': ', ui.stderr.getvalue())
81
self.assertEqual('', ui.stdout.readline())
82
# stdin should be empty
83
self.assertEqual('', ui.stdin.readline())
87
def test_text_factory_utf8_password(self):
88
"""Test an utf8 password.
90
We can't predict what encoding users will have for stdin, so we force
91
it to utf8 to test that we transport the password correctly.
93
ui = self.make_test_ui_factory(u'baz\u1234'.encode('utf8'))
94
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
95
pb = ui.nested_progress_bar()
97
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
99
u'Hello \u1234 %(user)s',
101
# We use StringIO objects, we need to decode them
102
self.assertEqual(u'baz\u1234', password.decode('utf8'))
103
self.assertEqual(u'Hello \u1234 some\u1234: ',
104
ui.stderr.getvalue().decode('utf8'))
105
# stdin and stdout should be empty
106
self.assertEqual('', ui.stdin.readline())
107
self.assertEqual('', ui.stdout.readline())
111
def test_text_ui_get_boolean(self):
112
stdin = tests.StringIOWrapper("y\n" # True
114
"yes with garbage\nY\n" # True
115
"not an answer\nno\n" # False
116
"I'm sure!\nyes\n" # True
119
stdout = tests.StringIOWrapper()
120
stderr = tests.StringIOWrapper()
121
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
122
self.assertEqual(True, factory.get_boolean(u""))
123
self.assertEqual(False, factory.get_boolean(u""))
124
self.assertEqual(True, factory.get_boolean(u""))
125
self.assertEqual(False, factory.get_boolean(u""))
126
self.assertEqual(True, factory.get_boolean(u""))
127
self.assertEqual(False, factory.get_boolean(u""))
156
128
self.assertEqual("foo\n", factory.stdin.read())
158
def test_silent_ui_getbool(self):
159
factory = bzrlib.ui.SilentUIFactory()
160
self.assert_get_bool_acceptance_of_user_input(factory)
162
def test_silent_factory_prompts_silently(self):
163
factory = bzrlib.ui.SilentUIFactory()
165
factory.stdin = StringIO("y\n")
168
self.apply_redirected(
169
None, stdout, stdout, factory.get_boolean, "foo")
171
self.assertEqual("", stdout.getvalue())
173
def test_text_ui_getbool(self):
174
factory = bzrlib.ui.text.TextUIFactory()
175
self.assert_get_bool_acceptance_of_user_input(factory)
129
# stdin should be empty
130
self.assertEqual('', factory.stdin.readline())
132
def test_text_ui_get_integer(self):
133
stdin = tests.StringIOWrapper(
136
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
137
stdout = tests.StringIOWrapper()
138
stderr = tests.StringIOWrapper()
139
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
140
self.assertEqual(1, factory.get_integer(u""))
141
self.assertEqual(-2, factory.get_integer(u""))
142
self.assertEqual(42, factory.get_integer(u""))
144
def test_text_factory_prompt(self):
145
# see <https://launchpad.net/bugs/365891>
146
StringIO = tests.StringIOWrapper
147
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
148
factory.prompt(u'foo %2e')
149
self.assertEqual('', factory.stdout.getvalue())
150
self.assertEqual('foo %2e', factory.stderr.getvalue())
177
152
def test_text_factory_prompts_and_clears(self):
178
153
# a get_boolean call should clear the pb before prompting
179
factory = bzrlib.ui.text.TextUIFactory()
180
factory.stdout = _TTYStringIO()
181
factory.stdin = StringIO("yada\ny\n")
182
pb = self.apply_redirected(
183
factory.stdin, factory.stdout, factory.stdout, factory.nested_progress_bar)
185
self.apply_redirected(
186
factory.stdin, factory.stdout, factory.stdout, pb.update, "foo", 0, 1)
189
self.apply_redirected(
190
None, factory.stdout, factory.stdout, factory.get_boolean, "what do you want")
192
# use a regular expression so that we don't depend on the particular
193
# screen width - could also set and restore $COLUMN if that has
194
# priority on all platforms, but it doesn't at present.
195
output = factory.stdout.getvalue()
197
"\r/ \\[ *\\] foo 0/1"
199
"\rwhat do you want\\? \\[y/n\\]:what do you want\\? \\[y/n\\]:",
201
self.fail("didn't match factory output %r, %s" % (factory, output))
154
out = test_progress._TTYStringIO()
155
self.overrideEnv('TERM', 'xterm')
156
factory = _mod_ui_text.TextUIFactory(
157
stdin=tests.StringIOWrapper("yada\ny\n"),
158
stdout=out, stderr=out)
159
factory._avail_width = lambda: 79
160
pb = factory.nested_progress_bar()
162
pb.show_spinner = False
163
pb.show_count = False
164
pb.update("foo", 0, 1)
165
self.assertEqual(True,
166
self.apply_redirected(None, factory.stdout,
169
u"what do you want"))
170
output = out.getvalue()
171
self.assertContainsRe(output,
173
self.assertContainsRe(output,
174
r"what do you want\? \[y/n\]: what do you want\? \[y/n\]: ")
175
# stdin should have been totally consumed
176
self.assertEqual('', factory.stdin.readline())
178
def test_text_tick_after_update(self):
179
ui_factory = _mod_ui_text.TextUIFactory(stdout=tests.StringIOWrapper(),
180
stderr=tests.StringIOWrapper())
181
pb = ui_factory.nested_progress_bar()
183
pb.update('task', 0, 3)
184
# Reset the clock, so that it actually tries to repaint itself
185
ui_factory._progress_view._last_repaint = time.time() - 1.0
190
def test_text_ui_getusername(self):
191
factory = _mod_ui_text.TextUIFactory(None, None, None)
192
factory.stdin = tests.StringIOWrapper("someuser\n\n")
193
factory.stdout = tests.StringIOWrapper()
194
factory.stderr = tests.StringIOWrapper()
195
factory.stdout.encoding = "utf8"
196
# there is no output from the base factory
197
self.assertEqual("someuser",
198
factory.get_username(u'Hello %(host)s', host='some'))
199
self.assertEquals("Hello some: ", factory.stderr.getvalue())
200
self.assertEquals('', factory.stdout.getvalue())
201
self.assertEqual("", factory.get_username(u"Gebruiker"))
202
# stdin should be empty
203
self.assertEqual('', factory.stdin.readline())
205
def test_text_ui_getusername_utf8(self):
206
ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
207
stdout=tests.StringIOWrapper(),
208
stderr=tests.StringIOWrapper())
209
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
210
pb = ui.nested_progress_bar()
212
# there is no output from the base factory
213
username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
214
ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234')
215
self.assertEquals(u"someuser\u1234", username.decode('utf8'))
216
self.assertEquals(u"Hello\u1234 some\u1234: ",
217
ui.stderr.getvalue().decode("utf8"))
218
self.assertEquals('', ui.stdout.getvalue())
222
def test_quietness(self):
223
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
224
ui_factory = _mod_ui_text.TextUIFactory(None,
225
test_progress._TTYStringIO(),
226
test_progress._TTYStringIO())
227
self.assertIsInstance(ui_factory._progress_view,
228
_mod_ui_text.TextProgressView)
229
ui_factory.be_quiet(True)
230
self.assertIsInstance(ui_factory._progress_view,
231
_mod_ui_text.NullProgressView)
233
def test_text_ui_show_user_warning(self):
234
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
235
from bzrlib.repofmt.knitpack_repo import RepositoryFormatKnitPack5
238
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
239
remote_fmt = remote.RemoteRepositoryFormat()
240
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
241
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
242
to_format=remote_fmt)
243
self.assertEquals('', out.getvalue())
244
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
245
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
246
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
247
"the same format for better performance.\n",
249
# and now with it suppressed please
252
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
253
ui.suppressed_warnings.add('cross_format_fetch')
254
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
255
to_format=remote_fmt)
256
self.assertEquals('', out.getvalue())
257
self.assertEquals('', err.getvalue())
260
class TestTextUIOutputStream(tests.TestCase):
261
"""Tests for output stream that synchronizes with progress bar."""
263
def test_output_clears_terminal(self):
264
stdout = tests.StringIOWrapper()
265
stderr = tests.StringIOWrapper()
268
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
269
uif.clear_term = lambda: clear_calls.append('clear')
271
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
272
stream.write("Hello world!\n")
273
stream.write("there's more...\n")
274
stream.writelines(["1\n", "2\n", "3\n"])
276
self.assertEqual(stdout.getvalue(),
280
self.assertEqual(['clear', 'clear', 'clear'],
286
class UITests(tests.TestCase):
288
def test_progress_construction(self):
289
"""TextUIFactory constructs the right progress view.
291
TTYStringIO = test_progress._TTYStringIO
292
FileStringIO = tests.StringIOWrapper
293
for (file_class, term, pb, expected_pb_class) in (
294
# on an xterm, either use them or not as the user requests,
295
# otherwise default on
296
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
297
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
298
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
299
# on a dumb terminal, again if there's explicit configuration do
300
# it, otherwise default off
301
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
302
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
303
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
304
# on a non-tty terminal, it's null regardless of $TERM
305
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
306
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
307
# however, it can still be forced on
308
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
310
self.overrideEnv('TERM', term)
311
self.overrideEnv('BZR_PROGRESS_BAR', pb)
312
stdin = file_class('')
313
stderr = file_class()
314
stdout = file_class()
315
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
316
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
317
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
318
self.assertIsInstance(uif.make_progress_view(),
320
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
322
def test_text_ui_non_terminal(self):
323
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
324
stdin = test_progress._NonTTYStringIO('')
325
stderr = test_progress._NonTTYStringIO()
326
stdout = test_progress._NonTTYStringIO()
327
for term_type in ['dumb', None, 'xterm']:
328
self.overrideEnv('TERM', term_type)
329
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
330
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
331
'TERM=%r' % (term_type,))
334
class SilentUITests(tests.TestCase):
336
def test_silent_factory_get_password(self):
337
# A silent factory that can't do user interaction can't get a
338
# password. Possibly it should raise a more specific error but it
340
ui = _mod_ui.SilentUIFactory()
341
stdout = tests.StringIOWrapper()
344
self.apply_redirected,
345
None, stdout, stdout, ui.get_password)
346
# and it didn't write anything out either
347
self.assertEqual('', stdout.getvalue())
349
def test_silent_ui_getbool(self):
350
factory = _mod_ui.SilentUIFactory()
351
stdout = tests.StringIOWrapper()
354
self.apply_redirected,
355
None, stdout, stdout, factory.get_boolean, u"foo")
358
class TestUIFactoryTests(tests.TestCase):
360
def test_test_ui_factory_progress(self):
361
# there's no output; we just want to make sure this doesn't crash -
362
# see https://bugs.launchpad.net/bzr/+bug/408201
363
ui = tests.TestUIFactory()
364
pb = ui.nested_progress_bar()
370
class CannedInputUIFactoryTests(tests.TestCase):
372
def test_canned_input_get_input(self):
373
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
374
self.assertEqual(True, uif.get_boolean(u'Extra cheese?'))
375
self.assertEqual('mbp', uif.get_username(u'Enter your user name'))
376
self.assertEqual('password',
377
uif.get_password(u'Password for %(host)s',
379
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
382
class TestBoolFromString(tests.TestCase):
384
def assertIsTrue(self, s, accepted_values=None):
385
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
386
self.assertEquals(True, res)
388
def assertIsFalse(self, s, accepted_values=None):
389
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
390
self.assertEquals(False, res)
392
def assertIsNone(self, s, accepted_values=None):
393
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
394
self.assertIs(None, res)
396
def test_know_valid_values(self):
397
self.assertIsTrue('true')
398
self.assertIsFalse('false')
399
self.assertIsTrue('1')
400
self.assertIsFalse('0')
401
self.assertIsTrue('on')
402
self.assertIsFalse('off')
403
self.assertIsTrue('yes')
404
self.assertIsFalse('no')
405
self.assertIsTrue('y')
406
self.assertIsFalse('n')
407
# Also try some case variations
408
self.assertIsTrue('True')
409
self.assertIsFalse('False')
410
self.assertIsTrue('On')
411
self.assertIsFalse('Off')
412
self.assertIsTrue('ON')
413
self.assertIsFalse('OFF')
414
self.assertIsTrue('oN')
415
self.assertIsFalse('oFf')
417
def test_invalid_values(self):
418
self.assertIsNone(None)
419
self.assertIsNone('doubt')
420
self.assertIsNone('frue')
421
self.assertIsNone('talse')
422
self.assertIsNone('42')
424
def test_provided_values(self):
425
av = dict(y=True, n=False, yes=True, no=False)
426
self.assertIsTrue('y', av)
427
self.assertIsTrue('Y', av)
428
self.assertIsTrue('Yes', av)
429
self.assertIsFalse('n', av)
430
self.assertIsFalse('N', av)
431
self.assertIsFalse('No', av)
432
self.assertIsNone('1', av)
433
self.assertIsNone('0', av)
434
self.assertIsNone('on', av)
435
self.assertIsNone('off', av)
438
class TestConfirmationUserInterfacePolicy(tests.TestCase):
440
def test_confirm_action_default(self):
441
base_ui = _mod_ui.NoninteractiveUIFactory()
442
for answer in [True, False]:
444
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
445
.confirm_action("Do something?",
446
"bzrlib.tests.do_something", {}),
449
def test_confirm_action_specific(self):
450
base_ui = _mod_ui.NoninteractiveUIFactory()
451
for default_answer in [True, False]:
452
for specific_answer in [True, False]:
453
for conf_id in ['given_id', 'other_id']:
454
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
455
base_ui, default_answer, dict(given_id=specific_answer))
456
result = wrapper.confirm_action("Do something?", conf_id, {})
457
if conf_id == 'given_id':
458
self.assertEquals(result, specific_answer)
460
self.assertEquals(result, default_answer)
463
base_ui = _mod_ui.NoninteractiveUIFactory()
464
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
465
base_ui, True, dict(a=2))
466
self.assertThat(repr(wrapper),
467
Equals("ConfirmationUserInterfacePolicy("
468
"NoninteractiveUIFactory(), True, {'a': 2})"))
471
class TestProgressRecordingUI(tests.TestCase):
472
"""Test test-oriented UIFactory that records progress updates"""
474
def test_nested_ignore_depth_beyond_one(self):
475
# we only want to capture the first level out progress, not
476
# want sub-components might do. So we have nested bars ignored.
477
factory = ProgressRecordingUIFactory()
478
pb1 = factory.nested_progress_bar()
479
pb1.update('foo', 0, 1)
480
pb2 = factory.nested_progress_bar()
481
pb2.update('foo', 0, 1)
484
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)