~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_ui.py

  • Committer: Joe Julian
  • Date: 2010-01-10 02:25:31 UTC
  • mto: (4634.119.7 2.0)
  • mto: This revision was merged to the branch mainline in revision 4959.
  • Revision ID: joe@julianfamily.org-20100110022531-wqk61rsagz8xsiga
Added MANIFEST.in to allow bdist_rpm to have all the required include files and tools. bdist_rpm will still fail to build correctly on some distributions due to a disttools bug http://bugs.python.org/issue644744

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