87
80
We can't predict what encoding users will have for stdin, so we force
88
81
it to utf8 to test that we transport the password correctly.
90
ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
91
stdout=StringIOWrapper())
92
ui.stdin.encoding = 'utf8'
93
ui.stdout.encoding = ui.stdin.encoding
83
ui = tests.TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
84
stdout=tests.StringIOWrapper(),
85
stderr=tests.StringIOWrapper())
86
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
94
87
pb = ui.nested_progress_bar()
96
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stdout,
89
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
98
91
u'Hello \u1234 %(user)s',
99
92
user=u'some\u1234')
100
93
# We use StringIO objects, we need to decode them
101
94
self.assertEqual(u'baz\u1234', password.decode('utf8'))
102
95
self.assertEqual(u'Hello \u1234 some\u1234: ',
103
ui.stdout.getvalue().decode('utf8'))
104
# stdin should be empty
96
ui.stderr.getvalue().decode('utf8'))
97
# stdin and stdout should be empty
105
98
self.assertEqual('', ui.stdin.readline())
109
def test_progress_note(self):
112
ui_factory = TextUIFactory(stdin=StringIO(''),
115
pb = ui_factory.nested_progress_bar()
117
result = pb.note('t')
118
self.assertEqual(None, result)
119
self.assertEqual("t\n", stdout.getvalue())
120
# Since there was no update() call, there should be no clear() call
121
self.failIf(re.search(r'^\r {10,}\r$',
122
stderr.getvalue()) is not None,
123
'We cleared the stderr without anything to put there')
127
def test_progress_note_clears(self):
130
# The PQM redirects the output to a file, so it
131
# defaults to creating a Dots progress bar. we
132
# need to force it to believe we are a TTY
133
ui_factory = TextUIFactory(
135
stdout=stdout, stderr=stderr)
136
pb = ui_factory.nested_progress_bar()
138
# Create a progress update that isn't throttled
140
result = pb.note('t')
141
self.assertEqual(None, result)
142
self.assertEqual("t\n", stdout.getvalue())
143
# the exact contents will depend on the terminal width and we don't
144
# care about that right now - but you're probably running it on at
145
# least a 10-character wide terminal :)
146
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
150
def test_progress_nested(self):
151
# test factory based nested and popping.
152
ui = TextUIFactory(None, None, None)
153
pb1 = ui.nested_progress_bar()
154
pb2 = ui.nested_progress_bar()
155
# You do get a warning if the outermost progress bar wasn't finished
156
# first - it's not clear if this is really useful or if it should just
157
# become orphaned -- mbp 20090120
158
warnings, _ = self.callCatchWarnings(pb1.finished)
159
if len(warnings) != 1:
160
self.fail("unexpected warnings: %r" % (warnings,))
164
def test_progress_stack(self):
165
# test the progress bar stack which the default text factory
169
# make a stack, which accepts parameters like a pb.
170
stack = self.applyDeprecated(
171
deprecated_in((1, 12, 0)),
173
to_file=stderr, to_messages_file=stdout)
175
self.assertFalse(getattr(stack, 'note', False))
176
pb1 = stack.get_nested()
177
pb2 = stack.get_nested()
178
warnings, _ = self.callCatchWarnings(pb1.finished)
179
self.assertEqual(len(warnings), 1)
182
# the text ui factory never actually removes the stack once its setup.
183
# we need to be able to nest again correctly from here.
184
pb1 = stack.get_nested()
185
pb2 = stack.get_nested()
186
warnings, _ = self.callCatchWarnings(pb1.finished)
187
self.assertEqual(len(warnings), 1)
191
def assert_get_bool_acceptance_of_user_input(self, factory):
192
factory.stdin = StringIO("y\nyes with garbage\n"
193
"yes\nn\nnot an answer\n"
195
factory.stdout = StringIO()
196
# there is no output from the base factory
197
self.assertEqual(True, factory.get_boolean(""))
198
self.assertEqual(True, factory.get_boolean(""))
199
self.assertEqual(False, factory.get_boolean(""))
99
self.assertEqual('', ui.stdout.readline())
103
def test_text_ui_get_boolean(self):
104
stdin = tests.StringIOWrapper("y\n" # True
106
"yes with garbage\nY\n" # True
107
"not an answer\nno\n" # False
108
"I'm sure!\nyes\n" # True
111
stdout = tests.StringIOWrapper()
112
stderr = tests.StringIOWrapper()
113
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
114
self.assertEqual(True, factory.get_boolean(""))
115
self.assertEqual(False, factory.get_boolean(""))
116
self.assertEqual(True, factory.get_boolean(""))
117
self.assertEqual(False, factory.get_boolean(""))
118
self.assertEqual(True, factory.get_boolean(""))
200
119
self.assertEqual(False, factory.get_boolean(""))
201
120
self.assertEqual("foo\n", factory.stdin.read())
202
121
# stdin should be empty
203
122
self.assertEqual('', factory.stdin.readline())
205
def test_silent_ui_getbool(self):
206
factory = SilentUIFactory()
207
self.assert_get_bool_acceptance_of_user_input(factory)
209
def test_silent_factory_prompts_silently(self):
210
factory = SilentUIFactory()
212
factory.stdin = StringIO("y\n")
213
self.assertEqual(True,
214
self.apply_redirected(None, stdout, stdout,
215
factory.get_boolean, "foo"))
216
self.assertEqual("", stdout.getvalue())
217
# stdin should be empty
218
self.assertEqual('', factory.stdin.readline())
220
def test_text_ui_getbool(self):
221
factory = TextUIFactory(None, None, None)
222
self.assert_get_bool_acceptance_of_user_input(factory)
124
def test_text_ui_get_integer(self):
125
stdin = tests.StringIOWrapper(
128
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
129
stdout = tests.StringIOWrapper()
130
stderr = tests.StringIOWrapper()
131
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
132
self.assertEqual(1, factory.get_integer(""))
133
self.assertEqual(-2, factory.get_integer(""))
134
self.assertEqual(42, factory.get_integer(""))
136
def test_text_factory_prompt(self):
137
# see <https://launchpad.net/bugs/365891>
138
StringIO = tests.StringIOWrapper
139
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
140
factory.prompt('foo %2e')
141
self.assertEqual('', factory.stdout.getvalue())
142
self.assertEqual('foo %2e', factory.stderr.getvalue())
224
144
def test_text_factory_prompts_and_clears(self):
225
145
# a get_boolean call should clear the pb before prompting
227
factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
146
out = test_progress._TTYStringIO()
147
os.environ['TERM'] = 'xterm'
148
factory = _mod_ui_text.TextUIFactory(
149
stdin=tests.StringIOWrapper("yada\ny\n"),
150
stdout=out, stderr=out)
151
factory._avail_width = lambda: 79
228
152
pb = factory.nested_progress_bar()
229
153
pb.show_bar = False
230
154
pb.show_spinner = False
258
class TestTextProgressView(TestCase):
259
"""Tests for text display of progress bars.
261
# XXX: These might be a bit easier to write if the rendering and
262
# state-maintaining parts of TextProgressView were more separate, and if
263
# the progress task called back directly to its own view not to the ui
264
# factory. -- mbp 20090312
266
def _make_factory(self):
268
uif = TextUIFactory(stderr=out)
269
uif._progress_view._width = 80
272
def test_render_progress_easy(self):
273
"""Just one task and one quarter done"""
274
out, uif = self._make_factory()
275
task = uif.nested_progress_bar()
276
task.update('reticulating splines', 5, 20)
278
'\r[####/ ] reticulating splines 5/20 \r'
281
def test_render_progress_nested(self):
282
"""Tasks proportionally contribute to overall progress"""
283
out, uif = self._make_factory()
284
task = uif.nested_progress_bar()
285
task.update('reticulating splines', 0, 2)
286
task2 = uif.nested_progress_bar()
287
task2.update('stage2', 1, 2)
288
# so we're in the first half of the main task, and half way through
291
r'[####\ ] reticulating splines:stage2 1/2'
292
, uif._progress_view._render_line())
293
# if the nested task is complete, then we're all the way through the
294
# first half of the overall work
295
task2.update('stage2', 2, 2)
297
r'[#########| ] reticulating splines:stage2 2/2'
298
, uif._progress_view._render_line())
300
def test_render_progress_sub_nested(self):
301
"""Intermediate tasks don't mess up calculation."""
302
out, uif = self._make_factory()
303
task_a = uif.nested_progress_bar()
304
task_a.update('a', 0, 2)
305
task_b = uif.nested_progress_bar()
307
task_c = uif.nested_progress_bar()
308
task_c.update('c', 1, 2)
309
# the top-level task is in its first half; the middle one has no
310
# progress indication, just a label; and the bottom one is half done,
311
# so the overall fraction is 1/4
313
r'[####| ] a:b:c 1/2'
314
, uif._progress_view._render_line())
182
def test_text_ui_getusername(self):
183
factory = _mod_ui_text.TextUIFactory(None, None, None)
184
factory.stdin = tests.StringIOWrapper("someuser\n\n")
185
factory.stdout = tests.StringIOWrapper()
186
factory.stderr = tests.StringIOWrapper()
187
factory.stdout.encoding = "utf8"
188
# there is no output from the base factory
189
self.assertEqual("someuser",
190
factory.get_username('Hello %(host)s', host='some'))
191
self.assertEquals("Hello some: ", factory.stderr.getvalue())
192
self.assertEquals('', factory.stdout.getvalue())
193
self.assertEqual("", factory.get_username("Gebruiker"))
194
# stdin should be empty
195
self.assertEqual('', factory.stdin.readline())
197
def test_text_ui_getusername_utf8(self):
198
ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
199
stdout=tests.StringIOWrapper(),
200
stderr=tests.StringIOWrapper())
201
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
202
pb = ui.nested_progress_bar()
204
# there is no output from the base factory
205
username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
206
ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234')
207
self.assertEquals(u"someuser\u1234", username.decode('utf8'))
208
self.assertEquals(u"Hello\u1234 some\u1234: ",
209
ui.stderr.getvalue().decode("utf8"))
210
self.assertEquals('', ui.stdout.getvalue())
214
def test_quietness(self):
215
os.environ['BZR_PROGRESS_BAR'] = 'text'
216
ui_factory = _mod_ui_text.TextUIFactory(None,
217
test_progress._TTYStringIO(),
218
test_progress._TTYStringIO())
219
self.assertIsInstance(ui_factory._progress_view,
220
_mod_ui_text.TextProgressView)
221
ui_factory.be_quiet(True)
222
self.assertIsInstance(ui_factory._progress_view,
223
_mod_ui_text.NullProgressView)
225
def test_text_ui_show_user_warning(self):
226
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
227
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5
230
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
231
remote_fmt = remote.RemoteRepositoryFormat()
232
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
233
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
234
to_format=remote_fmt)
235
self.assertEquals('', out.getvalue())
236
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
237
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
238
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
239
"the same format for better performance.\n",
241
# and now with it suppressed please
244
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
245
ui.suppressed_warnings.add('cross_format_fetch')
246
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
247
to_format=remote_fmt)
248
self.assertEquals('', out.getvalue())
249
self.assertEquals('', err.getvalue())
252
class TestTextUIOutputStream(tests.TestCase):
253
"""Tests for output stream that synchronizes with progress bar."""
255
def test_output_clears_terminal(self):
256
stdout = tests.StringIOWrapper()
257
stderr = tests.StringIOWrapper()
260
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
261
uif.clear_term = lambda: clear_calls.append('clear')
263
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
264
stream.write("Hello world!\n")
265
stream.write("there's more...\n")
266
stream.writelines(["1\n", "2\n", "3\n"])
268
self.assertEqual(stdout.getvalue(),
272
self.assertEqual(['clear', 'clear', 'clear'],
278
class UITests(tests.TestCase):
280
def test_progress_construction(self):
281
"""TextUIFactory constructs the right progress view.
283
TTYStringIO = test_progress._TTYStringIO
284
FileStringIO = tests.StringIOWrapper
285
for (file_class, term, pb, expected_pb_class) in (
286
# on an xterm, either use them or not as the user requests,
287
# otherwise default on
288
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
289
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
290
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
291
# on a dumb terminal, again if there's explicit configuration do
292
# it, otherwise default off
293
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
294
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
295
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
296
# on a non-tty terminal, it's null regardless of $TERM
297
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
298
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
299
# however, it can still be forced on
300
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
302
os.environ['TERM'] = term
304
if 'BZR_PROGRESS_BAR' in os.environ:
305
del os.environ['BZR_PROGRESS_BAR']
307
os.environ['BZR_PROGRESS_BAR'] = pb
308
stdin = file_class('')
309
stderr = file_class()
310
stdout = file_class()
311
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
312
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
313
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
314
self.assertIsInstance(uif.make_progress_view(),
316
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
318
def test_text_ui_non_terminal(self):
319
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
320
stdin = test_progress._NonTTYStringIO('')
321
stderr = test_progress._NonTTYStringIO()
322
stdout = test_progress._NonTTYStringIO()
323
for term_type in ['dumb', None, 'xterm']:
324
if term_type is None:
325
del os.environ['TERM']
327
os.environ['TERM'] = term_type
328
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
329
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
330
'TERM=%r' % (term_type,))
333
class SilentUITests(tests.TestCase):
335
def test_silent_factory_get_password(self):
336
# A silent factory that can't do user interaction can't get a
337
# password. Possibly it should raise a more specific error but it
339
ui = _mod_ui.SilentUIFactory()
340
stdout = tests.StringIOWrapper()
343
self.apply_redirected,
344
None, stdout, stdout, ui.get_password)
345
# and it didn't write anything out either
346
self.assertEqual('', stdout.getvalue())
348
def test_silent_ui_getbool(self):
349
factory = _mod_ui.SilentUIFactory()
350
stdout = tests.StringIOWrapper()
353
self.apply_redirected,
354
None, stdout, stdout, factory.get_boolean, "foo")
357
class TestUIFactoryTests(tests.TestCase):
359
def test_test_ui_factory_progress(self):
360
# there's no output; we just want to make sure this doesn't crash -
361
# see https://bugs.launchpad.net/bzr/+bug/408201
362
ui = tests.TestUIFactory()
363
pb = ui.nested_progress_bar()
369
class CannedInputUIFactoryTests(tests.TestCase):
371
def test_canned_input_get_input(self):
372
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
373
self.assertEqual(True, uif.get_boolean('Extra cheese?'))
374
self.assertEqual('mbp', uif.get_username('Enter your user name'))
375
self.assertEqual('password',
376
uif.get_password('Password for %(host)s',
378
self.assertEqual(42, uif.get_integer('And all that jazz ?'))
381
class TestBoolFromString(tests.TestCase):
383
def assertIsTrue(self, s, accepted_values=None):
384
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
385
self.assertEquals(True, res)
387
def assertIsFalse(self, s, accepted_values=None):
388
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
389
self.assertEquals(False, res)
391
def assertIsNone(self, s, accepted_values=None):
392
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
393
self.assertIs(None, res)
395
def test_know_valid_values(self):
396
self.assertIsTrue('true')
397
self.assertIsFalse('false')
398
self.assertIsTrue('1')
399
self.assertIsFalse('0')
400
self.assertIsTrue('on')
401
self.assertIsFalse('off')
402
self.assertIsTrue('yes')
403
self.assertIsFalse('no')
404
self.assertIsTrue('y')
405
self.assertIsFalse('n')
406
# Also try some case variations
407
self.assertIsTrue('True')
408
self.assertIsFalse('False')
409
self.assertIsTrue('On')
410
self.assertIsFalse('Off')
411
self.assertIsTrue('ON')
412
self.assertIsFalse('OFF')
413
self.assertIsTrue('oN')
414
self.assertIsFalse('oFf')
416
def test_invalid_values(self):
417
self.assertIsNone(None)
418
self.assertIsNone('doubt')
419
self.assertIsNone('frue')
420
self.assertIsNone('talse')
421
self.assertIsNone('42')
423
def test_provided_values(self):
424
av = dict(y=True, n=False, yes=True, no=False)
425
self.assertIsTrue('y', av)
426
self.assertIsTrue('Y', av)
427
self.assertIsTrue('Yes', av)
428
self.assertIsFalse('n', av)
429
self.assertIsFalse('N', av)
430
self.assertIsFalse('No', av)
431
self.assertIsNone('1', av)
432
self.assertIsNone('0', av)
433
self.assertIsNone('on', av)
434
self.assertIsNone('off', av)