blob: d3c152c42cf62f5c61ba7eb4e44728546c3ca672 [file] [log] [blame]
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001import functools
2import inspect
Zachary Warebaf45c52014-10-17 13:59:18 -05003import os
Serhiy Storchaka514f9732016-06-18 22:08:11 +03004import string
Zachary Warebaf45c52014-10-17 13:59:18 -05005import sys
6import tempfile
7import unittest
8
Erlend Egeberg Aasland0a3452e2021-06-24 01:46:25 +02009from test.support import (requires, verbose, SaveSignals, cpython_only,
10 check_disallow_instantiation)
Hai Shia7f5d932020-08-04 00:41:24 +080011from test.support.import_helper import import_module
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000012
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.
Neal Norwitz9f39f682006-01-06 04:18:21 +000016requires('curses')
17
R. David Murraya21e4ca2009-03-31 23:16:50 +000018# If either of these don't exist, skip the tests.
19curses = import_module('curses')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030020import_module('curses.ascii')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +020021import_module('curses.textpad')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +020022try:
23 import curses.panel
24except ImportError:
25 pass
R. David Murraya21e4ca2009-03-31 23:16:50 +000026
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030027def requires_curses_func(name):
28 return unittest.skipUnless(hasattr(curses, name),
29 'requires curses.%s' % name)
Mark Dickinson945e2422010-02-21 13:42:03 +000030
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +020031def 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
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020042def 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
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030051term = os.environ.get('TERM')
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +020052SHORT_MAX = 0x7fff
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030053
54# If newterm was supported we could use it instead of initscr and not exit
55@unittest.skipIf(not term or term == 'unknown',
Zachary Warebaf45c52014-10-17 13:59:18 -050056 "$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):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030060
Zachary Warebaf45c52014-10-17 13:59:18 -050061 @classmethod
62 def setUpClass(cls):
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020063 if verbose:
64 print(f'TERM={term}', file=sys.stderr, flush=True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030065 # testing setupterm() inside initscr/endwin
66 # causes terminal breakage
Serhiy Storchaka607501a2021-01-02 19:35:15 +020067 stdout_fd = sys.__stdout__.fileno()
68 curses.setupterm(fd=stdout_fd)
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000069
Zachary Warebaf45c52014-10-17 13:59:18 -050070 def setUp(self):
Serhiy Storchaka607501a2021-01-02 19:35:15 +020071 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.
Serhiy Storchakab6fc0c42021-01-04 12:30:20 +020091 tmp = open('/dev/tty', 'wb', buffering=0)
Serhiy Storchaka607501a2021-01-02 19:35:15 +020092 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
Victor Stinner19f68302017-10-31 03:14:01 -0700103 self.save_signals = SaveSignals()
104 self.save_signals.save()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200105 self.addCleanup(self.save_signals.restore)
106 if verbose and self.output is not None:
Zachary Warebaf45c52014-10-17 13:59:18 -0500107 # just to make the test output a little more readable
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200108 sys.stderr.flush()
109 sys.stdout.flush()
110 print(file=self.output, flush=True)
Zachary Warebaf45c52014-10-17 13:59:18 -0500111 self.stdscr = curses.initscr()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200112 if self.isatty:
113 curses.savetty()
114 self.addCleanup(curses.endwin)
115 self.addCleanup(curses.resetty)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200116 self.stdscr.erase()
Zachary Warebaf45c52014-10-17 13:59:18 -0500117
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200118 @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):
Zachary Warebaf45c52014-10-17 13:59:18 -0500184 stdscr = self.stdscr
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200185 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))
Zachary Warebaf45c52014-10-17 13:59:18 -0500190
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200191 win.cursyncup()
192 self.assertEqual(stdscr.getyx(), (4, 9))
Zachary Warebaf45c52014-10-17 13:59:18 -0500193
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200194 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500207
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200208 # 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500215
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200216 # 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500224
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200225 # 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
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200244 encoding = stdscr.encoding
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200245 # addch()
246 stdscr.refresh()
247 stdscr.move(0, 0)
248 stdscr.addch('A')
249 stdscr.addch(b'A')
250 stdscr.addch(65)
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200251 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))
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200259 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)
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200270 with self.assertRaises((UnicodeEncodeError, OverflowError)):
271 stdscr.echochar('\u20ac')
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200272 stdscr.echochar('A', curses.A_BOLD)
273 self.assertIs(stdscr.is_wintouched(), False)
274
275 def test_output_string(self):
276 stdscr = self.stdscr
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200277 encoding = stdscr.encoding
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200278 # 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')
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200284 s = 'àßçđ'
285 try:
286 func(s)
287 except UnicodeEncodeError:
288 self.assertRaises(UnicodeEncodeError, s.encode, encoding)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200289 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)
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200299 s = '\u0661\u0662\u0663\u0664'
300 try:
301 func(s, 3)
302 except UnicodeEncodeError:
303 self.assertRaises(UnicodeEncodeError, s.encode, encoding)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200304 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
Serhiy Storchakaa1e9a1e2021-01-31 23:21:55 +0200493 self.assertIn(win.getbkgd(), (0, 32))
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200494
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)
Serhiy Storchakab1dc1aa2021-04-05 16:50:24 +0300570 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)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200576
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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500591 win.border('|', '!', '-', '_',
592 '+', '\\', '#', '/')
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200593 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()
Zachary Warebaf45c52014-10-17 13:59:18 -0500614
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200615 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'~~~~~~~~')
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200619 win.box(b':', b'~')
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200620 win.box(65, 67)
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200621 self.assertRaises(TypeError, win.box, 65, 66, 67)
622 self.assertRaises(TypeError, win.box, 65)
623 win.box()
624
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200625 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500633
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200634 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])
Zachary Warebaf45c52014-10-17 13:59:18 -0500644
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200645 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500658
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200659 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500667
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200668 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500672
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200673 self.assertIsInstance(curses.tigetnum('cols'), int)
674 self.assertEqual(curses.tigetnum('hc'), -2)
675 self.assertEqual(curses.tigetnum('cr'), -2)
Zachary Warebaf45c52014-10-17 13:59:18 -0500676
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200677 self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None)))
678 self.assertIsNone(curses.tigetstr('hc'))
679 self.assertIsNone(curses.tigetstr('cols'))
Zachary Warebaf45c52014-10-17 13:59:18 -0500680
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200681 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)
Zachary Warebaf45c52014-10-17 13:59:18 -0500689
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200690 curses.putp(b'abc\n')
Zachary Warebaf45c52014-10-17 13:59:18 -0500691
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200692 def test_misc_module_funcs(self):
Zachary Warebaf45c52014-10-17 13:59:18 -0500693 curses.delay_output(1)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200694 curses.flushinp()
Zachary Warebaf45c52014-10-17 13:59:18 -0500695
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200696 curses.doupdate()
697 self.assertIs(curses.isendwin(), False)
698
Zachary Warebaf45c52014-10-17 13:59:18 -0500699 curses.napms(100)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200700
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
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200752 if self.isatty:
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200753 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)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700840 curses.set_escdelay(25)
841 self.assertEqual(curses.get_escdelay(), 25)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200842 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)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700848 curses.set_tabsize(4)
849 self.assertEqual(curses.get_tabsize(), 4)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200850 curses.set_tabsize(tabsize)
Zachary Warebaf45c52014-10-17 13:59:18 -0500851
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200852 @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))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200859
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
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200869 def test_has_colors(self):
870 self.assertIsInstance(curses.has_colors(), bool)
871 self.assertIsInstance(curses.can_change_color(), bool)
872
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200873 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
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200881 @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)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200885 maxcolor = curses.COLORS - 1
886 curses.color_content(maxcolor)
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200887
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):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200893 if not curses.can_change_color():
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200894 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
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200907 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))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200913
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
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200921 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)
Pablo Galindoab2d4812021-02-15 21:35:48 +0000929 # 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:
Pablo Galindod0204962021-02-15 22:15:49 +0000933 curses.init_pair(pair_limit - 1, 0, 0)
Pablo Galindoab2d4812021-02-15 21:35:48 +0000934 except ValueError:
935 pair_limit = curses.COLOR_PAIRS
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200936 return pair_limit
937
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200938 @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)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200944 maxpair = self.get_pair_limit() - 1
945 if maxpair > 0:
946 curses.pair_content(maxpair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500947
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200948 for pair in self.bad_pairs():
949 self.assertRaises(ValueError, curses.pair_content, pair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500950
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200951 @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))
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200959 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))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200968
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):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200987 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)')
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200992 self.assertEqual(curses.pair_content(0), (-1, -1))
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200993 self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)])
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500994
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300995 def test_keyname(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200996 # 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)
Zachary Warebaf45c52014-10-17 13:59:18 -05001003
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001004 @requires_curses_func('has_key')
1005 def test_has_key(self):
1006 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -05001007
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001008 @requires_curses_func('getmouse')
1009 def test_getmouse(self):
1010 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
1011 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +01001012 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001013 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()
Zachary Warebaf45c52014-10-17 13:59:18 -05001017
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001018 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001019 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
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001027 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001028 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
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001040 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001041 def test_userptr_segfault(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001042 w = curses.newwin(10, 10)
1043 panel = curses.panel.new_panel(w)
Zachary Warebaf45c52014-10-17 13:59:18 -05001044 class A:
1045 def __del__(self):
1046 panel.set_userptr(None)
1047 panel.set_userptr(A())
1048 panel.set_userptr(None)
1049
Erlend Egeberg Aasland9746cda2021-04-30 16:04:57 +02001050 @cpython_only
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001051 @requires_curses_func('panel')
Erlend Egeberg Aasland9746cda2021-04-30 16:04:57 +02001052 def test_disallow_instantiation(self):
1053 # Ensure that the type disallows instantiation (bpo-43916)
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001054 w = curses.newwin(10, 10)
1055 panel = curses.panel.new_panel(w)
Erlend Egeberg Aasland0a3452e2021-06-24 01:46:25 +02001056 check_disallow_instantiation(self, type(panel))
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001057
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001058 @requires_curses_func('is_term_resized')
1059 def test_is_term_resized(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001060 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)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001063
1064 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -05001065 def test_resize_term(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001066 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)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001077
1078 @requires_curses_func('resizeterm')
1079 def test_resizeterm(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001080 curses.update_lines_cols()
Zachary Warebaf45c52014-10-17 13:59:18 -05001081 lines, cols = curses.LINES, curses.COLS
1082 new_lines = lines - 1
1083 new_cols = cols + 1
1084 curses.resizeterm(new_lines, new_cols)
Zachary Warebaf45c52014-10-17 13:59:18 -05001085 self.assertEqual(curses.LINES, new_lines)
1086 self.assertEqual(curses.COLS, new_cols)
1087
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001088 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
Zachary Warebaf45c52014-10-17 13:59:18 -05001100 def test_issue6243(self):
1101 curses.ungetch(1025)
1102 self.stdscr.getkey()
1103
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001104 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +02001105 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
1106 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -05001107 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
Zachary Warebaf45c52014-10-17 13:59:18 -05001128 def test_encoding(self):
1129 stdscr = self.stdscr
1130 import codecs
1131 encoding = stdscr.encoding
1132 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -05001133 with self.assertRaises(TypeError):
1134 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -05001135 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]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001165 self.assertIn("[y, x,]", human_readable_signature)
1166
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001167 @requires_curses_window_meth('resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001168 def test_issue13051(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001169 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)
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001173 # this may cause infinite recursion, leading to a RuntimeError
1174 box._insert_printable_char('a')
1175
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001176
1177class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -05001178
Michael Feltc8b57382021-03-29 21:06:24 +02001179 @requires_curses_func('update_lines_cols')
Steve Dowerd2bc3892015-04-15 18:06:05 -04001180 def test_update_lines_cols(self):
Steve Dowerd2bc3892015-04-15 18:06:05 -04001181 curses.update_lines_cols()
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001182 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)
Steve Dowerd2bc3892015-04-15 18:06:05 -04001187
Serhiy Storchakab232df92018-10-30 13:22:42 +02001188 @requires_curses_func('ncurses_version')
1189 def test_ncurses_version(self):
1190 v = curses.ncurses_version
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +02001191 if verbose:
1192 print(f'ncurses_version = {curses.ncurses_version}', flush=True)
Serhiy Storchakab232df92018-10-30 13:22:42 +02001193 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)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001207
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001208 def test_has_extended_color_support(self):
1209 r = curses.has_extended_color_support()
1210 self.assertIsInstance(r, bool)
1211
1212
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001213class TestAscii(unittest.TestCase):
1214
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001215 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
Serhiy Storchaka283de2b2016-12-28 10:04:27 +02001245 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
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001264 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
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001283 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'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001292 self.assertEqual(unctrl(ord('A')), 'A')
1293 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001294 # Meta-bit characters
1295 self.assertEqual(unctrl('\x8a'), '!^J')
1296 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001297 self.assertEqual(unctrl(ord('\x8a')), '!^J')
1298 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001299
1300
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001301def 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
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001317if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -05001318 unittest.main()