14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for the bzrlib ui
17
"""Tests for the breezy ui."""
24
from StringIO import StringIO
21
from testtools.matchers import *
33
from bzrlib.symbol_versioning import (
36
from bzrlib.tests import test_progress
37
from bzrlib.ui import text as _mod_ui_text
33
from ..ui import text as _mod_ui_text
35
ProgressRecordingUIFactory,
39
class TestUIConfiguration(tests.TestCaseInTempDir):
41
def test_output_encoding_configuration(self):
42
enc = next(fixtures.generate_unicode_encodings())
43
config.GlobalStack().set('output_encoding', enc)
44
IO = ui_testing.BytesIOWithEncoding
45
ui = _mod_ui.make_ui_for_terminal(IO(), IO(), IO())
46
output = ui.make_output_stream()
47
self.assertEqual(output.encoding, enc)
40
50
class TestTextUIFactory(tests.TestCase):
52
def test_text_factory_confirm(self):
53
# turns into reading a regular boolean
54
ui = ui_testing.TestUIFactory('n\n')
55
self.assertEqual(ui.confirm_action(u'Should %(thing)s pass?',
56
'breezy.tests.test_ui.confirmation',
42
60
def test_text_factory_ascii_password(self):
43
ui = tests.TestUIFactory(stdin='secret\n',
44
stdout=tests.StringIOWrapper(),
45
stderr=tests.StringIOWrapper())
61
ui = ui_testing.TestUIFactory('secret\n')
46
62
pb = ui.nested_progress_bar()
48
64
self.assertEqual('secret',
60
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())
69
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$')
76
def test_text_factory_unicode_password(self):
77
"""Test a unicode password."""
78
ui = ui_testing.TextUIFactory(u'baz\u1234')
79
password = ui.get_password(u'Hello \u1234 %(user)s', user=u'some\u1234')
80
self.assertEqual(u'baz\u1234', password)
81
self.assertEqual(u'Hello \u1234 some\u1234: ', ui.stderr.getvalue())
82
# stdin and stdout should be empty
83
self.assertEqual('', ui.stdin.readline())
84
self.assertEqual('', ui.stdout.getvalue())
131
86
def test_text_ui_get_boolean(self):
132
stdin = tests.StringIOWrapper("y\n" # True
134
"yes with garbage\nY\n" # True
135
"not an answer\nno\n" # False
136
"I'm sure!\nyes\n" # True
139
stdout = tests.StringIOWrapper()
140
stderr = tests.StringIOWrapper()
141
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())
92
"yes with garbage\nY\n" # True
93
"not an answer\nno\n" # False
94
"I'm sure!\nyes\n" # True
97
factory = ui_testing.TextUIFactory(stdin_text)
98
self.assertEqual(True, factory.get_boolean(u""))
99
self.assertEqual(False, factory.get_boolean(u""))
100
self.assertEqual(True, factory.get_boolean(u""))
101
self.assertEqual(False, factory.get_boolean(u""))
102
self.assertEqual(True, factory.get_boolean(u""))
103
self.assertEqual(False, factory.get_boolean(u""))
104
self.assertEqual(True, factory.get_boolean(u""))
105
self.assertEqual(False, factory.get_boolean(u""))
106
self.assertEqual("foo\n", factory.stdin.read())
107
# stdin should be empty
108
self.assertEqual('', factory.stdin.readline())
109
# return false on EOF
110
self.assertEqual(False, factory.get_boolean(u""))
112
def test_text_ui_choose_bad_parameters(self):
113
factory = ui_testing.TextUIFactory(u"")
114
# invalid default index
115
self.assertRaises(ValueError, factory.choose, u"", u"&Yes\n&No", 3)
117
self.assertRaises(ValueError, factory.choose, u"", u"&choice\n&ChOiCe")
118
# duplicated shortcut
119
self.assertRaises(ValueError, factory.choose, u"", u"&choice1\nchoi&ce2")
121
def test_text_ui_choose_prompt_explicit(self):
122
# choices with explicit shortcuts
123
factory = ui_testing.TextUIFactory(u"")
124
factory.choose(u"prompt", u"&yes\n&No\nmore &info")
125
self.assertEqual("prompt ([y]es, [N]o, more [i]nfo): \n", factory.stderr.getvalue())
127
def test_text_ui_choose_prompt_automatic(self):
128
# automatic shortcuts
129
factory = ui_testing.TextUIFactory(u"")
130
factory.choose(u"prompt", u"yes\nNo\nmore info")
131
self.assertEqual("prompt ([y]es, [N]o, [m]ore info): \n", factory.stderr.getvalue())
133
def test_text_ui_choose_return_values(self):
134
choose = lambda: factory.choose(u"", u"&Yes\n&No\nMaybe\nmore &info", 3)
140
"b\na\nd \n" # bad shortcuts, all ignored
141
"yes with garbage\nY\n" # 0
142
"not an answer\nno\n" # 1
143
"info\nmore info\n" # 3
146
factory = ui_testing.TextUIFactory(stdin_text)
147
self.assertEqual(0, choose())
148
self.assertEqual(1, choose())
149
self.assertEqual(3, choose())
150
self.assertEqual(1, choose())
151
self.assertEqual(0, choose())
152
self.assertEqual(1, choose())
153
self.assertEqual(3, choose())
154
self.assertEqual(2, choose())
155
self.assertEqual("foo\n", factory.stdin.read())
156
# stdin should be empty
157
self.assertEqual('', factory.stdin.readline())
159
self.assertEqual(None, choose())
161
def test_text_ui_choose_no_default(self):
163
" \n" # no default, invalid!
166
factory = ui_testing.TextUIFactory(stdin_text)
167
self.assertEqual(0, factory.choose(u"", u"&Yes\n&No"))
168
self.assertEqual("foo\n", factory.stdin.read())
152
170
def test_text_ui_get_integer(self):
153
stdin = tests.StringIOWrapper(
156
174
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
157
stdout = tests.StringIOWrapper()
158
stderr = tests.StringIOWrapper()
159
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(""))
175
factory = ui_testing.TextUIFactory(stdin_text)
176
self.assertEqual(1, factory.get_integer(u""))
177
self.assertEqual(-2, factory.get_integer(u""))
178
self.assertEqual(42, factory.get_integer(u""))
164
180
def test_text_factory_prompt(self):
165
181
# see <https://launchpad.net/bugs/365891>
166
StringIO = tests.StringIOWrapper
167
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
168
factory.prompt('foo %2e')
182
factory = ui_testing.TextUIFactory()
183
factory.prompt(u'foo %2e')
169
184
self.assertEqual('', factory.stdout.getvalue())
170
185
self.assertEqual('foo %2e', factory.stderr.getvalue())
172
187
def test_text_factory_prompts_and_clears(self):
173
188
# a get_boolean call should clear the pb before prompting
174
out = test_progress._TTYStringIO()
175
os.environ['TERM'] = 'xterm'
176
factory = _mod_ui_text.TextUIFactory(
177
stdin=tests.StringIOWrapper("yada\ny\n"),
178
stdout=out, stderr=out)
189
out = ui_testing.StringIOAsTTY()
190
self.overrideEnv('TERM', 'xterm')
191
factory = ui_testing.TextUIFactory("yada\ny\n", stdout=out, stderr=out)
179
192
pb = factory.nested_progress_bar()
193
pb._avail_width = lambda: 79
180
194
pb.show_bar = False
181
195
pb.show_spinner = False
182
196
pb.show_count = False
209
222
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"))
223
ui = ui_testing.TextUIFactory('someuser\n\n')
224
self.assertEqual('someuser',
225
ui.get_username(u'Hello %(host)s', host='some'))
226
self.assertEqual('Hello some: ', ui.stderr.getvalue())
227
self.assertEqual('', ui.stdout.getvalue())
228
self.assertEqual('', ui.get_username(u"Gebruiker"))
221
229
# stdin should be empty
222
self.assertEqual('', factory.stdin.readline())
230
self.assertEqual('', ui.stdin.readline())
224
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())
228
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())
232
def test_text_ui_getusername_unicode(self):
233
ui = ui_testing.TextUIFactory(u'someuser\u1234')
234
username = ui.get_username(u'Hello %(host)s', host=u'some\u1234')
235
self.assertEqual(u"someuser\u1234", username)
236
self.assertEqual(u"Hello some\u1234: ", ui.stderr.getvalue())
237
self.assertEqual('', ui.stdout.getvalue())
241
239
def test_quietness(self):
242
os.environ['BZR_PROGRESS_BAR'] = 'text'
243
ui_factory = _mod_ui_text.TextUIFactory(None,
244
test_progress._TTYStringIO(),
245
test_progress._TTYStringIO())
240
self.overrideEnv('BRZ_PROGRESS_BAR', 'text')
241
ui_factory = ui_testing.TextUIFactory(
242
stderr=ui_testing.StringIOAsTTY())
246
243
self.assertIsInstance(ui_factory._progress_view,
247
244
_mod_ui_text.TextProgressView)
248
245
ui_factory.be_quiet(True)
250
247
_mod_ui_text.NullProgressView)
252
249
def test_text_ui_show_user_warning(self):
253
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
254
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5
257
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
250
from ..repofmt.groupcompress_repo import RepositoryFormat2a
251
from ..repofmt.knitpack_repo import RepositoryFormatKnitPack5
252
ui = ui_testing.TextUIFactory()
258
253
remote_fmt = remote.RemoteRepositoryFormat()
259
254
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
260
255
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
261
256
to_format=remote_fmt)
262
self.assertEquals('', out.getvalue())
263
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
257
self.assertEqual('', ui.stdout.getvalue())
258
self.assertEqual("Doing on-the-fly conversion from RepositoryFormat2a() to "
264
259
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
265
260
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
266
261
"the same format for better performance.\n",
262
ui.stderr.getvalue())
268
263
# and now with it suppressed please
271
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
264
ui = ui_testing.TextUIFactory()
272
265
ui.suppressed_warnings.add('cross_format_fetch')
273
266
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
274
267
to_format=remote_fmt)
275
self.assertEquals('', out.getvalue())
276
self.assertEquals('', err.getvalue())
268
self.assertEqual('', ui.stdout.getvalue())
269
self.assertEqual('', ui.stderr.getvalue())
279
272
class TestTextUIOutputStream(tests.TestCase):
280
273
"""Tests for output stream that synchronizes with progress bar."""
282
275
def test_output_clears_terminal(self):
283
stdout = tests.StringIOWrapper()
284
stderr = tests.StringIOWrapper()
287
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
278
uif = ui_testing.TextUIFactory()
288
279
uif.clear_term = lambda: clear_calls.append('clear')
290
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
291
stream.write("Hello world!\n")
292
stream.write("there's more...\n")
293
stream.writelines(["1\n", "2\n", "3\n"])
281
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout, 'utf-8', 'strict')
282
stream.write(u"Hello world!\n")
283
stream.write(u"there's more...\n")
284
stream.writelines([u"1\n", u"2\n", u"3\n"])
295
self.assertEqual(stdout.getvalue(),
286
self.assertEqual(uif.stdout.getvalue(),
299
290
self.assertEqual(['clear', 'clear', 'clear'],
326
317
# however, it can still be forced on
327
318
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
329
os.environ['TERM'] = term
331
if 'BZR_PROGRESS_BAR' in os.environ:
332
del os.environ['BZR_PROGRESS_BAR']
334
os.environ['BZR_PROGRESS_BAR'] = pb
335
stdin = file_class('')
320
self.overrideEnv('TERM', term)
321
self.overrideEnv('BRZ_PROGRESS_BAR', pb)
322
stdin = file_class(u'')
336
323
stderr = file_class()
337
324
stdout = file_class()
338
325
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
339
326
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
340
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
327
"TERM=%s BRZ_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
341
328
self.assertIsInstance(uif.make_progress_view(),
342
329
expected_pb_class,
343
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
330
"TERM=%s BRZ_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
345
332
def test_text_ui_non_terminal(self):
346
333
"""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()
334
stdin = stderr = stdout = ui_testing.StringIOWithEncoding()
350
335
for term_type in ['dumb', None, 'xterm']:
351
if term_type is None:
352
del os.environ['TERM']
354
os.environ['TERM'] = term_type
336
self.overrideEnv('TERM', term_type)
355
337
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
356
338
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
357
339
'TERM=%r' % (term_type,))
459
441
self.assertIsNone('0', av)
460
442
self.assertIsNone('on', av)
461
443
self.assertIsNone('off', av)
446
class TestConfirmationUserInterfacePolicy(tests.TestCase):
448
def test_confirm_action_default(self):
449
base_ui = _mod_ui.NoninteractiveUIFactory()
450
for answer in [True, False]:
452
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
453
.confirm_action("Do something?",
454
"breezy.tests.do_something", {}),
457
def test_confirm_action_specific(self):
458
base_ui = _mod_ui.NoninteractiveUIFactory()
459
for default_answer in [True, False]:
460
for specific_answer in [True, False]:
461
for conf_id in ['given_id', 'other_id']:
462
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
463
base_ui, default_answer, dict(given_id=specific_answer))
464
result = wrapper.confirm_action("Do something?", conf_id, {})
465
if conf_id == 'given_id':
466
self.assertEqual(result, specific_answer)
468
self.assertEqual(result, default_answer)
471
base_ui = _mod_ui.NoninteractiveUIFactory()
472
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
473
base_ui, True, dict(a=2))
474
self.assertThat(repr(wrapper),
475
Equals("ConfirmationUserInterfacePolicy("
476
"NoninteractiveUIFactory(), True, {'a': 2})"))
479
class TestProgressRecordingUI(tests.TestCase):
480
"""Test test-oriented UIFactory that records progress updates"""
482
def test_nested_ignore_depth_beyond_one(self):
483
# we only want to capture the first level out progress, not
484
# want sub-components might do. So we have nested bars ignored.
485
factory = ProgressRecordingUIFactory()
486
pb1 = factory.nested_progress_bar()
487
pb1.update('foo', 0, 1)
488
pb2 = factory.nested_progress_bar()
489
pb2.update('foo', 0, 1)
492
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)