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