70
90
We can't predict what encoding users will have for stdin, so we force
71
91
it to utf8 to test that we transport the password correctly.
73
ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
74
stdout=StringIOWrapper())
75
ui.stdin.encoding = 'utf8'
76
ui.stdout.encoding = ui.stdin.encoding
93
ui = self.make_test_ui_factory(u'baz\u1234'.encode('utf8'))
94
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
77
95
pb = ui.nested_progress_bar()
79
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stdout,
97
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
81
99
u'Hello \u1234 %(user)s',
82
100
user=u'some\u1234')
83
101
# We use StringIO objects, we need to decode them
84
102
self.assertEqual(u'baz\u1234', password.decode('utf8'))
85
103
self.assertEqual(u'Hello \u1234 some\u1234: ',
86
ui.stdout.getvalue().decode('utf8'))
90
def test_progress_note(self):
93
ui_factory = TextUIFactory(bar_type=TTYProgressBar)
94
pb = ui_factory.nested_progress_bar()
96
pb.to_messages_file = stdout
97
ui_factory._progress_bar_stack.bottom().to_file = stderr
99
self.assertEqual(None, result)
100
self.assertEqual("t\n", stdout.getvalue())
101
# Since there was no update() call, there should be no clear() call
102
self.failIf(re.search(r'^\r {10,}\r$', stderr.getvalue()) is not None,
103
'We cleared the stderr without anything to put there')
107
def test_progress_note_clears(self):
110
# The PQM redirects the output to a file, so it
111
# defaults to creating a Dots progress bar. we
112
# need to force it to believe we are a TTY
113
ui_factory = TextUIFactory(bar_type=TTYProgressBar)
114
pb = ui_factory.nested_progress_bar()
116
pb.to_messages_file = stdout
117
ui_factory._progress_bar_stack.bottom().to_file = stderr
118
# Create a progress update that isn't throttled
121
result = pb.note('t')
122
self.assertEqual(None, result)
123
self.assertEqual("t\n", stdout.getvalue())
124
# the exact contents will depend on the terminal width and we don't
125
# care about that right now - but you're probably running it on at
126
# least a 10-character wide terminal :)
127
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
131
def test_progress_nested(self):
132
# test factory based nested and popping.
134
pb1 = ui.nested_progress_bar()
135
pb2 = ui.nested_progress_bar()
136
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
140
def test_progress_stack(self):
141
# test the progress bar stack which the default text factory
145
# make a stack, which accepts parameters like a pb.
146
stack = ProgressBarStack(to_file=stderr, to_messages_file=stdout)
148
self.assertFalse(getattr(stack, 'note', False))
149
pb1 = stack.get_nested()
150
pb2 = stack.get_nested()
151
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
154
# the text ui factory never actually removes the stack once its setup.
155
# we need to be able to nest again correctly from here.
156
pb1 = stack.get_nested()
157
pb2 = stack.get_nested()
158
self.assertRaises(errors.MissingProgressBarFinish, pb1.finished)
162
def test_text_factory_setting_progress_bar(self):
163
# we should be able to choose the progress bar type used.
164
factory = bzrlib.ui.text.TextUIFactory(
165
bar_type=DotsProgressBar)
166
bar = factory.nested_progress_bar()
168
self.assertIsInstance(bar, DotsProgressBar)
170
def test_cli_stdin_is_default_stdin(self):
171
factory = bzrlib.ui.CLIUIFactory()
172
self.assertEqual(sys.stdin, factory.stdin)
174
def assert_get_bool_acceptance_of_user_input(self, factory):
175
factory.stdin = StringIO("y\nyes with garbage\nyes\nn\nnot an answer\nno\nfoo\n")
176
factory.stdout = StringIO()
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
116
"yes with garbage\nY\n" # True
117
"not an answer\nno\n" # False
118
"I'm sure!\nyes\n" # True
121
stdout = tests.StringIOWrapper()
122
stderr = tests.StringIOWrapper()
123
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
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""))
128
self.assertEqual(True, factory.get_boolean(u""))
129
self.assertEqual(False, factory.get_boolean(u""))
130
self.assertEqual(True, factory.get_boolean(u""))
131
self.assertEqual(False, factory.get_boolean(u""))
132
self.assertEqual("foo\n", factory.stdin.read())
133
# stdin should be empty
134
self.assertEqual('', factory.stdin.readline())
135
# return false on EOF
136
self.assertEqual(False, factory.get_boolean(u""))
138
def test_text_ui_choose_bad_parameters(self):
139
stdin = tests.StringIOWrapper()
140
stdout = tests.StringIOWrapper()
141
stderr = tests.StringIOWrapper()
142
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
143
# invalid default index
144
self.assertRaises(ValueError, factory.choose, u"", u"&Yes\n&No", 3)
146
self.assertRaises(ValueError, factory.choose, u"", u"&choice\n&ChOiCe")
147
# duplicated shortcut
148
self.assertRaises(ValueError, factory.choose, u"", u"&choice1\nchoi&ce2")
150
def test_text_ui_choose_prompt(self):
151
stdin = tests.StringIOWrapper()
152
stdout = tests.StringIOWrapper()
153
stderr = tests.StringIOWrapper()
154
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
155
# choices with explicit shortcuts
156
factory.choose(u"prompt", u"&yes\n&No\nmore &info")
157
self.assertEqual("prompt ([y]es, [N]o, more [i]nfo): \n", factory.stderr.getvalue())
158
# automatic shortcuts
159
factory.stderr.truncate(0)
160
factory.choose(u"prompt", u"yes\nNo\nmore info")
161
self.assertEqual("prompt ([y]es, [N]o, [m]ore info): \n", factory.stderr.getvalue())
163
def test_text_ui_choose_return_values(self):
164
choose = lambda: factory.choose(u"", u"&Yes\n&No\nMaybe\nmore &info", 3)
165
stdin = tests.StringIOWrapper("y\n" # 0
169
"b\na\nd \n" # bad shortcuts, all ignored
170
"yes with garbage\nY\n" # 0
171
"not an answer\nno\n" # 1
172
"info\nmore info\n" # 3
175
stdout = tests.StringIOWrapper()
176
stderr = tests.StringIOWrapper()
177
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
178
self.assertEqual(0, choose())
179
self.assertEqual(1, choose())
180
self.assertEqual(3, choose())
181
self.assertEqual(1, choose())
182
self.assertEqual(0, choose())
183
self.assertEqual(1, choose())
184
self.assertEqual(3, choose())
185
self.assertEqual(2, choose())
186
self.assertEqual("foo\n", factory.stdin.read())
187
# stdin should be empty
188
self.assertEqual('', factory.stdin.readline())
190
self.assertEqual(None, choose())
192
def test_text_ui_choose_no_default(self):
193
stdin = tests.StringIOWrapper(" \n" # no default, invalid!
196
stdout = tests.StringIOWrapper()
197
stderr = tests.StringIOWrapper()
198
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
199
self.assertEqual(0, factory.choose(u"", u"&Yes\n&No"))
200
self.assertEqual("foo\n", factory.stdin.read())
202
def test_text_ui_get_integer(self):
203
stdin = tests.StringIOWrapper(
206
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
207
stdout = tests.StringIOWrapper()
208
stderr = tests.StringIOWrapper()
209
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
210
self.assertEqual(1, factory.get_integer(u""))
211
self.assertEqual(-2, factory.get_integer(u""))
212
self.assertEqual(42, factory.get_integer(u""))
214
def test_text_factory_prompt(self):
215
# see <https://launchpad.net/bugs/365891>
216
StringIO = tests.StringIOWrapper
217
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
218
factory.prompt(u'foo %2e')
219
self.assertEqual('', factory.stdout.getvalue())
220
self.assertEqual('foo %2e', factory.stderr.getvalue())
222
def test_text_factory_prompts_and_clears(self):
223
# a get_boolean call should clear the pb before prompting
224
out = test_progress._TTYStringIO()
225
self.overrideEnv('TERM', 'xterm')
226
factory = _mod_ui_text.TextUIFactory(
227
stdin=tests.StringIOWrapper("yada\ny\n"),
228
stdout=out, stderr=out)
229
factory._avail_width = lambda: 79
230
pb = factory.nested_progress_bar()
232
pb.show_spinner = False
233
pb.show_count = False
234
pb.update("foo", 0, 1)
235
self.assertEqual(True,
236
self.apply_redirected(None, factory.stdout,
239
u"what do you want"))
240
output = out.getvalue()
241
self.assertContainsRe(output,
243
self.assertContainsString(output,
244
r"what do you want? ([y]es, [n]o): what do you want? ([y]es, [n]o): ")
245
# stdin should have been totally consumed
246
self.assertEqual('', factory.stdin.readline())
248
def test_text_tick_after_update(self):
249
ui_factory = _mod_ui_text.TextUIFactory(stdout=tests.StringIOWrapper(),
250
stderr=tests.StringIOWrapper())
251
pb = ui_factory.nested_progress_bar()
253
pb.update('task', 0, 3)
254
# Reset the clock, so that it actually tries to repaint itself
255
ui_factory._progress_view._last_repaint = time.time() - 1.0
260
def test_text_ui_getusername(self):
261
factory = _mod_ui_text.TextUIFactory(None, None, None)
262
factory.stdin = tests.StringIOWrapper("someuser\n\n")
263
factory.stdout = tests.StringIOWrapper()
264
factory.stderr = tests.StringIOWrapper()
265
factory.stdout.encoding = "utf8"
177
266
# there is no output from the base factory
178
self.assertEqual(True, factory.get_boolean(""))
179
self.assertEqual(True, factory.get_boolean(""))
180
self.assertEqual(False, factory.get_boolean(""))
181
self.assertEqual(False, factory.get_boolean(""))
182
self.assertEqual("foo\n", factory.stdin.read())
267
self.assertEqual("someuser",
268
factory.get_username(u'Hello %(host)s', host='some'))
269
self.assertEquals("Hello some: ", factory.stderr.getvalue())
270
self.assertEquals('', factory.stdout.getvalue())
271
self.assertEqual("", factory.get_username(u"Gebruiker"))
272
# stdin should be empty
273
self.assertEqual('', factory.stdin.readline())
275
def test_text_ui_getusername_utf8(self):
276
ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
277
stdout=tests.StringIOWrapper(),
278
stderr=tests.StringIOWrapper())
279
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
280
pb = ui.nested_progress_bar()
282
# there is no output from the base factory
283
username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
284
ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234')
285
self.assertEquals(u"someuser\u1234", username.decode('utf8'))
286
self.assertEquals(u"Hello\u1234 some\u1234: ",
287
ui.stderr.getvalue().decode("utf8"))
288
self.assertEquals('', ui.stdout.getvalue())
292
def test_quietness(self):
293
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
294
ui_factory = _mod_ui_text.TextUIFactory(None,
295
test_progress._TTYStringIO(),
296
test_progress._TTYStringIO())
297
self.assertIsInstance(ui_factory._progress_view,
298
_mod_ui_text.TextProgressView)
299
ui_factory.be_quiet(True)
300
self.assertIsInstance(ui_factory._progress_view,
301
_mod_ui_text.NullProgressView)
303
def test_text_ui_show_user_warning(self):
304
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
305
from bzrlib.repofmt.knitpack_repo import RepositoryFormatKnitPack5
308
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
309
remote_fmt = remote.RemoteRepositoryFormat()
310
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
311
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
312
to_format=remote_fmt)
313
self.assertEquals('', out.getvalue())
314
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
315
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
316
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
317
"the same format for better performance.\n",
319
# and now with it suppressed please
322
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
323
ui.suppressed_warnings.add('cross_format_fetch')
324
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
325
to_format=remote_fmt)
326
self.assertEquals('', out.getvalue())
327
self.assertEquals('', err.getvalue())
330
class TestTextUIOutputStream(tests.TestCase):
331
"""Tests for output stream that synchronizes with progress bar."""
333
def test_output_clears_terminal(self):
334
stdout = tests.StringIOWrapper()
335
stderr = tests.StringIOWrapper()
338
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
339
uif.clear_term = lambda: clear_calls.append('clear')
341
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
342
stream.write("Hello world!\n")
343
stream.write("there's more...\n")
344
stream.writelines(["1\n", "2\n", "3\n"])
346
self.assertEqual(stdout.getvalue(),
350
self.assertEqual(['clear', 'clear', 'clear'],
356
class UITests(tests.TestCase):
358
def test_progress_construction(self):
359
"""TextUIFactory constructs the right progress view.
361
TTYStringIO = test_progress._TTYStringIO
362
FileStringIO = tests.StringIOWrapper
363
for (file_class, term, pb, expected_pb_class) in (
364
# on an xterm, either use them or not as the user requests,
365
# otherwise default on
366
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
367
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
368
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
369
# on a dumb terminal, again if there's explicit configuration do
370
# it, otherwise default off
371
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
372
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
373
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
374
# on a non-tty terminal, it's null regardless of $TERM
375
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
376
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
377
# however, it can still be forced on
378
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
380
self.overrideEnv('TERM', term)
381
self.overrideEnv('BZR_PROGRESS_BAR', pb)
382
stdin = file_class('')
383
stderr = file_class()
384
stdout = file_class()
385
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
386
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
387
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
388
self.assertIsInstance(uif.make_progress_view(),
390
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
392
def test_text_ui_non_terminal(self):
393
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
394
stdin = test_progress._NonTTYStringIO('')
395
stderr = test_progress._NonTTYStringIO()
396
stdout = test_progress._NonTTYStringIO()
397
for term_type in ['dumb', None, 'xterm']:
398
self.overrideEnv('TERM', term_type)
399
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
400
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
401
'TERM=%r' % (term_type,))
404
class SilentUITests(tests.TestCase):
406
def test_silent_factory_get_password(self):
407
# A silent factory that can't do user interaction can't get a
408
# password. Possibly it should raise a more specific error but it
410
ui = _mod_ui.SilentUIFactory()
411
stdout = tests.StringIOWrapper()
414
self.apply_redirected,
415
None, stdout, stdout, ui.get_password)
416
# and it didn't write anything out either
417
self.assertEqual('', stdout.getvalue())
184
419
def test_silent_ui_getbool(self):
185
factory = bzrlib.ui.SilentUIFactory()
186
self.assert_get_bool_acceptance_of_user_input(factory)
188
def test_silent_factory_prompts_silently(self):
189
factory = bzrlib.ui.SilentUIFactory()
191
factory.stdin = StringIO("y\n")
194
self.apply_redirected(
195
None, stdout, stdout, factory.get_boolean, "foo")
197
self.assertEqual("", stdout.getvalue())
199
def test_text_ui_getbool(self):
200
factory = bzrlib.ui.text.TextUIFactory()
201
self.assert_get_bool_acceptance_of_user_input(factory)
203
def test_text_factory_prompts_and_clears(self):
204
# a get_boolean call should clear the pb before prompting
205
factory = bzrlib.ui.text.TextUIFactory(bar_type=DotsProgressBar)
206
factory.stdout = _TTYStringIO()
207
factory.stdin = StringIO("yada\ny\n")
208
pb = self.apply_redirected(
209
factory.stdin, factory.stdout, factory.stdout, factory.nested_progress_bar)
211
self.apply_redirected(
212
factory.stdin, factory.stdout, factory.stdout, pb.update, "foo", 0, 1)
215
self.apply_redirected(
216
None, factory.stdout, factory.stdout, factory.get_boolean, "what do you want")
218
output = factory.stdout.getvalue()
219
self.assertEqual("foo: .\n"
220
"what do you want? [y/n]: what do you want? [y/n]: ",
221
factory.stdout.getvalue())
420
factory = _mod_ui.SilentUIFactory()
421
stdout = tests.StringIOWrapper()
424
self.apply_redirected,
425
None, stdout, stdout, factory.get_boolean, u"foo")
428
class TestUIFactoryTests(tests.TestCase):
430
def test_test_ui_factory_progress(self):
431
# there's no output; we just want to make sure this doesn't crash -
432
# see https://bugs.launchpad.net/bzr/+bug/408201
433
ui = tests.TestUIFactory()
434
pb = ui.nested_progress_bar()
440
class CannedInputUIFactoryTests(tests.TestCase):
442
def test_canned_input_get_input(self):
443
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
444
self.assertEqual(True, uif.get_boolean(u'Extra cheese?'))
445
self.assertEqual('mbp', uif.get_username(u'Enter your user name'))
446
self.assertEqual('password',
447
uif.get_password(u'Password for %(host)s',
449
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
452
class TestBoolFromString(tests.TestCase):
454
def assertIsTrue(self, s, accepted_values=None):
455
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
456
self.assertEquals(True, res)
458
def assertIsFalse(self, s, accepted_values=None):
459
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
460
self.assertEquals(False, res)
462
def assertIsNone(self, s, accepted_values=None):
463
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
464
self.assertIs(None, res)
466
def test_know_valid_values(self):
467
self.assertIsTrue('true')
468
self.assertIsFalse('false')
469
self.assertIsTrue('1')
470
self.assertIsFalse('0')
471
self.assertIsTrue('on')
472
self.assertIsFalse('off')
473
self.assertIsTrue('yes')
474
self.assertIsFalse('no')
475
self.assertIsTrue('y')
476
self.assertIsFalse('n')
477
# Also try some case variations
478
self.assertIsTrue('True')
479
self.assertIsFalse('False')
480
self.assertIsTrue('On')
481
self.assertIsFalse('Off')
482
self.assertIsTrue('ON')
483
self.assertIsFalse('OFF')
484
self.assertIsTrue('oN')
485
self.assertIsFalse('oFf')
487
def test_invalid_values(self):
488
self.assertIsNone(None)
489
self.assertIsNone('doubt')
490
self.assertIsNone('frue')
491
self.assertIsNone('talse')
492
self.assertIsNone('42')
494
def test_provided_values(self):
495
av = dict(y=True, n=False, yes=True, no=False)
496
self.assertIsTrue('y', av)
497
self.assertIsTrue('Y', av)
498
self.assertIsTrue('Yes', av)
499
self.assertIsFalse('n', av)
500
self.assertIsFalse('N', av)
501
self.assertIsFalse('No', av)
502
self.assertIsNone('1', av)
503
self.assertIsNone('0', av)
504
self.assertIsNone('on', av)
505
self.assertIsNone('off', av)
508
class TestConfirmationUserInterfacePolicy(tests.TestCase):
510
def test_confirm_action_default(self):
511
base_ui = _mod_ui.NoninteractiveUIFactory()
512
for answer in [True, False]:
514
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
515
.confirm_action("Do something?",
516
"bzrlib.tests.do_something", {}),
519
def test_confirm_action_specific(self):
520
base_ui = _mod_ui.NoninteractiveUIFactory()
521
for default_answer in [True, False]:
522
for specific_answer in [True, False]:
523
for conf_id in ['given_id', 'other_id']:
524
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
525
base_ui, default_answer, dict(given_id=specific_answer))
526
result = wrapper.confirm_action("Do something?", conf_id, {})
527
if conf_id == 'given_id':
528
self.assertEquals(result, specific_answer)
530
self.assertEquals(result, default_answer)
533
base_ui = _mod_ui.NoninteractiveUIFactory()
534
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
535
base_ui, True, dict(a=2))
536
self.assertThat(repr(wrapper),
537
Equals("ConfirmationUserInterfacePolicy("
538
"NoninteractiveUIFactory(), True, {'a': 2})"))
541
class TestProgressRecordingUI(tests.TestCase):
542
"""Test test-oriented UIFactory that records progress updates"""
544
def test_nested_ignore_depth_beyond_one(self):
545
# we only want to capture the first level out progress, not
546
# want sub-components might do. So we have nested bars ignored.
547
factory = ProgressRecordingUIFactory()
548
pb1 = factory.nested_progress_bar()
549
pb1.update('foo', 0, 1)
550
pb2 = factory.nested_progress_bar()
551
pb2.update('foo', 0, 1)
554
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)