17
17
"""Tests for the bzrlib ui
22
from StringIO import StringIO
24
from testtools.matchers import *
24
26
from bzrlib import (
29
from bzrlib.symbol_versioning import (
32
from bzrlib.tests import (
32
from bzrlib.tests import test_progress
33
35
from bzrlib.ui import text as _mod_ui_text
36
from bzrlib.tests.testui import (
37
ProgressRecordingUIFactory,
41
class TTYStringIO(StringIO):
42
"""A helper class which makes a StringIO look like a terminal"""
48
class NonTTYStringIO(StringIO):
49
"""Helper that implements isatty() but returns False"""
55
class TestUIConfiguration(tests.TestCaseWithTransport):
57
def test_output_encoding_configuration(self):
58
enc = fixtures.generate_unicode_encodings().next()
59
config.GlobalStack().set('output_encoding', enc)
60
ui = tests.TestUIFactory(stdin=None,
61
stdout=tests.StringIOWrapper(),
62
stderr=tests.StringIOWrapper())
63
output = ui.make_output_stream()
64
self.assertEqual(output.encoding, enc)
36
67
class TestTextUIFactory(tests.TestCase):
69
def make_test_ui_factory(self, stdin_contents):
70
ui = tests.TestUIFactory(stdin=stdin_contents,
71
stdout=tests.StringIOWrapper(),
72
stderr=tests.StringIOWrapper())
75
def test_text_factory_confirm(self):
76
# turns into reading a regular boolean
77
ui = self.make_test_ui_factory('n\n')
78
self.assertEqual(ui.confirm_action(u'Should %(thing)s pass?',
79
'bzrlib.tests.test_ui.confirmation',
38
83
def test_text_factory_ascii_password(self):
39
ui = tests.TestUIFactory(stdin='secret\n',
40
stdout=tests.StringIOWrapper(),
41
stderr=tests.StringIOWrapper())
84
ui = self.make_test_ui_factory('secret\n')
42
85
pb = ui.nested_progress_bar()
44
87
self.assertEqual('secret',
56
99
def test_text_factory_utf8_password(self):
57
"""Test an utf8 password.
59
We can't predict what encoding users will have for stdin, so we force
60
it to utf8 to test that we transport the password correctly.
62
ui = tests.TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
63
stdout=tests.StringIOWrapper(),
64
stderr=tests.StringIOWrapper())
100
"""Test an utf8 password."""
101
ui = _mod_ui_text.TextUIFactory(None, None, None)
102
ui.stdin = tests.StringIOWrapper(u'baz\u1234'.encode('utf8'))
103
ui.stdout = tests.StringIOWrapper()
104
ui.stderr = tests.StringIOWrapper()
65
105
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
66
pb = ui.nested_progress_bar()
68
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
70
u'Hello \u1234 %(user)s',
72
# We use StringIO objects, we need to decode them
73
self.assertEqual(u'baz\u1234', password.decode('utf8'))
74
self.assertEqual(u'Hello \u1234 some\u1234: ',
75
ui.stderr.getvalue().decode('utf8'))
76
# stdin and stdout should be empty
77
self.assertEqual('', ui.stdin.readline())
78
self.assertEqual('', ui.stdout.readline())
82
def test_progress_note(self):
83
stderr = tests.StringIOWrapper()
84
stdout = tests.StringIOWrapper()
85
ui_factory = _mod_ui_text.TextUIFactory(stdin=tests.StringIOWrapper(''),
88
pb = ui_factory.nested_progress_bar()
90
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
93
self.assertEqual(None, result)
94
self.assertEqual("t\n", stdout.getvalue())
95
# Since there was no update() call, there should be no clear() call
96
self.failIf(re.search(r'^\r {10,}\r$',
97
stderr.getvalue()) is not None,
98
'We cleared the stderr without anything to put there')
102
def test_progress_note_clears(self):
103
stderr = test_progress._TTYStringIO()
104
stdout = test_progress._TTYStringIO()
105
# so that we get a TextProgressBar
106
os.environ['TERM'] = 'xterm'
107
ui_factory = _mod_ui_text.TextUIFactory(
108
stdin=tests.StringIOWrapper(''),
109
stdout=stdout, stderr=stderr)
110
self.assertIsInstance(ui_factory._progress_view,
111
_mod_ui_text.TextProgressView)
112
pb = ui_factory.nested_progress_bar()
114
# Create a progress update that isn't throttled
116
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
118
self.assertEqual(None, result)
119
self.assertEqual("t\n", stdout.getvalue())
120
# the exact contents will depend on the terminal width and we don't
121
# care about that right now - but you're probably running it on at
122
# least a 10-character wide terminal :)
123
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
127
def test_progress_nested(self):
128
# test factory based nested and popping.
129
ui = _mod_ui_text.TextUIFactory(None, None, None)
130
pb1 = ui.nested_progress_bar()
131
pb2 = ui.nested_progress_bar()
132
# You do get a warning if the outermost progress bar wasn't finished
133
# first - it's not clear if this is really useful or if it should just
134
# become orphaned -- mbp 20090120
135
warnings, _ = self.callCatchWarnings(pb1.finished)
136
if len(warnings) != 1:
137
self.fail("unexpected warnings: %r" % (warnings,))
106
password = ui.get_password(u'Hello \u1234 %(user)s', user=u'some\u1234')
107
self.assertEqual(u'baz\u1234', password)
108
self.assertEqual(u'Hello \u1234 some\u1234: ',
109
ui.stderr.getvalue().decode('utf8'))
110
# stdin and stdout should be empty
111
self.assertEqual('', ui.stdin.readline())
112
self.assertEqual('', ui.stdout.getvalue())
141
114
def test_text_ui_get_boolean(self):
142
115
stdin = tests.StringIOWrapper("y\n" # True
144
119
"yes with garbage\nY\n" # True
145
120
"not an answer\nno\n" # False
146
121
"I'm sure!\nyes\n" # True
149
124
stdout = tests.StringIOWrapper()
150
125
stderr = tests.StringIOWrapper()
151
126
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
152
self.assertEqual(True, factory.get_boolean(""))
153
self.assertEqual(False, factory.get_boolean(""))
154
self.assertEqual(True, factory.get_boolean(""))
155
self.assertEqual(False, factory.get_boolean(""))
156
self.assertEqual(True, factory.get_boolean(""))
157
self.assertEqual(False, factory.get_boolean(""))
158
self.assertEqual("foo\n", factory.stdin.read())
159
# stdin should be empty
160
self.assertEqual('', factory.stdin.readline())
127
self.assertEqual(True, factory.get_boolean(u""))
128
self.assertEqual(False, factory.get_boolean(u""))
129
self.assertEqual(True, factory.get_boolean(u""))
130
self.assertEqual(False, factory.get_boolean(u""))
131
self.assertEqual(True, factory.get_boolean(u""))
132
self.assertEqual(False, factory.get_boolean(u""))
133
self.assertEqual(True, factory.get_boolean(u""))
134
self.assertEqual(False, factory.get_boolean(u""))
135
self.assertEqual("foo\n", factory.stdin.read())
136
# stdin should be empty
137
self.assertEqual('', factory.stdin.readline())
138
# return false on EOF
139
self.assertEqual(False, factory.get_boolean(u""))
141
def test_text_ui_choose_bad_parameters(self):
142
stdin = tests.StringIOWrapper()
143
stdout = tests.StringIOWrapper()
144
stderr = tests.StringIOWrapper()
145
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
146
# invalid default index
147
self.assertRaises(ValueError, factory.choose, u"", u"&Yes\n&No", 3)
149
self.assertRaises(ValueError, factory.choose, u"", u"&choice\n&ChOiCe")
150
# duplicated shortcut
151
self.assertRaises(ValueError, factory.choose, u"", u"&choice1\nchoi&ce2")
153
def test_text_ui_choose_prompt(self):
154
stdin = tests.StringIOWrapper()
155
stdout = tests.StringIOWrapper()
156
stderr = tests.StringIOWrapper()
157
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
158
# choices with explicit shortcuts
159
factory.choose(u"prompt", u"&yes\n&No\nmore &info")
160
self.assertEqual("prompt ([y]es, [N]o, more [i]nfo): \n", factory.stderr.getvalue())
161
# automatic shortcuts
162
factory.stderr.truncate(0)
163
factory.choose(u"prompt", u"yes\nNo\nmore info")
164
self.assertEqual("prompt ([y]es, [N]o, [m]ore info): \n", factory.stderr.getvalue())
166
def test_text_ui_choose_return_values(self):
167
choose = lambda: factory.choose(u"", u"&Yes\n&No\nMaybe\nmore &info", 3)
168
stdin = tests.StringIOWrapper("y\n" # 0
172
"b\na\nd \n" # bad shortcuts, all ignored
173
"yes with garbage\nY\n" # 0
174
"not an answer\nno\n" # 1
175
"info\nmore info\n" # 3
178
stdout = tests.StringIOWrapper()
179
stderr = tests.StringIOWrapper()
180
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
181
self.assertEqual(0, choose())
182
self.assertEqual(1, choose())
183
self.assertEqual(3, choose())
184
self.assertEqual(1, choose())
185
self.assertEqual(0, choose())
186
self.assertEqual(1, choose())
187
self.assertEqual(3, choose())
188
self.assertEqual(2, choose())
189
self.assertEqual("foo\n", factory.stdin.read())
190
# stdin should be empty
191
self.assertEqual('', factory.stdin.readline())
193
self.assertEqual(None, choose())
195
def test_text_ui_choose_no_default(self):
196
stdin = tests.StringIOWrapper(" \n" # no default, invalid!
199
stdout = tests.StringIOWrapper()
200
stderr = tests.StringIOWrapper()
201
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
202
self.assertEqual(0, factory.choose(u"", u"&Yes\n&No"))
203
self.assertEqual("foo\n", factory.stdin.read())
162
205
def test_text_ui_get_integer(self):
163
206
stdin = tests.StringIOWrapper(
167
210
stdout = tests.StringIOWrapper()
168
211
stderr = tests.StringIOWrapper()
169
212
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
170
self.assertEqual(1, factory.get_integer(""))
171
self.assertEqual(-2, factory.get_integer(""))
172
self.assertEqual(42, factory.get_integer(""))
213
self.assertEqual(1, factory.get_integer(u""))
214
self.assertEqual(-2, factory.get_integer(u""))
215
self.assertEqual(42, factory.get_integer(u""))
174
217
def test_text_factory_prompt(self):
175
218
# see <https://launchpad.net/bugs/365891>
176
219
StringIO = tests.StringIOWrapper
177
220
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
178
factory.prompt('foo %2e')
221
factory.prompt(u'foo %2e')
179
222
self.assertEqual('', factory.stdout.getvalue())
180
223
self.assertEqual('foo %2e', factory.stderr.getvalue())
182
225
def test_text_factory_prompts_and_clears(self):
183
226
# a get_boolean call should clear the pb before prompting
184
out = test_progress._TTYStringIO()
185
os.environ['TERM'] = 'xterm'
228
self.overrideEnv('TERM', 'xterm')
186
229
factory = _mod_ui_text.TextUIFactory(
187
230
stdin=tests.StringIOWrapper("yada\ny\n"),
188
231
stdout=out, stderr=out)
232
factory._avail_width = lambda: 79
189
233
pb = factory.nested_progress_bar()
190
234
pb.show_bar = False
191
235
pb.show_spinner = False
219
263
def test_text_ui_getusername(self):
220
factory = _mod_ui_text.TextUIFactory(None, None, None)
221
factory.stdin = tests.StringIOWrapper("someuser\n\n")
222
factory.stdout = tests.StringIOWrapper()
223
factory.stderr = tests.StringIOWrapper()
224
factory.stdout.encoding = "utf8"
225
# there is no output from the base factory
226
self.assertEqual("someuser",
227
factory.get_username('Hello %(host)s', host='some'))
228
self.assertEquals("Hello some: ", factory.stderr.getvalue())
229
self.assertEquals('', factory.stdout.getvalue())
230
self.assertEqual("", factory.get_username("Gebruiker"))
264
ui = _mod_ui_text.TextUIFactory(None, None, None)
265
ui.stdin = tests.StringIOWrapper('someuser\n\n')
266
ui.stdout = tests.StringIOWrapper()
267
ui.stderr = tests.StringIOWrapper()
268
ui.stdout.encoding = 'utf8'
269
self.assertEqual('someuser',
270
ui.get_username(u'Hello %(host)s', host='some'))
271
self.assertEqual('Hello some: ', ui.stderr.getvalue())
272
self.assertEqual('', ui.stdout.getvalue())
273
self.assertEqual('', ui.get_username(u"Gebruiker"))
231
274
# stdin should be empty
232
self.assertEqual('', factory.stdin.readline())
275
self.assertEqual('', ui.stdin.readline())
234
277
def test_text_ui_getusername_utf8(self):
235
ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
236
stdout=tests.StringIOWrapper(),
237
stderr=tests.StringIOWrapper())
278
ui = _mod_ui_text.TextUIFactory(None, None, None)
279
ui.stdin = tests.StringIOWrapper(u'someuser\u1234'.encode('utf8'))
280
ui.stdout = tests.StringIOWrapper()
281
ui.stderr = tests.StringIOWrapper()
238
282
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
239
pb = ui.nested_progress_bar()
241
# there is no output from the base factory
242
username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
243
ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234')
244
self.assertEquals(u"someuser\u1234", username.decode('utf8'))
245
self.assertEquals(u"Hello\u1234 some\u1234: ",
246
ui.stderr.getvalue().decode("utf8"))
247
self.assertEquals('', ui.stdout.getvalue())
283
username = ui.get_username(u'Hello %(host)s', host=u'some\u1234')
284
self.assertEqual(u"someuser\u1234", username)
285
self.assertEqual(u"Hello some\u1234: ",
286
ui.stderr.getvalue().decode("utf8"))
287
self.assertEqual('', ui.stdout.getvalue())
251
289
def test_quietness(self):
252
os.environ['BZR_PROGRESS_BAR'] = 'text'
290
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
253
291
ui_factory = _mod_ui_text.TextUIFactory(None,
254
test_progress._TTYStringIO(),
255
test_progress._TTYStringIO())
256
294
self.assertIsInstance(ui_factory._progress_view,
257
295
_mod_ui_text.TextProgressView)
258
296
ui_factory.be_quiet(True)
259
297
self.assertIsInstance(ui_factory._progress_view,
260
298
_mod_ui_text.NullProgressView)
300
def test_text_ui_show_user_warning(self):
301
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
302
from bzrlib.repofmt.knitpack_repo import RepositoryFormatKnitPack5
305
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
306
remote_fmt = remote.RemoteRepositoryFormat()
307
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
308
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
309
to_format=remote_fmt)
310
self.assertEqual('', out.getvalue())
311
self.assertEqual("Doing on-the-fly conversion from RepositoryFormat2a() to "
312
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
313
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
314
"the same format for better performance.\n",
316
# and now with it suppressed please
319
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
320
ui.suppressed_warnings.add('cross_format_fetch')
321
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
322
to_format=remote_fmt)
323
self.assertEqual('', out.getvalue())
324
self.assertEqual('', err.getvalue())
263
327
class TestTextUIOutputStream(tests.TestCase):
264
328
"""Tests for output stream that synchronizes with progress bar."""
329
388
def test_text_ui_non_terminal(self):
330
389
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
331
stdin = test_progress._NonTTYStringIO('')
332
stderr = test_progress._NonTTYStringIO()
333
stdout = test_progress._NonTTYStringIO()
390
stdin = NonTTYStringIO('')
391
stderr = NonTTYStringIO()
392
stdout = NonTTYStringIO()
334
393
for term_type in ['dumb', None, 'xterm']:
335
if term_type is None:
336
del os.environ['TERM']
338
os.environ['TERM'] = term_type
394
self.overrideEnv('TERM', term_type)
339
395
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
340
396
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
341
397
'TERM=%r' % (term_type,))
382
438
def test_canned_input_get_input(self):
383
439
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
384
self.assertEqual(True, uif.get_boolean('Extra cheese?'))
385
self.assertEqual('mbp', uif.get_username('Enter your user name'))
440
self.assertEqual(True, uif.get_boolean(u'Extra cheese?'))
441
self.assertEqual('mbp', uif.get_username(u'Enter your user name'))
386
442
self.assertEqual('password',
387
uif.get_password('Password for %(host)s',
443
uif.get_password(u'Password for %(host)s',
388
444
host='example.com'))
389
self.assertEqual(42, uif.get_integer('And all that jazz ?'))
445
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
392
448
class TestBoolFromString(tests.TestCase):
394
450
def assertIsTrue(self, s, accepted_values=None):
395
451
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
396
self.assertEquals(True, res)
452
self.assertEqual(True, res)
398
454
def assertIsFalse(self, s, accepted_values=None):
399
455
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
400
self.assertEquals(False, res)
456
self.assertEqual(False, res)
402
458
def assertIsNone(self, s, accepted_values=None):
403
459
res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
443
499
self.assertIsNone('0', av)
444
500
self.assertIsNone('on', av)
445
501
self.assertIsNone('off', av)
504
class TestConfirmationUserInterfacePolicy(tests.TestCase):
506
def test_confirm_action_default(self):
507
base_ui = _mod_ui.NoninteractiveUIFactory()
508
for answer in [True, False]:
510
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
511
.confirm_action("Do something?",
512
"bzrlib.tests.do_something", {}),
515
def test_confirm_action_specific(self):
516
base_ui = _mod_ui.NoninteractiveUIFactory()
517
for default_answer in [True, False]:
518
for specific_answer in [True, False]:
519
for conf_id in ['given_id', 'other_id']:
520
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
521
base_ui, default_answer, dict(given_id=specific_answer))
522
result = wrapper.confirm_action("Do something?", conf_id, {})
523
if conf_id == 'given_id':
524
self.assertEqual(result, specific_answer)
526
self.assertEqual(result, default_answer)
529
base_ui = _mod_ui.NoninteractiveUIFactory()
530
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
531
base_ui, True, dict(a=2))
532
self.assertThat(repr(wrapper),
533
Equals("ConfirmationUserInterfacePolicy("
534
"NoninteractiveUIFactory(), True, {'a': 2})"))
537
class TestProgressRecordingUI(tests.TestCase):
538
"""Test test-oriented UIFactory that records progress updates"""
540
def test_nested_ignore_depth_beyond_one(self):
541
# we only want to capture the first level out progress, not
542
# want sub-components might do. So we have nested bars ignored.
543
factory = ProgressRecordingUIFactory()
544
pb1 = factory.nested_progress_bar()
545
pb1.update('foo', 0, 1)
546
pb2 = factory.nested_progress_bar()
547
pb2.update('foo', 0, 1)
550
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)