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