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