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