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
35
from ..ui import text as _mod_ui_text
37
ProgressRecordingUIFactory,
41
class TestUIConfiguration(tests.TestCaseInTempDir):
43
def test_output_encoding_configuration(self):
44
enc = next(fixtures.generate_unicode_encodings())
45
config.GlobalStack().set('output_encoding', enc)
46
IO = ui_testing.BytesIOWithEncoding
47
ui = _mod_ui.make_ui_for_terminal(IO(), IO(), IO())
48
output = ui.make_output_stream()
49
self.assertEqual(output.encoding, enc)
40
52
class TestTextUIFactory(tests.TestCase):
54
def test_text_factory_confirm(self):
55
# turns into reading a regular boolean
56
ui = ui_testing.TestUIFactory('n\n')
57
self.assertEqual(ui.confirm_action(u'Should %(thing)s pass?',
58
'breezy.tests.test_ui.confirmation',
42
62
def test_text_factory_ascii_password(self):
43
ui = tests.TestUIFactory(stdin='secret\n',
44
stdout=tests.StringIOWrapper(),
45
stderr=tests.StringIOWrapper())
63
ui = ui_testing.TestUIFactory('secret\n')
46
64
pb = ui.nested_progress_bar()
48
66
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$')
78
def test_text_factory_unicode_password(self):
79
"""Test a unicode password."""
80
ui = ui_testing.TextUIFactory(u'baz\u1234')
81
password = ui.get_password(u'Hello \u1234 %(user)s', user=u'some\u1234')
82
self.assertEqual(u'baz\u1234', password)
83
self.assertEqual(u'Hello \u1234 some\u1234: ', ui.stderr.getvalue())
84
# stdin and stdout should be empty
85
self.assertEqual('', ui.stdin.readline())
86
self.assertEqual('', ui.stdout.getvalue())
131
88
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())
94
"yes with garbage\nY\n" # True
95
"not an answer\nno\n" # False
96
"I'm sure!\nyes\n" # True
99
factory = ui_testing.TextUIFactory(stdin_text)
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(True, factory.get_boolean(u""))
107
self.assertEqual(False, factory.get_boolean(u""))
108
self.assertEqual("foo\n", factory.stdin.read())
109
# stdin should be empty
110
self.assertEqual('', factory.stdin.readline())
111
# return false on EOF
112
self.assertEqual(False, factory.get_boolean(u""))
114
def test_text_ui_choose_bad_parameters(self):
115
factory = ui_testing.TextUIFactory(u"")
116
# invalid default index
117
self.assertRaises(ValueError, factory.choose, u"", u"&Yes\n&No", 3)
119
self.assertRaises(ValueError, factory.choose, u"", u"&choice\n&ChOiCe")
120
# duplicated shortcut
121
self.assertRaises(ValueError, factory.choose, u"", u"&choice1\nchoi&ce2")
123
def test_text_ui_choose_prompt_explicit(self):
124
# choices with explicit shortcuts
125
factory = ui_testing.TextUIFactory(u"")
126
factory.choose(u"prompt", u"&yes\n&No\nmore &info")
127
self.assertEqual("prompt ([y]es, [N]o, more [i]nfo): \n", factory.stderr.getvalue())
129
def test_text_ui_choose_prompt_automatic(self):
130
# automatic shortcuts
131
factory = ui_testing.TextUIFactory(u"")
132
factory.choose(u"prompt", u"yes\nNo\nmore info")
133
self.assertEqual("prompt ([y]es, [N]o, [m]ore info): \n", factory.stderr.getvalue())
135
def test_text_ui_choose_return_values(self):
136
choose = lambda: factory.choose(u"", u"&Yes\n&No\nMaybe\nmore &info", 3)
142
"b\na\nd \n" # bad shortcuts, all ignored
143
"yes with garbage\nY\n" # 0
144
"not an answer\nno\n" # 1
145
"info\nmore info\n" # 3
148
factory = ui_testing.TextUIFactory(stdin_text)
149
self.assertEqual(0, choose())
150
self.assertEqual(1, choose())
151
self.assertEqual(3, choose())
152
self.assertEqual(1, choose())
153
self.assertEqual(0, choose())
154
self.assertEqual(1, choose())
155
self.assertEqual(3, choose())
156
self.assertEqual(2, choose())
157
self.assertEqual("foo\n", factory.stdin.read())
158
# stdin should be empty
159
self.assertEqual('', factory.stdin.readline())
161
self.assertEqual(None, choose())
163
def test_text_ui_choose_no_default(self):
165
" \n" # no default, invalid!
168
factory = ui_testing.TextUIFactory(stdin_text)
169
self.assertEqual(0, factory.choose(u"", u"&Yes\n&No"))
170
self.assertEqual("foo\n", factory.stdin.read())
152
172
def test_text_ui_get_integer(self):
153
stdin = tests.StringIOWrapper(
156
176
"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(""))
177
factory = ui_testing.TextUIFactory(stdin_text)
178
self.assertEqual(1, factory.get_integer(u""))
179
self.assertEqual(-2, factory.get_integer(u""))
180
self.assertEqual(42, factory.get_integer(u""))
164
182
def test_text_factory_prompt(self):
165
183
# see <https://launchpad.net/bugs/365891>
166
StringIO = tests.StringIOWrapper
167
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
168
factory.prompt('foo %2e')
184
factory = ui_testing.TextUIFactory()
185
factory.prompt(u'foo %2e')
169
186
self.assertEqual('', factory.stdout.getvalue())
170
187
self.assertEqual('foo %2e', factory.stderr.getvalue())
172
189
def test_text_factory_prompts_and_clears(self):
173
190
# 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)
191
out = ui_testing.StringIOAsTTY()
192
self.overrideEnv('TERM', 'xterm')
193
factory = ui_testing.TextUIFactory("yada\ny\n", stdout=out, stderr=out)
179
194
pb = factory.nested_progress_bar()
195
pb._avail_width = lambda: 79
180
196
pb.show_bar = False
181
197
pb.show_spinner = False
182
198
pb.show_count = False
209
224
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"))
225
ui = ui_testing.TextUIFactory('someuser\n\n')
226
self.assertEqual('someuser',
227
ui.get_username(u'Hello %(host)s', host='some'))
228
self.assertEqual('Hello some: ', ui.stderr.getvalue())
229
self.assertEqual('', ui.stdout.getvalue())
230
self.assertEqual('', ui.get_username(u"Gebruiker"))
221
231
# stdin should be empty
222
self.assertEqual('', factory.stdin.readline())
232
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())
234
def test_text_ui_getusername_unicode(self):
235
ui = ui_testing.TextUIFactory(u'someuser\u1234')
236
username = ui.get_username(u'Hello %(host)s', host=u'some\u1234')
237
self.assertEqual(u"someuser\u1234", username)
238
self.assertEqual(u"Hello some\u1234: ", ui.stderr.getvalue())
239
self.assertEqual('', ui.stdout.getvalue())
241
241
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())
242
self.overrideEnv('BRZ_PROGRESS_BAR', 'text')
243
ui_factory = ui_testing.TextUIFactory(
244
stderr=ui_testing.StringIOAsTTY())
246
245
self.assertIsInstance(ui_factory._progress_view,
247
246
_mod_ui_text.TextProgressView)
248
247
ui_factory.be_quiet(True)
250
249
_mod_ui_text.NullProgressView)
252
251
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)
252
from ..bzr.groupcompress_repo import RepositoryFormat2a
253
from ..bzr.knitpack_repo import RepositoryFormatKnitPack5
254
ui = ui_testing.TextUIFactory()
258
255
remote_fmt = remote.RemoteRepositoryFormat()
259
256
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
260
257
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
261
258
to_format=remote_fmt)
262
self.assertEquals('', out.getvalue())
263
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
264
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
265
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
266
"the same format for better performance.\n",
259
self.assertEqual('', ui.stdout.getvalue())
260
self.assertContainsRe(
261
ui.stderr.getvalue(),
262
"^Doing on-the-fly conversion from RepositoryFormat2a\(\) to "
263
"RemoteRepositoryFormat\(_network_name="
264
"b?'Bazaar RepositoryFormatKnitPack5 \(bzr 1.6\)\\\\n'\)\.\n"
265
"This may take some time. Upgrade the repositories to "
266
"the same format for better performance\.\n$")
268
267
# and now with it suppressed please
271
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
268
ui = ui_testing.TextUIFactory()
272
269
ui.suppressed_warnings.add('cross_format_fetch')
273
270
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
274
271
to_format=remote_fmt)
275
self.assertEquals('', out.getvalue())
276
self.assertEquals('', err.getvalue())
272
self.assertEqual('', ui.stdout.getvalue())
273
self.assertEqual('', ui.stderr.getvalue())
279
276
class TestTextUIOutputStream(tests.TestCase):
280
277
"""Tests for output stream that synchronizes with progress bar."""
282
279
def test_output_clears_terminal(self):
283
stdout = tests.StringIOWrapper()
284
stderr = tests.StringIOWrapper()
287
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
282
uif = ui_testing.TextUIFactory()
288
283
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"])
285
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout, 'utf-8', 'strict')
286
stream.write(u"Hello world!\n")
287
stream.write(u"there's more...\n")
288
stream.writelines([u"1\n", u"2\n", u"3\n"])
295
self.assertEqual(stdout.getvalue(),
290
self.assertEqual(uif.stdout.getvalue(),
299
294
self.assertEqual(['clear', 'clear', 'clear'],
326
321
# however, it can still be forced on
327
322
(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('')
324
self.overrideEnv('TERM', term)
325
self.overrideEnv('BRZ_PROGRESS_BAR', pb)
326
stdin = file_class(u'')
336
327
stderr = file_class()
337
328
stdout = file_class()
338
329
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
339
330
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
340
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
331
"TERM=%s BRZ_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
341
332
self.assertIsInstance(uif.make_progress_view(),
342
333
expected_pb_class,
343
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
334
"TERM=%s BRZ_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
345
336
def test_text_ui_non_terminal(self):
346
337
"""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()
338
stdin = stderr = stdout = ui_testing.StringIOWithEncoding()
350
339
for term_type in ['dumb', None, 'xterm']:
351
if term_type is None:
352
del os.environ['TERM']
354
os.environ['TERM'] = term_type
340
self.overrideEnv('TERM', term_type)
355
341
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
356
342
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
357
343
'TERM=%r' % (term_type,))
459
445
self.assertIsNone('0', av)
460
446
self.assertIsNone('on', av)
461
447
self.assertIsNone('off', av)
450
class TestConfirmationUserInterfacePolicy(tests.TestCase):
452
def test_confirm_action_default(self):
453
base_ui = _mod_ui.NoninteractiveUIFactory()
454
for answer in [True, False]:
456
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
457
.confirm_action("Do something?",
458
"breezy.tests.do_something", {}),
461
def test_confirm_action_specific(self):
462
base_ui = _mod_ui.NoninteractiveUIFactory()
463
for default_answer in [True, False]:
464
for specific_answer in [True, False]:
465
for conf_id in ['given_id', 'other_id']:
466
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
467
base_ui, default_answer, dict(given_id=specific_answer))
468
result = wrapper.confirm_action("Do something?", conf_id, {})
469
if conf_id == 'given_id':
470
self.assertEqual(result, specific_answer)
472
self.assertEqual(result, default_answer)
475
base_ui = _mod_ui.NoninteractiveUIFactory()
476
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
477
base_ui, True, dict(a=2))
478
self.assertThat(repr(wrapper),
479
Equals("ConfirmationUserInterfacePolicy("
480
"NoninteractiveUIFactory(), True, {'a': 2})"))
483
class TestProgressRecordingUI(tests.TestCase):
484
"""Test test-oriented UIFactory that records progress updates"""
486
def test_nested_ignore_depth_beyond_one(self):
487
# we only want to capture the first level out progress, not
488
# want sub-components might do. So we have nested bars ignored.
489
factory = ProgressRecordingUIFactory()
490
pb1 = factory.nested_progress_bar()
491
pb1.update('foo', 0, 1)
492
pb2 = factory.nested_progress_bar()
493
pb2.update('foo', 0, 1)
496
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)