Home | History | Annotate | Download | only in test
      1 #
      2 # Test script for the curses module
      3 #
      4 # This script doesn't actually display anything very coherent. but it
      5 # does call (nearly) every method and function.
      6 #
      7 # Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
      8 # init_color()
      9 # Only called, not tested: getmouse(), ungetmouse()
     10 #
     11 
     12 import os
     13 import string
     14 import sys
     15 import tempfile
     16 import unittest
     17 
     18 from test.support import (requires, import_module, verbose, run_unittest,
     19                           SaveSignals)
     20 
     21 
     22 # Optionally test curses module.  This currently requires that the
     23 # 'curses' resource be given on the regrtest command line using the -u
     24 # option.  If not available, nothing after this line will be executed.
     25 requires('curses')
     26 
     27 # If either of these don't exist, skip the tests.
     28 curses = import_module('curses')
     29 import_module('curses.ascii')
     30 import_module('curses.textpad')
     31 try:
     32     import curses.panel
     33 except ImportError:
     34     pass
     35 
     36 def requires_curses_func(name):
     37     return unittest.skipUnless(hasattr(curses, name),
     38                                'requires curses.%s' % name)
     39 
     40 term = os.environ.get('TERM')
     41 
     42 # If newterm was supported we could use it instead of initscr and not exit
     43 @unittest.skipIf(not term or term == 'unknown',
     44                  "$TERM=%r, calling initscr() may cause exit" % term)
     45 @unittest.skipIf(sys.platform == "cygwin",
     46                  "cygwin's curses mostly just hangs")
     47 class TestCurses(unittest.TestCase):
     48 
     49     @classmethod
     50     def setUpClass(cls):
     51         if not sys.__stdout__.isatty():
     52             # Temporary skip tests on non-tty
     53             raise unittest.SkipTest('sys.__stdout__ is not a tty')
     54             cls.tmp = tempfile.TemporaryFile()
     55             fd = cls.tmp.fileno()
     56         else:
     57             cls.tmp = None
     58             fd = sys.__stdout__.fileno()
     59         # testing setupterm() inside initscr/endwin
     60         # causes terminal breakage
     61         curses.setupterm(fd=fd)
     62 
     63     @classmethod
     64     def tearDownClass(cls):
     65         if cls.tmp:
     66             cls.tmp.close()
     67             del cls.tmp
     68 
     69     def setUp(self):
     70         self.save_signals = SaveSignals()
     71         self.save_signals.save()
     72         if verbose:
     73             # just to make the test output a little more readable
     74             print('')
     75         self.stdscr = curses.initscr()
     76         curses.savetty()
     77 
     78     def tearDown(self):
     79         curses.resetty()
     80         curses.endwin()
     81         self.save_signals.restore()
     82 
     83     def test_window_funcs(self):
     84         "Test the methods of windows"
     85         stdscr = self.stdscr
     86         win = curses.newwin(10,10)
     87         win = curses.newwin(5,5, 5,5)
     88         win2 = curses.newwin(15,15, 5,5)
     89 
     90         for meth in [stdscr.addch, stdscr.addstr]:
     91             for args in [('a'), ('a', curses.A_BOLD),
     92                          (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
     93                 meth(*args)
     94 
     95         for meth in [stdscr.clear, stdscr.clrtobot,
     96                      stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
     97                      stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
     98                      stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
     99                      stdscr.getparyx, stdscr.getyx, stdscr.inch,
    100                      stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
    101                      win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
    102                      stdscr.standout, stdscr.standend, stdscr.syncdown,
    103                      stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
    104             meth()
    105 
    106         stdscr.addnstr('1234', 3)
    107         stdscr.addnstr('1234', 3, curses.A_BOLD)
    108         stdscr.addnstr(4,4, '1234', 3)
    109         stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
    110 
    111         stdscr.attron(curses.A_BOLD)
    112         stdscr.attroff(curses.A_BOLD)
    113         stdscr.attrset(curses.A_BOLD)
    114         stdscr.bkgd(' ')
    115         stdscr.bkgd(' ', curses.A_REVERSE)
    116         stdscr.bkgdset(' ')
    117         stdscr.bkgdset(' ', curses.A_REVERSE)
    118 
    119         win.border(65, 66, 67, 68,
    120                    69, 70, 71, 72)
    121         win.border('|', '!', '-', '_',
    122                    '+', '\\', '#', '/')
    123         with self.assertRaises(TypeError,
    124                                msg="Expected win.border() to raise TypeError"):
    125             win.border(65, 66, 67, 68,
    126                        69, [], 71, 72)
    127 
    128         win.box(65, 67)
    129         win.box('!', '_')
    130         win.box(b':', b'~')
    131         self.assertRaises(TypeError, win.box, 65, 66, 67)
    132         self.assertRaises(TypeError, win.box, 65)
    133         win.box()
    134 
    135         stdscr.clearok(1)
    136 
    137         win4 = stdscr.derwin(2,2)
    138         win4 = stdscr.derwin(1,1, 5,5)
    139         win4.mvderwin(9,9)
    140 
    141         stdscr.echochar('a')
    142         stdscr.echochar('a', curses.A_BOLD)
    143         stdscr.hline('-', 5)
    144         stdscr.hline('-', 5, curses.A_BOLD)
    145         stdscr.hline(1,1,'-', 5)
    146         stdscr.hline(1,1,'-', 5, curses.A_BOLD)
    147 
    148         stdscr.idcok(1)
    149         stdscr.idlok(1)
    150         if hasattr(stdscr, 'immedok'):
    151             stdscr.immedok(1)
    152             stdscr.immedok(0)
    153         stdscr.insch('c')
    154         stdscr.insdelln(1)
    155         stdscr.insnstr('abc', 3)
    156         stdscr.insnstr('abc', 3, curses.A_BOLD)
    157         stdscr.insnstr(5, 5, 'abc', 3)
    158         stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
    159 
    160         stdscr.insstr('def')
    161         stdscr.insstr('def', curses.A_BOLD)
    162         stdscr.insstr(5, 5, 'def')
    163         stdscr.insstr(5, 5, 'def', curses.A_BOLD)
    164         stdscr.is_linetouched(0)
    165         stdscr.keypad(1)
    166         stdscr.leaveok(1)
    167         stdscr.move(3,3)
    168         win.mvwin(2,2)
    169         stdscr.nodelay(1)
    170         stdscr.notimeout(1)
    171         win2.overlay(win)
    172         win2.overwrite(win)
    173         win2.overlay(win, 1, 2, 2, 1, 3, 3)
    174         win2.overwrite(win, 1, 2, 2, 1, 3, 3)
    175         stdscr.redrawln(1,2)
    176 
    177         stdscr.scrollok(1)
    178         stdscr.scroll()
    179         stdscr.scroll(2)
    180         stdscr.scroll(-3)
    181 
    182         stdscr.move(12, 2)
    183         stdscr.setscrreg(10,15)
    184         win3 = stdscr.subwin(10,10)
    185         win3 = stdscr.subwin(10,10, 5,5)
    186         if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
    187             stdscr.syncok(1)
    188         stdscr.timeout(5)
    189         stdscr.touchline(5,5)
    190         stdscr.touchline(5,5,0)
    191         stdscr.vline('a', 3)
    192         stdscr.vline('a', 3, curses.A_STANDOUT)
    193         if hasattr(stdscr, 'chgat'):
    194             stdscr.chgat(5, 2, 3, curses.A_BLINK)
    195             stdscr.chgat(3, curses.A_BOLD)
    196             stdscr.chgat(5, 8, curses.A_UNDERLINE)
    197             stdscr.chgat(curses.A_BLINK)
    198         stdscr.refresh()
    199 
    200         stdscr.vline(1,1, 'a', 3)
    201         stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
    202 
    203         if hasattr(stdscr, 'resize'):
    204             stdscr.resize(25, 80)
    205         if hasattr(stdscr, 'enclose'):
    206             stdscr.enclose(10, 10)
    207 
    208         self.assertRaises(ValueError, stdscr.getstr, -400)
    209         self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
    210         self.assertRaises(ValueError, stdscr.instr, -2)
    211         self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
    212 
    213 
    214     def test_module_funcs(self):
    215         "Test module-level functions"
    216         for func in [curses.baudrate, curses.beep, curses.can_change_color,
    217                      curses.cbreak, curses.def_prog_mode, curses.doupdate,
    218                      curses.flash, curses.flushinp,
    219                      curses.has_colors, curses.has_ic, curses.has_il,
    220                      curses.isendwin, curses.killchar, curses.longname,
    221                      curses.nocbreak, curses.noecho, curses.nonl,
    222                      curses.noqiflush, curses.noraw,
    223                      curses.reset_prog_mode, curses.termattrs,
    224                      curses.termname, curses.erasechar]:
    225             func()
    226         if hasattr(curses, 'filter'):
    227             curses.filter()
    228         if hasattr(curses, 'getsyx'):
    229             curses.getsyx()
    230 
    231         # Functions that actually need arguments
    232         if curses.tigetstr("cnorm"):
    233             curses.curs_set(1)
    234         curses.delay_output(1)
    235         curses.echo() ; curses.echo(1)
    236 
    237         with tempfile.TemporaryFile() as f:
    238             self.stdscr.putwin(f)
    239             f.seek(0)
    240             curses.getwin(f)
    241 
    242         curses.halfdelay(1)
    243         curses.intrflush(1)
    244         curses.meta(1)
    245         curses.napms(100)
    246         curses.newpad(50,50)
    247         win = curses.newwin(5,5)
    248         win = curses.newwin(5,5, 1,1)
    249         curses.nl() ; curses.nl(1)
    250         curses.putp(b'abc')
    251         curses.qiflush()
    252         curses.raw() ; curses.raw(1)
    253         if hasattr(curses, 'setsyx'):
    254             curses.setsyx(5,5)
    255         curses.tigetflag('hc')
    256         curses.tigetnum('co')
    257         curses.tigetstr('cr')
    258         curses.tparm(b'cr')
    259         if hasattr(curses, 'typeahead'):
    260             curses.typeahead(sys.__stdin__.fileno())
    261         curses.unctrl('a')
    262         curses.ungetch('a')
    263         if hasattr(curses, 'use_env'):
    264             curses.use_env(1)
    265 
    266     # Functions only available on a few platforms
    267     def test_colors_funcs(self):
    268         if not curses.has_colors():
    269             self.skipTest('requires colors support')
    270         curses.start_color()
    271         curses.init_pair(2, 1,1)
    272         curses.color_content(1)
    273         curses.color_pair(2)
    274         curses.pair_content(curses.COLOR_PAIRS - 1)
    275         curses.pair_number(0)
    276 
    277         if hasattr(curses, 'use_default_colors'):
    278             curses.use_default_colors()
    279 
    280     @requires_curses_func('keyname')
    281     def test_keyname(self):
    282         curses.keyname(13)
    283 
    284     @requires_curses_func('has_key')
    285     def test_has_key(self):
    286         curses.has_key(13)
    287 
    288     @requires_curses_func('getmouse')
    289     def test_getmouse(self):
    290         (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
    291         if availmask == 0:
    292             self.skipTest('mouse stuff not available')
    293         curses.mouseinterval(10)
    294         # just verify these don't cause errors
    295         curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
    296         m = curses.getmouse()
    297 
    298     @requires_curses_func('panel')
    299     def test_userptr_without_set(self):
    300         w = curses.newwin(10, 10)
    301         p = curses.panel.new_panel(w)
    302         # try to access userptr() before calling set_userptr() -- segfaults
    303         with self.assertRaises(curses.panel.error,
    304                                msg='userptr should fail since not set'):
    305             p.userptr()
    306 
    307     @requires_curses_func('panel')
    308     def test_userptr_memory_leak(self):
    309         w = curses.newwin(10, 10)
    310         p = curses.panel.new_panel(w)
    311         obj = object()
    312         nrefs = sys.getrefcount(obj)
    313         for i in range(100):
    314             p.set_userptr(obj)
    315 
    316         p.set_userptr(None)
    317         self.assertEqual(sys.getrefcount(obj), nrefs,
    318                          "set_userptr leaked references")
    319 
    320     @requires_curses_func('panel')
    321     def test_userptr_segfault(self):
    322         w = curses.newwin(10, 10)
    323         panel = curses.panel.new_panel(w)
    324         class A:
    325             def __del__(self):
    326                 panel.set_userptr(None)
    327         panel.set_userptr(A())
    328         panel.set_userptr(None)
    329 
    330     @requires_curses_func('panel')
    331     def test_new_curses_panel(self):
    332         w = curses.newwin(10, 10)
    333         panel = curses.panel.new_panel(w)
    334         self.assertRaises(TypeError, type(panel))
    335 
    336     @requires_curses_func('is_term_resized')
    337     def test_is_term_resized(self):
    338         curses.is_term_resized(*self.stdscr.getmaxyx())
    339 
    340     @requires_curses_func('resize_term')
    341     def test_resize_term(self):
    342         curses.resize_term(*self.stdscr.getmaxyx())
    343 
    344     @requires_curses_func('resizeterm')
    345     def test_resizeterm(self):
    346         stdscr = self.stdscr
    347         lines, cols = curses.LINES, curses.COLS
    348         new_lines = lines - 1
    349         new_cols = cols + 1
    350         curses.resizeterm(new_lines, new_cols)
    351 
    352         self.assertEqual(curses.LINES, new_lines)
    353         self.assertEqual(curses.COLS, new_cols)
    354 
    355     def test_issue6243(self):
    356         curses.ungetch(1025)
    357         self.stdscr.getkey()
    358 
    359     def test_issue10570(self):
    360         b = curses.tparm(curses.tigetstr("cup"), 5, 3)
    361         self.assertIs(type(b), bytes)
    362 
    363     def test_issue13051(self):
    364         stdscr = self.stdscr
    365         if not hasattr(stdscr, 'resize'):
    366             raise unittest.SkipTest('requires curses.window.resize')
    367         box = curses.textpad.Textbox(stdscr, insert_mode=True)
    368         lines, cols = stdscr.getmaxyx()
    369         stdscr.resize(lines-2, cols-2)
    370         # this may cause infinite recursion, leading to a RuntimeError
    371         box._insert_printable_char('a')
    372 
    373 
    374 class TestAscii(unittest.TestCase):
    375 
    376     def test_controlnames(self):
    377         for name in curses.ascii.controlnames:
    378             self.assertTrue(hasattr(curses.ascii, name), name)
    379 
    380     def test_ctypes(self):
    381         def check(func, expected):
    382             self.assertEqual(func(i), expected)
    383             self.assertEqual(func(c), expected)
    384 
    385         for i in range(256):
    386             c = b = chr(i)
    387             check(curses.ascii.isalnum, b.isalnum())
    388             check(curses.ascii.isalpha, b.isalpha())
    389             check(curses.ascii.isdigit, b.isdigit())
    390             check(curses.ascii.islower, b.islower())
    391             check(curses.ascii.isspace, b.isspace())
    392             check(curses.ascii.isupper, b.isupper())
    393 
    394             check(curses.ascii.isascii, i < 128)
    395             check(curses.ascii.ismeta, i >= 128)
    396             check(curses.ascii.isctrl, i < 32)
    397             check(curses.ascii.iscntrl, i < 32 or i == 127)
    398             check(curses.ascii.isblank, c in ' \t')
    399             check(curses.ascii.isgraph, 32 < i <= 126)
    400             check(curses.ascii.isprint, 32 <= i <= 126)
    401             check(curses.ascii.ispunct, c in string.punctuation)
    402             check(curses.ascii.isxdigit, c in string.hexdigits)
    403 
    404         for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
    405             self.assertFalse(curses.ascii.isalnum(i))
    406             self.assertFalse(curses.ascii.isalpha(i))
    407             self.assertFalse(curses.ascii.isdigit(i))
    408             self.assertFalse(curses.ascii.islower(i))
    409             self.assertFalse(curses.ascii.isspace(i))
    410             self.assertFalse(curses.ascii.isupper(i))
    411 
    412             self.assertFalse(curses.ascii.isascii(i))
    413             self.assertFalse(curses.ascii.isctrl(i))
    414             self.assertFalse(curses.ascii.iscntrl(i))
    415             self.assertFalse(curses.ascii.isblank(i))
    416             self.assertFalse(curses.ascii.isgraph(i))
    417             self.assertFalse(curses.ascii.isprint(i))
    418             self.assertFalse(curses.ascii.ispunct(i))
    419             self.assertFalse(curses.ascii.isxdigit(i))
    420 
    421         self.assertFalse(curses.ascii.ismeta(-1))
    422 
    423     def test_ascii(self):
    424         ascii = curses.ascii.ascii
    425         self.assertEqual(ascii('\xc1'), 'A')
    426         self.assertEqual(ascii('A'), 'A')
    427         self.assertEqual(ascii(ord('\xc1')), ord('A'))
    428 
    429     def test_ctrl(self):
    430         ctrl = curses.ascii.ctrl
    431         self.assertEqual(ctrl('J'), '\n')
    432         self.assertEqual(ctrl('\n'), '\n')
    433         self.assertEqual(ctrl('@'), '\0')
    434         self.assertEqual(ctrl(ord('J')), ord('\n'))
    435 
    436     def test_alt(self):
    437         alt = curses.ascii.alt
    438         self.assertEqual(alt('\n'), '\x8a')
    439         self.assertEqual(alt('A'), '\xc1')
    440         self.assertEqual(alt(ord('A')), 0xc1)
    441 
    442     def test_unctrl(self):
    443         unctrl = curses.ascii.unctrl
    444         self.assertEqual(unctrl('a'), 'a')
    445         self.assertEqual(unctrl('A'), 'A')
    446         self.assertEqual(unctrl(';'), ';')
    447         self.assertEqual(unctrl(' '), ' ')
    448         self.assertEqual(unctrl('\x7f'), '^?')
    449         self.assertEqual(unctrl('\n'), '^J')
    450         self.assertEqual(unctrl('\0'), '^@')
    451         self.assertEqual(unctrl(ord('A')), 'A')
    452         self.assertEqual(unctrl(ord('\n')), '^J')
    453         # Meta-bit characters
    454         self.assertEqual(unctrl('\x8a'), '!^J')
    455         self.assertEqual(unctrl('\xc1'), '!A')
    456         self.assertEqual(unctrl(ord('\x8a')), '!^J')
    457         self.assertEqual(unctrl(ord('\xc1')), '!A')
    458 
    459 
    460 def test_main():
    461     run_unittest(TestCurses, TestAscii)
    462 
    463 
    464 if __name__ == "__main__":
    465     unittest.main()
    466