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('get_escdelay') 836 def test_escdelay(self): 837 escdelay = curses.get_escdelay() 838 self.assertIsInstance(escdelay, int) 839 curses.set_escdelay(25) 840 self.assertEqual(curses.get_escdelay(), 25) 841 curses.set_escdelay(escdelay) 842 843 @requires_curses_func('get_tabsize') 844 def test_tabsize(self): 845 tabsize = curses.get_tabsize() 846 self.assertIsInstance(tabsize, int) 847 curses.set_tabsize(4) 848 self.assertEqual(curses.get_tabsize(), 4) 849 curses.set_tabsize(tabsize) 850 851 @requires_curses_func('getsyx') 852 def test_getsyx(self): 853 y, x = curses.getsyx() 854 self.assertIsInstance(y, int) 855 self.assertIsInstance(x, int) 856 curses.setsyx(4, 5) 857 self.assertEqual(curses.getsyx(), (4, 5)) 858 859 def bad_colors(self): 860 return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) 861 862 def bad_pairs(self): 863 return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64) 864 865 def test_has_colors(self): 866 self.assertIsInstance(curses.has_colors(), bool) 867 self.assertIsInstance(curses.can_change_color(), bool) 868 869 def test_start_color(self): 870 if not curses.has_colors(): 871 self.skipTest('requires colors support') 872 curses.start_color() 873 if verbose: 874 print(f'COLORS = {curses.COLORS}', file=sys.stderr) 875 print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr) 876 877 @requires_colors 878 def test_color_content(self): 879 self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0)) 880 curses.color_content(0) 881 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 882 curses.color_content(maxcolor) 883 884 for color in self.bad_colors(): 885 self.assertRaises(OverflowError, curses.color_content, color) 886 if curses.COLORS <= SHORT_MAX: 887 self.assertRaises(curses.error, curses.color_content, curses.COLORS) 888 self.assertRaises(curses.error, curses.color_content, -1) 889 890 @requires_colors 891 def test_init_color(self): 892 if not curses.can_change_color(): 893 self.skipTest('cannot change color') 894 895 old = curses.color_content(0) 896 try: 897 curses.init_color(0, *old) 898 except curses.error: 899 self.skipTest('cannot change color (init_color() failed)') 900 self.addCleanup(curses.init_color, 0, *old) 901 curses.init_color(0, 0, 0, 0) 902 self.assertEqual(curses.color_content(0), (0, 0, 0)) 903 curses.init_color(0, 1000, 1000, 1000) 904 self.assertEqual(curses.color_content(0), (1000, 1000, 1000)) 905 906 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 907 old = curses.color_content(maxcolor) 908 curses.init_color(maxcolor, *old) 909 self.addCleanup(curses.init_color, maxcolor, *old) 910 curses.init_color(maxcolor, 0, 500, 1000) 911 self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000)) 912 913 for color in self.bad_colors(): 914 self.assertRaises(OverflowError, curses.init_color, color, 0, 0, 0) 915 if curses.COLORS <= SHORT_MAX: 916 self.assertRaises(curses.error, curses.init_color, curses.COLORS, 0, 0, 0) 917 self.assertRaises(curses.error, curses.init_color, -1, 0, 0, 0) 918 for comp in (-1, 1001): 919 self.assertRaises(curses.error, curses.init_color, 0, comp, 0, 0) 920 self.assertRaises(curses.error, curses.init_color, 0, 0, comp, 0) 921 self.assertRaises(curses.error, curses.init_color, 0, 0, 0, comp) 922 923 def get_pair_limit(self): 924 return min(curses.COLOR_PAIRS, SHORT_MAX) 925 926 @requires_colors 927 def test_pair_content(self): 928 if not hasattr(curses, 'use_default_colors'): 929 self.assertEqual(curses.pair_content(0), 930 (curses.COLOR_WHITE, curses.COLOR_BLACK)) 931 curses.pair_content(0) 932 maxpair = self.get_pair_limit() - 1 933 if maxpair > 0: 934 curses.pair_content(maxpair) 935 936 for pair in self.bad_pairs(): 937 self.assertRaises(OverflowError, curses.pair_content, pair) 938 self.assertRaises(curses.error, curses.pair_content, -1) 939 940 @requires_colors 941 def test_init_pair(self): 942 old = curses.pair_content(1) 943 curses.init_pair(1, *old) 944 self.addCleanup(curses.init_pair, 1, *old) 945 946 curses.init_pair(1, 0, 0) 947 self.assertEqual(curses.pair_content(1), (0, 0)) 948 maxcolor = min(curses.COLORS - 1, SHORT_MAX) 949 curses.init_pair(1, maxcolor, 0) 950 self.assertEqual(curses.pair_content(1), (maxcolor, 0)) 951 curses.init_pair(1, 0, maxcolor) 952 self.assertEqual(curses.pair_content(1), (0, maxcolor)) 953 maxpair = self.get_pair_limit() - 1 954 if maxpair > 1: 955 curses.init_pair(maxpair, 0, 0) 956 self.assertEqual(curses.pair_content(maxpair), (0, 0)) 957 958 for pair in self.bad_pairs(): 959 self.assertRaises(OverflowError, curses.init_pair, pair, 0, 0) 960 self.assertRaises(curses.error, curses.init_pair, -1, 0, 0) 961 for color in self.bad_colors(): 962 self.assertRaises(OverflowError, curses.init_pair, 1, color, 0) 963 self.assertRaises(OverflowError, curses.init_pair, 1, 0, color) 964 if curses.COLORS <= SHORT_MAX: 965 self.assertRaises(curses.error, curses.init_pair, 1, curses.COLORS, 0) 966 self.assertRaises(curses.error, curses.init_pair, 1, 0, curses.COLORS) 967 968 @requires_colors 969 def test_color_attrs(self): 970 for pair in 0, 1, 255: 971 attr = curses.color_pair(pair) 972 self.assertEqual(curses.pair_number(attr), pair, attr) 973 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair) 974 self.assertEqual(curses.color_pair(0), 0) 975 self.assertEqual(curses.pair_number(0), 0) 976 977 @requires_curses_func('use_default_colors') 978 @requires_colors 979 def test_use_default_colors(self): 980 old = curses.pair_content(0) 981 try: 982 curses.use_default_colors() 983 except curses.error: 984 self.skipTest('cannot change color (use_default_colors() failed)') 985 self.assertEqual(curses.pair_content(0), (-1, -1)) 986 self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)]) 987 988 def test_keyname(self): 989 # TODO: key_name() 990 self.assertEqual(curses.keyname(65), b'A') 991 self.assertEqual(curses.keyname(13), b'^M') 992 self.assertEqual(curses.keyname(127), b'^?') 993 self.assertEqual(curses.keyname(0), b'^@') 994 self.assertRaises(ValueError, curses.keyname, -1) 995 self.assertIsInstance(curses.keyname(256), bytes) 996 997 @requires_curses_func('has_key') 998 def test_has_key(self): 999 curses.has_key(13) 1000 1001 @requires_curses_func('getmouse') 1002 def test_getmouse(self): 1003 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED) 1004 if availmask == 0: 1005 self.skipTest('mouse stuff not available') 1006 curses.mouseinterval(10) 1007 # just verify these don't cause errors 1008 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) 1009 m = curses.getmouse() 1010 1011 @requires_curses_func('panel') 1012 def test_userptr_without_set(self): 1013 w = curses.newwin(10, 10) 1014 p = curses.panel.new_panel(w) 1015 # try to access userptr() before calling set_userptr() -- segfaults 1016 with self.assertRaises(curses.panel.error, 1017 msg='userptr should fail since not set'): 1018 p.userptr() 1019 1020 @requires_curses_func('panel') 1021 def test_userptr_memory_leak(self): 1022 w = curses.newwin(10, 10) 1023 p = curses.panel.new_panel(w) 1024 obj = object() 1025 nrefs = sys.getrefcount(obj) 1026 for i in range(100): 1027 p.set_userptr(obj) 1028 1029 p.set_userptr(None) 1030 self.assertEqual(sys.getrefcount(obj), nrefs, 1031 "set_userptr leaked references") 1032 1033 @requires_curses_func('panel') 1034 def test_userptr_segfault(self): 1035 w = curses.newwin(10, 10) 1036 panel = curses.panel.new_panel(w) 1037 class A: 1038 def __del__(self): 1039 panel.set_userptr(None) 1040 panel.set_userptr(A()) 1041 panel.set_userptr(None) 1042 1043 @requires_curses_func('panel') 1044 def test_new_curses_panel(self): 1045 w = curses.newwin(10, 10) 1046 panel = curses.panel.new_panel(w) 1047 self.assertRaises(TypeError, type(panel)) 1048 1049 @requires_curses_func('is_term_resized') 1050 def test_is_term_resized(self): 1051 lines, cols = curses.LINES, curses.COLS 1052 self.assertIs(curses.is_term_resized(lines, cols), False) 1053 self.assertIs(curses.is_term_resized(lines-1, cols-1), True) 1054 1055 @requires_curses_func('resize_term') 1056 def test_resize_term(self): 1057 curses.update_lines_cols() 1058 lines, cols = curses.LINES, curses.COLS 1059 new_lines = lines - 1 1060 new_cols = cols + 1 1061 curses.resize_term(new_lines, new_cols) 1062 self.assertEqual(curses.LINES, new_lines) 1063 self.assertEqual(curses.COLS, new_cols) 1064 1065 curses.resize_term(lines, cols) 1066 self.assertEqual(curses.LINES, lines) 1067 self.assertEqual(curses.COLS, cols) 1068 1069 @requires_curses_func('resizeterm') 1070 def test_resizeterm(self): 1071 curses.update_lines_cols() 1072 lines, cols = curses.LINES, curses.COLS 1073 new_lines = lines - 1 1074 new_cols = cols + 1 1075 curses.resizeterm(new_lines, new_cols) 1076 self.assertEqual(curses.LINES, new_lines) 1077 self.assertEqual(curses.COLS, new_cols) 1078 1079 curses.resizeterm(lines, cols) 1080 self.assertEqual(curses.LINES, lines) 1081 self.assertEqual(curses.COLS, cols) 1082 1083 def test_ungetch(self): 1084 curses.ungetch(b'A') 1085 self.assertEqual(self.stdscr.getkey(), 'A') 1086 curses.ungetch('B') 1087 self.assertEqual(self.stdscr.getkey(), 'B') 1088 curses.ungetch(67) 1089 self.assertEqual(self.stdscr.getkey(), 'C') 1090 1091 def test_issue6243(self): 1092 curses.ungetch(1025) 1093 self.stdscr.getkey() 1094 1095 @requires_curses_func('unget_wch') 1096 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8), 1097 "unget_wch is broken in ncurses 5.7 and earlier") 1098 def test_unget_wch(self): 1099 stdscr = self.stdscr 1100 encoding = stdscr.encoding 1101 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'): 1102 try: 1103 ch.encode(encoding) 1104 except UnicodeEncodeError: 1105 continue 1106 try: 1107 curses.unget_wch(ch) 1108 except Exception as err: 1109 self.fail("unget_wch(%a) failed with encoding %s: %s" 1110 % (ch, stdscr.encoding, err)) 1111 read = stdscr.get_wch() 1112 self.assertEqual(read, ch) 1113 1114 code = ord(ch) 1115 curses.unget_wch(code) 1116 read = stdscr.get_wch() 1117 self.assertEqual(read, ch) 1118 1119 def test_encoding(self): 1120 stdscr = self.stdscr 1121 import codecs 1122 encoding = stdscr.encoding 1123 codecs.lookup(encoding) 1124 with self.assertRaises(TypeError): 1125 stdscr.encoding = 10 1126 stdscr.encoding = encoding 1127 with self.assertRaises(TypeError): 1128 del stdscr.encoding 1129 1130 def test_issue21088(self): 1131 stdscr = self.stdscr 1132 # 1133 # http://bugs.python.org/issue21088 1134 # 1135 # the bug: 1136 # when converting curses.window.addch to Argument Clinic 1137 # the first two parameters were switched. 1138 1139 # if someday we can represent the signature of addch 1140 # we will need to rewrite this test. 1141 try: 1142 signature = inspect.signature(stdscr.addch) 1143 self.assertFalse(signature) 1144 except ValueError: 1145 # not generating a signature is fine. 1146 pass 1147 1148 # So. No signature for addch. 1149 # But Argument Clinic gave us a human-readable equivalent 1150 # as the first line of the docstring. So we parse that, 1151 # and ensure that the parameters appear in the correct order. 1152 # Since this is parsing output from Argument Clinic, we can 1153 # be reasonably certain the generated parsing code will be 1154 # correct too. 1155 human_readable_signature = stdscr.addch.__doc__.split("\n")[0] 1156 self.assertIn("[y, x,]", human_readable_signature) 1157 1158 @requires_curses_window_meth('resize') 1159 def test_issue13051(self): 1160 win = curses.newwin(5, 15, 2, 5) 1161 box = curses.textpad.Textbox(win, insert_mode=True) 1162 lines, cols = win.getmaxyx() 1163 win.resize(lines-2, cols-2) 1164 # this may cause infinite recursion, leading to a RuntimeError 1165 box._insert_printable_char('a') 1166 1167 1168class MiscTests(unittest.TestCase): 1169 1170 def test_update_lines_cols(self): 1171 curses.update_lines_cols() 1172 lines, cols = curses.LINES, curses.COLS 1173 curses.LINES = curses.COLS = 0 1174 curses.update_lines_cols() 1175 self.assertEqual(curses.LINES, lines) 1176 self.assertEqual(curses.COLS, cols) 1177 1178 @requires_curses_func('ncurses_version') 1179 def test_ncurses_version(self): 1180 v = curses.ncurses_version 1181 if verbose: 1182 print(f'ncurses_version = {curses.ncurses_version}', flush=True) 1183 self.assertIsInstance(v[:], tuple) 1184 self.assertEqual(len(v), 3) 1185 self.assertIsInstance(v[0], int) 1186 self.assertIsInstance(v[1], int) 1187 self.assertIsInstance(v[2], int) 1188 self.assertIsInstance(v.major, int) 1189 self.assertIsInstance(v.minor, int) 1190 self.assertIsInstance(v.patch, int) 1191 self.assertEqual(v[0], v.major) 1192 self.assertEqual(v[1], v.minor) 1193 self.assertEqual(v[2], v.patch) 1194 self.assertGreaterEqual(v.major, 0) 1195 self.assertGreaterEqual(v.minor, 0) 1196 self.assertGreaterEqual(v.patch, 0) 1197 1198 1199class TestAscii(unittest.TestCase): 1200 1201 def test_controlnames(self): 1202 for name in curses.ascii.controlnames: 1203 self.assertTrue(hasattr(curses.ascii, name), name) 1204 1205 def test_ctypes(self): 1206 def check(func, expected): 1207 with self.subTest(ch=c, func=func): 1208 self.assertEqual(func(i), expected) 1209 self.assertEqual(func(c), expected) 1210 1211 for i in range(256): 1212 c = chr(i) 1213 b = bytes([i]) 1214 check(curses.ascii.isalnum, b.isalnum()) 1215 check(curses.ascii.isalpha, b.isalpha()) 1216 check(curses.ascii.isdigit, b.isdigit()) 1217 check(curses.ascii.islower, b.islower()) 1218 check(curses.ascii.isspace, b.isspace()) 1219 check(curses.ascii.isupper, b.isupper()) 1220 1221 check(curses.ascii.isascii, i < 128) 1222 check(curses.ascii.ismeta, i >= 128) 1223 check(curses.ascii.isctrl, i < 32) 1224 check(curses.ascii.iscntrl, i < 32 or i == 127) 1225 check(curses.ascii.isblank, c in ' \t') 1226 check(curses.ascii.isgraph, 32 < i <= 126) 1227 check(curses.ascii.isprint, 32 <= i <= 126) 1228 check(curses.ascii.ispunct, c in string.punctuation) 1229 check(curses.ascii.isxdigit, c in string.hexdigits) 1230 1231 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1): 1232 self.assertFalse(curses.ascii.isalnum(i)) 1233 self.assertFalse(curses.ascii.isalpha(i)) 1234 self.assertFalse(curses.ascii.isdigit(i)) 1235 self.assertFalse(curses.ascii.islower(i)) 1236 self.assertFalse(curses.ascii.isspace(i)) 1237 self.assertFalse(curses.ascii.isupper(i)) 1238 1239 self.assertFalse(curses.ascii.isascii(i)) 1240 self.assertFalse(curses.ascii.isctrl(i)) 1241 self.assertFalse(curses.ascii.iscntrl(i)) 1242 self.assertFalse(curses.ascii.isblank(i)) 1243 self.assertFalse(curses.ascii.isgraph(i)) 1244 self.assertFalse(curses.ascii.isprint(i)) 1245 self.assertFalse(curses.ascii.ispunct(i)) 1246 self.assertFalse(curses.ascii.isxdigit(i)) 1247 1248 self.assertFalse(curses.ascii.ismeta(-1)) 1249 1250 def test_ascii(self): 1251 ascii = curses.ascii.ascii 1252 self.assertEqual(ascii('\xc1'), 'A') 1253 self.assertEqual(ascii('A'), 'A') 1254 self.assertEqual(ascii(ord('\xc1')), ord('A')) 1255 1256 def test_ctrl(self): 1257 ctrl = curses.ascii.ctrl 1258 self.assertEqual(ctrl('J'), '\n') 1259 self.assertEqual(ctrl('\n'), '\n') 1260 self.assertEqual(ctrl('@'), '\0') 1261 self.assertEqual(ctrl(ord('J')), ord('\n')) 1262 1263 def test_alt(self): 1264 alt = curses.ascii.alt 1265 self.assertEqual(alt('\n'), '\x8a') 1266 self.assertEqual(alt('A'), '\xc1') 1267 self.assertEqual(alt(ord('A')), 0xc1) 1268 1269 def test_unctrl(self): 1270 unctrl = curses.ascii.unctrl 1271 self.assertEqual(unctrl('a'), 'a') 1272 self.assertEqual(unctrl('A'), 'A') 1273 self.assertEqual(unctrl(';'), ';') 1274 self.assertEqual(unctrl(' '), ' ') 1275 self.assertEqual(unctrl('\x7f'), '^?') 1276 self.assertEqual(unctrl('\n'), '^J') 1277 self.assertEqual(unctrl('\0'), '^@') 1278 self.assertEqual(unctrl(ord('A')), 'A') 1279 self.assertEqual(unctrl(ord('\n')), '^J') 1280 # Meta-bit characters 1281 self.assertEqual(unctrl('\x8a'), '!^J') 1282 self.assertEqual(unctrl('\xc1'), '!A') 1283 self.assertEqual(unctrl(ord('\x8a')), '!^J') 1284 self.assertEqual(unctrl(ord('\xc1')), '!A') 1285 1286 1287def lorem_ipsum(win): 1288 text = [ 1289 'Lorem ipsum', 1290 'dolor sit amet,', 1291 'consectetur', 1292 'adipiscing elit,', 1293 'sed do eiusmod', 1294 'tempor incididunt', 1295 'ut labore et', 1296 'dolore magna', 1297 'aliqua.', 1298 ] 1299 maxy, maxx = win.getmaxyx() 1300 for y, line in enumerate(text[:maxy]): 1301 win.addstr(y, 0, line[:maxx - (y == maxy - 1)]) 1302 1303if __name__ == '__main__': 1304 unittest.main() 1305