summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_curses.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2021-01-31 15:22:27 (GMT)
committerGitHub <noreply@github.com>2021-01-31 15:22:27 (GMT)
commitd64fd4bb5bb4fd2e3277f39d3ad99b5a8d193e1b (patch)
tree6c43e1420810847d49240908aa9460a2cbc7988f /Lib/test/test_curses.py
parent42b1806af90b86ec393ca7da14e99ce95ec6c53b (diff)
downloadcpython-d64fd4bb5bb4fd2e3277f39d3ad99b5a8d193e1b.zip
cpython-d64fd4bb5bb4fd2e3277f39d3ad99b5a8d193e1b.tar.gz
cpython-d64fd4bb5bb4fd2e3277f39d3ad99b5a8d193e1b.tar.bz2
bpo-43016: Rewrite tests for curses (GH-24312)
Diffstat (limited to 'Lib/test/test_curses.py')
-rw-r--r--Lib/test/test_curses.py1002
1 files changed, 787 insertions, 215 deletions
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index f2cad05..29286bc 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -1,18 +1,9 @@
-#
-# Test script for the curses module
-#
-# This script doesn't actually display anything very coherent. but it
-# does call (nearly) every method and function.
-#
-# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr()
-# Only called, not tested: getmouse(), ungetmouse()
-#
-
+import functools
+import inspect
import os
import string
import sys
import tempfile
-import functools
import unittest
from test.support import requires, verbose, SaveSignals
@@ -21,7 +12,6 @@ from test.support.import_helper import import_module
# Optionally test curses module. This currently requires that the
# 'curses' resource be given on the regrtest command line using the -u
# option. If not available, nothing after this line will be executed.
-import inspect
requires('curses')
# If either of these don't exist, skip the tests.
@@ -37,6 +27,17 @@ def requires_curses_func(name):
return unittest.skipUnless(hasattr(curses, name),
'requires curses.%s' % name)
+def requires_curses_window_meth(name):
+ def deco(test):
+ @functools.wraps(test)
+ def wrapped(self, *args, **kwargs):
+ if not hasattr(self.stdscr, name):
+ raise unittest.SkipTest('requires curses.window.%s' % name)
+ test(self, *args, **kwargs)
+ return wrapped
+ return deco
+
+
def requires_colors(test):
@functools.wraps(test)
def wrapped(self, *args, **kwargs):
@@ -111,213 +112,732 @@ class TestCurses(unittest.TestCase):
curses.savetty()
self.addCleanup(curses.endwin)
self.addCleanup(curses.resetty)
+ self.stdscr.erase()
+
+ @requires_curses_func('filter')
+ def test_filter(self):
+ # TODO: Should be called before initscr() or newterm() are called.
+ # TODO: nofilter()
+ curses.filter()
+
+ @requires_curses_func('use_env')
+ def test_use_env(self):
+ # TODO: Should be called before initscr() or newterm() are called.
+ # TODO: use_tioctl()
+ curses.use_env(False)
+ curses.use_env(True)
+
+ def test_create_windows(self):
+ win = curses.newwin(5, 10)
+ self.assertEqual(win.getbegyx(), (0, 0))
+ self.assertEqual(win.getparyx(), (-1, -1))
+ self.assertEqual(win.getmaxyx(), (5, 10))
+
+ win = curses.newwin(10, 15, 2, 5)
+ self.assertEqual(win.getbegyx(), (2, 5))
+ self.assertEqual(win.getparyx(), (-1, -1))
+ self.assertEqual(win.getmaxyx(), (10, 15))
+
+ win2 = win.subwin(3, 7)
+ self.assertEqual(win2.getbegyx(), (3, 7))
+ self.assertEqual(win2.getparyx(), (1, 2))
+ self.assertEqual(win2.getmaxyx(), (9, 13))
+
+ win2 = win.subwin(5, 10, 3, 7)
+ self.assertEqual(win2.getbegyx(), (3, 7))
+ self.assertEqual(win2.getparyx(), (1, 2))
+ self.assertEqual(win2.getmaxyx(), (5, 10))
+
+ win3 = win.derwin(2, 3)
+ self.assertEqual(win3.getbegyx(), (4, 8))
+ self.assertEqual(win3.getparyx(), (2, 3))
+ self.assertEqual(win3.getmaxyx(), (8, 12))
+
+ win3 = win.derwin(6, 11, 2, 3)
+ self.assertEqual(win3.getbegyx(), (4, 8))
+ self.assertEqual(win3.getparyx(), (2, 3))
+ self.assertEqual(win3.getmaxyx(), (6, 11))
+
+ win.mvwin(0, 1)
+ self.assertEqual(win.getbegyx(), (0, 1))
+ self.assertEqual(win.getparyx(), (-1, -1))
+ self.assertEqual(win.getmaxyx(), (10, 15))
+ self.assertEqual(win2.getbegyx(), (3, 7))
+ self.assertEqual(win2.getparyx(), (1, 2))
+ self.assertEqual(win2.getmaxyx(), (5, 10))
+ self.assertEqual(win3.getbegyx(), (4, 8))
+ self.assertEqual(win3.getparyx(), (2, 3))
+ self.assertEqual(win3.getmaxyx(), (6, 11))
+
+ win2.mvderwin(2, 1)
+ self.assertEqual(win2.getbegyx(), (3, 7))
+ self.assertEqual(win2.getparyx(), (2, 1))
+ self.assertEqual(win2.getmaxyx(), (5, 10))
+
+ win3.mvderwin(2, 1)
+ self.assertEqual(win3.getbegyx(), (4, 8))
+ self.assertEqual(win3.getparyx(), (2, 1))
+ self.assertEqual(win3.getmaxyx(), (6, 11))
+
+ def test_move_cursor(self):
+ stdscr = self.stdscr
+ win = stdscr.subwin(10, 15, 2, 5)
+ stdscr.move(1, 2)
+ win.move(2, 4)
+ self.assertEqual(stdscr.getyx(), (1, 2))
+ self.assertEqual(win.getyx(), (2, 4))
+
+ win.cursyncup()
+ self.assertEqual(stdscr.getyx(), (4, 9))
- def test_window_funcs(self):
- "Test the methods of windows"
+ def test_refresh_control(self):
+ stdscr = self.stdscr
+ # touchwin()/untouchwin()/is_wintouched()
+ stdscr.refresh()
+ self.assertIs(stdscr.is_wintouched(), False)
+ stdscr.touchwin()
+ self.assertIs(stdscr.is_wintouched(), True)
+ stdscr.refresh()
+ self.assertIs(stdscr.is_wintouched(), False)
+ stdscr.touchwin()
+ self.assertIs(stdscr.is_wintouched(), True)
+ stdscr.untouchwin()
+ self.assertIs(stdscr.is_wintouched(), False)
+
+ # touchline()/untouchline()/is_linetouched()
+ stdscr.touchline(5, 2)
+ self.assertIs(stdscr.is_linetouched(5), True)
+ self.assertIs(stdscr.is_linetouched(6), True)
+ self.assertIs(stdscr.is_wintouched(), True)
+ stdscr.touchline(5, 1, False)
+ self.assertIs(stdscr.is_linetouched(5), False)
+
+ # syncup()
+ win = stdscr.subwin(10, 15, 2, 5)
+ win2 = win.subwin(5, 10, 3, 7)
+ win2.touchwin()
+ stdscr.untouchwin()
+ win2.syncup()
+ self.assertIs(win.is_wintouched(), True)
+ self.assertIs(stdscr.is_wintouched(), True)
+
+ # syncdown()
+ stdscr.touchwin()
+ win.untouchwin()
+ win2.untouchwin()
+ win2.syncdown()
+ self.assertIs(win2.is_wintouched(), True)
+
+ # syncok()
+ if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
+ win.untouchwin()
+ stdscr.untouchwin()
+ for syncok in [False, True]:
+ win2.syncok(syncok)
+ win2.addch('a')
+ self.assertIs(win.is_wintouched(), syncok)
+ self.assertIs(stdscr.is_wintouched(), syncok)
+
+ def test_output_character(self):
+ stdscr = self.stdscr
+ # addch()
+ stdscr.refresh()
+ stdscr.move(0, 0)
+ stdscr.addch('A')
+ stdscr.addch(b'A')
+ stdscr.addch(65)
+ stdscr.addch('\u20ac')
+ stdscr.addch('A', curses.A_BOLD)
+ stdscr.addch(1, 2, 'A')
+ stdscr.addch(2, 3, 'A', curses.A_BOLD)
+ self.assertIs(stdscr.is_wintouched(), True)
+
+ # echochar()
+ stdscr.refresh()
+ stdscr.move(0, 0)
+ stdscr.echochar('A')
+ stdscr.echochar(b'A')
+ stdscr.echochar(65)
+ self.assertRaises(OverflowError, stdscr.echochar, '\u20ac')
+ stdscr.echochar('A', curses.A_BOLD)
+ self.assertIs(stdscr.is_wintouched(), False)
+
+ def test_output_string(self):
+ stdscr = self.stdscr
+ # addstr()/insstr()
+ for func in [stdscr.addstr, stdscr.insstr]:
+ with self.subTest(func.__qualname__):
+ stdscr.move(0, 0)
+ func('abcd')
+ func(b'abcd')
+ func('àßçđ')
+ func('abcd', curses.A_BOLD)
+ func(1, 2, 'abcd')
+ func(2, 3, 'abcd', curses.A_BOLD)
+
+ # addnstr()/insnstr()
+ for func in [stdscr.addnstr, stdscr.insnstr]:
+ with self.subTest(func.__qualname__):
+ stdscr.move(0, 0)
+ func('1234', 3)
+ func(b'1234', 3)
+ func('\u0661\u0662\u0663\u0664', 3)
+ func('1234', 5)
+ func('1234', 3, curses.A_BOLD)
+ func(1, 2, '1234', 3)
+ func(2, 3, '1234', 3, curses.A_BOLD)
+
+ def test_output_string_embedded_null_chars(self):
+ # reject embedded null bytes and characters
stdscr = self.stdscr
- win = curses.newwin(10,10)
- win = curses.newwin(5,5, 5,5)
- win2 = curses.newwin(15,15, 5,5)
-
- for meth in [stdscr.addch, stdscr.addstr]:
- for args in [('a',), ('a', curses.A_BOLD),
- (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
- with self.subTest(meth=meth.__qualname__, args=args):
- meth(*args)
-
- for meth in [stdscr.clear, stdscr.clrtobot,
- stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
- stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
- stdscr.getbkgd, stdscr.getmaxyx,
- stdscr.getparyx, stdscr.getyx, stdscr.inch,
- stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
- win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
- stdscr.standout, stdscr.standend, stdscr.syncdown,
- stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
- with self.subTest(meth=meth.__qualname__):
- meth()
-
- stdscr.addnstr('1234', 3)
- stdscr.addnstr('1234', 3, curses.A_BOLD)
- stdscr.addnstr(4,4, '1234', 3)
- stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
-
- stdscr.attron(curses.A_BOLD)
- stdscr.attroff(curses.A_BOLD)
- stdscr.attrset(curses.A_BOLD)
- stdscr.bkgd(' ')
- stdscr.bkgd(' ', curses.A_REVERSE)
- stdscr.bkgdset(' ')
- stdscr.bkgdset(' ', curses.A_REVERSE)
+ for arg in ['a\0', b'a\0']:
+ with self.subTest(arg=arg):
+ self.assertRaises(ValueError, stdscr.addstr, arg)
+ self.assertRaises(ValueError, stdscr.addnstr, arg, 1)
+ self.assertRaises(ValueError, stdscr.insstr, arg)
+ self.assertRaises(ValueError, stdscr.insnstr, arg, 1)
- win.border(65, 66, 67, 68,
- 69, 70, 71, 72)
+ def test_read_from_window(self):
+ stdscr = self.stdscr
+ stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD)
+ # inch()
+ stdscr.move(0, 1)
+ self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD)
+ self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD)
+ stdscr.move(0, 0)
+ # instr()
+ self.assertEqual(stdscr.instr()[:6], b' ABCD ')
+ self.assertEqual(stdscr.instr(3)[:6], b' AB')
+ self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ')
+ self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ')
+ self.assertRaises(ValueError, stdscr.instr, -2)
+ self.assertRaises(ValueError, stdscr.instr, 0, 2, -2)
+
+ def test_getch(self):
+ win = curses.newwin(5, 12, 5, 2)
+
+ # TODO: Test with real input by writing to master fd.
+ for c in 'spam\n'[::-1]:
+ curses.ungetch(c)
+ self.assertEqual(win.getch(3, 1), b's'[0])
+ self.assertEqual(win.getyx(), (3, 1))
+ self.assertEqual(win.getch(3, 4), b'p'[0])
+ self.assertEqual(win.getyx(), (3, 4))
+ self.assertEqual(win.getch(), b'a'[0])
+ self.assertEqual(win.getyx(), (3, 4))
+ self.assertEqual(win.getch(), b'm'[0])
+ self.assertEqual(win.getch(), b'\n'[0])
+
+ def test_getstr(self):
+ win = curses.newwin(5, 12, 5, 2)
+ curses.echo()
+ self.addCleanup(curses.noecho)
+
+ self.assertRaises(ValueError, win.getstr, -400)
+ self.assertRaises(ValueError, win.getstr, 2, 3, -400)
+
+ # TODO: Test with real input by writing to master fd.
+ for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]:
+ curses.ungetch(c)
+ self.assertEqual(win.getstr(3, 1, 2), b'Lo')
+ self.assertEqual(win.instr(3, 0), b' Lo ')
+ self.assertEqual(win.getstr(3, 5, 10), b'ipsum')
+ self.assertEqual(win.instr(3, 0), b' Lo ipsum ')
+ self.assertEqual(win.getstr(1, 5), b'dolor')
+ self.assertEqual(win.instr(1, 0), b' dolor ')
+ self.assertEqual(win.getstr(2), b'si')
+ self.assertEqual(win.instr(1, 0), b'si dolor ')
+ self.assertEqual(win.getstr(), b'amet')
+ self.assertEqual(win.instr(1, 0), b'amet dolor ')
+
+ def test_clear(self):
+ win = curses.newwin(5, 15, 5, 2)
+ lorem_ipsum(win)
+
+ win.move(0, 8)
+ win.clrtoeol()
+ self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip')
+ self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,')
+
+ win.move(0, 3)
+ win.clrtobot()
+ self.assertEqual(win.instr(0, 0).rstrip(), b'Lor')
+ self.assertEqual(win.instr(1, 0).rstrip(), b'')
+
+ for func in [win.erase, win.clear]:
+ lorem_ipsum(win)
+ func()
+ self.assertEqual(win.instr(0, 0).rstrip(), b'')
+ self.assertEqual(win.instr(1, 0).rstrip(), b'')
+
+ def test_insert_delete(self):
+ win = curses.newwin(5, 15, 5, 2)
+ lorem_ipsum(win)
+
+ win.move(0, 2)
+ win.delch()
+ self.assertEqual(win.instr(0, 0), b'Loem ipsum ')
+ win.delch(0, 7)
+ self.assertEqual(win.instr(0, 0), b'Loem ipum ')
+
+ win.move(1, 5)
+ win.deleteln()
+ self.assertEqual(win.instr(0, 0), b'Loem ipum ')
+ self.assertEqual(win.instr(1, 0), b'consectetur ')
+ self.assertEqual(win.instr(2, 0), b'adipiscing elit')
+ self.assertEqual(win.instr(3, 0), b'sed do eiusmod ')
+ self.assertEqual(win.instr(4, 0), b' ')
+
+ win.move(1, 5)
+ win.insertln()
+ self.assertEqual(win.instr(0, 0), b'Loem ipum ')
+ self.assertEqual(win.instr(1, 0), b' ')
+ self.assertEqual(win.instr(2, 0), b'consectetur ')
+
+ win.clear()
+ lorem_ipsum(win)
+ win.move(1, 5)
+ win.insdelln(2)
+ self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
+ self.assertEqual(win.instr(1, 0), b' ')
+ self.assertEqual(win.instr(2, 0), b' ')
+ self.assertEqual(win.instr(3, 0), b'dolor sit amet,')
+
+ win.clear()
+ lorem_ipsum(win)
+ win.move(1, 5)
+ win.insdelln(-2)
+ self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
+ self.assertEqual(win.instr(1, 0), b'adipiscing elit')
+ self.assertEqual(win.instr(2, 0), b'sed do eiusmod ')
+ self.assertEqual(win.instr(3, 0), b' ')
+
+ def test_scroll(self):
+ win = curses.newwin(5, 15, 5, 2)
+ lorem_ipsum(win)
+ win.scrollok(True)
+ win.scroll()
+ self.assertEqual(win.instr(0, 0), b'dolor sit amet,')
+ win.scroll(2)
+ self.assertEqual(win.instr(0, 0), b'adipiscing elit')
+ win.scroll(-3)
+ self.assertEqual(win.instr(0, 0), b' ')
+ self.assertEqual(win.instr(2, 0), b' ')
+ self.assertEqual(win.instr(3, 0), b'adipiscing elit')
+ win.scrollok(False)
+
+ def test_attributes(self):
+ # TODO: attr_get(), attr_set(), ...
+ win = curses.newwin(5, 15, 5, 2)
+ win.attron(curses.A_BOLD)
+ win.attroff(curses.A_BOLD)
+ win.attrset(curses.A_BOLD)
+
+ win.standout()
+ win.standend()
+
+ @requires_curses_window_meth('chgat')
+ def test_chgat(self):
+ win = curses.newwin(5, 15, 5, 2)
+ win.addstr(2, 0, 'Lorem ipsum')
+ win.addstr(3, 0, 'dolor sit amet')
+
+ win.move(2, 8)
+ win.chgat(curses.A_BLINK)
+ self.assertEqual(win.inch(2, 7), b'p'[0])
+ self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK)
+ self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK)
+
+ win.move(2, 1)
+ win.chgat(3, curses.A_BOLD)
+ self.assertEqual(win.inch(2, 0), b'L'[0])
+ self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD)
+ self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD)
+ self.assertEqual(win.inch(2, 4), b'm'[0])
+
+ win.chgat(3, 2, curses.A_UNDERLINE)
+ self.assertEqual(win.inch(3, 1), b'o'[0])
+ self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE)
+ self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
+
+ win.chgat(3, 4, 7, curses.A_BLINK)
+ self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE)
+ self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK)
+ self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK)
+ self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE)
+ self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
+
+ def test_background(self):
+ win = curses.newwin(5, 15, 5, 2)
+ win.addstr(0, 0, 'Lorem ipsum')
+
+ self.assertEqual(win.getbkgd(), 0)
+
+ # bkgdset()
+ win.bkgdset('_')
+ self.assertEqual(win.getbkgd(), b'_'[0])
+ win.bkgdset(b'#')
+ self.assertEqual(win.getbkgd(), b'#'[0])
+ win.bkgdset(65)
+ self.assertEqual(win.getbkgd(), 65)
+ win.bkgdset(0)
+ self.assertEqual(win.getbkgd(), 32)
+
+ win.bkgdset('#', curses.A_REVERSE)
+ self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
+ self.assertEqual(win.inch(0, 0), b'L'[0])
+ self.assertEqual(win.inch(0, 5), b' '[0])
+ win.bkgdset(0)
+
+ # bkgd()
+ win.bkgd('_')
+ self.assertEqual(win.getbkgd(), b'_'[0])
+ self.assertEqual(win.inch(0, 0), b'L'[0])
+ self.assertEqual(win.inch(0, 5), b'_'[0])
+
+ win.bkgd('#', curses.A_REVERSE)
+ self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
+ self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE)
+ self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE)
+
+ def test_overlay(self):
+ srcwin = curses.newwin(5, 18, 3, 4)
+ lorem_ipsum(srcwin)
+ dstwin = curses.newwin(7, 17, 5, 7)
+ for i in range(6):
+ dstwin.addstr(i, 0, '_'*17)
+
+ srcwin.overlay(dstwin)
+ self.assertEqual(dstwin.instr(0, 0), b'sectetur_________')
+ self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____')
+ self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______')
+ self.assertEqual(dstwin.instr(3, 0), b'_________________')
+
+ srcwin.overwrite(dstwin)
+ self.assertEqual(dstwin.instr(0, 0), b'sectetur __')
+ self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __')
+ self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __')
+ self.assertEqual(dstwin.instr(3, 0), b'_________________')
+
+ srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11)
+ self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____')
+ self.assertEqual(dstwin.instr(4, 0), b'__ectetur________')
+ self.assertEqual(dstwin.instr(5, 0), b'_________________')
+
+ srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11)
+ self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____')
+ self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____')
+ self.assertEqual(dstwin.instr(5, 0), b'_________________')
+
+ def test_refresh(self):
+ win = curses.newwin(5, 15, 2, 5)
+ win.noutrefresh()
+ win.redrawln(1, 2)
+ win.redrawwin()
+ win.refresh()
+ curses.doupdate()
+
+ @requires_curses_window_meth('resize')
+ def test_resize(self):
+ win = curses.newwin(5, 15, 2, 5)
+ win.resize(4, 20)
+ self.assertEqual(win.getmaxyx(), (4, 20))
+ win.resize(5, 15)
+ self.assertEqual(win.getmaxyx(), (5, 15))
+
+ @requires_curses_window_meth('enclose')
+ def test_enclose(self):
+ win = curses.newwin(5, 15, 2, 5)
+ # TODO: Return bool instead of 1/0
+ self.assertTrue(win.enclose(2, 5))
+ self.assertFalse(win.enclose(1, 5))
+ self.assertFalse(win.enclose(2, 4))
+ self.assertTrue(win.enclose(6, 19))
+ self.assertFalse(win.enclose(7, 19))
+ self.assertFalse(win.enclose(6, 20))
+
+ def test_putwin(self):
+ win = curses.newwin(5, 12, 1, 2)
+ win.addstr(2, 1, 'Lorem ipsum')
+ with tempfile.TemporaryFile() as f:
+ win.putwin(f)
+ del win
+ f.seek(0)
+ win = curses.getwin(f)
+ self.assertEqual(win.getbegyx(), (1, 2))
+ self.assertEqual(win.getmaxyx(), (5, 12))
+ self.assertEqual(win.instr(2, 0), b' Lorem ipsum')
+
+ def test_borders_and_lines(self):
+ win = curses.newwin(5, 10, 5, 2)
win.border('|', '!', '-', '_',
'+', '\\', '#', '/')
- with self.assertRaises(TypeError,
- msg="Expected win.border() to raise TypeError"):
- win.border(65, 66, 67, 68,
- 69, [], 71, 72)
-
- win.box(65, 67)
- win.box('!', '_')
+ self.assertEqual(win.instr(0, 0), b'+--------\\')
+ self.assertEqual(win.instr(1, 0), b'| !')
+ self.assertEqual(win.instr(4, 0), b'#________/')
+ win.border(b'|', b'!', b'-', b'_',
+ b'+', b'\\', b'#', b'/')
+ win.border(65, 66, 67, 68,
+ 69, 70, 71, 72)
+ self.assertRaises(TypeError, win.border,
+ 65, 66, 67, 68, 69, [], 71, 72)
+ self.assertRaises(TypeError, win.border,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73)
+ self.assertRaises(TypeError, win.border,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73)
+ win.border(65, 66, 67, 68, 69, 70, 71)
+ win.border(65, 66, 67, 68, 69, 70)
+ win.border(65, 66, 67, 68, 69)
+ win.border(65, 66, 67, 68)
+ win.border(65, 66, 67)
+ win.border(65, 66)
+ win.border(65)
+ win.border()
+
+ win.box(':', '~')
+ self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~')
+ self.assertEqual(win.instr(1, 0), b': :')
+ self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~')
win.box(b':', b'~')
+ win.box(65, 67)
self.assertRaises(TypeError, win.box, 65, 66, 67)
self.assertRaises(TypeError, win.box, 65)
win.box()
- stdscr.clearok(1)
+ win.move(1, 2)
+ win.hline('-', 5)
+ self.assertEqual(win.instr(1, 1, 7), b' ----- ')
+ win.hline(b'-', 5)
+ win.hline(45, 5)
+ win.hline('-', 5, curses.A_BOLD)
+ win.hline(1, 1, '-', 5)
+ win.hline(1, 1, '-', 5, curses.A_BOLD)
+
+ win.move(1, 2)
+ win.vline('a', 3)
+ win.vline(b'a', 3)
+ win.vline(97, 3)
+ win.vline('a', 3, curses.A_STANDOUT)
+ win.vline(1, 1, 'a', 3)
+ win.vline(1, 1, ';', 2, curses.A_STANDOUT)
+ self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT)
+ self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT)
+ self.assertEqual(win.inch(3, 1), b'a'[0])
+
+ def test_unctrl(self):
+ # TODO: wunctrl()
+ self.assertEqual(curses.unctrl(b'A'), b'A')
+ self.assertEqual(curses.unctrl('A'), b'A')
+ self.assertEqual(curses.unctrl(65), b'A')
+ self.assertEqual(curses.unctrl(b'\n'), b'^J')
+ self.assertEqual(curses.unctrl('\n'), b'^J')
+ self.assertEqual(curses.unctrl(10), b'^J')
+ self.assertRaises(TypeError, curses.unctrl, b'')
+ self.assertRaises(TypeError, curses.unctrl, b'AB')
+ self.assertRaises(TypeError, curses.unctrl, '')
+ self.assertRaises(TypeError, curses.unctrl, 'AB')
+ self.assertRaises(OverflowError, curses.unctrl, 2**64)
+
+ def test_endwin(self):
+ if not self.isatty:
+ self.skipTest('requires terminal')
+ self.assertIs(curses.isendwin(), False)
+ curses.endwin()
+ self.assertIs(curses.isendwin(), True)
+ curses.doupdate()
+ self.assertIs(curses.isendwin(), False)
+
+ def test_terminfo(self):
+ self.assertIsInstance(curses.tigetflag('hc'), int)
+ self.assertEqual(curses.tigetflag('cols'), -1)
+ self.assertEqual(curses.tigetflag('cr'), -1)
+
+ self.assertIsInstance(curses.tigetnum('cols'), int)
+ self.assertEqual(curses.tigetnum('hc'), -2)
+ self.assertEqual(curses.tigetnum('cr'), -2)
+
+ self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None)))
+ self.assertIsNone(curses.tigetstr('hc'))
+ self.assertIsNone(curses.tigetstr('cols'))
+
+ cud = curses.tigetstr('cud')
+ if cud is not None:
+ # See issue10570.
+ self.assertIsInstance(cud, bytes)
+ curses.tparm(cud, 2)
+ cud_2 = curses.tparm(cud, 2)
+ self.assertIsInstance(cud_2, bytes)
+ curses.putp(cud_2)
+
+ curses.putp(b'abc\n')
+
+ def test_misc_module_funcs(self):
+ curses.delay_output(1)
+ curses.flushinp()
- win4 = stdscr.derwin(2,2)
- win4 = stdscr.derwin(1,1, 5,5)
- win4.mvderwin(9,9)
+ curses.doupdate()
+ self.assertIs(curses.isendwin(), False)
- stdscr.echochar('a')
- stdscr.echochar('a', curses.A_BOLD)
- stdscr.hline('-', 5)
- stdscr.hline('-', 5, curses.A_BOLD)
- stdscr.hline(1,1,'-', 5)
- stdscr.hline(1,1,'-', 5, curses.A_BOLD)
+ curses.napms(100)
+
+ curses.newpad(50, 50)
+
+ def test_env_queries(self):
+ # TODO: term_attrs(), erasewchar(), killwchar()
+ self.assertIsInstance(curses.termname(), bytes)
+ self.assertIsInstance(curses.longname(), bytes)
+ self.assertIsInstance(curses.baudrate(), int)
+ self.assertIsInstance(curses.has_ic(), bool)
+ self.assertIsInstance(curses.has_il(), bool)
+ self.assertIsInstance(curses.termattrs(), int)
+
+ c = curses.killchar()
+ self.assertIsInstance(c, bytes)
+ self.assertEqual(len(c), 1)
+ c = curses.erasechar()
+ self.assertIsInstance(c, bytes)
+ self.assertEqual(len(c), 1)
+
+ def test_output_options(self):
+ stdscr = self.stdscr
+
+ stdscr.clearok(True)
+ stdscr.clearok(False)
+
+ stdscr.idcok(True)
+ stdscr.idcok(False)
+
+ stdscr.idlok(False)
+ stdscr.idlok(True)
- stdscr.idcok(1)
- stdscr.idlok(1)
if hasattr(stdscr, 'immedok'):
- stdscr.immedok(1)
- stdscr.immedok(0)
- stdscr.insch('c')
- stdscr.insdelln(1)
- stdscr.insnstr('abc', 3)
- stdscr.insnstr('abc', 3, curses.A_BOLD)
- stdscr.insnstr(5, 5, 'abc', 3)
- stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
-
- stdscr.insstr('def')
- stdscr.insstr('def', curses.A_BOLD)
- stdscr.insstr(5, 5, 'def')
- stdscr.insstr(5, 5, 'def', curses.A_BOLD)
- stdscr.is_linetouched(0)
- stdscr.keypad(1)
- stdscr.leaveok(1)
- stdscr.move(3,3)
- win.mvwin(2,2)
- stdscr.nodelay(1)
- stdscr.notimeout(1)
- win2.overlay(win)
- win2.overwrite(win)
- win2.overlay(win, 1, 2, 2, 1, 3, 3)
- win2.overwrite(win, 1, 2, 2, 1, 3, 3)
- stdscr.redrawln(1,2)
-
- stdscr.scrollok(1)
- stdscr.scroll()
- stdscr.scroll(2)
- stdscr.scroll(-3)
-
- stdscr.move(12, 2)
- stdscr.setscrreg(10,15)
- win3 = stdscr.subwin(10,10)
- win3 = stdscr.subwin(10,10, 5,5)
- if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
- stdscr.syncok(1)
- stdscr.timeout(5)
- stdscr.touchline(5,5)
- stdscr.touchline(5,5,0)
- stdscr.vline('a', 3)
- stdscr.vline('a', 3, curses.A_STANDOUT)
- if hasattr(stdscr, 'chgat'):
- stdscr.chgat(5, 2, 3, curses.A_BLINK)
- stdscr.chgat(3, curses.A_BOLD)
- stdscr.chgat(5, 8, curses.A_UNDERLINE)
- stdscr.chgat(curses.A_BLINK)
- stdscr.refresh()
+ stdscr.immedok(True)
+ stdscr.immedok(False)
- stdscr.vline(1,1, 'a', 3)
- stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
+ stdscr.leaveok(True)
+ stdscr.leaveok(False)
- if hasattr(stdscr, 'resize'):
- stdscr.resize(25, 80)
- if hasattr(stdscr, 'enclose'):
- stdscr.enclose(10, 10)
+ stdscr.scrollok(True)
+ stdscr.scrollok(False)
- with tempfile.TemporaryFile() as f:
- self.stdscr.putwin(f)
- f.seek(0)
- curses.getwin(f)
+ stdscr.setscrreg(5, 10)
- self.assertRaises(ValueError, stdscr.getstr, -400)
- self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
- self.assertRaises(ValueError, stdscr.instr, -2)
- self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
+ curses.nonl()
+ curses.nl(True)
+ curses.nl(False)
+ curses.nl()
- def test_embedded_null_chars(self):
- # reject embedded null bytes and characters
+
+ def test_input_options(self):
stdscr = self.stdscr
- for arg in ['a', b'a']:
- with self.subTest(arg=arg):
- self.assertRaises(ValueError, stdscr.addstr, 'a\0')
- self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1)
- self.assertRaises(ValueError, stdscr.insstr, 'a\0')
- self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1)
-
- def test_module_funcs(self):
- "Test module-level functions"
- for func in [curses.baudrate, curses.beep, curses.can_change_color,
- curses.doupdate, curses.flash, curses.flushinp,
- curses.has_colors, curses.has_ic, curses.has_il,
- curses.isendwin, curses.killchar, curses.longname,
- curses.noecho, curses.nonl, curses.noqiflush,
- curses.termattrs, curses.termname, curses.erasechar,
- curses.has_extended_color_support]:
- with self.subTest(func=func.__qualname__):
- func()
+
if self.isatty:
- for func in [curses.cbreak, curses.def_prog_mode,
- curses.nocbreak, curses.noraw,
- curses.reset_prog_mode]:
- with self.subTest(func=func.__qualname__):
- func()
- if hasattr(curses, 'filter'):
- curses.filter()
- if hasattr(curses, 'getsyx'):
- curses.getsyx()
-
- # Functions that actually need arguments
- if curses.tigetstr("cnorm"):
- curses.curs_set(1)
- curses.delay_output(1)
- curses.echo() ; curses.echo(1)
+ curses.nocbreak()
+ curses.cbreak()
+ curses.cbreak(False)
+ curses.cbreak(True)
+
+ curses.intrflush(True)
+ curses.intrflush(False)
+
+ curses.raw()
+ curses.raw(False)
+ curses.raw(True)
+ curses.noraw()
+ curses.noecho()
+ curses.echo()
+ curses.echo(False)
+ curses.echo(True)
+
+ curses.halfdelay(255)
curses.halfdelay(1)
- if self.isatty:
- curses.intrflush(1)
- curses.meta(1)
- curses.napms(100)
- curses.newpad(50,50)
- win = curses.newwin(5,5)
- win = curses.newwin(5,5, 1,1)
- curses.nl() ; curses.nl(1)
- curses.putp(b'abc')
+
+ stdscr.keypad(True)
+ stdscr.keypad(False)
+
+ curses.meta(True)
+ curses.meta(False)
+
+ stdscr.nodelay(True)
+ stdscr.nodelay(False)
+
+ curses.noqiflush()
+ curses.qiflush(True)
+ curses.qiflush(False)
curses.qiflush()
- if self.isatty:
- curses.raw() ; curses.raw(1)
+
+ stdscr.notimeout(True)
+ stdscr.notimeout(False)
+
+ stdscr.timeout(-1)
+ stdscr.timeout(0)
+ stdscr.timeout(5)
+
+ @requires_curses_func('typeahead')
+ def test_typeahead(self):
+ curses.typeahead(sys.__stdin__.fileno())
+ curses.typeahead(-1)
+
+ def test_prog_mode(self):
+ if not self.isatty:
+ self.skipTest('requires terminal')
+ curses.def_prog_mode()
+ curses.reset_prog_mode()
+
+ def test_beep(self):
+ if (curses.tigetstr("bel") is not None
+ or curses.tigetstr("flash") is not None):
+ curses.beep()
+ else:
+ try:
+ curses.beep()
+ except curses.error:
+ self.skipTest('beep() failed')
+
+ def test_flash(self):
+ if (curses.tigetstr("bel") is not None
+ or curses.tigetstr("flash") is not None):
+ curses.flash()
+ else:
+ try:
+ curses.flash()
+ except curses.error:
+ self.skipTest('flash() failed')
+
+ def test_curs_set(self):
+ for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]:
+ if curses.tigetstr(cap) is not None:
+ curses.curs_set(vis)
+ else:
+ try:
+ curses.curs_set(vis)
+ except curses.error:
+ pass
+
+ @requires_curses_func('get_escdelay')
+ def test_escdelay(self):
+ escdelay = curses.get_escdelay()
+ self.assertIsInstance(escdelay, int)
curses.set_escdelay(25)
self.assertEqual(curses.get_escdelay(), 25)
+ curses.set_escdelay(escdelay)
+
+ @requires_curses_func('get_tabsize')
+ def test_tabsize(self):
+ tabsize = curses.get_tabsize()
+ self.assertIsInstance(tabsize, int)
curses.set_tabsize(4)
self.assertEqual(curses.get_tabsize(), 4)
- if hasattr(curses, 'setsyx'):
- curses.setsyx(5,5)
- curses.tigetflag('hc')
- curses.tigetnum('co')
- curses.tigetstr('cr')
- curses.tparm(b'cr')
- if hasattr(curses, 'typeahead'):
- curses.typeahead(sys.__stdin__.fileno())
- curses.unctrl('a')
- curses.ungetch('a')
- if hasattr(curses, 'use_env'):
- curses.use_env(1)
-
- # Functions only available on a few platforms
+ curses.set_tabsize(tabsize)
+
+ @requires_curses_func('getsyx')
+ def test_getsyx(self):
+ y, x = curses.getsyx()
+ self.assertIsInstance(y, int)
+ self.assertIsInstance(x, int)
+ curses.setsyx(4, 5)
+ self.assertEqual(curses.getsyx(), (4, 5))
def bad_colors(self):
return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
@@ -328,6 +848,10 @@ class TestCurses(unittest.TestCase):
def bad_pairs(self):
return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
+ def test_has_colors(self):
+ self.assertIsInstance(curses.has_colors(), bool)
+ self.assertIsInstance(curses.can_change_color(), bool)
+
def test_start_color(self):
if not curses.has_colors():
self.skipTest('requires colors support')
@@ -348,7 +872,7 @@ class TestCurses(unittest.TestCase):
@requires_colors
def test_init_color(self):
- if not curses.can_change_color:
+ if not curses.can_change_color():
self.skipTest('cannot change color')
old = curses.color_content(0)
@@ -435,14 +959,22 @@ class TestCurses(unittest.TestCase):
@requires_curses_func('use_default_colors')
@requires_colors
def test_use_default_colors(self):
- self.assertIn(curses.pair_content(0),
- ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
- curses.use_default_colors()
+ old = curses.pair_content(0)
+ try:
+ curses.use_default_colors()
+ except curses.error:
+ self.skipTest('cannot change color (use_default_colors() failed)')
self.assertEqual(curses.pair_content(0), (-1, -1))
+ self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)])
- @requires_curses_func('keyname')
def test_keyname(self):
- curses.keyname(13)
+ # TODO: key_name()
+ self.assertEqual(curses.keyname(65), b'A')
+ self.assertEqual(curses.keyname(13), b'^M')
+ self.assertEqual(curses.keyname(127), b'^?')
+ self.assertEqual(curses.keyname(0), b'^@')
+ self.assertRaises(ValueError, curses.keyname, -1)
+ self.assertIsInstance(curses.keyname(256), bytes)
@requires_curses_func('has_key')
def test_has_key(self):
@@ -498,22 +1030,46 @@ class TestCurses(unittest.TestCase):
@requires_curses_func('is_term_resized')
def test_is_term_resized(self):
- curses.is_term_resized(*self.stdscr.getmaxyx())
+ lines, cols = curses.LINES, curses.COLS
+ self.assertIs(curses.is_term_resized(lines, cols), False)
+ self.assertIs(curses.is_term_resized(lines-1, cols-1), True)
@requires_curses_func('resize_term')
def test_resize_term(self):
- curses.resize_term(*self.stdscr.getmaxyx())
+ curses.update_lines_cols()
+ lines, cols = curses.LINES, curses.COLS
+ new_lines = lines - 1
+ new_cols = cols + 1
+ curses.resize_term(new_lines, new_cols)
+ self.assertEqual(curses.LINES, new_lines)
+ self.assertEqual(curses.COLS, new_cols)
+
+ curses.resize_term(lines, cols)
+ self.assertEqual(curses.LINES, lines)
+ self.assertEqual(curses.COLS, cols)
@requires_curses_func('resizeterm')
def test_resizeterm(self):
+ curses.update_lines_cols()
lines, cols = curses.LINES, curses.COLS
new_lines = lines - 1
new_cols = cols + 1
curses.resizeterm(new_lines, new_cols)
-
self.assertEqual(curses.LINES, new_lines)
self.assertEqual(curses.COLS, new_cols)
+ curses.resizeterm(lines, cols)
+ self.assertEqual(curses.LINES, lines)
+ self.assertEqual(curses.COLS, cols)
+
+ def test_ungetch(self):
+ curses.ungetch(b'A')
+ self.assertEqual(self.stdscr.getkey(), 'A')
+ curses.ungetch('B')
+ self.assertEqual(self.stdscr.getkey(), 'B')
+ curses.ungetch(67)
+ self.assertEqual(self.stdscr.getkey(), 'C')
+
def test_issue6243(self):
curses.ungetch(1025)
self.stdscr.getkey()
@@ -542,10 +1098,6 @@ class TestCurses(unittest.TestCase):
read = stdscr.get_wch()
self.assertEqual(read, ch)
- def test_issue10570(self):
- b = curses.tparm(curses.tigetstr("cup"), 5, 3)
- self.assertIs(type(b), bytes)
-
def test_encoding(self):
stdscr = self.stdscr
import codecs
@@ -585,26 +1137,25 @@ class TestCurses(unittest.TestCase):
human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
self.assertIn("[y, x,]", human_readable_signature)
+ @requires_curses_window_meth('resize')
def test_issue13051(self):
- stdscr = self.stdscr
- if not hasattr(stdscr, 'resize'):
- raise unittest.SkipTest('requires curses.window.resize')
- box = curses.textpad.Textbox(stdscr, insert_mode=True)
- lines, cols = stdscr.getmaxyx()
- stdscr.resize(lines-2, cols-2)
+ win = curses.newwin(5, 15, 2, 5)
+ box = curses.textpad.Textbox(win, insert_mode=True)
+ lines, cols = win.getmaxyx()
+ win.resize(lines-2, cols-2)
# this may cause infinite recursion, leading to a RuntimeError
box._insert_printable_char('a')
class MiscTests(unittest.TestCase):
- @requires_curses_func('update_lines_cols')
def test_update_lines_cols(self):
- # this doesn't actually test that LINES and COLS are updated,
- # because we can't automate changing them. See Issue #4254 for
- # a manual test script. We can only test that the function
- # can be called.
curses.update_lines_cols()
+ lines, cols = curses.LINES, curses.COLS
+ curses.LINES = curses.COLS = 0
+ curses.update_lines_cols()
+ self.assertEqual(curses.LINES, lines)
+ self.assertEqual(curses.COLS, cols)
@requires_curses_func('ncurses_version')
def test_ncurses_version(self):
@@ -626,6 +1177,11 @@ class MiscTests(unittest.TestCase):
self.assertGreaterEqual(v.minor, 0)
self.assertGreaterEqual(v.patch, 0)
+ def test_has_extended_color_support(self):
+ r = curses.has_extended_color_support()
+ self.assertIsInstance(r, bool)
+
+
class TestAscii(unittest.TestCase):
def test_controlnames(self):
@@ -714,5 +1270,21 @@ class TestAscii(unittest.TestCase):
self.assertEqual(unctrl(ord('\xc1')), '!A')
+def lorem_ipsum(win):
+ text = [
+ 'Lorem ipsum',
+ 'dolor sit amet,',
+ 'consectetur',
+ 'adipiscing elit,',
+ 'sed do eiusmod',
+ 'tempor incididunt',
+ 'ut labore et',
+ 'dolore magna',
+ 'aliqua.',
+ ]
+ maxy, maxx = win.getmaxyx()
+ for y, line in enumerate(text[:maxy]):
+ win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
+
if __name__ == '__main__':
unittest.main()