17
17
"""Tests for the bzrlib ui
24
22
from StringIO import StringIO
24
from testtools.matchers import *
26
26
from bzrlib import (
33
from bzrlib.symbol_versioning import (
32
from bzrlib.tests import (
36
from bzrlib.tests import test_progress
37
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.assertEquals(output.encoding, enc)
40
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.assertEquals(ui.confirm_action(u'Should %(thing)s pass?',
79
'bzrlib.tests.test_ui.confirmation',
42
83
def test_text_factory_ascii_password(self):
43
ui = tests.TestUIFactory(stdin='secret\n',
44
stdout=tests.StringIOWrapper(),
45
stderr=tests.StringIOWrapper())
84
ui = self.make_test_ui_factory('secret\n')
46
85
pb = ui.nested_progress_bar()
48
87
self.assertEqual('secret',
60
99
def test_text_factory_utf8_password(self):
61
"""Test an utf8 password.
63
We can't predict what encoding users will have for stdin, so we force
64
it to utf8 to test that we transport the password correctly.
66
ui = tests.TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
67
stdout=tests.StringIOWrapper(),
68
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()
69
105
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
70
pb = ui.nested_progress_bar()
72
password = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
74
u'Hello \u1234 %(user)s',
76
# We use StringIO objects, we need to decode them
77
self.assertEqual(u'baz\u1234', password.decode('utf8'))
78
self.assertEqual(u'Hello \u1234 some\u1234: ',
79
ui.stderr.getvalue().decode('utf8'))
80
# stdin and stdout should be empty
81
self.assertEqual('', ui.stdin.readline())
82
self.assertEqual('', ui.stdout.readline())
86
def test_progress_note(self):
87
stderr = tests.StringIOWrapper()
88
stdout = tests.StringIOWrapper()
89
ui_factory = _mod_ui_text.TextUIFactory(stdin=tests.StringIOWrapper(''),
92
pb = ui_factory.nested_progress_bar()
94
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
97
self.assertEqual(None, result)
98
self.assertEqual("t\n", stdout.getvalue())
99
# Since there was no update() call, there should be no clear() call
100
self.failIf(re.search(r'^\r {10,}\r$',
101
stderr.getvalue()) is not None,
102
'We cleared the stderr without anything to put there')
106
def test_progress_note_clears(self):
107
stderr = test_progress._TTYStringIO()
108
stdout = test_progress._TTYStringIO()
109
# so that we get a TextProgressBar
110
os.environ['TERM'] = 'xterm'
111
ui_factory = _mod_ui_text.TextUIFactory(
112
stdin=tests.StringIOWrapper(''),
113
stdout=stdout, stderr=stderr)
114
self.assertIsInstance(ui_factory._progress_view,
115
_mod_ui_text.TextProgressView)
116
pb = ui_factory.nested_progress_bar()
118
# Create a progress update that isn't throttled
120
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
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$')
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())
131
114
def test_text_ui_get_boolean(self):
132
115
stdin = tests.StringIOWrapper("y\n" # True
134
119
"yes with garbage\nY\n" # True
135
120
"not an answer\nno\n" # False
136
121
"I'm sure!\nyes\n" # True
139
124
stdout = tests.StringIOWrapper()
140
125
stderr = tests.StringIOWrapper()
141
126
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
142
self.assertEqual(True, factory.get_boolean(""))
143
self.assertEqual(False, factory.get_boolean(""))
144
self.assertEqual(True, factory.get_boolean(""))
145
self.assertEqual(False, factory.get_boolean(""))
146
self.assertEqual(True, factory.get_boolean(""))
147
self.assertEqual(False, factory.get_boolean(""))
148
self.assertEqual("foo\n", factory.stdin.read())
149
# stdin should be empty
150
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())
152
205
def test_text_ui_get_integer(self):
153
206
stdin = tests.StringIOWrapper(
157
210
stdout = tests.StringIOWrapper()
158
211
stderr = tests.StringIOWrapper()
159
212
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
160
self.assertEqual(1, factory.get_integer(""))
161
self.assertEqual(-2, factory.get_integer(""))
162
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""))
164
217
def test_text_factory_prompt(self):
165
218
# see <https://launchpad.net/bugs/365891>
166
219
StringIO = tests.StringIOWrapper
167
220
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
168
factory.prompt('foo %2e')
221
factory.prompt(u'foo %2e')
169
222
self.assertEqual('', factory.stdout.getvalue())
170
223
self.assertEqual('foo %2e', factory.stderr.getvalue())
172
225
def test_text_factory_prompts_and_clears(self):
173
226
# a get_boolean call should clear the pb before prompting
174
out = test_progress._TTYStringIO()
175
os.environ['TERM'] = 'xterm'
228
self.overrideEnv('TERM', 'xterm')
176
229
factory = _mod_ui_text.TextUIFactory(
177
230
stdin=tests.StringIOWrapper("yada\ny\n"),
178
231
stdout=out, stderr=out)
232
factory._avail_width = lambda: 79
179
233
pb = factory.nested_progress_bar()
180
234
pb.show_bar = False
181
235
pb.show_spinner = False
209
263
def test_text_ui_getusername(self):
210
factory = _mod_ui_text.TextUIFactory(None, None, None)
211
factory.stdin = tests.StringIOWrapper("someuser\n\n")
212
factory.stdout = tests.StringIOWrapper()
213
factory.stderr = tests.StringIOWrapper()
214
factory.stdout.encoding = "utf8"
215
# there is no output from the base factory
216
self.assertEqual("someuser",
217
factory.get_username('Hello %(host)s', host='some'))
218
self.assertEquals("Hello some: ", factory.stderr.getvalue())
219
self.assertEquals('', factory.stdout.getvalue())
220
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.assertEquals('Hello some: ', ui.stderr.getvalue())
272
self.assertEquals('', ui.stdout.getvalue())
273
self.assertEqual('', ui.get_username(u"Gebruiker"))
221
274
# stdin should be empty
222
self.assertEqual('', factory.stdin.readline())
275
self.assertEqual('', ui.stdin.readline())
224
277
def test_text_ui_getusername_utf8(self):
225
ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
226
stdout=tests.StringIOWrapper(),
227
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()
228
282
ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
229
pb = ui.nested_progress_bar()
231
# there is no output from the base factory
232
username = self.apply_redirected(ui.stdin, ui.stdout, ui.stderr,
233
ui.get_username, u'Hello\u1234 %(host)s', host=u'some\u1234')
234
self.assertEquals(u"someuser\u1234", username.decode('utf8'))
235
self.assertEquals(u"Hello\u1234 some\u1234: ",
236
ui.stderr.getvalue().decode("utf8"))
237
self.assertEquals('', ui.stdout.getvalue())
283
username = ui.get_username(u'Hello %(host)s', host=u'some\u1234')
284
self.assertEquals(u"someuser\u1234", username)
285
self.assertEquals(u"Hello some\u1234: ",
286
ui.stderr.getvalue().decode("utf8"))
287
self.assertEquals('', ui.stdout.getvalue())
241
289
def test_quietness(self):
242
os.environ['BZR_PROGRESS_BAR'] = 'text'
290
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
243
291
ui_factory = _mod_ui_text.TextUIFactory(None,
244
test_progress._TTYStringIO(),
245
test_progress._TTYStringIO())
246
294
self.assertIsInstance(ui_factory._progress_view,
247
295
_mod_ui_text.TextProgressView)
248
296
ui_factory.be_quiet(True)
345
388
def test_text_ui_non_terminal(self):
346
389
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
347
stdin = test_progress._NonTTYStringIO('')
348
stderr = test_progress._NonTTYStringIO()
349
stdout = test_progress._NonTTYStringIO()
390
stdin = NonTTYStringIO('')
391
stderr = NonTTYStringIO()
392
stdout = NonTTYStringIO()
350
393
for term_type in ['dumb', None, 'xterm']:
351
if term_type is None:
352
del os.environ['TERM']
354
os.environ['TERM'] = term_type
394
self.overrideEnv('TERM', term_type)
355
395
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
356
396
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
357
397
'TERM=%r' % (term_type,))
398
438
def test_canned_input_get_input(self):
399
439
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
400
self.assertEqual(True, uif.get_boolean('Extra cheese?'))
401
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'))
402
442
self.assertEqual('password',
403
uif.get_password('Password for %(host)s',
443
uif.get_password(u'Password for %(host)s',
404
444
host='example.com'))
405
self.assertEqual(42, uif.get_integer('And all that jazz ?'))
445
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
408
448
class TestBoolFromString(tests.TestCase):
459
499
self.assertIsNone('0', av)
460
500
self.assertIsNone('on', av)
461
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.assertEquals(result, specific_answer)
526
self.assertEquals(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)