103
def test_progress_construction(self):
104
"""TextUIFactory constructs the right progress view.
106
for (file_class, term, pb, expected_pb_class) in (
107
# on an xterm, either use them or not as the user requests,
108
# otherwise default on
109
(_TTYStringIO, 'xterm', 'none', NullProgressView),
110
(_TTYStringIO, 'xterm', 'text', TextProgressView),
111
(_TTYStringIO, 'xterm', None, TextProgressView),
112
# on a dumb terminal, again if there's explicit configuration do
113
# it, otherwise default off
114
(_TTYStringIO, 'dumb', 'none', NullProgressView),
115
(_TTYStringIO, 'dumb', 'text', TextProgressView),
116
(_TTYStringIO, 'dumb', None, NullProgressView),
117
# on a non-tty terminal, it's null regardless of $TERM
118
(StringIO, 'xterm', None, NullProgressView),
119
(StringIO, 'dumb', None, NullProgressView),
120
# however, it can still be forced on
121
(StringIO, 'dumb', 'text', TextProgressView),
123
os.environ['TERM'] = term
125
if 'BZR_PROGRESS_BAR' in os.environ:
126
del os.environ['BZR_PROGRESS_BAR']
128
os.environ['BZR_PROGRESS_BAR'] = pb
129
stdin = file_class('')
130
stderr = file_class()
131
stdout = file_class()
132
uif = make_ui_for_terminal(stdin, stdout, stderr)
133
self.assertIsInstance(uif, TextUIFactory,
134
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
135
self.assertIsInstance(uif.make_progress_view(),
137
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
139
def test_text_ui_non_terminal(self):
140
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
141
stdin = _NonTTYStringIO('')
142
stderr = _NonTTYStringIO()
143
stdout = _NonTTYStringIO()
144
for term_type in ['dumb', None, 'xterm']:
145
if term_type is None:
146
del os.environ['TERM']
148
os.environ['TERM'] = term_type
149
uif = make_ui_for_terminal(stdin, stdout, stderr)
150
self.assertIsInstance(uif, TextUIFactory,
151
'TERM=%r' % (term_type,))
153
def test_progress_note(self):
156
ui_factory = TextUIFactory(stdin=StringIO(''),
159
pb = ui_factory.nested_progress_bar()
161
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
164
self.assertEqual(None, result)
165
self.assertEqual("t\n", stdout.getvalue())
166
# Since there was no update() call, there should be no clear() call
167
self.failIf(re.search(r'^\r {10,}\r$',
168
stderr.getvalue()) is not None,
169
'We cleared the stderr without anything to put there')
173
def test_progress_note_clears(self):
174
stderr = _TTYStringIO()
175
stdout = _TTYStringIO()
176
# so that we get a TextProgressBar
177
os.environ['TERM'] = 'xterm'
178
ui_factory = TextUIFactory(
180
stdout=stdout, stderr=stderr)
181
self.assertIsInstance(ui_factory._progress_view,
183
pb = ui_factory.nested_progress_bar()
185
# Create a progress update that isn't throttled
187
result = self.applyDeprecated(deprecated_in((2, 1, 0)),
189
self.assertEqual(None, result)
190
self.assertEqual("t\n", stdout.getvalue())
191
# the exact contents will depend on the terminal width and we don't
192
# care about that right now - but you're probably running it on at
193
# least a 10-character wide terminal :)
194
self.assertContainsRe(stderr.getvalue(), r'\r {10,}\r$')
198
def test_progress_nested(self):
199
# test factory based nested and popping.
200
ui = TextUIFactory(None, None, None)
201
pb1 = ui.nested_progress_bar()
202
pb2 = ui.nested_progress_bar()
203
# You do get a warning if the outermost progress bar wasn't finished
204
# first - it's not clear if this is really useful or if it should just
205
# become orphaned -- mbp 20090120
206
warnings, _ = self.callCatchWarnings(pb1.finished)
207
if len(warnings) != 1:
208
self.fail("unexpected warnings: %r" % (warnings,))
111
212
def test_text_ui_get_boolean(self):
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""))
213
stdin = StringIO("y\n" # True
215
"yes with garbage\nY\n" # True
216
"not an answer\nno\n" # False
217
"I'm sure!\nyes\n" # True
222
factory = TextUIFactory(stdin, stdout, stderr)
223
self.assertEqual(True, factory.get_boolean(""))
224
self.assertEqual(False, factory.get_boolean(""))
225
self.assertEqual(True, factory.get_boolean(""))
226
self.assertEqual(False, factory.get_boolean(""))
227
self.assertEqual(True, factory.get_boolean(""))
228
self.assertEqual(False, factory.get_boolean(""))
229
self.assertEqual("foo\n", factory.stdin.read())
230
# stdin should be empty
231
self.assertEqual('', factory.stdin.readline())
214
233
def test_text_factory_prompt(self):
215
234
# see <https://launchpad.net/bugs/365891>
216
StringIO = tests.StringIOWrapper
217
factory = _mod_ui_text.TextUIFactory(StringIO(), StringIO(), StringIO())
218
factory.prompt(u'foo %2e')
235
factory = TextUIFactory(StringIO(), StringIO(), StringIO())
236
factory.prompt('foo %2e')
219
237
self.assertEqual('', factory.stdout.getvalue())
220
238
self.assertEqual('foo %2e', factory.stderr.getvalue())
222
240
def test_text_factory_prompts_and_clears(self):
223
241
# a get_boolean call should clear the pb before prompting
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
243
os.environ['TERM'] = 'xterm'
244
factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
230
245
pb = factory.nested_progress_bar()
231
246
pb.show_bar = False
232
247
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'],
356
class UITests(tests.TestCase):
358
def test_progress_construction(self):
359
"""TextUIFactory constructs the right progress view.
361
TTYStringIO = test_progress._TTYStringIO
362
FileStringIO = tests.StringIOWrapper
363
for (file_class, term, pb, expected_pb_class) in (
364
# on an xterm, either use them or not as the user requests,
365
# otherwise default on
366
(TTYStringIO, 'xterm', 'none', _mod_ui_text.NullProgressView),
367
(TTYStringIO, 'xterm', 'text', _mod_ui_text.TextProgressView),
368
(TTYStringIO, 'xterm', None, _mod_ui_text.TextProgressView),
369
# on a dumb terminal, again if there's explicit configuration do
370
# it, otherwise default off
371
(TTYStringIO, 'dumb', 'none', _mod_ui_text.NullProgressView),
372
(TTYStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
373
(TTYStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
374
# on a non-tty terminal, it's null regardless of $TERM
375
(FileStringIO, 'xterm', None, _mod_ui_text.NullProgressView),
376
(FileStringIO, 'dumb', None, _mod_ui_text.NullProgressView),
377
# however, it can still be forced on
378
(FileStringIO, 'dumb', 'text', _mod_ui_text.TextProgressView),
380
self.overrideEnv('TERM', term)
381
self.overrideEnv('BZR_PROGRESS_BAR', pb)
382
stdin = file_class('')
383
stderr = file_class()
384
stdout = file_class()
385
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
386
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
387
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
388
self.assertIsInstance(uif.make_progress_view(),
390
"TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
392
def test_text_ui_non_terminal(self):
393
"""Even on non-ttys, make_ui_for_terminal gives a text ui."""
394
stdin = test_progress._NonTTYStringIO('')
395
stderr = test_progress._NonTTYStringIO()
396
stdout = test_progress._NonTTYStringIO()
397
for term_type in ['dumb', None, 'xterm']:
398
self.overrideEnv('TERM', term_type)
399
uif = _mod_ui.make_ui_for_terminal(stdin, stdout, stderr)
400
self.assertIsInstance(uif, _mod_ui_text.TextUIFactory,
401
'TERM=%r' % (term_type,))
404
class SilentUITests(tests.TestCase):
307
class CLIUITests(TestCase):
309
def test_cli_factory_deprecated(self):
310
uif = self.applyDeprecated(deprecated_in((1, 18, 0)),
312
StringIO(), StringIO(), StringIO())
313
self.assertIsInstance(uif, UIFactory)
316
class SilentUITests(TestCase):
406
318
def test_silent_factory_get_password(self):
407
319
# A silent factory that can't do user interaction can't get a
408
320
# password. Possibly it should raise a more specific error but it
410
ui = _mod_ui.SilentUIFactory()
411
stdout = tests.StringIOWrapper()
322
ui = SilentUIFactory()
412
324
self.assertRaises(
413
325
NotImplementedError,
414
326
self.apply_redirected,
503
413
self.assertIsNone('0', av)
504
414
self.assertIsNone('on', av)
505
415
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)