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, 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. 25requires('curses') 26 27# If either of these don't exist, skip the tests. 28curses = import_module('curses') 29import_module('curses.ascii') 30import_module('curses.textpad') 31try: 32 import curses.panel 33except ImportError: 34 pass 35 36def requires_curses_func(name): 37 return unittest.skipUnless(hasattr(curses, name), 38 'requires curses.%s' % name) 39 40term = 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") 47class 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 374class 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 460def test_main(): 461 run_unittest(TestCurses, TestAscii) 462 463 464if __name__ == "__main__": 465 unittest.main() 466