[Python-checkins] [3.8] bpo-43016: Rewrite tests for curses (GH-24312). (GH-24399) (GH-24401)

serhiy-storchaka webhook-mailer at python.org
Sun Jan 31 12:44:21 EST 2021


https://github.com/python/cpython/commit/7ca947e93bff2b9d78312dc8ed8e0a2b7d25d3f3
commit: 7ca947e93bff2b9d78312dc8ed8e0a2b7d25d3f3
branch: 3.8
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2021-01-31T19:44:12+02:00
summary:

[3.8] bpo-43016: Rewrite tests for curses (GH-24312). (GH-24399) (GH-24401)

(cherry picked from commit d64fd4bb5bb4fd2e3277f39d3ad99b5a8d193e1b).
(cherry picked from commit e9d4960d15c5282904cf26e469ce7cee39f634f7)

files:
M Lib/test/test_curses.py

diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index 8991134e08063..f47c9876eed88 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, import_module, verbose, SaveSignals
@@ -20,7 +11,6 @@
 # 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.
@@ -36,6 +26,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):
@@ -110,208 +111,716 @@ def setUp(self):
             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
-        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)
+        # 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
+        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()
+
+        curses.doupdate()
+        self.assertIs(curses.isendwin(), False)
+
+        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)
 
-        win4 = stdscr.derwin(2,2)
-        win4 = stdscr.derwin(1,1, 5,5)
-        win4.mvderwin(9,9)
+        stdscr.idcok(True)
+        stdscr.idcok(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)
+        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]:
-            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)
-        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
+
+        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('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 (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
@@ -319,6 +828,10 @@ def bad_colors(self):
     def bad_pairs(self):
         return (-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')
@@ -342,7 +855,7 @@ def test_color_content(self):
 
     @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)
@@ -430,14 +943,22 @@ def test_color_attrs(self):
     @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):
@@ -493,22 +1014,46 @@ def test_new_curses_panel(self):
 
     @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()
@@ -537,10 +1082,6 @@ def test_unget_wch(self):
             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
@@ -580,26 +1121,25 @@ def test_issue21088(self):
         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):
@@ -621,6 +1161,7 @@ def test_ncurses_version(self):
         self.assertGreaterEqual(v.minor, 0)
         self.assertGreaterEqual(v.patch, 0)
 
+
 class TestAscii(unittest.TestCase):
 
     def test_controlnames(self):
@@ -709,5 +1250,21 @@ def test_unctrl(self):
         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()



More information about the Python-checkins mailing list