17
17
"""Tests for the bzrlib ui
21
from StringIO import StringIO
22
from StringIO import StringIO
24
from testtools.matchers import *
26
26
from bzrlib import (
31
from bzrlib.symbol_versioning import (
32
34
from bzrlib.tests 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)
39
from bzrlib.tests.test_progress import (
43
from bzrlib.ui import (
49
from bzrlib.ui.text import (
55
56
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',
71
58
def test_text_factory_ascii_password(self):
72
ui = self.make_test_ui_factory('secret\n')
59
ui = tests.TestUIFactory(stdin='secret\n',
60
stdout=tests.StringIOWrapper(),
61
stderr=tests.StringIOWrapper())
73
62
pb = ui.nested_progress_bar()
75
64
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,))
111
161
def test_text_ui_get_boolean(self):
112
stdin = tests.StringIOWrapper("y\n" # True
114
"yes with garbage\nY\n" # True
115
"not an answer\nno\n" # False
116
"I'm sure!\nyes\n" # True
119
stdout = tests.StringIOWrapper()
120
stderr = tests.StringIOWrapper()
121
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
122
self.assertEqual(True, factory.get_boolean(u""))
123
self.assertEqual(False, factory.get_boolean(u""))
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""))
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(""))
128
178
self.assertEqual("foo\n", factory.stdin.read())
129
179
# stdin should be empty
130
180
self.assertEqual('', factory.stdin.readline())
132
def test_text_ui_get_integer(self):
133
stdin = tests.StringIOWrapper(
136
"hmmm\nwhat else ?\nCome on\nok 42\n4.24\n42\n")
137
stdout = tests.StringIOWrapper()
138
stderr = tests.StringIOWrapper()
139
factory = _mod_ui_text.TextUIFactory(stdin, stdout, stderr)
140
self.assertEqual(1, factory.get_integer(u""))
141
self.assertEqual(-2, factory.get_integer(u""))
142
self.assertEqual(42, factory.get_integer(u""))
144
182
def test_text_factory_prompt(self):
145
183
# see <https://launchpad.net/bugs/365891>
146
StringIO = tests.StringIOWrapper
147
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
148
factory.prompt(u'foo %2e')
184
factory = TextUIFactory(StringIO(), StringIO(), StringIO())
185
factory.prompt('foo %2e')
149
186
self.assertEqual('', factory.stdout.getvalue())
150
187
self.assertEqual('foo %2e', factory.stderr.getvalue())
152
189
def test_text_factory_prompts_and_clears(self):
153
190
# a get_boolean call should clear the pb before prompting
154
out = test_progress._TTYStringIO()
155
self.overrideEnv('TERM', 'xterm')
156
factory = _mod_ui_text.TextUIFactory(
157
stdin=tests.StringIOWrapper("yada\ny\n"),
158
stdout=out, stderr=out)
159
factory._avail_width = lambda: 79
192
os.environ['TERM'] = 'xterm'
193
factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
160
194
pb = factory.nested_progress_bar()
161
195
pb.show_bar = False
162
196
pb.show_spinner = False
190
223
def test_text_ui_getusername(self):
191
factory = _mod_ui_text.TextUIFactory(None, None, None)
192
factory.stdin = tests.StringIOWrapper("someuser\n\n")
193
factory.stdout = tests.StringIOWrapper()
194
factory.stderr = tests.StringIOWrapper()
224
factory = TextUIFactory(None, None, None)
225
factory.stdin = StringIO("someuser\n\n")
226
factory.stdout = StringIO()
227
factory.stderr = StringIO()
195
228
factory.stdout.encoding = "utf8"
196
229
# there is no output from the base factory
197
230
self.assertEqual("someuser",
198
factory.get_username(u'Hello %(host)s', host='some'))
231
factory.get_username('Hello %(host)s', host='some'))
199
232
self.assertEquals("Hello some: ", factory.stderr.getvalue())
200
233
self.assertEquals('', factory.stdout.getvalue())
201
self.assertEqual("", factory.get_username(u"Gebruiker"))
234
self.assertEqual("", factory.get_username("Gebruiker"))
202
235
# stdin should be empty
203
236
self.assertEqual('', factory.stdin.readline())
222
def test_quietness(self):
223
self.overrideEnv('BZR_PROGRESS_BAR', 'text')
224
ui_factory = _mod_ui_text.TextUIFactory(None,
225
test_progress._TTYStringIO(),
226
test_progress._TTYStringIO())
227
self.assertIsInstance(ui_factory._progress_view,
228
_mod_ui_text.TextProgressView)
229
ui_factory.be_quiet(True)
230
self.assertIsInstance(ui_factory._progress_view,
231
_mod_ui_text.NullProgressView)
233
def test_text_ui_show_user_warning(self):
234
from bzrlib.repofmt.groupcompress_repo import RepositoryFormat2a
235
from bzrlib.repofmt.knitpack_repo import RepositoryFormatKnitPack5
238
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
239
remote_fmt = remote.RemoteRepositoryFormat()
240
remote_fmt._network_name = RepositoryFormatKnitPack5().network_name()
241
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
242
to_format=remote_fmt)
243
self.assertEquals('', out.getvalue())
244
self.assertEquals("Doing on-the-fly conversion from RepositoryFormat2a() to "
245
"RemoteRepositoryFormat(_network_name='Bazaar RepositoryFormatKnitPack5 "
246
"(bzr 1.6)\\n').\nThis may take some time. Upgrade the repositories to "
247
"the same format for better performance.\n",
249
# and now with it suppressed please
252
ui = tests.TextUIFactory(stdin=None, stdout=out, stderr=err)
253
ui.suppressed_warnings.add('cross_format_fetch')
254
ui.show_user_warning('cross_format_fetch', from_format=RepositoryFormat2a(),
255
to_format=remote_fmt)
256
self.assertEquals('', out.getvalue())
257
self.assertEquals('', err.getvalue())
260
class TestTextUIOutputStream(tests.TestCase):
261
"""Tests for output stream that synchronizes with progress bar."""
263
def test_output_clears_terminal(self):
264
stdout = tests.StringIOWrapper()
265
stderr = tests.StringIOWrapper()
268
uif = _mod_ui_text.TextUIFactory(None, stdout, stderr)
269
uif.clear_term = lambda: clear_calls.append('clear')
271
stream = _mod_ui_text.TextUIOutputStream(uif, uif.stdout)
272
stream.write("Hello world!\n")
273
stream.write("there's more...\n")
274
stream.writelines(["1\n", "2\n", "3\n"])
276
self.assertEqual(stdout.getvalue(),
280
self.assertEqual(['clear', 'clear', 'clear'],
286
256
class UITests(tests.TestCase):
288
258
def test_progress_construction(self):
289
259
"""TextUIFactory constructs the right progress view.
291
TTYStringIO = test_progress._TTYStringIO
292
FileStringIO = tests.StringIOWrapper
293
261
for (file_class, term, pb, expected_pb_class) in (
294
262
# on an xterm, either use them or not as the user requests,
295
263
# otherwise default on
296
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
297
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
298
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
264
(_TTYStringIO, 'xterm', 'none', NullProgressView),
265
(_TTYStringIO, 'xterm', 'text', TextProgressView),
266
(_TTYStringIO, 'xterm', None, TextProgressView),
299
267
# on a dumb terminal, again if there's explicit configuration do
300
268
# it, otherwise default off
301
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
302
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
303
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
269
(_TTYStringIO, 'dumb', 'none', NullProgressView),
270
(_TTYStringIO, 'dumb', 'text', TextProgressView),
271
(_TTYStringIO, 'dumb', None, NullProgressView),
304
272
# on a non-tty terminal, it's null regardless of $TERM
305
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
306
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
273
(StringIO, 'xterm', None, NullProgressView),
274
(StringIO, 'dumb', None, NullProgressView),
307
275
# however, it can still be forced on
308
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
276
(StringIO, 'dumb', 'text', TextProgressView),
310
self.overrideEnv('TERM', term)
311
self.overrideEnv('BZR_PROGRESS_BAR', pb)
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
312
284
stdin = file_class('')
313
285
stderr = file_class()
314
286
stdout = file_class()
315
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
316
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
287
uif = make_ui_for_terminal(stdin, stdout, stderr)
288
self.assertIsInstance(uif, TextUIFactory,
317
289
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
318
290
self.assertIsInstance(uif.make_progress_view(),
319
291
expected_pb_class,
322
294
def test_text_ui_non_terminal(self):
323
295
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
324
stdin = test_progress._NonTTYStringIO('')
325
stderr = test_progress._NonTTYStringIO()
326
stdout = test_progress._NonTTYStringIO()
296
stdin = _NonTTYStringIO('')
297
stderr = _NonTTYStringIO()
298
stdout = _NonTTYStringIO()
327
299
for term_type in ['dumb', None, 'xterm']:
328
self.overrideEnv('TERM', term_type)
329
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
330
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
300
if term_type is None:
301
del os.environ['TERM']
303
os.environ['TERM'] = term_type
304
uif = make_ui_for_terminal(stdin, stdout, stderr)
305
self.assertIsInstance(uif, TextUIFactory,
331
306
'TERM=%r' % (term_type,))
334
class SilentUITests(tests.TestCase):
309
class SilentUITests(TestCase):
336
311
def test_silent_factory_get_password(self):
337
312
# A silent factory that can't do user interaction can't get a
338
313
# password. Possibly it should raise a more specific error but it
340
ui = _mod_ui.SilentUIFactory()
341
stdout = tests.StringIOWrapper()
315
ui = SilentUIFactory()
342
317
self.assertRaises(
343
318
NotImplementedError,
344
319
self.apply_redirected,
347
322
self.assertEqual('', stdout.getvalue())
349
324
def test_silent_ui_getbool(self):
350
factory = _mod_ui.SilentUIFactory()
351
stdout = tests.StringIOWrapper()
325
factory = SilentUIFactory()
352
327
self.assertRaises(
353
328
NotImplementedError,
354
329
self.apply_redirected,
355
None, stdout, stdout, factory.get_boolean, u"foo")
358
class TestUIFactoryTests(tests.TestCase):
330
None, stdout, stdout, factory.get_boolean, "foo")
333
class TestUIFactoryTests(TestCase):
360
335
def test_test_ui_factory_progress(self):
361
336
# there's no output; we just want to make sure this doesn't crash -
362
# see https://bugs.launchpad.net/bzr/+bug/408201
363
ui = tests.TestUIFactory()
337
# see https://bugs.edge.launchpad.net/bzr/+bug/408201
364
339
pb = ui.nested_progress_bar()
365
340
pb.update('hello')
370
class CannedInputUIFactoryTests(tests.TestCase):
345
class CannedInputUIFactoryTests(TestCase):
372
347
def test_canned_input_get_input(self):
373
uif = _mod_ui.CannedInputUIFactory([True, 'mbp', 'password', 42])
374
self.assertEqual(True, uif.get_boolean(u'Extra cheese?'))
375
self.assertEqual('mbp', uif.get_username(u'Enter your user name'))
376
self.assertEqual('password',
377
uif.get_password(u'Password for %(host)s',
379
self.assertEqual(42, uif.get_integer(u'And all that jazz ?'))
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'),
382
355
class TestBoolFromString(tests.TestCase):
433
406
self.assertIsNone('0', av)
434
407
self.assertIsNone('on', av)
435
408
self.assertIsNone('off', av)
438
class TestConfirmationUserInterfacePolicy(tests.TestCase):
440
def test_confirm_action_default(self):
441
base_ui = _mod_ui.NoninteractiveUIFactory()
442
for answer in [True, False]:
444
_mod_ui.ConfirmationUserInterfacePolicy(base_ui, answer, {})
445
.confirm_action("Do something?",
446
"bzrlib.tests.do_something", {}),
449
def test_confirm_action_specific(self):
450
base_ui = _mod_ui.NoninteractiveUIFactory()
451
for default_answer in [True, False]:
452
for specific_answer in [True, False]:
453
for conf_id in ['given_id', 'other_id']:
454
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
455
base_ui, default_answer, dict(given_id=specific_answer))
456
result = wrapper.confirm_action("Do something?", conf_id, {})
457
if conf_id == 'given_id':
458
self.assertEquals(result, specific_answer)
460
self.assertEquals(result, default_answer)
463
base_ui = _mod_ui.NoninteractiveUIFactory()
464
wrapper = _mod_ui.ConfirmationUserInterfacePolicy(
465
base_ui, True, dict(a=2))
466
self.assertThat(repr(wrapper),
467
Equals("ConfirmationUserInterfacePolicy("
468
"NoninteractiveUIFactory(), True, {'a': 2})"))
471
class TestProgressRecordingUI(tests.TestCase):
472
"""Test test-oriented UIFactory that records progress updates"""
474
def test_nested_ignore_depth_beyond_one(self):
475
# we only want to capture the first level out progress, not
476
# want sub-components might do. So we have nested bars ignored.
477
factory = ProgressRecordingUIFactory()
478
pb1 = factory.nested_progress_bar()
479
pb1.update('foo', 0, 1)
480
pb2 = factory.nested_progress_bar()
481
pb2.update('foo', 0, 1)
484
self.assertEqual([("update", 0, 1, 'foo')], factory._calls)