1import functools 2import inspect 3import os 4import string 5import sys 6import tempfile 7import unittest 8 9from test.support import requires, import_module, verbose, SaveSignals 10 11# Optionally test curses module. This currently requires that the 12# 'curses' resource be given on the regrtest command line using the -u 13# option. If not available, nothing after this line will be executed. 14requires('curses') 15 16# If either of these don't exist, skip the tests. 17curses = import_module('curses') 18import_module('curses.ascii') 19import_module('curses.textpad') 20try: 21 import curses.panel 22except ImportError: 23 pass 24 25def requires_curses_func(name): 26 return unittest.skipUnless(hasattr(curses, name), 27 'requires curses.%s' % name) 28 29def requires_curses_window_meth(name): 30 def deco(test): 31 @functools.wraps(test) 32 def wrapped(self, *args, **kwargs): 33 if not hasattr(self.stdscr, name): 34 raise unittest.SkipTest('requires curses.window.%s' % name) 35 test(self, *args, **kwargs) 36 return wrapped 37 return deco 38 39 40def requires_colors(test): 41 @functools.wraps(test) 42 def wrapped(self, *args, **kwargs): 43 if not curses.has_colors(): 44 self.skipTest('requires colors support') 45 curses.start_color() 46 test(self, *args, **kwargs) 47 return wrapped 48 49term = os.environ.get('TERM') 50SHORT_MAX = 0x7fff 51 52# If newterm was supported we could use it instead of initscr and not exit 53@unittest.skipIf(not term or term == 'unknown', 54 "$TERM=%r, calling initscr() may cause exit" % term) 55@unittest.skipIf(sys.platform == "cygwin", 56 "cygwin's curses mostly just hangs") 57class TestCurses(unittest.TestCase): 58 59 @classmethod 60 def setUpClass(cls): 61 if verbose: 62 print(f'TERM={term}', file=sys.stderr, flush=True) 63 # testing setupterm() inside initscr/endwin 64 # causes terminal breakage 65 stdout_fd = sys.__stdout__.fileno() 66 curses.setupterm(fd=stdout_fd) 67 68 def setUp(self): 69 self.isatty = True 70 self.output = sys.__stdout__ 71 stdout_fd = sys.__stdout__.fileno() 72 if not sys.__stdout__.isatty(): 73 # initstr() unconditionally uses C stdout. 74 # If it is redirected to file or pipe, try to attach it 75 # to terminal. 76 # First, save a copy of the file descriptor of stdout, so it 77 # can be restored after finishing the test. 78 dup_fd = os.dup(stdout_fd) 79 self.addCleanup(os.close, dup_fd) 80 self.addCleanup(os.dup2, dup_fd, stdout_fd) 81 82 if sys.__stderr__.isatty(): 83 # If stderr is connected to terminal, use it. 84 tmp = sys.__stderr__ 85 self.output = sys.__stderr__ 86 else: 87 try: 88 # Try to open the terminal device. 89 tmp = open('/dev/tty', 'wb', buffering=0) 90 except OSError: 91 # As a fallback, use regular file to write control codes. 92 # Some functions (like savetty) will not work, but at 93 # least the garbage control sequences will not be mixed 94 # with the testing report. 95 tmp = tempfile.TemporaryFile(mode='wb', buffering=0) 96 self.isatty = False 97 self.addCleanup(tmp.close) 98 self.output = None 99 os.dup2(tmp.fileno(), stdout_fd) 100 101 self.save_signals = SaveSignals() 102 self.save_signals.save() 103 self.addCleanup(self.save_signals.restore) 104 if verbose and self.output is not None: 105 # just to make the test output a little more readable 106 sys.stderr.flush() 107 sys.stdout.flush() 108 print(file=self.output, flush=True) 109 self.stdscr = curses.initscr() 110 if self.isatty: 111 curses.savetty() 112 self.addCleanup(curses.endwin) 113 self.addCleanup(curses.resetty) 114 self.stdscr.erase() 115 116 @requires_curses_func('filter') 117 def test_filter(self): 118 # TODO: Should be called before initscr() or newterm() are called. 119 # TODO: nofilter() 120 curses.filter() 121 122 @requires_curses_func('use_env') 123 def test_use_env(self): 124 # TODO: Should be called before initscr() or newterm() are called. 125 # TODO: use_tioctl() 126 curses.use_env(False) 127 curses.use_env(True) 128 129 def test_create_windows(self): 130 win = curses.newwin(5, 10) 131 self.assertEqual(win.getbegyx(), (0, 0)) 132 self.assertEqual(win.getparyx(), (-1, -1)) 133 self.assertEqual(win.getmaxyx(), (5, 10)) 134 135 win = curses.newwin(10, 15, 2, 5) 136 self.assertEqual(win.getbegyx(), (2, 5)) 137 self.assertEqual(win.getparyx(), (-1, -1)) 138 self.assertEqual(win.getmaxyx(), (10, 15)) 139 140 win2 = win.subwin(3, 7) 141 self.assertEqual(win2.getbegyx(), (3, 7)) 142 self.assertEqual(win2.getparyx(), (1, 2)) 143 self.assertEqual(win2.getmaxyx(), (9, 13)) 144 145 win2 = win.subwin(5, 10, 3, 7) 146 self.assertEqual(win2.getbegyx(), (3, 7)) 147 self.assertEqual(win2.getparyx(), (1, 2)) 148 self.assertEqual(win2.getmaxyx(), (5, 10)) 149 150 win3 = win.derwin(2, 3) 151 self.assertEqual(win3.getbegyx(), (4, 8)) 152 self.assertEqual(win3.getparyx(), (2, 3)) 153 self.assertEqual(win3.getmaxyx(), (8, 12)) 154 155 win3 = win.derwin(6, 11, 2, 3) 156 self.assertEqual(win3.getbegyx(), (4, 8)) 157 self.assertEqual(win3.getparyx(), (2, 3)) 158 self.assertEqual(win3.getmaxyx(), (6, 11)) 159 160 win.mvwin(0, 1) 161 self.assertEqual(win.getbegyx(), (0, 1)) 162 self.assertEqual(win.getparyx(), (-1, -1)) 163 self.assertEqual(win.getmaxyx(), (10, 15)) 164 self.assertEqual(win2.getbegyx(), (3, 7)) 165 self.assertEqual(win2.getparyx(), (1, 2)) 166 self.assertEqual(win2.getmaxyx(), (5, 10)) 167 self.assertEqual(win3.getbegyx(), (4, 8)) 168 self.assertEqual(win3.getparyx(), (2, 3)) 169 self.assertEqual(win3.getmaxyx(), (6, 11)) 170 171 win2.mvderwin(2, 1) 172 self.assertEqual(win2.getbegyx(), (3, 7)) 173 self.assertEqual(win2.getparyx(), (2, 1)) 174 self.assertEqual(win2.getmaxyx(), (5, 10)) 175 176 win3.mvderwin(2, 1) 177 self.assertEqual(win3.getbegyx(), (4, 8)) 178 self.assertEqual(win3.getparyx(), (2, 1)) 179 self.assertEqual(win3.getmaxyx(), (6, 11)) 180 181 def test_move_cursor(self): 182 stdscr = self.stdscr 183 win = stdscr.subwin(10, 15, 2, 5) 184 stdscr.move(1, 2) 185 win.move(2, 4) 186 self.assertEqual(stdscr.getyx(), (1, 2)) 187 self.assertEqual(win.getyx(), (2, 4)) 188 189 win.cursyncup() 190 self.assertEqual(stdscr.getyx(), (4, 9)) 191 192 def test_refresh_control(self): 193 stdscr = self.stdscr 194 # touchwin()/untouchwin()/is_wintouched() 195 stdscr.refresh() 196 self.assertIs(stdscr.is_wintouched(), False) 197 stdscr.touchwin() 198 self.assertIs(stdscr.is_wintouched(), True) 199 stdscr.refresh() 200 self.assertIs(stdscr.is_wintouched(), False) 201 stdscr.touchwin() 202 self.assertIs(stdscr.is_wintouched(), True) 203 stdscr.untouchwin() 204 self.assertIs(stdscr.is_wintouched(), False) 205 206 # touchline()/untouchline()/is_linetouched() 207 stdscr.touchline(5, 2) 208 self.assertIs(stdscr.is_linetouched(5), True) 209 self.assertIs(stdscr.is_linetouched(6), True) 210 self.assertIs(stdscr.is_wintouched(), True) 211 stdscr.touchline(5, 1, False) 212 self.assertIs(stdscr.is_linetouched(5), False) 213 214 # syncup() 215 win = stdscr.subwin(10, 15, 2, 5) 216 win2 = win.subwin(5, 10, 3, 7) 217 win2.touchwin() 218 stdscr.untouchwin() 219 win2.syncup() 220 self.assertIs(win.is_wintouched(), True) 221 self.assertIs(stdscr.is_wintouched(), True) 222 223 # syncdown() 224 stdscr.touchwin() 225 win.untouchwin() 226 win2.untouchwin() 227 win2.syncdown() 228 self.assertIs(win2.is_wintouched(), True) 229 230 # syncok() 231 if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"): 232 win.untouchwin() 233 stdscr.untouchwin() 234 for syncok in [False, True]: 235 win2.syncok(syncok) 236 win2.addch('a') 237 self.assertIs(win.is_wintouched(), syncok) 238 self.assertIs(stdscr.is_wintouched(), syncok) 239 240 def test_output_character(self): 241 stdscr = self.stdscr 242 encoding = stdscr.encoding 243 # addch() 244 stdscr.refresh() 245 stdscr.move(0, 0) 246 stdscr.addch('A') 247 stdscr.addch(b'A') 248 stdscr.addch(65) 249 c = '\u20ac' 250 try: 251 stdscr.addch(c) 252 except UnicodeEncodeError: 253 self.assertRaises(UnicodeEncodeError, c.encode, encoding) 254 except OverflowError: 255 encoded = c.encode(encoding) 256 self.assertNotEqual(len(encoded), 1, repr(encoded)) 257 stdscr.addch('A', curses.A_BOLD) 258 stdscr.addch(1, 2, 'A') 259 stdscr.addch(2, 3, 'A', curses.A_BOLD) 260 self.assertIs(stdscr.is_wintouched(), True) 261 262 # echochar() 263 stdscr.refresh() 264 stdscr.move(0, 0) 265 stdscr.echochar('A') 266 stdscr.echochar(b'A') 267 stdscr.echochar(65) 268 with self.assertRaises((UnicodeEncodeError, OverflowError)): 269 stdscr.echochar('\u20ac') 270 stdscr.echochar('A', curses.A_BOLD) 271 self.assertIs(stdscr.is_wintouched(), False) 272 273 def test_output_string(self): 274 stdscr = self.stdscr 275 encoding = stdscr.encoding 276 # addstr()/insstr() 277 for func in [stdscr.addstr, stdscr.insstr]: 278 with self.subTest(func.__qualname__): 279 stdscr.move(0, 0) 280 func('abcd') 281 func(b'abcd') 282 s = 'àßçđ' 283 try: 284 func(s) 285 except UnicodeEncodeError: 286 self.assertRaises(UnicodeEncodeError, s.encode, encoding) 287 func('abcd', curses.A_BOLD) 288 func(1, 2, 'abcd') 289 func(2, 3, 'abcd', curses.A_BOLD) 290 291 # addnstr()/insnstr() 292 for func in [stdscr.addnstr, stdscr.insnstr]: 293 with self.subTest(func.__qualname__): 294 stdscr.move(0, 0) 295 func('1234', 3) 296 func(b'1234', 3) 297 s = '\u0661\u0662\u0663\u0664' 298 try: 299 func(s, 3) 300 except UnicodeEncodeError: 301 self.assertRaises(UnicodeEncodeError, s.encode, encoding) 302 func('1234', 5) 303 func('1234', 3, curses.A_BOLD) 304 func(1, 2, '1234', 3) 305 func(2, 3, '1234', 3, curses.A_BOLD) 306 307 def test_output_string_embedded_null_chars(self): 308 # reject embedded null bytes and characters 309 stdscr = self.stdscr 310 for arg in ['a\0', b'a\0']: 311 with self.subTest(arg=arg): 312 self.assertRaises(ValueError, stdscr.addstr, arg) 313 self.assertRaises(ValueError, stdscr.addnstr, arg, 1) 314 self.assertRaises(ValueError, stdscr.insstr, arg) 315 self.assertRaises(ValueError, stdscr.insnstr, arg, 1) 316 317 def test_read_from_window(self): 318 stdscr = self.stdscr 319 stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD) 320 # inch() 321 stdscr.move(0, 1) 322 self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD) 323 self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD) 324 stdscr.move(0, 0) 325 # instr() 326 self.assertEqual(stdscr.instr()[:6], b' ABCD ') 327 self.assertEqual(stdscr.instr(3)[:6], b' AB') 328 self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ') 329 self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ') 330 self.assertRaises(ValueError, stdscr.instr, -2) 331 self.assertRaises(ValueError, stdscr.instr, 0, 2, -2) 332 333 def test_getch(self): 334 win = curses.newwin(5, 12, 5, 2) 335 336 # TODO: Test with real input by writing to master fd. 337 for c in 'spam\n'[::-1]: 338 curses.ungetch(c) 339 self.assertEqual(win.getch(3, 1), b's'[0]) 340 self.assertEqual(win.getyx(), (3, 1)) 341 self.assertEqual(win.getch(3, 4), b'p'[0]) 342 self.assertEqual(win.getyx(), (3, 4)) 343 self.assertEqual(win.getch(), b'a'[0]) 344 self.assertEqual(win.getyx(), (3, 4)) 345 self.assertEqual(win.getch(), b'm'[0]) 346 self.assertEqual(win.getch(), b'\n'[0]) 347 348 def test_getstr(self): 349 win = curses.newwin(5, 12, 5, 2) 350 curses.echo() 351 self.addCleanup(curses.noecho) 352 353 self.assertRaises(ValueError, win.getstr, -400) 354 self.assertRaises(ValueError, win.getstr, 2, 3, -400) 355 356 # TODO: Test with real input by writing to master fd. 357 for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]: 358 curses.ungetch(c) 359 self.assertEqual(win.getstr(3, 1, 2), b'Lo') 360 self.assertEqual(win.instr(3, 0), b' Lo ') 361 self.assertEqual(win.getstr(3, 5, 10), b'ipsum') 362 self.assertEqual(win.instr(3, 0), b' Lo ipsum ') 363 self.assertEqual(win.getstr(1, 5), b'dolor') 364 self.assertEqual(win.instr(1, 0), b' dolor ') 365 self.assertEqual(win.getstr(2), b'si') 366 self.assertEqual(win.instr(1, 0), b'si dolor ') 367 self.assertEqual(win.getstr(), b'amet') 368 self.assertEqual(win.instr(1, 0), b'amet dolor ') 369 370 def test_clear(self): 371 win = curses.newwin(5, 15, 5, 2) 372 lorem_ipsum(win) 373 374 win.move(0, 8) 375 win.clrtoeol() 376 self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip') 377 self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,') 378 379 win.move(0, 3) 380 win.clrtobot() 381 self.assertEqual(win.instr(0, 0).rstrip(), b'Lor') 382 self.assertEqual(win.instr(1, 0).rstrip(), b'') 383 384 for func in [win.erase, win.clear]: 385 lorem_ipsum(win) 386 func() 387 self.assertEqual(win.instr(0, 0).rstrip(), b'') 388 self.assertEqual(win.instr(1, 0).rstrip(), b'') 389 390 def test_insert_delete(self): 391 win = curses.newwin(5, 15, 5, 2) 392 lorem_ipsum(win) 393 394 win.move(0, 2) 395 win.delch() 396 self.assertEqual(win.instr(0, 0), b'Loem ipsum ') 397 win.delch(0, 7) 398 self.assertEqual(win.instr(0, 0), b'Loem ipum ') 399 400 win.move(1, 5) 401 win.deleteln() 402 self.assertEqual(win.instr(0, 0), b'Loem ipum ') 403 self.assertEqual(win.instr(1, 0), b'consectetur ') 404 self.assertEqual(win.instr(2, 0), b'adipiscing elit') 405 self.assertEqual(win.instr(3, 0), b'sed do eiusmod ') 406 self.assertEqual(win.instr(4, 0), b' ') 407 408 win.move(1, 5) 409 win.insertln() 410 self.assertEqual(win.instr(0, 0), b'Loem ipum ') 411 self.assertEqual(win.instr(1, 0), b' ') 412 self.assertEqual(win.instr(2, 0), b'consectetur ') 413 414 win.clear() 415 lorem_ipsum(win) 416 win.move(1, 5) 417 win.insdelln(2) 418 self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') 419 self.assertEqual(win.instr(1, 0), b' ') 420 self.assertEqual(win.instr(2, 0), b' ') 421 self.assertEqual(win.instr(3, 0), b'dolor sit amet,') 422 423 win.clear() 424 lorem_ipsum(win) 425 win.move(1, 5) 426 win.insdelln(-2) 427 self.assertEqual(win.instr(0, 0), b'Lorem ipsum ') 428 self.assertEqual(win.instr(1, 0), b'adipiscing elit') 429 self.assertEqual(win.instr(2, 0), b'sed do eiusmod ') 430 self.assertEqual(win.instr(3, 0), b' ') 431 432 def test_scroll(self): 433 win = curses.newwin(5, 15, 5, 2) 434 lorem_ipsum(win) 435 win.scrollok(True) 436 win.scroll() 437 self.assertEqual(win.instr(0, 0), b'dolor sit amet,') 438 win.scroll(2) 439 self.assertEqual(win.instr(0, 0), b'adipiscing elit') 440 win.scroll(-3) 441 self.assertEqual(win.instr(0, 0), b' ') 442 self.assertEqual(win.instr(2, 0), b' ') 443 self.assertEqual(win.instr(3, 0), b'adipiscing elit') 444 win.scrollok(False) 445 446 def test_attributes(self): 447 # TODO: attr_get(), attr_set(), ... 448 win = curses.newwin(5, 15, 5, 2) 449 win.attron(curses.A_BOLD) 450 win.attroff(curses.A_BOLD) 451 win.attrset(curses.A_BOLD) 452 453 win.standout() 454 win.standend() 455 456 @requires_curses_window_meth('chgat') 457 def test_chgat(self): 458 win = curses.newwin(5, 15, 5, 2) 459 win.addstr(2, 0, 'Lorem ipsum') 460 win.addstr(3, 0, 'dolor sit amet') 461 462 win.move(2, 8) 463 win.chgat(curses.A_BLINK) 464 self.assertEqual(win.inch(2, 7), b'p'[0]) 465 self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK) 466 self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK) 467 468 win.move(2, 1) 469 win.chgat(3, curses.A_BOLD) 470 self.assertEqual(win.inch(2, 0), b'L'[0]) 471 self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD) 472 self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD) 473 self.assertEqual(win.inch(2, 4), b'm'[0]) 474 475 win.chgat(3, 2, curses.A_UNDERLINE) 476 self.assertEqual(win.inch(3, 1), b'o'[0]) 477 self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE) 478 self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) 479 480 win.chgat(3, 4, 7, curses.A_BLINK) 481 self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE) 482 self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK) 483 self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK) 484 self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE) 485 self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE) 486 487 def test_background(self): 488 win = curses.newwin(5, 15, 5, 2) 489 win.addstr(0, 0, 'Lorem ipsum') 490 491 self.assertIn(win.getbkgd(), (0, 32)) 492 493 # bkgdset() 494 win.bkgdset('_') 495 self.assertEqual(win.getbkgd(), b'_'[0]) 496 win.bkgdset(b'#') 497 self.assertEqual(win.getbkgd(), b'#'[0]) 498 win.bkgdset(65) 499 self.assertEqual(win.getbkgd(), 65) 500 win.bkgdset(0) 501 self.assertEqual(win.getbkgd(), 32) 502 503 win.bkgdset('#', curses.A_REVERSE) 504 self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) 505 self.assertEqual(win.inch(0, 0), b'L'[0]) 506 self.assertEqual(win.inch(0, 5), b' '[0]) 507 win.bkgdset(0) 508 509 # bkgd() 510 win.bkgd('_') 511 self.assertEqual(win.getbkgd(), b'_'[0]) 512 self.assertEqual(win.inch(0, 0), b'L'[0]) 513 self.assertEqual(win.inch(0, 5), b'_'[0]) 514 515 win.bkgd('#', curses.A_REVERSE) 516 self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE) 517 self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE) 518 self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE) 519 520 def test_overlay(self): 521 srcwin = curses.newwin(5, 18, 3, 4) 522 lorem_ipsum(srcwin) 523 dstwin = curses.newwin(7, 17, 5, 7) 524 for i in range(6): 525 dstwin.addstr(i, 0, '_'*17) 526 527 srcwin.overlay(dstwin) 528 self.assertEqual(dstwin.instr(0, 0), b'sectetur_________') 529 self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____') 530 self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______') 531 self.assertEqual(dstwin.instr(3, 0), b'_________________') 532 533 srcwin.overwrite(dstwin) 534 self.assertEqual(dstwin.instr(0, 0), b'sectetur __') 535 self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __') 536 self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __') 537 self.assertEqual(dstwin.instr(3, 0), b'_________________') 538 539 srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11) 540 self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____') 541 self.assertEqual(dstwin.instr(4, 0), b'__ectetur________') 542 self.assertEqual(dstwin.instr(5, 0), b'_________________') 543 544 srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11) 545 self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____') 546 self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____') 547 self.assertEqual(dstwin.instr(5, 0), b'_________________') 548 549 def test_refresh(self): 550 win = curses.newwin(5, 15, 2, 5) 551 win.noutrefresh() 552 win.redrawln(1, 2) 553 win.redrawwin() 554 win.refresh() 555 curses.doupdate() 556 557 @requires_curses_window_meth('resize') 558 def test_resize(self): 559 win = curses.newwin(5, 15, 2, 5) 560 win.resize(4, 20) 561 self.assertEqual(win.getmaxyx(), (4, 20)) 562 win.resize(5, 15) 563 self.assertEqual(win.getmaxyx(), (5, 15)) 564 565 @requires_curses_window_meth('enclose') 566 def test_enclose(self): 567 win = curses.newwin(5, 15, 2, 5) 568 # TODO: Return bool instead of 1/0 569 self.assertTrue(win.enclose(2, 5)) 570 self.assertFalse(win.enclose(1, 5)) 571 self.assertFalse(win.enclose(2, 4)) 572 self.assertTrue(win.enclose(6, 19)) 573 self.assertFalse(win.enclose(7, 19)) 574 self.assertFalse(win.enclose(6, 20)) 575 576 def test_putwin(self): 577 win = curses.newwin(5, 12, 1, 2) 578 win.addstr(2, 1, 'Lorem ipsum') 579 with tempfile.TemporaryFile() as f: 580 win.putwin(f) 581 del win 582 f.seek(0) 583 win = curses.getwin(f) 584 self.assertEqual(win.getbegyx(), (1, 2)) 585 self.assertEqual(win.getmaxyx(), (5, 12)) 586 self.assertEqual(win.instr(2, 0), b' Lorem ipsum') 587 588 def test_borders_and_lines(self): 589 win = curses.newwin(5, 10, 5, 2) 590 win.border('|', '!', '-', '_', 591 '+', '\\', '#', '/') 592 self.assertEqual(win.instr(0, 0), b'+--------\\') 593 self.assertEqual(win.instr(1, 0), b'| !') 594 self.assertEqual(win.instr(4, 0), b'#________/') 595 win.border(b'|', b'!', b'-', b'_', 596 b'+', b'\\', b'#', b'/') 597 win.border(65, 66, 67, 68, 598 69, 70, 71, 72) 599 self.assertRaises(TypeError, win.border, 600 65, 66, 67, 68, 69, [], 71, 72) 601 self.assertRaises(TypeError, win.border, 602 65, 66, 67, 68, 69, 70, 71, 72, 73) 603 self.assertRaises(TypeError, win.border, 604 65, 66, 67, 68, 69, 70, 71, 72, 73) 605 win.border(65, 66, 67, 68, 69, 70, 71) 606 win.border(65, 66, 67, 68, 69, 70) 607 win.border(65, 66, 67, 68, 69) 608 win.border(65, 66, 67, 68) 609 win.border(65, 66, 67) 610 win.border(65, 66) 611 win.border(65) 612 win.border() 613 614 win.box(':', '~') 615 self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~') 616 self.assertEqual(win.instr(1, 0), b': :') 617 self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~') 618 win.box(b':', b'~') 619 win.box(65, 67) 620 self.assertRaises(TypeError, win.box, 65, 66, 67) 621 self.assertRaises(TypeError, win.box, 65) 622 win.box() 623 624 win.move(1, 2) 625 win.hline('-', 5) 626 self.assertEqual(win.instr(1, 1, 7), b' ----- ') 627 win.hline(b'-', 5) 628 win.hline(45, 5) 629 win.hline('-', 5, curses.A_BOLD) 630 win.hline(1, 1, '-', 5) 631 win.hline(1, 1, '-', 5, curses.A_BOLD) 632 633 win.move(1, 2) 634 win.vline('a', 3) 635 win.vline(b'a', 3) 636 win.vline(97, 3) 637 win.vline('a', 3, curses.A_STANDOUT) 638 win.vline(1, 1, 'a', 3) 639 win.vline(1, 1, ';', 2, curses.A_STANDOUT) 640 self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT) 641 self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT) 642 self.assertEqual(win.inch(3, 1), b'a'[0]) 643 644 def test_unctrl(self): 645 # TODO: wunctrl() 646 self.assertEqual(curses.unctrl(b'A'), b'A') 647 self.assertEqual(curses.unctrl('A'), b'A') 648 self.assertEqual(curses.unctrl(65), b'A') 649 self.assertEqual(curses.unctrl(b'\n'), b'^J') 650 self.assertEqual(curses.unctrl('\n'), b'^J') 651 self.assertEqual(curses.unctrl(10), b'^J') 652 self.assertRaises(TypeError, curses.unctrl, b'') 653 self.assertRaises(TypeError, curses.unctrl, b'AB') 654 self.assertRaises(TypeError, curses.unctrl, '') 655 self.assertRaises(TypeError, curses.unctrl, 'AB') 656 self.assertRaises(OverflowError, curses.unctrl, 2**64) 657 658 def test_endwin(self): 659 if not self.isatty: 660 self.skipTest('requires terminal') 661 self.assertIs(curses.isendwin(), False) 662 curses.endwin() 663 self.assertIs(curses.isendwin(), True) 664 curses.doupdate() 665 self.assertIs(curses.isendwin(), False) 666 667 def test_terminfo(self): 668 self.assertIsInstance(curses.tigetflag('hc'), int) 669 self.assertEqual(curses.tigetflag('cols'), -1) 670 self.assertEqual(curses.tigetflag('cr'), -1) 671 672 self.assertIsInstance(curses.tigetnum('cols'), int) 673 self.assertEqual(curses.tigetnum('hc'), -2) 674 self.assertEqual(curses.tigetnum('cr'), -2) 675 676 self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None))) 677 self.assertIsNone(curses.tigetstr('hc')) 678 self.assertIsNone(curses.tigetstr('cols')) 679 680 cud = curses.tigetstr('cud') 681 if cud is not None: 682 # See issue10570. 683 self.assertIsInstance(cud, bytes) 684 curses.tparm(cud, 2) 685 cud_2 = curses.tparm(cud, 2) 686 self.assertIsInstance(cud_2, bytes) 687 curses.putp(cud_2) 688 689 curses.putp(b'abc\n') 690 691 def test_misc_module_funcs(self): 692 curses.delay_output(1) 693 curses.flushinp() 694 695 curses.doupdate() 696 self.assertIs(curses.isendwin(), False) 697 698 curses.napms(100) 699 700 curses.newpad(50, 50) 701 702 def test_env_queries(self): 703 # TODO: term_attrs(), erasewchar(), killwchar() 704 self.assertIsInstance(curses.termname(), bytes) 705 self.assertIsInstance(curses.longname(), bytes) 706 self.assertIsInstance(curses.baudrate(), int) 707 self.assertIsInstance(curses.has_ic(), bool) 708 self.assertIsInstance(curses.has_il(), bool) 709 self.assertIsInstance(curses.termattrs(), int) 710 711 c = curses.killchar() 712 self.assertIsInstance(c, bytes) 713 self.assertEqual(len(c), 1) 714 c = curses.erasechar() 715 self.assertIsInstance(c, bytes) 716 self.assertEqual(len(c), 1) 717 718 def test_output_options(self): 719 stdscr = self.stdscr 720 721 stdscr.clearok(True) 722 stdscr.clearok(False) 723 724 stdscr.idcok(True) 725 stdscr.idcok(False) 726 727 stdscr.idlok(False) 728 stdscr.idlok(True) 729 730 if hasattr(stdscr, 'immedok'): 731 stdscr.immedok(True) 732 stdscr.immedok(False) 733 734 stdscr.leaveok(True) 735 stdscr.leaveok(False) 736 737 stdscr.scrollok(True) 738 stdscr.scrollok(False) 739 740 stdscr.setscrreg(5, 10) 741 742 curses.nonl() 743 curses.nl(True) 744 curses.nl(False) 745 curses.nl() 746 747 748 def test_input_options(self): 749 stdscr = self.stdscr 750 751 if self.isatty: 752 curses.nocbreak() 753 curses.cbreak() 754 curses.cbreak(False) 755 curses.cbreak(True) 756 757 curses.intrflush(True) 758 curses.intrflush(False) 759 760 curses.raw() 761 curses.raw(False) 762 curses.raw(True) 763 curses.noraw() 764 765 curses.noecho() 766 curses.echo() 767 curses.echo(False) 768 curses.echo(True) 769 770 curses.halfdelay(255) 771 curses.halfdelay(1) 772 773 stdscr.keypad(True) 774 stdscr.keypad(False) 775 776 curses.meta(True) 777 curses.meta(False) 778 779 stdscr.nodelay(True) 780 stdscr.nodelay(False) 781 782 curses.noqiflush() 783 curses.qiflush(True) 784 curses.qiflush(False) 785 curses.qiflush() 786 787 stdscr.notimeout(True) 788 stdscr.notimeout(False) 789 790 stdscr.timeout(-1) 791 stdscr.timeout(0) 792 stdscr.timeout(5) 793 794 @requires_curses_func('typeahead') 795 def test_typeahead(self): 796 curses.typeahead(sys.__stdin__.fileno()) 797 curses.typeahead(-1) 798 799 def test_prog_mode(self): 800 if not self.isatty: 801 self.skipTest('requires terminal') 802 curses.def_prog_mode() 803 curses.reset_prog_mode() 804 805 def test_beep(self): 806 if (curses.tigetstr("bel") is not None 807 or curses.tigetstr("flash") is not None): 808 curses.beep() 809 else: 810 try: 811 curses.beep() 812 except curses.error: 813 self.skipTest('beep() failed') 814 815 def test_flash(self): 816 if (curses.tigetstr("bel") is not None 817 or curses.tigetstr("flash") is not None): 818 curses.flash() 819 else: 820 try: 821 curses.flash() 822 except curses.error: 823 self.skipTest('flash() failed') 824 825 def test_curs_set(self): 826 for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]: 827 if curses.tigetstr(cap) is not None: 828 curses.curs_set(vis) 829 else: 830 try: 831 curses.curs_set(vis) 832 except curses.error: 833 pass 834 835 @requires_curses_func('getsyx') 836 def test_getsyx(self): 837 y, x = curses.getsyx() 838 self.assertIsInstance(y, int) 839 self.assertIsInstance(x, int) 840 curses.setsyx(4, 5) 841 self.assertEqual(curses.getsyx(), (4, 5)) 842 843 def bad_colors(self): 844 return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) 845 846 def bad_pairs(self): 847 return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) 848 849 def test_has_colors(self): 850 self.assertIsInstance(curses.has_colors(), bool) 851 self.assertIsInstance(curses.can_change_color(), bool) 852 853 def test_start_color(self): 854 if not curses.has_colors(): 855 self.skipTest('requires colors support') 856 curses.start_color() 857 if verbose: 858 print(f'COLORS = {curses.COLORS}', file=sys.stderr) 859 print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr) 860 861 @requires_colors 862 def test_color_content(self): 863 self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0)) 864 curses.color_content(0) 865 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 866 curses.color_content(maxcolor) 867 868 for color in self.bad_colors(): 869 self.assertRaises(OverflowError, curses.color_content, color) 870 if curses.COLORS <= SHORT_MAX: 871 self.assertRaises(curses.error, curses.color_content, curses.COLORS) 872 self.assertRaises(curses.error, curses.color_content, -1) 873 874 @requires_colors 875 def test_init_color(self): 876 if not curses.can_change_color(): 877 self.skipTest('cannot change color') 878 879 old = curses.color_content(0) 880 try: 881 curses.init_color(0, *old) 882 except curses.error: 883 self.skipTest('cannot change color (init_color() failed)') 884 self.addCleanup(curses.init_color, 0, *old) 885 curses.init_color(0, 0, 0, 0) 886 self.assertEqual(curses.color_content(0), (0, 0, 0)) 887 curses.init_color(0, 1000, 1000, 1000) 888 self.assertEqual(curses.color_content(0), (1000, 1000, 1000)) 889 890 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 891 old = curses.color_content(maxcolor) 892 curses.init_color(maxcolor, *old) 893 self.addCleanup(curses.init_color, maxcolor, *old) 894 curses.init_color(maxcolor, 0, 500, 1000) 895 self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000)) 896 897 for color in self.bad_colors(): 898 self.assertRaises(OverflowError, curses.init_color, color, 0, 0, 0) 899 if curses.COLORS <= SHORT_MAX: 900 self.assertRaises(curses.error, curses.init_color, curses.COLORS, 0, 0, 0) 901 self.assertRaises(curses.error, curses.init_color, -1, 0, 0, 0) 902 for comp in (-1, 1001): 903 self.assertRaises(curses.error, curses.init_color, 0, comp, 0, 0) 904 self.assertRaises(curses.error, curses.init_color, 0, 0, comp, 0) 905 self.assertRaises(curses.error, curses.init_color, 0, 0, 0, comp) 906 907 def get_pair_limit(self): 908 return min(curses.COLOR_PAIRS, SHORT_MAX) 909 910 @requires_colors 911 def test_pair_content(self): 912 if not hasattr(curses, 'use_default_colors'): 913 self.assertEqual(curses.pair_content(0), 914 (curses.COLOR_WHITE, curses.COLOR_BLACK)) 915 curses.pair_content(0) 916 maxpair = self.get_pair_limit() - 1 917 if maxpair > 0: 918 curses.pair_content(maxpair) 919 920 for pair in self.bad_pairs(): 921 self.assertRaises(OverflowError, curses.pair_content, pair) 922 self.assertRaises(curses.error, curses.pair_content, -1) 923 924 @requires_colors 925 def test_init_pair(self): 926 old = curses.pair_content(1) 927 curses.init_pair(1, *old) 928 self.addCleanup(curses.init_pair, 1, *old) 929 930 curses.init_pair(1, 0, 0) 931 self.assertEqual(curses.pair_content(1), (0, 0)) 932 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 933 curses.init_pair(1, maxcolor, 0) 934 self.assertEqual(curses.pair_content(1), (maxcolor, 0)) 935 curses.init_pair(1, 0, maxcolor) 936 self.assertEqual(curses.pair_content(1), (0, maxcolor)) 937 maxpair = self.get_pair_limit() - 1 938 if maxpair > 1: 939 curses.init_pair(maxpair, 0, 0) 940 self.assertEqual(curses.pair_content(maxpair), (0, 0)) 941 942 for pair in self.bad_pairs(): 943 self.assertRaises(OverflowError, curses.init_pair, pair, 0, 0) 944 self.assertRaises(curses.error, curses.init_pair, -1, 0, 0) 945 for color in self.bad_colors(): 946 self.assertRaises(OverflowError, curses.init_pair, 1, color, 0) 947 self.assertRaises(OverflowError, curses.init_pair, 1, 0, color) 948 if curses.COLORS <= SHORT_MAX: 949 self.assertRaises(curses.error, curses.init_pair, 1, curses.COLORS, 0) 950 self.assertRaises(curses.error, curses.init_pair, 1, 0, curses.COLORS) 951 952 @requires_colors 953 def test_color_attrs(self): 954 for pair in 0, 1, 255: 955 attr = curses.color_pair(pair) 956 self.assertEqual(curses.pair_number(attr), pair, attr) 957 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair) 958 self.assertEqual(curses.color_pair(0), 0) 959 self.assertEqual(curses.pair_number(0), 0) 960 961 @requires_curses_func('use_default_colors') 962 @requires_colors 963 def test_use_default_colors(self): 964 old = curses.pair_content(0) 965 try: 966 curses.use_default_colors() 967 except curses.error: 968 self.skipTest('cannot change color (use_default_colors() failed)') 969 self.assertEqual(curses.pair_content(0), (-1, -1)) 970 self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)]) 971 972 def test_keyname(self): 973 # TODO: key_name() 974 self.assertEqual(curses.keyname(65), b'A') 975 self.assertEqual(curses.keyname(13), b'^M') 976 self.assertEqual(curses.keyname(127), b'^?') 977 self.assertEqual(curses.keyname(0), b'^@') 978 self.assertRaises(ValueError, curses.keyname, -1) 979 self.assertIsInstance(curses.keyname(256), bytes) 980 981 @requires_curses_func('has_key') 982 def test_has_key(self): 983 curses.has_key(13) 984 985 @requires_curses_func('getmouse') 986 def test_getmouse(self): 987 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) 988 if availmask == 0: 989 self.skipTest('mouse stuff not available') 990 curses.mouseinterval(10) 991 # just verify these don't cause errors 992 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) 993 m = curses.getmouse() 994 995 @requires_curses_func('panel') 996 def test_userptr_without_set(self): 997 w = curses.newwin(10, 10) 998 p = curses.panel.new_panel(w) 999 # try to access userptr() before calling set_userptr() -- segfaults 1000 with self.assertRaises(curses.panel.error, 1001 msg='userptr should fail since not set'): 1002 p.userptr() 1003 1004 @requires_curses_func('panel') 1005 def test_userptr_memory_leak(self): 1006 w = curses.newwin(10, 10) 1007 p = curses.panel.new_panel(w) 1008 obj = object() 1009 nrefs = sys.getrefcount(obj) 1010 for i in range(100): 1011 p.set_userptr(obj) 1012 1013 p.set_userptr(None) 1014 self.assertEqual(sys.getrefcount(obj), nrefs, 1015 "set_userptr leaked references") 1016 1017 @requires_curses_func('panel') 1018 def test_userptr_segfault(self): 1019 w = curses.newwin(10, 10) 1020 panel = curses.panel.new_panel(w) 1021 class A: 1022 def __del__(self): 1023 panel.set_userptr(None) 1024 panel.set_userptr(A()) 1025 panel.set_userptr(None) 1026 1027 @requires_curses_func('panel') 1028 def test_new_curses_panel(self): 1029 w = curses.newwin(10, 10) 1030 panel = curses.panel.new_panel(w) 1031 self.assertRaises(TypeError, type(panel)) 1032 1033 @requires_curses_func('is_term_resized') 1034 def test_is_term_resized(self): 1035 lines, cols = curses.LINES, curses.COLS 1036 self.assertIs(curses.is_term_resized(lines, cols), False) 1037 self.assertIs(curses.is_term_resized(lines-1, cols-1), True) 1038 1039 @requires_curses_func('resize_term') 1040 def test_resize_term(self): 1041 curses.update_lines_cols() 1042 lines, cols = curses.LINES, curses.COLS 1043 new_lines = lines - 1 1044 new_cols = cols + 1 1045 curses.resize_term(new_lines, new_cols) 1046 self.assertEqual(curses.LINES, new_lines) 1047 self.assertEqual(curses.COLS, new_cols) 1048 1049 curses.resize_term(lines, cols) 1050 self.assertEqual(curses.LINES, lines) 1051 self.assertEqual(curses.COLS, cols) 1052 1053 @requires_curses_func('resizeterm') 1054 def test_resizeterm(self): 1055 curses.update_lines_cols() 1056 lines, cols = curses.LINES, curses.COLS 1057 new_lines = lines - 1 1058 new_cols = cols + 1 1059 curses.resizeterm(new_lines, new_cols) 1060 self.assertEqual(curses.LINES, new_lines) 1061 self.assertEqual(curses.COLS, new_cols) 1062 1063 curses.resizeterm(lines, cols) 1064 self.assertEqual(curses.LINES, lines) 1065 self.assertEqual(curses.COLS, cols) 1066 1067 def test_ungetch(self): 1068 curses.ungetch(b'A') 1069 self.assertEqual(self.stdscr.getkey(), 'A') 1070 curses.ungetch('B') 1071 self.assertEqual(self.stdscr.getkey(), 'B') 1072 curses.ungetch(67) 1073 self.assertEqual(self.stdscr.getkey(), 'C') 1074 1075 def test_issue6243(self): 1076 curses.ungetch(1025) 1077 self.stdscr.getkey() 1078 1079 @requires_curses_func('unget_wch') 1080 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8), 1081 "unget_wch is broken in ncurses 5.7 and earlier") 1082 def test_unget_wch(self): 1083 stdscr = self.stdscr 1084 encoding = stdscr.encoding 1085 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'): 1086 try: 1087 ch.encode(encoding) 1088 except UnicodeEncodeError: 1089 continue 1090 try: 1091 curses.unget_wch(ch) 1092 except Exception as err: 1093 self.fail("unget_wch(%a) failed with encoding %s: %s" 1094 % (ch, stdscr.encoding, err)) 1095 read = stdscr.get_wch() 1096 self.assertEqual(read, ch) 1097 1098 code = ord(ch) 1099 curses.unget_wch(code) 1100 read = stdscr.get_wch() 1101 self.assertEqual(read, ch) 1102 1103 def test_encoding(self): 1104 stdscr = self.stdscr 1105 import codecs 1106 encoding = stdscr.encoding 1107 codecs.lookup(encoding) 1108 with self.assertRaises(TypeError): 1109 stdscr.encoding = 10 1110 stdscr.encoding = encoding 1111 with self.assertRaises(TypeError): 1112 del stdscr.encoding 1113 1114 def test_issue21088(self): 1115 stdscr = self.stdscr 1116 # 1117 # http://bugs.python.org/issue21088 1118 # 1119 # the bug: 1120 # when converting curses.window.addch to Argument Clinic 1121 # the first two parameters were switched. 1122 1123 # if someday we can represent the signature of addch 1124 # we will need to rewrite this test. 1125 try: 1126 signature = inspect.signature(stdscr.addch) 1127 self.assertFalse(signature) 1128 except ValueError: 1129 # not generating a signature is fine. 1130 pass 1131 1132 # So. No signature for addch. 1133 # But Argument Clinic gave us a human-readable equivalent 1134 # as the first line of the docstring. So we parse that, 1135 # and ensure that the parameters appear in the correct order. 1136 # Since this is parsing output from Argument Clinic, we can 1137 # be reasonably certain the generated parsing code will be 1138 # correct too. 1139 human_readable_signature = stdscr.addch.__doc__.split("\n")[0] 1140 self.assertIn("[y, x,]", human_readable_signature) 1141 1142 @requires_curses_window_meth('resize') 1143 def test_issue13051(self): 1144 win = curses.newwin(5, 15, 2, 5) 1145 box = curses.textpad.Textbox(win, insert_mode=True) 1146 lines, cols = win.getmaxyx() 1147 win.resize(lines-2, cols-2) 1148 # this may cause infinite recursion, leading to a RuntimeError 1149 box._insert_printable_char('a') 1150 1151 1152class MiscTests(unittest.TestCase): 1153 1154 @requires_curses_func('update_lines_cols') 1155 def test_update_lines_cols(self): 1156 curses.update_lines_cols() 1157 lines, cols = curses.LINES, curses.COLS 1158 curses.LINES = curses.COLS = 0 1159 curses.update_lines_cols() 1160 self.assertEqual(curses.LINES, lines) 1161 self.assertEqual(curses.COLS, cols) 1162 1163 @requires_curses_func('ncurses_version') 1164 def test_ncurses_version(self): 1165 v = curses.ncurses_version 1166 if verbose: 1167 print(f'ncurses_version = {curses.ncurses_version}', flush=True) 1168 self.assertIsInstance(v[:], tuple) 1169 self.assertEqual(len(v), 3) 1170 self.assertIsInstance(v[0], int) 1171 self.assertIsInstance(v[1], int) 1172 self.assertIsInstance(v[2], int) 1173 self.assertIsInstance(v.major, int) 1174 self.assertIsInstance(v.minor, int) 1175 self.assertIsInstance(v.patch, int) 1176 self.assertEqual(v[0], v.major) 1177 self.assertEqual(v[1], v.minor) 1178 self.assertEqual(v[2], v.patch) 1179 self.assertGreaterEqual(v.major, 0) 1180 self.assertGreaterEqual(v.minor, 0) 1181 self.assertGreaterEqual(v.patch, 0) 1182 1183 1184class TestAscii(unittest.TestCase): 1185 1186 def test_controlnames(self): 1187 for name in curses.ascii.controlnames: 1188 self.assertTrue(hasattr(curses.ascii, name), name) 1189 1190 def test_ctypes(self): 1191 def check(func, expected): 1192 with self.subTest(ch=c, func=func): 1193 self.assertEqual(func(i), expected) 1194 self.assertEqual(func(c), expected) 1195 1196 for i in range(256): 1197 c = chr(i) 1198 b = bytes([i]) 1199 check(curses.ascii.isalnum, b.isalnum()) 1200 check(curses.ascii.isalpha, b.isalpha()) 1201 check(curses.ascii.isdigit, b.isdigit()) 1202 check(curses.ascii.islower, b.islower()) 1203 check(curses.ascii.isspace, b.isspace()) 1204 check(curses.ascii.isupper, b.isupper()) 1205 1206 check(curses.ascii.isascii, i < 128) 1207 check(curses.ascii.ismeta, i >= 128) 1208 check(curses.ascii.isctrl, i < 32) 1209 check(curses.ascii.iscntrl, i < 32 or i == 127) 1210 check(curses.ascii.isblank, c in ' \t') 1211 check(curses.ascii.isgraph, 32 < i <= 126) 1212 check(curses.ascii.isprint, 32 <= i <= 126) 1213 check(curses.ascii.ispunct, c in string.punctuation) 1214 check(curses.ascii.isxdigit, c in string.hexdigits) 1215 1216 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1): 1217 self.assertFalse(curses.ascii.isalnum(i)) 1218 self.assertFalse(curses.ascii.isalpha(i)) 1219 self.assertFalse(curses.ascii.isdigit(i)) 1220 self.assertFalse(curses.ascii.islower(i)) 1221 self.assertFalse(curses.ascii.isspace(i)) 1222 self.assertFalse(curses.ascii.isupper(i)) 1223 1224 self.assertFalse(curses.ascii.isascii(i)) 1225 self.assertFalse(curses.ascii.isctrl(i)) 1226 self.assertFalse(curses.ascii.iscntrl(i)) 1227 self.assertFalse(curses.ascii.isblank(i)) 1228 self.assertFalse(curses.ascii.isgraph(i)) 1229 self.assertFalse(curses.ascii.isprint(i)) 1230 self.assertFalse(curses.ascii.ispunct(i)) 1231 self.assertFalse(curses.ascii.isxdigit(i)) 1232 1233 self.assertFalse(curses.ascii.ismeta(-1)) 1234 1235 def test_ascii(self): 1236 ascii = curses.ascii.ascii 1237 self.assertEqual(ascii('\xc1'), 'A') 1238 self.assertEqual(ascii('A'), 'A') 1239 self.assertEqual(ascii(ord('\xc1')), ord('A')) 1240 1241 def test_ctrl(self): 1242 ctrl = curses.ascii.ctrl 1243 self.assertEqual(ctrl('J'), '\n') 1244 self.assertEqual(ctrl('\n'), '\n') 1245 self.assertEqual(ctrl('@'), '\0') 1246 self.assertEqual(ctrl(ord('J')), ord('\n')) 1247 1248 def test_alt(self): 1249 alt = curses.ascii.alt 1250 self.assertEqual(alt('\n'), '\x8a') 1251 self.assertEqual(alt('A'), '\xc1') 1252 self.assertEqual(alt(ord('A')), 0xc1) 1253 1254 def test_unctrl(self): 1255 unctrl = curses.ascii.unctrl 1256 self.assertEqual(unctrl('a'), 'a') 1257 self.assertEqual(unctrl('A'), 'A') 1258 self.assertEqual(unctrl(';'), ';') 1259 self.assertEqual(unctrl(' '), ' ') 1260 self.assertEqual(unctrl('\x7f'), '^?') 1261 self.assertEqual(unctrl('\n'), '^J') 1262 self.assertEqual(unctrl('\0'), '^@') 1263 self.assertEqual(unctrl(ord('A')), 'A') 1264 self.assertEqual(unctrl(ord('\n')), '^J') 1265 # Meta-bit characters 1266 self.assertEqual(unctrl('\x8a'), '!^J') 1267 self.assertEqual(unctrl('\xc1'), '!A') 1268 self.assertEqual(unctrl(ord('\x8a')), '!^J') 1269 self.assertEqual(unctrl(ord('\xc1')), '!A') 1270 1271 1272def lorem_ipsum(win): 1273 text = [ 1274 'Lorem ipsum', 1275 'dolor sit amet,', 1276 'consectetur', 1277 'adipiscing elit,', 1278 'sed do eiusmod', 1279 'tempor incididunt', 1280 'ut labore et', 1281 'dolore magna', 1282 'aliqua.', 1283 ] 1284 maxy, maxx = win.getmaxyx() 1285 for y, line in enumerate(text[:maxy]): 1286 win.addstr(y, 0, line[:maxx - (y == maxy - 1)]) 1287 1288if __name__ == '__main__': 1289 unittest.main() 1290