1# -*- coding: utf-8 -*-
2"""Automated tests (as opposed to human-verified test patterns)
3
4It was tempting to mock out curses to get predictable output from ``tigetstr``,
5but there are concrete integration-testing benefits in not doing so. For
6instance, ``tigetstr`` changed its return type in Python 3.2.3. So instead, we
7simply create all our test ``Terminal`` instances with a known terminal type.
8All we require from the host machine is that a standard terminfo definition of
9xterm-256color exists.
10
11"""
12from curses import tigetstr, tparm
13from functools import partial
14import sys
15
16from nose import SkipTest
17from nose.tools import eq_
18from six import StringIO
19
20# This tests that __all__ is correct, since we use below everything that should
21# be imported:
22from blessings import *
23
24
25TestTerminal = partial(Terminal, kind='xterm-256color')
26
27
28def unicode_cap(cap):
29    """Return the result of ``tigetstr`` except as Unicode."""
30    return tigetstr(cap).decode('latin1')
31
32
33def unicode_parm(cap, *parms):
34    """Return the result of ``tparm(tigetstr())`` except as Unicode."""
35    return tparm(tigetstr(cap), *parms).decode('latin1')
36
37
38def test_capability():
39    """Check that a capability lookup works.
40
41    Also test that Terminal grabs a reasonable default stream. This test
42    assumes it will be run from a tty.
43
44    """
45    t = TestTerminal()
46    sc = unicode_cap('sc')
47    eq_(t.save, sc)
48    eq_(t.save, sc)  # Make sure caching doesn't screw it up.
49
50
51def test_capability_without_tty():
52    """Assert capability templates are '' when stream is not a tty."""
53    t = TestTerminal(stream=StringIO())
54    eq_(t.save, u'')
55    eq_(t.red, u'')
56
57
58def test_capability_with_forced_tty():
59    """If we force styling, capabilities had better not (generally) be
60    empty."""
61    t = TestTerminal(stream=StringIO(), force_styling=True)
62    eq_(t.save, unicode_cap('sc'))
63
64
65def test_parametrization():
66    """Test parametrizing a capability."""
67    eq_(TestTerminal().cup(3, 4), unicode_parm('cup', 3, 4))
68
69
70def test_height_and_width():
71    """Assert that ``height_and_width()`` returns ints."""
72    t = TestTerminal()  # kind shouldn't matter.
73    assert isinstance(t.height, int)
74    assert isinstance(t.width, int)
75
76
77def test_stream_attr():
78    """Make sure Terminal exposes a ``stream`` attribute that defaults to
79    something sane."""
80    eq_(Terminal().stream, sys.__stdout__)
81
82
83def test_location():
84    """Make sure ``location()`` does what it claims."""
85    t = TestTerminal(stream=StringIO(), force_styling=True)
86
87    with t.location(3, 4):
88        t.stream.write(u'hi')
89
90    eq_(t.stream.getvalue(), unicode_cap('sc') +
91                             unicode_parm('cup', 4, 3) +
92                             u'hi' +
93                             unicode_cap('rc'))
94
95
96def test_horizontal_location():
97    """Make sure we can move the cursor horizontally without changing rows."""
98    t = TestTerminal(stream=StringIO(), force_styling=True)
99    with t.location(x=5):
100        pass
101    eq_(t.stream.getvalue(), unicode_cap('sc') +
102                             unicode_parm('hpa', 5) +
103                             unicode_cap('rc'))
104
105
106def test_null_location():
107    """Make sure ``location()`` with no args just does position restoration."""
108    t = TestTerminal(stream=StringIO(), force_styling=True)
109    with t.location():
110        pass
111    eq_(t.stream.getvalue(), unicode_cap('sc') +
112                             unicode_cap('rc'))
113
114
115def test_zero_location():
116    """Make sure ``location()`` pays attention to 0-valued args."""
117    t = TestTerminal(stream=StringIO(), force_styling=True)
118    with t.location(0, 0):
119        pass
120    eq_(t.stream.getvalue(), unicode_cap('sc') +
121                             unicode_parm('cup', 0, 0) +
122                             unicode_cap('rc'))
123
124
125def test_null_fileno():
126    """Make sure ``Terminal`` works when ``fileno`` is ``None``.
127
128    This simulates piping output to another program.
129
130    """
131    out = StringIO()
132    out.fileno = None
133    t = TestTerminal(stream=out)
134    eq_(t.save, u'')
135
136
137def test_mnemonic_colors():
138    """Make sure color shortcuts work."""
139    def color(num):
140        return unicode_parm('setaf', num)
141
142    def on_color(num):
143        return unicode_parm('setab', num)
144
145    # Avoid testing red, blue, yellow, and cyan, since they might someday
146    # change depending on terminal type.
147    t = TestTerminal()
148    eq_(t.white, color(7))
149    eq_(t.green, color(2))  # Make sure it's different than white.
150    eq_(t.on_black, on_color(0))
151    eq_(t.on_green, on_color(2))
152    eq_(t.bright_black, color(8))
153    eq_(t.bright_green, color(10))
154    eq_(t.on_bright_black, on_color(8))
155    eq_(t.on_bright_green, on_color(10))
156
157
158def test_callable_numeric_colors():
159    """``color(n)`` should return a formatting wrapper."""
160    t = TestTerminal()
161    eq_(t.color(5)('smoo'), t.magenta + 'smoo' + t.normal)
162    eq_(t.color(5)('smoo'), t.color(5) + 'smoo' + t.normal)
163    eq_(t.on_color(2)('smoo'), t.on_green + 'smoo' + t.normal)
164    eq_(t.on_color(2)('smoo'), t.on_color(2) + 'smoo' + t.normal)
165
166
167def test_null_callable_numeric_colors():
168    """``color(n)`` should be a no-op on null terminals."""
169    t = TestTerminal(stream=StringIO())
170    eq_(t.color(5)('smoo'), 'smoo')
171    eq_(t.on_color(6)('smoo'), 'smoo')
172
173
174def test_naked_color_cap():
175    """``term.color`` should return a stringlike capability."""
176    t = TestTerminal()
177    eq_(t.color + '', t.setaf + '')
178
179
180def test_number_of_colors_without_tty():
181    """``number_of_colors`` should return 0 when there's no tty."""
182    # Hypothesis: once setupterm() has run and decided the tty supports 256
183    # colors, it never changes its mind.
184    raise SkipTest
185
186    t = TestTerminal(stream=StringIO())
187    eq_(t.number_of_colors, 0)
188    t = TestTerminal(stream=StringIO(), force_styling=True)
189    eq_(t.number_of_colors, 0)
190
191
192def test_number_of_colors_with_tty():
193    """``number_of_colors`` should work."""
194    t = TestTerminal()
195    eq_(t.number_of_colors, 256)
196
197
198def test_formatting_functions():
199    """Test crazy-ass formatting wrappers, both simple and compound."""
200    t = TestTerminal()
201    # By now, it should be safe to use sugared attributes. Other tests test
202    # those.
203    eq_(t.bold(u'hi'), t.bold + u'hi' + t.normal)
204    eq_(t.green('hi'), t.green + u'hi' + t.normal)  # Plain strs for Python 2.x
205    # Test some non-ASCII chars, probably not necessary:
206    eq_(t.bold_green(u'boö'), t.bold + t.green + u'boö' + t.normal)
207    eq_(t.bold_underline_green_on_red('boo'),
208        t.bold + t.underline + t.green + t.on_red + u'boo' + t.normal)
209    # Don't spell things like this:
210    eq_(t.on_bright_red_bold_bright_green_underline('meh'),
211        t.on_bright_red + t.bold + t.bright_green + t.underline + u'meh' +
212                          t.normal)
213
214
215def test_formatting_functions_without_tty():
216    """Test crazy-ass formatting wrappers when there's no tty."""
217    t = TestTerminal(stream=StringIO())
218    eq_(t.bold(u'hi'), u'hi')
219    eq_(t.green('hi'), u'hi')
220    # Test non-ASCII chars, no longer really necessary:
221    eq_(t.bold_green(u'boö'), u'boö')
222    eq_(t.bold_underline_green_on_red('loo'), u'loo')
223    eq_(t.on_bright_red_bold_bright_green_underline('meh'), u'meh')
224
225
226def test_nice_formatting_errors():
227    """Make sure you get nice hints if you misspell a formatting wrapper."""
228    t = TestTerminal()
229    try:
230        t.bold_misspelled('hey')
231    except TypeError as e:
232        assert 'probably misspelled' in e.args[0]
233
234    try:
235        t.bold_misspelled(u'hey')  # unicode
236    except TypeError as e:
237        assert 'probably misspelled' in e.args[0]
238
239    try:
240        t.bold_misspelled(None)  # an arbitrary non-string
241    except TypeError as e:
242        assert 'probably misspelled' not in e.args[0]
243
244    try:
245        t.bold_misspelled('a', 'b')  # >1 string arg
246    except TypeError as e:
247        assert 'probably misspelled' not in e.args[0]
248
249
250def test_init_descriptor_always_initted():
251    """We should be able to get a height and width even on no-tty Terminals."""
252    t = Terminal(stream=StringIO())
253    eq_(type(t.height), int)
254
255
256def test_force_styling_none():
257    """If ``force_styling=None`` is passed to the constructor, don't ever do
258    styling."""
259    t = TestTerminal(force_styling=None)
260    eq_(t.save, '')
261
262
263def test_null_callable_string():
264    """Make sure NullCallableString tolerates all numbers and kinds of args it
265    might receive."""
266    t = TestTerminal(stream=StringIO())
267    eq_(t.clear, '')
268    eq_(t.move(1, 2), '')
269    eq_(t.move_x(1), '')
270