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 12import os 13import string 14import sys 15import tempfile 16import unittest 17 18from test.support import requires, import_module, verbose, SaveSignals 19 20# Optionally test curses module. This currently requires that the 21# 'curses' resource be given on the regrtest command line using the -u 22# option. If not available, nothing after this line will be executed. 23import inspect 24requires('curses') 25 26# If either of these don't exist, skip the tests. 27curses = import_module('curses') 28import_module('curses.ascii') 29import_module('curses.textpad') 30try: 31 import curses.panel 32except ImportError: 33 pass 34 35def requires_curses_func(name): 36 return unittest.skipUnless(hasattr(curses, name), 37 'requires curses.%s' % name) 38 39term = os.environ.get('TERM') 40 41# If newterm was supported we could use it instead of initscr and not exit 42@unittest.skipIf(not term or term == 'unknown', 43 "$TERM=%r, calling initscr() may cause exit" % term) 44@unittest.skipIf(sys.platform == "cygwin", 45 "cygwin's curses mostly just hangs") 46class TestCurses(unittest.TestCase): 47 48 @classmethod 49 def setUpClass(cls): 50 if not sys.__stdout__.isatty(): 51 # Temporary skip tests on non-tty 52 raise unittest.SkipTest('sys.__stdout__ is not a tty') 53 cls.tmp = tempfile.TemporaryFile() 54 fd = cls.tmp.fileno() 55 else: 56 cls.tmp = None 57 fd = sys.__stdout__.fileno() 58 # testing setupterm() inside initscr/endwin 59 # causes terminal breakage 60 curses.setupterm(fd=fd) 61 62 @classmethod 63 def tearDownClass(cls): 64 if cls.tmp: 65 cls.tmp.close() 66 del cls.tmp 67 68 def setUp(self): 69 self.save_signals = SaveSignals() 70 self.save_signals.save() 71 if verbose: 72 # just to make the test output a little more readable 73 print() 74 self.stdscr = curses.initscr() 75 curses.savetty() 76 77 def tearDown(self): 78 curses.resetty() 79 curses.endwin() 80 self.save_signals.restore() 81 82 def test_window_funcs(self): 83 "Test the methods of windows" 84 stdscr = self.stdscr 85 win = curses.newwin(10,10) 86 win = curses.newwin(5,5, 5,5) 87 win2 = curses.newwin(15,15, 5,5) 88 89 for meth in [stdscr.addch, stdscr.addstr]: 90 for args in [('a',), ('a', curses.A_BOLD), 91 (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]: 92 with self.subTest(meth=meth.__qualname__, args=args): 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 with self.subTest(meth=meth.__qualname__): 105 meth() 106 107 stdscr.addnstr('1234', 3) 108 stdscr.addnstr('1234', 3, curses.A_BOLD) 109 stdscr.addnstr(4,4, '1234', 3) 110 stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD) 111 112 stdscr.attron(curses.A_BOLD) 113 stdscr.attroff(curses.A_BOLD) 114 stdscr.attrset(curses.A_BOLD) 115 stdscr.bkgd(' ') 116 stdscr.bkgd(' ', curses.A_REVERSE) 117 stdscr.bkgdset(' ') 118 stdscr.bkgdset(' ', curses.A_REVERSE) 119 120 win.border(65, 66, 67, 68, 121 69, 70, 71, 72) 122 win.border('|', '!', '-', '_', 123 '+', '\\', '#', '/') 124 with self.assertRaises(TypeError, 125 msg="Expected win.border() to raise TypeError"): 126 win.border(65, 66, 67, 68, 127 69, [], 71, 72) 128 129 win.box(65, 67) 130 win.box('!', '_') 131 win.box(b':', b'~') 132 self.assertRaises(TypeError, win.box, 65, 66, 67) 133 self.assertRaises(TypeError, win.box, 65) 134 win.box() 135 136 stdscr.clearok(1) 137 138 win4 = stdscr.derwin(2,2) 139 win4 = stdscr.derwin(1,1, 5,5) 140 win4.mvderwin(9,9) 141 142 stdscr.echochar('a') 143 stdscr.echochar('a', curses.A_BOLD) 144 stdscr.hline('-', 5) 145 stdscr.hline('-', 5, curses.A_BOLD) 146 stdscr.hline(1,1,'-', 5) 147 stdscr.hline(1,1,'-', 5, curses.A_BOLD) 148 149 stdscr.idcok(1) 150 stdscr.idlok(1) 151 if hasattr(stdscr, 'immedok'): 152 stdscr.immedok(1) 153 stdscr.immedok(0) 154 stdscr.insch('c') 155 stdscr.insdelln(1) 156 stdscr.insnstr('abc', 3) 157 stdscr.insnstr('abc', 3, curses.A_BOLD) 158 stdscr.insnstr(5, 5, 'abc', 3) 159 stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD) 160 161 stdscr.insstr('def') 162 stdscr.insstr('def', curses.A_BOLD) 163 stdscr.insstr(5, 5, 'def') 164 stdscr.insstr(5, 5, 'def', curses.A_BOLD) 165 stdscr.is_linetouched(0) 166 stdscr.keypad(1) 167 stdscr.leaveok(1) 168 stdscr.move(3,3) 169 win.mvwin(2,2) 170 stdscr.nodelay(1) 171 stdscr.notimeout(1) 172 win2.overlay(win) 173 win2.overwrite(win) 174 win2.overlay(win, 1, 2, 2, 1, 3, 3) 175 win2.overwrite(win, 1, 2, 2, 1, 3, 3) 176 stdscr.redrawln(1,2) 177 178 stdscr.scrollok(1) 179 stdscr.scroll() 180 stdscr.scroll(2) 181 stdscr.scroll(-3) 182 183 stdscr.move(12, 2) 184 stdscr.setscrreg(10,15) 185 win3 = stdscr.subwin(10,10) 186 win3 = stdscr.subwin(10,10, 5,5) 187 if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"): 188 stdscr.syncok(1) 189 stdscr.timeout(5) 190 stdscr.touchline(5,5) 191 stdscr.touchline(5,5,0) 192 stdscr.vline('a', 3) 193 stdscr.vline('a', 3, curses.A_STANDOUT) 194 if hasattr(stdscr, 'chgat'): 195 stdscr.chgat(5, 2, 3, curses.A_BLINK) 196 stdscr.chgat(3, curses.A_BOLD) 197 stdscr.chgat(5, 8, curses.A_UNDERLINE) 198 stdscr.chgat(curses.A_BLINK) 199 stdscr.refresh() 200 201 stdscr.vline(1,1, 'a', 3) 202 stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT) 203 204 if hasattr(stdscr, 'resize'): 205 stdscr.resize(25, 80) 206 if hasattr(stdscr, 'enclose'): 207 stdscr.enclose(10, 10) 208 209 self.assertRaises(ValueError, stdscr.getstr, -400) 210 self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400) 211 self.assertRaises(ValueError, stdscr.instr, -2) 212 self.assertRaises(ValueError, stdscr.instr, 2, 3, -2) 213 214 def test_embedded_null_chars(self): 215 # reject embedded null bytes and characters 216 stdscr = self.stdscr 217 for arg in ['a', b'a']: 218 with self.subTest(arg=arg): 219 self.assertRaises(ValueError, stdscr.addstr, 'a\0') 220 self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1) 221 self.assertRaises(ValueError, stdscr.insstr, 'a\0') 222 self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1) 223 224 def test_module_funcs(self): 225 "Test module-level functions" 226 for func in [curses.baudrate, curses.beep, curses.can_change_color, 227 curses.cbreak, curses.def_prog_mode, curses.doupdate, 228 curses.flash, curses.flushinp, 229 curses.has_colors, curses.has_ic, curses.has_il, 230 curses.isendwin, curses.killchar, curses.longname, 231 curses.nocbreak, curses.noecho, curses.nonl, 232 curses.noqiflush, curses.noraw, 233 curses.reset_prog_mode, curses.termattrs, 234 curses.termname, curses.erasechar]: 235 with self.subTest(func=func.__qualname__): 236 func() 237 if hasattr(curses, 'filter'): 238 curses.filter() 239 if hasattr(curses, 'getsyx'): 240 curses.getsyx() 241 242 # Functions that actually need arguments 243 if curses.tigetstr("cnorm"): 244 curses.curs_set(1) 245 curses.delay_output(1) 246 curses.echo() ; curses.echo(1) 247 248 with tempfile.TemporaryFile() as f: 249 self.stdscr.putwin(f) 250 f.seek(0) 251 curses.getwin(f) 252 253 curses.halfdelay(1) 254 curses.intrflush(1) 255 curses.meta(1) 256 curses.napms(100) 257 curses.newpad(50,50) 258 win = curses.newwin(5,5) 259 win = curses.newwin(5,5, 1,1) 260 curses.nl() ; curses.nl(1) 261 curses.putp(b'abc') 262 curses.qiflush() 263 curses.raw() ; curses.raw(1) 264 if hasattr(curses, 'setsyx'): 265 curses.setsyx(5,5) 266 curses.tigetflag('hc') 267 curses.tigetnum('co') 268 curses.tigetstr('cr') 269 curses.tparm(b'cr') 270 if hasattr(curses, 'typeahead'): 271 curses.typeahead(sys.__stdin__.fileno()) 272 curses.unctrl('a') 273 curses.ungetch('a') 274 if hasattr(curses, 'use_env'): 275 curses.use_env(1) 276 277 # Functions only available on a few platforms 278 def test_colors_funcs(self): 279 if not curses.has_colors(): 280 self.skipTest('requires colors support') 281 curses.start_color() 282 curses.init_pair(2, 1,1) 283 curses.color_content(1) 284 curses.color_pair(2) 285 curses.pair_content(curses.COLOR_PAIRS - 1) 286 curses.pair_number(0) 287 288 if hasattr(curses, 'use_default_colors'): 289 curses.use_default_colors() 290 291 @requires_curses_func('keyname') 292 def test_keyname(self): 293 curses.keyname(13) 294 295 @requires_curses_func('has_key') 296 def test_has_key(self): 297 curses.has_key(13) 298 299 @requires_curses_func('getmouse') 300 def test_getmouse(self): 301 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) 302 if availmask == 0: 303 self.skipTest('mouse stuff not available') 304 curses.mouseinterval(10) 305 # just verify these don't cause errors 306 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) 307 m = curses.getmouse() 308 309 @requires_curses_func('panel') 310 def test_userptr_without_set(self): 311 w = curses.newwin(10, 10) 312 p = curses.panel.new_panel(w) 313 # try to access userptr() before calling set_userptr() -- segfaults 314 with self.assertRaises(curses.panel.error, 315 msg='userptr should fail since not set'): 316 p.userptr() 317 318 @requires_curses_func('panel') 319 def test_userptr_memory_leak(self): 320 w = curses.newwin(10, 10) 321 p = curses.panel.new_panel(w) 322 obj = object() 323 nrefs = sys.getrefcount(obj) 324 for i in range(100): 325 p.set_userptr(obj) 326 327 p.set_userptr(None) 328 self.assertEqual(sys.getrefcount(obj), nrefs, 329 "set_userptr leaked references") 330 331 @requires_curses_func('panel') 332 def test_userptr_segfault(self): 333 w = curses.newwin(10, 10) 334 panel = curses.panel.new_panel(w) 335 class A: 336 def __del__(self): 337 panel.set_userptr(None) 338 panel.set_userptr(A()) 339 panel.set_userptr(None) 340 341 @requires_curses_func('panel') 342 def test_new_curses_panel(self): 343 w = curses.newwin(10, 10) 344 panel = curses.panel.new_panel(w) 345 self.assertRaises(TypeError, type(panel)) 346 347 @requires_curses_func('is_term_resized') 348 def test_is_term_resized(self): 349 curses.is_term_resized(*self.stdscr.getmaxyx()) 350 351 @requires_curses_func('resize_term') 352 def test_resize_term(self): 353 curses.resize_term(*self.stdscr.getmaxyx()) 354 355 @requires_curses_func('resizeterm') 356 def test_resizeterm(self): 357 stdscr = self.stdscr 358 lines, cols = curses.LINES, curses.COLS 359 new_lines = lines - 1 360 new_cols = cols + 1 361 curses.resizeterm(new_lines, new_cols) 362 363 self.assertEqual(curses.LINES, new_lines) 364 self.assertEqual(curses.COLS, new_cols) 365 366 def test_issue6243(self): 367 curses.ungetch(1025) 368 self.stdscr.getkey() 369 370 @requires_curses_func('unget_wch') 371 # XXX Remove the decorator when ncurses on OpenBSD be updated 372 @unittest.skipIf(sys.platform.startswith("openbsd"), 373 "OpenBSD's curses (v.5.7) has bugs") 374 def test_unget_wch(self): 375 stdscr = self.stdscr 376 encoding = stdscr.encoding 377 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'): 378 try: 379 ch.encode(encoding) 380 except UnicodeEncodeError: 381 continue 382 try: 383 curses.unget_wch(ch) 384 except Exception as err: 385 self.fail("unget_wch(%a) failed with encoding %s: %s" 386 % (ch, stdscr.encoding, err)) 387 read = stdscr.get_wch() 388 self.assertEqual(read, ch) 389 390 code = ord(ch) 391 curses.unget_wch(code) 392 read = stdscr.get_wch() 393 self.assertEqual(read, ch) 394 395 def test_issue10570(self): 396 b = curses.tparm(curses.tigetstr("cup"), 5, 3) 397 self.assertIs(type(b), bytes) 398 399 def test_encoding(self): 400 stdscr = self.stdscr 401 import codecs 402 encoding = stdscr.encoding 403 codecs.lookup(encoding) 404 with self.assertRaises(TypeError): 405 stdscr.encoding = 10 406 stdscr.encoding = encoding 407 with self.assertRaises(TypeError): 408 del stdscr.encoding 409 410 def test_issue21088(self): 411 stdscr = self.stdscr 412 # 413 # http://bugs.python.org/issue21088 414 # 415 # the bug: 416 # when converting curses.window.addch to Argument Clinic 417 # the first two parameters were switched. 418 419 # if someday we can represent the signature of addch 420 # we will need to rewrite this test. 421 try: 422 signature = inspect.signature(stdscr.addch) 423 self.assertFalse(signature) 424 except ValueError: 425 # not generating a signature is fine. 426 pass 427 428 # So. No signature for addch. 429 # But Argument Clinic gave us a human-readable equivalent 430 # as the first line of the docstring. So we parse that, 431 # and ensure that the parameters appear in the correct order. 432 # Since this is parsing output from Argument Clinic, we can 433 # be reasonably certain the generated parsing code will be 434 # correct too. 435 human_readable_signature = stdscr.addch.__doc__.split("\n")[0] 436 self.assertIn("[y, x,]", human_readable_signature) 437 438 def test_issue13051(self): 439 stdscr = self.stdscr 440 if not hasattr(stdscr, 'resize'): 441 raise unittest.SkipTest('requires curses.window.resize') 442 box = curses.textpad.Textbox(stdscr, insert_mode=True) 443 lines, cols = stdscr.getmaxyx() 444 stdscr.resize(lines-2, cols-2) 445 # this may cause infinite recursion, leading to a RuntimeError 446 box._insert_printable_char('a') 447 448 449class MiscTests(unittest.TestCase): 450 451 @requires_curses_func('update_lines_cols') 452 def test_update_lines_cols(self): 453 # this doesn't actually test that LINES and COLS are updated, 454 # because we can't automate changing them. See Issue #4254 for 455 # a manual test script. We can only test that the function 456 # can be called. 457 curses.update_lines_cols() 458 459 460class TestAscii(unittest.TestCase): 461 462 def test_controlnames(self): 463 for name in curses.ascii.controlnames: 464 self.assertTrue(hasattr(curses.ascii, name), name) 465 466 def test_ctypes(self): 467 def check(func, expected): 468 with self.subTest(ch=c, func=func): 469 self.assertEqual(func(i), expected) 470 self.assertEqual(func(c), expected) 471 472 for i in range(256): 473 c = chr(i) 474 b = bytes([i]) 475 check(curses.ascii.isalnum, b.isalnum()) 476 check(curses.ascii.isalpha, b.isalpha()) 477 check(curses.ascii.isdigit, b.isdigit()) 478 check(curses.ascii.islower, b.islower()) 479 check(curses.ascii.isspace, b.isspace()) 480 check(curses.ascii.isupper, b.isupper()) 481 482 check(curses.ascii.isascii, i < 128) 483 check(curses.ascii.ismeta, i >= 128) 484 check(curses.ascii.isctrl, i < 32) 485 check(curses.ascii.iscntrl, i < 32 or i == 127) 486 check(curses.ascii.isblank, c in ' \t') 487 check(curses.ascii.isgraph, 32 < i <= 126) 488 check(curses.ascii.isprint, 32 <= i <= 126) 489 check(curses.ascii.ispunct, c in string.punctuation) 490 check(curses.ascii.isxdigit, c in string.hexdigits) 491 492 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1): 493 self.assertFalse(curses.ascii.isalnum(i)) 494 self.assertFalse(curses.ascii.isalpha(i)) 495 self.assertFalse(curses.ascii.isdigit(i)) 496 self.assertFalse(curses.ascii.islower(i)) 497 self.assertFalse(curses.ascii.isspace(i)) 498 self.assertFalse(curses.ascii.isupper(i)) 499 500 self.assertFalse(curses.ascii.isascii(i)) 501 self.assertFalse(curses.ascii.isctrl(i)) 502 self.assertFalse(curses.ascii.iscntrl(i)) 503 self.assertFalse(curses.ascii.isblank(i)) 504 self.assertFalse(curses.ascii.isgraph(i)) 505 self.assertFalse(curses.ascii.isprint(i)) 506 self.assertFalse(curses.ascii.ispunct(i)) 507 self.assertFalse(curses.ascii.isxdigit(i)) 508 509 self.assertFalse(curses.ascii.ismeta(-1)) 510 511 def test_ascii(self): 512 ascii = curses.ascii.ascii 513 self.assertEqual(ascii('\xc1'), 'A') 514 self.assertEqual(ascii('A'), 'A') 515 self.assertEqual(ascii(ord('\xc1')), ord('A')) 516 517 def test_ctrl(self): 518 ctrl = curses.ascii.ctrl 519 self.assertEqual(ctrl('J'), '\n') 520 self.assertEqual(ctrl('\n'), '\n') 521 self.assertEqual(ctrl('@'), '\0') 522 self.assertEqual(ctrl(ord('J')), ord('\n')) 523 524 def test_alt(self): 525 alt = curses.ascii.alt 526 self.assertEqual(alt('\n'), '\x8a') 527 self.assertEqual(alt('A'), '\xc1') 528 self.assertEqual(alt(ord('A')), 0xc1) 529 530 def test_unctrl(self): 531 unctrl = curses.ascii.unctrl 532 self.assertEqual(unctrl('a'), 'a') 533 self.assertEqual(unctrl('A'), 'A') 534 self.assertEqual(unctrl(';'), ';') 535 self.assertEqual(unctrl(' '), ' ') 536 self.assertEqual(unctrl('\x7f'), '^?') 537 self.assertEqual(unctrl('\n'), '^J') 538 self.assertEqual(unctrl('\0'), '^@') 539 self.assertEqual(unctrl(ord('A')), 'A') 540 self.assertEqual(unctrl(ord('\n')), '^J') 541 # Meta-bit characters 542 self.assertEqual(unctrl('\x8a'), '!^J') 543 self.assertEqual(unctrl('\xc1'), '!A') 544 self.assertEqual(unctrl(ord('\x8a')), '!^J') 545 self.assertEqual(unctrl(ord('\xc1')), '!A') 546 547 548if __name__ == '__main__': 549 unittest.main() 550