summaryrefslogtreecommitdiff
path: root/blessings/tests/test_core.py
diff options
context:
space:
mode:
Diffstat (limited to 'blessings/tests/test_core.py')
-rw-r--r--blessings/tests/test_core.py457
1 files changed, 457 insertions, 0 deletions
diff --git a/blessings/tests/test_core.py b/blessings/tests/test_core.py
new file mode 100644
index 0000000..ce1e838
--- /dev/null
+++ b/blessings/tests/test_core.py
@@ -0,0 +1,457 @@
+# -*- coding: utf-8 -*-
+"Core blessings Terminal() tests."
+
+# std
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+import collections
+import warnings
+import platform
+import locale
+import sys
+import imp
+import os
+
+# local
+from .accessories import (
+ as_subprocess,
+ TestTerminal,
+ unicode_cap,
+ all_terms
+)
+
+# 3rd party
+import mock
+import pytest
+
+
+def test_export_only_Terminal():
+ "Ensure only Terminal instance is exported for import * statements."
+ import blessings
+ assert blessings.__all__ == ('Terminal',)
+
+
+def test_null_location(all_terms):
+ "Make sure ``location()`` with no args just does position restoration."
+ @as_subprocess
+ def child(kind):
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ with t.location():
+ pass
+ expected_output = u''.join(
+ (unicode_cap('sc'), unicode_cap('rc')))
+ assert (t.stream.getvalue() == expected_output)
+
+ child(all_terms)
+
+
+def test_flipped_location_move(all_terms):
+ "``location()`` and ``move()`` receive counter-example arguments."
+ @as_subprocess
+ def child(kind):
+ buf = StringIO()
+ t = TestTerminal(stream=buf, force_styling=True)
+ y, x = 10, 20
+ with t.location(y, x):
+ xy_val = t.move(x, y)
+ yx_val = buf.getvalue()[len(t.sc):]
+ assert xy_val == yx_val
+
+ child(all_terms)
+
+
+def test_yield_keypad():
+ "Ensure ``keypad()`` writes keyboard_xmit and keyboard_local."
+ @as_subprocess
+ def child(kind):
+ # given,
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ expected_output = u''.join((t.smkx, t.rmkx))
+
+ # exercise,
+ with t.keypad():
+ pass
+
+ # verify.
+ assert (t.stream.getvalue() == expected_output)
+
+ child(kind='xterm')
+
+
+def test_null_fileno():
+ "Make sure ``Terminal`` works when ``fileno`` is ``None``."
+ @as_subprocess
+ def child():
+ # This simulates piping output to another program.
+ out = StringIO()
+ out.fileno = None
+ t = TestTerminal(stream=out)
+ assert (t.save == u'')
+
+ child()
+
+
+def test_number_of_colors_without_tty():
+ "``number_of_colors`` should return 0 when there's no tty."
+ @as_subprocess
+ def child_256_nostyle():
+ t = TestTerminal(stream=StringIO())
+ assert (t.number_of_colors == 0)
+
+ @as_subprocess
+ def child_256_forcestyle():
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ assert (t.number_of_colors == 256)
+
+ @as_subprocess
+ def child_8_forcestyle():
+ t = TestTerminal(kind='ansi', stream=StringIO(),
+ force_styling=True)
+ assert (t.number_of_colors == 8)
+
+ @as_subprocess
+ def child_0_forcestyle():
+ t = TestTerminal(kind='vt220', stream=StringIO(),
+ force_styling=True)
+ assert (t.number_of_colors == 0)
+
+ child_0_forcestyle()
+ child_8_forcestyle()
+ child_256_forcestyle()
+ child_256_nostyle()
+
+
+def test_number_of_colors_with_tty():
+ "test ``number_of_colors`` 0, 8, and 256."
+ @as_subprocess
+ def child_256():
+ t = TestTerminal()
+ assert (t.number_of_colors == 256)
+
+ @as_subprocess
+ def child_8():
+ t = TestTerminal(kind='ansi')
+ assert (t.number_of_colors == 8)
+
+ @as_subprocess
+ def child_0():
+ t = TestTerminal(kind='vt220')
+ assert (t.number_of_colors == 0)
+
+ child_0()
+ child_8()
+ child_256()
+
+
+def test_init_descriptor_always_initted(all_terms):
+ "Test height and width with non-tty Terminals."
+ @as_subprocess
+ def child(kind):
+ t = TestTerminal(kind=kind, stream=StringIO())
+ assert t._init_descriptor == sys.__stdout__.fileno()
+ assert (isinstance(t.height, int))
+ assert (isinstance(t.width, int))
+ assert t.height == t._height_and_width()[0]
+ assert t.width == t._height_and_width()[1]
+
+ child(all_terms)
+
+
+def test_force_styling_none(all_terms):
+ "If ``force_styling=None`` is used, don't ever do styling."
+ @as_subprocess
+ def child(kind):
+ t = TestTerminal(kind=kind, force_styling=None)
+ assert (t.save == '')
+ assert (t.color(9) == '')
+ assert (t.bold('oi') == 'oi')
+
+ child(all_terms)
+
+
+def test_setupterm_singleton_issue33():
+ "A warning is emitted if a new terminal ``kind`` is used per process."
+ @as_subprocess
+ def child():
+ warnings.filterwarnings("error", category=UserWarning)
+
+ # instantiate first terminal, of type xterm-256color
+ term = TestTerminal(force_styling=True)
+
+ try:
+ # a second instantiation raises UserWarning
+ term = TestTerminal(kind="vt220", force_styling=True)
+ except UserWarning:
+ err = sys.exc_info()[1]
+ assert (err.args[0].startswith(
+ 'A terminal of kind "vt220" has been requested')
+ ), err.args[0]
+ assert ('a terminal of kind "xterm-256color" will '
+ 'continue to be returned' in err.args[0]), err.args[0]
+ else:
+ # unless term is not a tty and setupterm() is not called
+ assert not term.is_a_tty or False, 'Should have thrown exception'
+ warnings.resetwarnings()
+
+ child()
+
+
+def test_setupterm_invalid_issue39():
+ "A warning is emitted if TERM is invalid."
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=878089
+
+ # if TERM is unset, defaults to 'unknown', which should
+ # fail to lookup and emit a warning, only.
+ @as_subprocess
+ def child():
+ warnings.filterwarnings("error", category=UserWarning)
+
+ try:
+ term = TestTerminal(kind='unknown', force_styling=True)
+ except UserWarning:
+ err = sys.exc_info()[1]
+ assert err.args[0] == (
+ "Failed to setupterm(kind='unknown'): "
+ "setupterm: could not find terminal")
+ else:
+ assert not term.is_a_tty and not term.does_styling, (
+ 'Should have thrown exception')
+ warnings.resetwarnings()
+
+ child()
+
+
+def test_setupterm_invalid_has_no_styling():
+ "An unknown TERM type does not perform styling."
+ # https://bugzilla.mozilla.org/show_bug.cgi?id=878089
+
+ # if TERM is unset, defaults to 'unknown', which should
+ # fail to lookup and emit a warning, only.
+ @as_subprocess
+ def child():
+ warnings.filterwarnings("ignore", category=UserWarning)
+
+ term = TestTerminal(kind='unknown', force_styling=True)
+ assert term.kind is None
+ assert term.does_styling is False
+ assert term.number_of_colors == 0
+ warnings.resetwarnings()
+
+ child()
+
+
+@pytest.mark.skipif(platform.python_implementation() == 'PyPy',
+ reason='PyPy freezes')
+def test_missing_ordereddict_uses_module(monkeypatch):
+ "ordereddict module is imported when without collections.OrderedDict."
+ import blessings.keyboard
+
+ if hasattr(collections, 'OrderedDict'):
+ monkeypatch.delattr('collections.OrderedDict')
+
+ try:
+ imp.reload(blessings.keyboard)
+ except ImportError as err:
+ assert err.args[0] in ("No module named ordereddict", # py2
+ "No module named 'ordereddict'") # py3
+ sys.modules['ordereddict'] = mock.Mock()
+ sys.modules['ordereddict'].OrderedDict = -1
+ imp.reload(blessings.keyboard)
+ assert blessings.keyboard.OrderedDict == -1
+ del sys.modules['ordereddict']
+ monkeypatch.undo()
+ imp.reload(blessings.keyboard)
+ else:
+ assert platform.python_version_tuple() < ('2', '7') # reached by py2.6
+
+
+@pytest.mark.skipif(platform.python_implementation() == 'PyPy',
+ reason='PyPy freezes')
+def test_python3_2_raises_exception(monkeypatch):
+ "Test python version 3.0 through 3.2 raises an exception."
+ import blessings
+
+ monkeypatch.setattr('platform.python_version_tuple',
+ lambda: ('3', '2', '2'))
+
+ try:
+ imp.reload(blessings)
+ except ImportError as err:
+ assert err.args[0] == (
+ 'Blessings needs Python 3.2.3 or greater for Python 3 '
+ 'support due to http://bugs.python.org/issue10570.')
+ monkeypatch.undo()
+ imp.reload(blessings)
+ else:
+ assert False, 'Exception should have been raised'
+
+
+def test_IOUnsupportedOperation_dummy(monkeypatch):
+ "Ensure dummy exception is used when io is without UnsupportedOperation."
+ import blessings.terminal
+ import io
+ if hasattr(io, 'UnsupportedOperation'):
+ monkeypatch.delattr('io.UnsupportedOperation')
+
+ imp.reload(blessings.terminal)
+ assert blessings.terminal.IOUnsupportedOperation.__doc__.startswith(
+ "A dummy exception to take the place of")
+ monkeypatch.undo()
+ imp.reload(blessings.terminal)
+
+
+def test_without_dunder():
+ "Ensure dunder does not remain in module (py2x InterruptedError test."
+ import blessings.terminal
+ assert '_' not in dir(blessings.terminal)
+
+
+def test_IOUnsupportedOperation():
+ "Ensure stream that throws IOUnsupportedOperation results in non-tty."
+ @as_subprocess
+ def child():
+ import blessings.terminal
+
+ def side_effect():
+ raise blessings.terminal.IOUnsupportedOperation
+
+ mock_stream = mock.Mock()
+ mock_stream.fileno = side_effect
+
+ term = TestTerminal(stream=mock_stream)
+ assert term.stream == mock_stream
+ assert term.does_styling is False
+ assert term.is_a_tty is False
+ assert term.number_of_colors is 0
+
+ child()
+
+
+def test_winsize_IOError_returns_environ():
+ """When _winsize raises IOError, defaults from os.environ given."""
+ @as_subprocess
+ def child():
+ def side_effect(fd):
+ raise IOError
+
+ term = TestTerminal()
+ term._winsize = side_effect
+ os.environ['COLUMNS'] = '1984'
+ os.environ['LINES'] = '1888'
+ assert term._height_and_width() == (1888, 1984, None, None)
+
+ child()
+
+
+def test_yield_fullscreen(all_terms):
+ "Ensure ``fullscreen()`` writes enter_fullscreen and exit_fullscreen."
+ @as_subprocess
+ def child(kind):
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ t.enter_fullscreen = u'BEGIN'
+ t.exit_fullscreen = u'END'
+ with t.fullscreen():
+ pass
+ expected_output = u''.join((t.enter_fullscreen, t.exit_fullscreen))
+ assert (t.stream.getvalue() == expected_output)
+
+ child(all_terms)
+
+
+def test_yield_hidden_cursor(all_terms):
+ "Ensure ``hidden_cursor()`` writes hide_cursor and normal_cursor."
+ @as_subprocess
+ def child(kind):
+ t = TestTerminal(stream=StringIO(), force_styling=True)
+ t.hide_cursor = u'BEGIN'
+ t.normal_cursor = u'END'
+ with t.hidden_cursor():
+ pass
+ expected_output = u''.join((t.hide_cursor, t.normal_cursor))
+ assert (t.stream.getvalue() == expected_output)
+
+ child(all_terms)
+
+
+def test_no_preferredencoding_fallback_ascii():
+ "Ensure empty preferredencoding value defaults to ascii."
+ @as_subprocess
+ def child():
+ with mock.patch('locale.getpreferredencoding') as get_enc:
+ get_enc.return_value = u''
+ t = TestTerminal()
+ assert t._encoding == 'ascii'
+
+ child()
+
+
+def test_unknown_preferredencoding_warned_and_fallback_ascii():
+ "Ensure a locale without a codecs incrementaldecoder emits a warning."
+ @as_subprocess
+ def child():
+ with mock.patch('locale.getpreferredencoding') as get_enc:
+ with warnings.catch_warnings(record=True) as warned:
+ get_enc.return_value = '---unknown--encoding---'
+ t = TestTerminal()
+ assert t._encoding == 'ascii'
+ assert len(warned) == 1
+ assert issubclass(warned[-1].category, UserWarning)
+ assert "fallback to ASCII" in str(warned[-1].message)
+
+ child()
+
+
+def test_win32_missing_tty_modules(monkeypatch):
+ "Ensure dummy exception is used when io is without UnsupportedOperation."
+ @as_subprocess
+ def child():
+ OLD_STYLE = False
+ try:
+ original_import = getattr(__builtins__, '__import__')
+ OLD_STYLE = True
+ except AttributeError:
+ original_import = __builtins__['__import__']
+
+ tty_modules = ('termios', 'fcntl', 'tty')
+
+ def __import__(name, *args, **kwargs):
+ if name in tty_modules:
+ raise ImportError
+ return original_import(name, *args, **kwargs)
+
+ for module in tty_modules:
+ sys.modules.pop(module, None)
+
+ warnings.filterwarnings("error", category=UserWarning)
+ try:
+ if OLD_STYLE:
+ __builtins__.__import__ = __import__
+ else:
+ __builtins__['__import__'] = __import__
+ try:
+ import blessings.terminal
+ imp.reload(blessings.terminal)
+ except UserWarning:
+ err = sys.exc_info()[1]
+ assert err.args[0] == blessings.terminal.msg_nosupport
+
+ warnings.filterwarnings("ignore", category=UserWarning)
+ import blessings.terminal
+ imp.reload(blessings.terminal)
+ assert blessings.terminal.HAS_TTY is False
+ term = blessings.terminal.Terminal('ansi')
+ assert term.height == 24
+ assert term.width == 80
+
+ finally:
+ if OLD_STYLE:
+ setattr(__builtins__, '__import__', original_import)
+ else:
+ __builtins__['__import__'] = original_import
+ warnings.resetwarnings()
+ import blessings.terminal
+ imp.reload(blessings.terminal)
+
+ child()