1 import functools
2 import inspect
3 import os
4 import string
5 import sys
6 import tempfile
7 import unittest
8
9 from test.support import (requires, verbose, SaveSignals, cpython_only,
10 check_disallow_instantiation)
11 from 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.
16 requires('curses')
17
18 # If either of these don't exist, skip the tests.
19 curses = import_module('curses')
20 import_module('curses.ascii')
21 import_module('curses.textpad')
22 try:
23 import curses.panel
24 except ImportError:
25 pass
26
27 def requires_curses_func(name):
28 return unittest.skipUnless(hasattr(curses, name),
29 'requires curses.%s' % name)
30
31 def 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
42 def 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
51 term = os.environ.get('TERM')
52 SHORT_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")
59 class ESC[4;38;5;81mTestCurses(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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 ESC[4;38;5;81mA:
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
1182 class ESC[4;38;5;81mMiscTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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
1218 class ESC[4;38;5;81mTestAscii(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
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
1306 def 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
1322 if __name__ == '__main__':
1323 unittest.main()