17
17
"""Tests for the bzrlib ui
21
22
from StringIO import StringIO
24
from testtools.matchers import *
26
26
from bzrlib import (
31
from bzrlib.symbol_versioning import (
34
32
from bzrlib.tests import (
39
from bzrlib.tests.test_progress import (
43
from bzrlib.ui import (
49
from bzrlib.ui.text import (
36
from bzrlib.ui import text as _mod_ui_text
37
from bzrlib.tests.testui import (
38
ProgressRecordingUIFactory,
42
class TestUIConfiguration(tests.TestCaseWithTransport):
44
def test_output_encoding_configuration(self):
45
enc = fixtures.generate_unicode_encodings().next()
46
config.GlobalConfig().set_user_option('output_encoding',
48
ui = tests.TestUIFactory(stdin=None,
49
stdout=tests.StringIOWrapper(),
50
stderr=tests.StringIOWrapper())
51
output = ui.make_output_stream()
52
self.assertEquals(output.encoding, enc)
56
55
class TestTextUIFactory(tests.TestCase):
57
def make_test_ui_factory(self, stdin_contents):
58
ui = tests.TestUIFactory(stdin=stdin_contents,
59
stdout=tests.StringIOWrapper(),
60
stderr=tests.StringIOWrapper())
63
def test_text_factory_confirm(self):
64
# turns into reading a regular boolean
65
ui = self.make_test_ui_factory('n\n')
66
self.assertEquals(ui.confirm_action(u'Should %(thing)s pass?',
67
'bzrlib.tests.test_ui.confirmation',
58
71
def test_text_factory_ascii_password(self):
59
ui = tests.TestUIFactory(stdin='secret\n',
60
stdout=tests.StringIOWrapper(),
61
stderr=tests.StringIOWrapper())
72
ui = self.make_test_ui_factory('secret\n')
62
73
pb = ui.nested_progress_bar()
64
75
self.assertEqual('secret',
102
def test_progress_note(self):
105
ui_factory = TextUIFactory(stdin=StringIO(''),
108
pb = ui_factory.nested_progress_bar()
110
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
113
self.assertEqual(None, result)
114
self.assertEqual("t\n", stdout.getvalue())
115
# Since there was no update() call, there should be no clear() call
116
self.failIf(re.search(r'^\r {10,}\r$',
117
stderr.getvalue()) is not None,
118
'We cleared the stderr without anything to put there')
122
def test_progress_note_clears(self):
123
stderr = _TTYStringIO()
124
stdout = _TTYStringIO()
125
# so that we get a TextProgressBar
126
os.environ['TERM'] = 'xterm'
127
ui_factory = TextUIFactory(
129
stdout=stdout, stderr=stderr)
130
self.assertIsInstance(ui_factory._progress_view,
132
pb = ui_factory.nested_progress_bar()
134
# Create a progress update that isn't throttled
136
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
138
self.assertEqual(None, result)
139
self.assertEqual("t\n", stdout.getvalue())
140
# the exact contents will depend on the terminal width and we don't
141
# care about that right now - but you're probably running it on at
142
# least a 10-character wide terminal :)
143
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
147
def test_progress_nested(self):
148
# test factory based nested and popping.
149
ui = TextUIFactory(None, None, None)
150
pb1 = ui.nested_progress_bar()
151
pb2 = ui.nested_progress_bar()
152
# You do get a warning if the outermost progress bar wasn't finished
153
# first - it's not clear if this is really useful or if it should just
154
# become orphaned -- mbp 20090120
155
warnings, _ = self.callCatchWarnings(pb1.finished)
156
if len(warnings) != 1:
157
self.fail("unexpected warnings: %r" % (warnings,))
161
111
def test_text_ui_get_boolean(self):
162
stdin = StringIO("y\n" # True
164
"yes with garbage\nY\n" # True
165
"not an answer\nno\n" # False
166
"I'm sure!\nyes\n" # True
171
factory = TextUIFactory(stdin, stdout, stderr)
172
self.assertEqual(True, factory.get_boolean(""))
173
self.assertEqual(False, factory.get_boolean(""))
174
self.assertEqual(True, factory.get_boolean(""))
175
self.assertEqual(False, factory.get_boolean(""))
176
self.assertEqual(True, factory.get_boolean(""))
177
self.assertEqual(False, factory.get_boolean(""))
178
self.assertEqual("foo\n", factory.stdin.read())
179
# stdin should be empty
180
self.assertEqual('', factory.stdin.readline())
112
stdin = tests.StringIOWrapper("y\n" # True
116
"yes with garbage\nY\n" # True
117
"not an answer\nno\n" # False
118
"I'm sure!\nyes\n" # True
121
stdout = tests.StringIOWrapper()
122
stderr = tests.StringIOWrapper()
123
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
124
self.assertEqual(True, factory.get_boolean(u""))
125
self.assertEqual(False, factory.get_boolean(u""))
126
self.assertEqual(True, factory.get_boolean(u""))
127
self.assertEqual(False, factory.get_boolean(u""))
128
self.assertEqual(True, factory.get_boolean(u""))
129
self.assertEqual(False, factory.get_boolean(u""))
130
self.assertEqual(True, factory.get_boolean(u""))
131
self.assertEqual(False, factory.get_boolean(u""))
132
self.assertEqual("foo\n", factory.stdin.read())
133
# stdin should be empty
134
self.assertEqual('', factory.stdin.readline())
135
# return false on EOF
136
self.assertEqual(False, factory.get_boolean(u""))
138
def test_text_ui_choose_bad_parameters(self):
139
stdin = tests.StringIOWrapper()
140
stdout = tests.StringIOWrapper()
141
stderr = tests.StringIOWrapper()
142
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
143
# invalid default index
144
self.assertRaises(ValueError, factory.choose, u"", u"&Yes\n&No", 3)
146
self.assertRaises(ValueError, factory.choose, u"", u"&choice\n&ChOiCe")
147
# duplicated shortcut
148
self.assertRaises(ValueError, factory.choose, u"", u"&choice1\nchoi&ce2")
150
def test_text_ui_choose_prompt(self):
151
stdin = tests.StringIOWrapper()
152
stdout = tests.StringIOWrapper()
153
stderr = tests.StringIOWrapper()
154
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
155
# choices with explicit shortcuts
156
factory.choose(u"prompt", u"&yes\n&No\nmore &info")
157
self.assertEqual("prompt ([y]es, [N]o, more [i]nfo): \n", factory.stderr.getvalue())
158
# automatic shortcuts
159
factory.stderr.truncate(0)
160
factory.choose(u"prompt", u"yes\nNo\nmore info")
161
self.assertEqual("prompt ([y]es, [N]o, [m]ore info): \n", factory.stderr.getvalue())
163
def test_text_ui_choose_return_values(self):
164
choose = lambda: factory.choose(u"", u"&Yes\n&No\nMaybe\nmore &info", 3)
165
stdin = tests.StringIOWrapper("y\n" # 0
169
"b\na\nd \n" # bad shortcuts, all ignored
170
"yes with garbage\nY\n" # 0
171
"not an answer\nno\n" # 1
172
"info\nmore info\n" # 3
175
stdout = tests.StringIOWrapper()
176
stderr = tests.StringIOWrapper()
177
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
178
self.assertEqual(0, choose())
179
self.assertEqual(1, choose())
180
self.assertEqual(3, choose())
181
self.assertEqual(1, choose())
182
self.assertEqual(0, choose())
183
self.assertEqual(1, choose())
184
self.assertEqual(3, choose())
185
self.assertEqual(2, choose())
186
self.assertEqual("foo\n", factory.stdin.read())
187
# stdin should be empty
188
self.assertEqual('', factory.stdin.readline())
190
self.assertEqual(None, choose())
192
def test_text_ui_choose_no_default(self):
193
stdin = tests.StringIOWrapper(" \n" # no default, invalid!
196
stdout = tests.StringIOWrapper()
197
stderr = tests.StringIOWrapper()
198
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
199
self.assertEqual(0, factory.choose(u"", u"&Yes\n&No"))
200
self.assertEqual("foo\n", factory.stdin.read())
202
def test_text_ui_get_integer(self):
203
stdin = tests.StringIOWrapper(
206
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
207
stdout = tests.StringIOWrapper()
208
stderr = tests.StringIOWrapper()
209
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
210
self.assertEqual(1, factory.get_integer(u""))
211
self.assertEqual(-2, factory.get_integer(u""))
212
self.assertEqual(42, factory.get_integer(u""))
182
214
def test_text_factory_prompt(self):
183
215
# see <https://launchpad.net/bugs/365891>
184
factory = TextUIFactory(StringIO(), StringIO(), StringIO())
185
factory.prompt('foo %2e')
216
StringIO = tests.StringIOWrapper
217
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
218
factory.prompt(u'foo %2e')
186
219
self.assertEqual('', factory.stdout.getvalue())
187
220
self.assertEqual('foo %2e', factory.stderr.getvalue())
189
222
def test_text_factory_prompts_and_clears(self):
190
223
# a get_boolean call should clear the pb before prompting
192
os.environ['TERM'] = 'xterm'
193
factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
224
out = test_progress._TTYStringIO()
225
self.overrideEnv('TERM', 'xterm')
226
factory = _mod_ui_text.TextUIFactory(
227
stdin=tests.StringIOWrapper("yada\ny\n"),
228
stdout=out, stderr=out)
229
factory._avail_width = lambda: 79
194
230
pb = factory.nested_progress_bar()
195
231
pb.show_bar = False
196
232
pb.show_spinner = False
292
def test_quietness(self):
293
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
294
ui_factory = _mod_ui_text.TextUIFactory(None,
295
test_progress._TTYStringIO(),
296
test_progress._TTYStringIO())
297
self.assertIsInstance(ui_factory._progress_view,
298
_mod_ui_text.TextProgressView)
299
ui_factory.be_quiet(True)
300
self.assertIsInstance(ui_factory._progress_view,
301
_mod_ui_text.NullProgressView)
303
def test_text_ui_show_user_warning(self):
304
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
305
from bzrlib.repofmt.knitpack_repo import RepositoryFormatKnitPack5
308
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
309
remote_fmt = remote.RemoteRepositoryFormat()
310
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
311
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
312
to_format=remote_fmt)
313
self.assertEquals('', out.getvalue())
314
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
315
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
316
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
317
"the same format for better performance.\n",
319
# and now with it suppressed please
322
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
323
ui.suppressed_warnings.add('cross_format_fetch')
324
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
325
to_format=remote_fmt)
326
self.assertEquals('', out.getvalue())
327
self.assertEquals('', err.getvalue())
330
class TestTextUIOutputStream(tests.TestCase):
331
"""Tests for output stream that synchronizes with progress bar."""
333
def test_output_clears_terminal(self):
334
stdout = tests.StringIOWrapper()
335
stderr = tests.StringIOWrapper()
338
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
339
uif.clear_term = lambda: clear_calls.append('clear')
341
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
342
stream.write("Hello world!\n")
343
stream.write("there's more...\n")
344
stream.writelines(["1\n", "2\n", "3\n"])
346
self.assertEqual(stdout.getvalue(),
350
self.assertEqual(['clear', 'clear', 'clear'],
256
356
class UITests(tests.TestCase):
258
358
def test_progress_construction(self):
259
359
"""TextUIFactory constructs the right progress view.
361
TTYStringIO = test_progress._TTYStringIO
362
FileStringIO = tests.StringIOWrapper
261
363
for (file_class, term, pb, expected_pb_class) in (
262
364
# on an xterm, either use them or not as the user requests,
263
365
# otherwise default on
264
(_TTYStringIO, 'xterm', 'none', NullProgressView),
265
(_TTYStringIO, 'xterm', 'text', TextProgressView),
266
(_TTYStringIO, 'xterm', None, TextProgressView),
366
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
367
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
368
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
267
369
# on a dumb terminal, again if there's explicit configuration do
268
370
# it, otherwise default off
269
(_TTYStringIO, 'dumb', 'none', NullProgressView),
270
(_TTYStringIO, 'dumb', 'text', TextProgressView),
271
(_TTYStringIO, 'dumb', None, NullProgressView),
371
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
372
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
373
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
272
374
# on a non-tty terminal, it's null regardless of $TERM
273
(StringIO, 'xterm', None, NullProgressView),
274
(StringIO, 'dumb', None, NullProgressView),
375
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
376
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
275
377
# however, it can still be forced on
276
(StringIO, 'dumb', 'text', TextProgressView),
378
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
278
os.environ['TERM'] = term
280
if 'BZR_PROGRESS_BAR' in os.environ:
281
del os.environ['BZR_PROGRESS_BAR']
283
os.environ['BZR_PROGRESS_BAR'] = pb
380
self.overrideEnv('TERM', term)
381
self.overrideEnv('BZR_PROGRESS_BAR', pb)
284
382
stdin = file_class('')
285
383
stderr = file_class()
286
384
stdout = file_class()
287
uif = make_ui_for_terminal(stdin, stdout, stderr)
288
self.assertIsInstance(uif, TextUIFactory,
385
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
386
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
289
387
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
290
388
self.assertIsInstance(uif.make_progress_view(),
291
389
expected_pb_class,
322
417
self.assertEqual('', stdout.getvalue())
324
419
def test_silent_ui_getbool(self):
325
factory = SilentUIFactory()
420
factory = _mod_ui.SilentUIFactory()
421
stdout = tests.StringIOWrapper()
327
422
self.assertRaises(
328
423
NotImplementedError,
329
424
self.apply_redirected,
330
None, stdout, stdout, factory.get_boolean, "foo")
333
class TestUIFactoryTests(TestCase):
425
None, stdout, stdout, factory.get_boolean, u"foo")
428
class TestUIFactoryTests(tests.TestCase):
335
430
def test_test_ui_factory_progress(self):
336
431
# there's no output; we just want to make sure this doesn't crash -
337
# see https://bugs.edge.launchpad.net/bzr/+bug/408201
432
# see https://bugs.launchpad.net/bzr/+bug/408201
433
ui = tests.TestUIFactory()
339
434
pb = ui.nested_progress_bar()
340
435
pb.update('hello')
345
class CannedInputUIFactoryTests(TestCase):
440
class CannedInputUIFactoryTests(tests.TestCase):
347
442
def test_canned_input_get_input(self):
348
uif = CannedInputUIFactory([True, 'mbp', 'password'])
349
self.assertEqual(uif.get_boolean('Extra cheese?'), True)
350
self.assertEqual(uif.get_username('Enter your user name'), 'mbp')
351
self.assertEqual(uif.get_password('Password for %(host)s', host='example.com'),
443
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
444
self.assertEqual(True, uif.get_boolean(u'Extra cheese?'))
445
self.assertEqual('mbp', uif.get_username(u'Enter your user name'))
446
self.assertEqual('password',
447
uif.get_password(u'Password for %(host)s',
449
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
355
452
class TestBoolFromString(tests.TestCase):
406
503
self.assertIsNone('0', av)
407
504
self.assertIsNone('on', av)
408
505
self.assertIsNone('off', av)
508
class TestConfirmationUserInterfacePolicy(tests.TestCase):
510
def test_confirm_action_default(self):
511
base_ui = _mod_ui.NoninteractiveUIFactory()
512
for answer in [True, False]:
514
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
515
.confirm_action("Do something?",
516
"bzrlib.tests.do_something", {}),
519
def test_confirm_action_specific(self):
520
base_ui = _mod_ui.NoninteractiveUIFactory()
521
for default_answer in [True, False]:
522
for specific_answer in [True, False]:
523
for conf_id in ['given_id', 'other_id']:
524
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
525
base_ui, default_answer, dict(given_id=specific_answer))
526
result = wrapper.confirm_action("Do something?", conf_id, {})
527
if conf_id == 'given_id':
528
self.assertEquals(result, specific_answer)
530
self.assertEquals(result, default_answer)
533
base_ui = _mod_ui.NoninteractiveUIFactory()
534
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
535
base_ui, True, dict(a=2))
536
self.assertThat(repr(wrapper),
537
Equals("ConfirmationUserInterfacePolicy("
538
"NoninteractiveUIFactory(), True, {'a': 2})"))
541
class TestProgressRecordingUI(tests.TestCase):
542
"""Test test-oriented UIFactory that records progress updates"""
544
def test_nested_ignore_depth_beyond_one(self):
545
# we only want to capture the first level out progress, not
546
# want sub-components might do. So we have nested bars ignored.
547
factory = ProgressRecordingUIFactory()
548
pb1 = factory.nested_progress_bar()
549
pb1.update('foo', 0, 1)
550
pb2 = factory.nested_progress_bar()
551
pb2.update('foo', 0, 1)
554
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)