~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_ui.py

  • Committer: John Arbash Meinel
  • Date: 2009-07-29 21:35:05 UTC
  • mfrom: (4576 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4577.
  • Revision ID: john@arbash-meinel.com-20090729213505-tkqsvy1zfpocu75w
Merge bzr.dev 4576 in prep for NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
import sys
24
24
import time
25
25
 
26
 
import bzrlib
27
 
import bzrlib.errors as errors
 
26
from bzrlib import (
 
27
    errors,
 
28
    tests,
 
29
    ui as _mod_ui,
 
30
    )
28
31
from bzrlib.symbol_versioning import (
29
32
    deprecated_in,
30
33
    )
33
36
    TestUIFactory,
34
37
    StringIOWrapper,
35
38
    )
36
 
from bzrlib.tests.test_progress import _TTYStringIO
 
39
from bzrlib.tests.test_progress import (
 
40
    _NonTTYStringIO,
 
41
    _TTYStringIO,
 
42
    )
37
43
from bzrlib.ui import (
 
44
    CannedInputUIFactory,
38
45
    CLIUIFactory,
39
46
    SilentUIFactory,
 
47
    UIFactory,
 
48
    make_ui_for_terminal,
40
49
    )
41
50
from bzrlib.ui.text import (
 
51
    NullProgressView,
42
52
    TextProgressView,
43
53
    TextUIFactory,
44
54
    )
45
55
 
46
56
 
47
 
class UITests(TestCase):
48
 
 
49
 
    def test_silent_factory(self):
50
 
        ui = SilentUIFactory()
51
 
        stdout = StringIO()
52
 
        self.assertEqual(None,
53
 
                         self.apply_redirected(None, stdout, stdout,
54
 
                                               ui.get_password))
55
 
        self.assertEqual('', stdout.getvalue())
56
 
        self.assertEqual(None,
57
 
                         self.apply_redirected(None, stdout, stdout,
58
 
                                               ui.get_password,
59
 
                                               u'Hello\u1234 %(user)s',
60
 
                                               user=u'some\u1234'))
61
 
        self.assertEqual('', stdout.getvalue())
 
57
class UITests(tests.TestCase):
62
58
 
63
59
    def test_text_factory_ascii_password(self):
64
 
        ui = TestUIFactory(stdin='secret\n', stdout=StringIOWrapper(),
65
 
                           stderr=StringIOWrapper())
 
60
        ui = tests.TestUIFactory(stdin='secret\n',
 
61
                                 stdout=tests.StringIOWrapper(),
 
62
                                 stderr=tests.StringIOWrapper())
66
63
        pb = ui.nested_progress_bar()
67
64
        try:
68
65
            self.assertEqual('secret',
83
80
        We can't predict what encoding users will have for stdin, so we force
84
81
        it to utf8 to test that we transport the password correctly.
85
82
        """
86
 
        ui = TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
87
 
                           stdout=StringIOWrapper(),
88
 
                           stderr=StringIOWrapper())
 
83
        ui = tests.TestUIFactory(stdin=u'baz\u1234'.encode('utf8'),
 
84
                                 stdout=tests.StringIOWrapper(),
 
85
                                 stderr=tests.StringIOWrapper())
89
86
        ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = 'utf8'
90
87
        pb = ui.nested_progress_bar()
91
88
        try:
103
100
        finally:
104
101
            pb.finished()
105
102
 
 
103
    def test_progress_construction(self):
 
104
        """TextUIFactory constructs the right progress view.
 
105
        """
 
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),
 
122
            ):
 
123
            os.environ['TERM'] = term
 
124
            if pb is None:
 
125
                if 'BZR_PROGRESS_BAR' in os.environ:
 
126
                    del os.environ['BZR_PROGRESS_BAR']
 
127
            else:
 
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(),
 
136
                expected_pb_class,
 
137
                "TERM=%s BZR_PROGRESS_BAR=%s uif=%r" % (term, pb, uif,))
 
138
 
 
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']
 
147
            else:
 
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,))
 
152
 
106
153
    def test_progress_note(self):
107
154
        stderr = StringIO()
108
155
        stdout = StringIO()
122
169
            pb.finished()
123
170
 
124
171
    def test_progress_note_clears(self):
125
 
        stderr = StringIO()
126
 
        stdout = StringIO()
127
 
        # The PQM redirects the output to a file, so it
128
 
        # defaults to creating a Dots progress bar. we
129
 
        # need to force it to believe we are a TTY
 
172
        stderr = _TTYStringIO()
 
173
        stdout = _TTYStringIO()
 
174
        # so that we get a TextProgressBar
 
175
        os.environ['TERM'] = 'xterm'
130
176
        ui_factory = TextUIFactory(
131
177
            stdin=StringIO(''),
132
178
            stdout=stdout, stderr=stderr)
 
179
        self.assertIsInstance(ui_factory._progress_view,
 
180
            TextProgressView)
133
181
        pb = ui_factory.nested_progress_bar()
134
182
        try:
135
183
            # Create a progress update that isn't throttled
158
206
        pb2.finished()
159
207
        pb1.finished()
160
208
 
161
 
    def assert_get_bool_acceptance_of_user_input(self, factory):
162
 
        factory.stdin = StringIO("y\nyes with garbage\n"
163
 
                                 "yes\nn\nnot an answer\n"
164
 
                                 "no\nfoo\n")
165
 
        factory.stdout = StringIO()
166
 
        factory.stderr = StringIO()
167
 
        # there is no output from the base factory
168
 
        self.assertEqual(True, factory.get_boolean(""))
169
 
        self.assertEqual(True, factory.get_boolean(""))
170
 
        self.assertEqual(False, factory.get_boolean(""))
 
209
    def test_text_ui_get_boolean(self):
 
210
        stdin = StringIO("y\n" # True
 
211
                         "n\n" # False
 
212
                         "yes with garbage\nY\n" # True
 
213
                         "not an answer\nno\n" # False
 
214
                         "I'm sure!\nyes\n" # True
 
215
                         "NO\n" # False
 
216
                         "foo\n")
 
217
        stdout = StringIO()
 
218
        stderr = StringIO()
 
219
        factory = TextUIFactory(stdin, stdout, stderr)
 
220
        self.assertEqual(True, factory.get_boolean(""))
 
221
        self.assertEqual(False, factory.get_boolean(""))
 
222
        self.assertEqual(True, factory.get_boolean(""))
 
223
        self.assertEqual(False, factory.get_boolean(""))
 
224
        self.assertEqual(True, factory.get_boolean(""))
171
225
        self.assertEqual(False, factory.get_boolean(""))
172
226
        self.assertEqual("foo\n", factory.stdin.read())
173
227
        # stdin should be empty
174
228
        self.assertEqual('', factory.stdin.readline())
175
229
 
176
 
    def test_silent_ui_getbool(self):
177
 
        factory = SilentUIFactory()
178
 
        self.assert_get_bool_acceptance_of_user_input(factory)
179
 
 
180
 
    def test_silent_factory_prompts_silently(self):
181
 
        factory = SilentUIFactory()
182
 
        stdout = StringIO()
183
 
        factory.stdin = StringIO("y\n")
184
 
        self.assertEqual(True,
185
 
                         self.apply_redirected(None, stdout, stdout,
186
 
                                               factory.get_boolean, "foo"))
187
 
        self.assertEqual("", stdout.getvalue())
188
 
        # stdin should be empty
189
 
        self.assertEqual('', factory.stdin.readline())
190
 
 
191
 
    def test_text_ui_getbool(self):
192
 
        factory = TextUIFactory(None, None, None)
193
 
        self.assert_get_bool_acceptance_of_user_input(factory)
194
 
 
195
230
    def test_text_factory_prompt(self):
196
231
        # see <https://launchpad.net/bugs/365891>
197
 
        factory = TextUIFactory(None, StringIO(), StringIO(), StringIO())
 
232
        factory = TextUIFactory(StringIO(), StringIO(), StringIO())
198
233
        factory.prompt('foo %2e')
199
234
        self.assertEqual('', factory.stdout.getvalue())
200
235
        self.assertEqual('foo %2e', factory.stderr.getvalue())
202
237
    def test_text_factory_prompts_and_clears(self):
203
238
        # a get_boolean call should clear the pb before prompting
204
239
        out = _TTYStringIO()
 
240
        os.environ['TERM'] = 'xterm'
205
241
        factory = TextUIFactory(stdin=StringIO("yada\ny\n"), stdout=out, stderr=out)
206
242
        pb = factory.nested_progress_bar()
207
243
        pb.show_bar = False
232
268
        finally:
233
269
            pb.finished()
234
270
 
235
 
    def test_silent_ui_getusername(self):
236
 
        factory = SilentUIFactory()
237
 
        factory.stdin = StringIO("someuser\n\n")
238
 
        factory.stdout = StringIO()
239
 
        factory.stderr = StringIO()
240
 
        self.assertEquals(None,
241
 
            factory.get_username(u'Hello\u1234 %(host)s', host=u'some\u1234'))
242
 
        self.assertEquals("", factory.stdout.getvalue())
243
 
        self.assertEquals("", factory.stderr.getvalue())
244
 
        self.assertEquals("someuser\n\n", factory.stdin.getvalue())
245
 
 
246
271
    def test_text_ui_getusername(self):
247
272
        factory = TextUIFactory(None, None, None)
248
273
        factory.stdin = StringIO("someuser\n\n")
259
284
        self.assertEqual('', factory.stdin.readline())
260
285
 
261
286
    def test_text_ui_getusername_utf8(self):
262
 
        ui = TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
263
 
                           stdout=StringIOWrapper(), stderr=StringIOWrapper())
 
287
        ui = tests.TestUIFactory(stdin=u'someuser\u1234'.encode('utf8'),
 
288
                                 stdout=tests.StringIOWrapper(),
 
289
                                 stderr=tests.StringIOWrapper())
264
290
        ui.stderr.encoding = ui.stdout.encoding = ui.stdin.encoding = "utf8"
265
291
        pb = ui.nested_progress_bar()
266
292
        try:
275
301
            pb.finished()
276
302
 
277
303
 
278
 
class TestTextProgressView(TestCase):
279
 
    """Tests for text display of progress bars.
280
 
    """
281
 
    # XXX: These might be a bit easier to write if the rendering and
282
 
    # state-maintaining parts of TextProgressView were more separate, and if
283
 
    # the progress task called back directly to its own view not to the ui
284
 
    # factory. -- mbp 20090312
 
304
class CLIUITests(TestCase):
 
305
 
 
306
    def test_cli_factory_deprecated(self):
 
307
        uif = self.applyDeprecated(deprecated_in((1, 18, 0)),
 
308
            CLIUIFactory,
 
309
            StringIO(), StringIO(), StringIO())
 
310
        self.assertIsInstance(uif, UIFactory)
 
311
 
 
312
 
 
313
class SilentUITests(TestCase):
 
314
 
 
315
    def test_silent_factory_get_password(self):
 
316
        # A silent factory that can't do user interaction can't get a
 
317
        # password.  Possibly it should raise a more specific error but it
 
318
        # can't succeed.
 
319
        ui = SilentUIFactory()
 
320
        stdout = StringIO()
 
321
        self.assertRaises(
 
322
            NotImplementedError,
 
323
            self.apply_redirected,
 
324
            None, stdout, stdout, ui.get_password)
 
325
        # and it didn't write anything out either
 
326
        self.assertEqual('', stdout.getvalue())
 
327
 
 
328
    def test_silent_ui_getbool(self):
 
329
        factory = SilentUIFactory()
 
330
        stdout = StringIO()
 
331
        self.assertRaises(
 
332
            NotImplementedError,
 
333
            self.apply_redirected,
 
334
            None, stdout, stdout, factory.get_boolean, "foo")
 
335
 
 
336
 
 
337
class CannedInputUIFactoryTests(TestCase):
285
338
    
286
 
    def _make_factory(self):
287
 
        out = StringIO()
288
 
        uif = TextUIFactory(stderr=out)
289
 
        uif._progress_view._width = 80
290
 
        return out, uif
291
 
 
292
 
    def test_render_progress_easy(self):
293
 
        """Just one task and one quarter done"""
294
 
        out, uif = self._make_factory()
295
 
        task = uif.nested_progress_bar()
296
 
        task.update('reticulating splines', 5, 20)
297
 
        self.assertEqual(
298
 
'\r[####/               ] reticulating splines 5/20                               \r'
299
 
            , out.getvalue())
300
 
 
301
 
    def test_render_progress_nested(self):
302
 
        """Tasks proportionally contribute to overall progress"""
303
 
        out, uif = self._make_factory()
304
 
        task = uif.nested_progress_bar()
305
 
        task.update('reticulating splines', 0, 2)
306
 
        task2 = uif.nested_progress_bar()
307
 
        task2.update('stage2', 1, 2)
308
 
        # so we're in the first half of the main task, and half way through
309
 
        # that
310
 
        self.assertEqual(
311
 
r'[####\               ] reticulating splines:stage2 1/2'
312
 
            , uif._progress_view._render_line())
313
 
        # if the nested task is complete, then we're all the way through the
314
 
        # first half of the overall work
315
 
        task2.update('stage2', 2, 2)
316
 
        self.assertEqual(
317
 
r'[#########|          ] reticulating splines:stage2 2/2'
318
 
            , uif._progress_view._render_line())
319
 
 
320
 
    def test_render_progress_sub_nested(self):
321
 
        """Intermediate tasks don't mess up calculation."""
322
 
        out, uif = self._make_factory()
323
 
        task_a = uif.nested_progress_bar()
324
 
        task_a.update('a', 0, 2)
325
 
        task_b = uif.nested_progress_bar()
326
 
        task_b.update('b')
327
 
        task_c = uif.nested_progress_bar()
328
 
        task_c.update('c', 1, 2)
329
 
        # the top-level task is in its first half; the middle one has no
330
 
        # progress indication, just a label; and the bottom one is half done,
331
 
        # so the overall fraction is 1/4
332
 
        self.assertEqual(
333
 
            r'[####|               ] a:b:c 1/2'
334
 
            , uif._progress_view._render_line())
335
 
 
 
339
    def test_canned_input_get_input(self):
 
340
        uif = CannedInputUIFactory([True, 'mbp', 'password'])
 
341
        self.assertEqual(uif.get_boolean('Extra cheese?'), True)
 
342
        self.assertEqual(uif.get_username('Enter your user name'), 'mbp')
 
343
        self.assertEqual(uif.get_password('Password for %(host)s', host='example.com'),
 
344
            'password')
 
345
 
 
346
 
 
347
class TestBoolFromString(tests.TestCase):
 
348
 
 
349
    def assertIsTrue(self, s, accepted_values=None):
 
350
        res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
 
351
        self.assertEquals(True, res)
 
352
 
 
353
    def assertIsFalse(self, s, accepted_values=None):
 
354
        res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
 
355
        self.assertEquals(False, res)
 
356
 
 
357
    def assertIsNone(self, s, accepted_values=None):
 
358
        res = _mod_ui.bool_from_string(s, accepted_values=accepted_values)
 
359
        self.assertIs(None, res)
 
360
 
 
361
    def test_know_valid_values(self):
 
362
        self.assertIsTrue('true')
 
363
        self.assertIsFalse('false')
 
364
        self.assertIsTrue('1')
 
365
        self.assertIsFalse('0')
 
366
        self.assertIsTrue('on')
 
367
        self.assertIsFalse('off')
 
368
        self.assertIsTrue('yes')
 
369
        self.assertIsFalse('no')
 
370
        self.assertIsTrue('y')
 
371
        self.assertIsFalse('n')
 
372
        # Also try some case variations
 
373
        self.assertIsTrue('True')
 
374
        self.assertIsFalse('False')
 
375
        self.assertIsTrue('On')
 
376
        self.assertIsFalse('Off')
 
377
        self.assertIsTrue('ON')
 
378
        self.assertIsFalse('OFF')
 
379
        self.assertIsTrue('oN')
 
380
        self.assertIsFalse('oFf')
 
381
 
 
382
    def test_invalid_values(self):
 
383
        self.assertIsNone(None)
 
384
        self.assertIsNone('doubt')
 
385
        self.assertIsNone('frue')
 
386
        self.assertIsNone('talse')
 
387
        self.assertIsNone('42')
 
388
 
 
389
    def test_provided_values(self):
 
390
        av = dict(y=True, n=False, yes=True, no=False)
 
391
        self.assertIsTrue('y', av)
 
392
        self.assertIsTrue('Y', av)
 
393
        self.assertIsTrue('Yes', av)
 
394
        self.assertIsFalse('n', av)
 
395
        self.assertIsFalse('N', av)
 
396
        self.assertIsFalse('No', av)
 
397
        self.assertIsNone('1', av)
 
398
        self.assertIsNone('0', av)
 
399
        self.assertIsNone('on', av)
 
400
        self.assertIsNone('off', av)