blob: f6bd27d02e43158d16355fae621795d340c74553 [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
Hai Shia7f5d932020-08-04 00:41:24 +08009from test.support import requires, verbose, SaveSignals
10from 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)
569 # TODO: Return bool instead of 1/0
570 self.assertTrue(win.enclose(2, 5))
571 self.assertFalse(win.enclose(1, 5))
572 self.assertFalse(win.enclose(2, 4))
573 self.assertTrue(win.enclose(6, 19))
574 self.assertFalse(win.enclose(7, 19))
575 self.assertFalse(win.enclose(6, 20))
576
577 def test_putwin(self):
578 win = curses.newwin(5, 12, 1, 2)
579 win.addstr(2, 1, 'Lorem ipsum')
580 with tempfile.TemporaryFile() as f:
581 win.putwin(f)
582 del win
583 f.seek(0)
584 win = curses.getwin(f)
585 self.assertEqual(win.getbegyx(), (1, 2))
586 self.assertEqual(win.getmaxyx(), (5, 12))
587 self.assertEqual(win.instr(2, 0), b' Lorem ipsum')
588
589 def test_borders_and_lines(self):
590 win = curses.newwin(5, 10, 5, 2)
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:
933 curses.init_pair(pair_limit, 0, 0)
934 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
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001050 @requires_curses_func('panel')
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001051 def test_new_curses_panel(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001052 w = curses.newwin(10, 10)
1053 panel = curses.panel.new_panel(w)
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001054 self.assertRaises(TypeError, type(panel))
1055
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001056 @requires_curses_func('is_term_resized')
1057 def test_is_term_resized(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001058 lines, cols = curses.LINES, curses.COLS
1059 self.assertIs(curses.is_term_resized(lines, cols), False)
1060 self.assertIs(curses.is_term_resized(lines-1, cols-1), True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001061
1062 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -05001063 def test_resize_term(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001064 curses.update_lines_cols()
1065 lines, cols = curses.LINES, curses.COLS
1066 new_lines = lines - 1
1067 new_cols = cols + 1
1068 curses.resize_term(new_lines, new_cols)
1069 self.assertEqual(curses.LINES, new_lines)
1070 self.assertEqual(curses.COLS, new_cols)
1071
1072 curses.resize_term(lines, cols)
1073 self.assertEqual(curses.LINES, lines)
1074 self.assertEqual(curses.COLS, cols)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001075
1076 @requires_curses_func('resizeterm')
1077 def test_resizeterm(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001078 curses.update_lines_cols()
Zachary Warebaf45c52014-10-17 13:59:18 -05001079 lines, cols = curses.LINES, curses.COLS
1080 new_lines = lines - 1
1081 new_cols = cols + 1
1082 curses.resizeterm(new_lines, new_cols)
Zachary Warebaf45c52014-10-17 13:59:18 -05001083 self.assertEqual(curses.LINES, new_lines)
1084 self.assertEqual(curses.COLS, new_cols)
1085
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001086 curses.resizeterm(lines, cols)
1087 self.assertEqual(curses.LINES, lines)
1088 self.assertEqual(curses.COLS, cols)
1089
1090 def test_ungetch(self):
1091 curses.ungetch(b'A')
1092 self.assertEqual(self.stdscr.getkey(), 'A')
1093 curses.ungetch('B')
1094 self.assertEqual(self.stdscr.getkey(), 'B')
1095 curses.ungetch(67)
1096 self.assertEqual(self.stdscr.getkey(), 'C')
1097
Zachary Warebaf45c52014-10-17 13:59:18 -05001098 def test_issue6243(self):
1099 curses.ungetch(1025)
1100 self.stdscr.getkey()
1101
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001102 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +02001103 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
1104 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -05001105 def test_unget_wch(self):
1106 stdscr = self.stdscr
1107 encoding = stdscr.encoding
1108 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
1109 try:
1110 ch.encode(encoding)
1111 except UnicodeEncodeError:
1112 continue
1113 try:
1114 curses.unget_wch(ch)
1115 except Exception as err:
1116 self.fail("unget_wch(%a) failed with encoding %s: %s"
1117 % (ch, stdscr.encoding, err))
1118 read = stdscr.get_wch()
1119 self.assertEqual(read, ch)
1120
1121 code = ord(ch)
1122 curses.unget_wch(code)
1123 read = stdscr.get_wch()
1124 self.assertEqual(read, ch)
1125
Zachary Warebaf45c52014-10-17 13:59:18 -05001126 def test_encoding(self):
1127 stdscr = self.stdscr
1128 import codecs
1129 encoding = stdscr.encoding
1130 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -05001131 with self.assertRaises(TypeError):
1132 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -05001133 stdscr.encoding = encoding
1134 with self.assertRaises(TypeError):
1135 del stdscr.encoding
1136
1137 def test_issue21088(self):
1138 stdscr = self.stdscr
1139 #
1140 # http://bugs.python.org/issue21088
1141 #
1142 # the bug:
1143 # when converting curses.window.addch to Argument Clinic
1144 # the first two parameters were switched.
1145
1146 # if someday we can represent the signature of addch
1147 # we will need to rewrite this test.
1148 try:
1149 signature = inspect.signature(stdscr.addch)
1150 self.assertFalse(signature)
1151 except ValueError:
1152 # not generating a signature is fine.
1153 pass
1154
1155 # So. No signature for addch.
1156 # But Argument Clinic gave us a human-readable equivalent
1157 # as the first line of the docstring. So we parse that,
1158 # and ensure that the parameters appear in the correct order.
1159 # Since this is parsing output from Argument Clinic, we can
1160 # be reasonably certain the generated parsing code will be
1161 # correct too.
1162 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001163 self.assertIn("[y, x,]", human_readable_signature)
1164
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001165 @requires_curses_window_meth('resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001166 def test_issue13051(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001167 win = curses.newwin(5, 15, 2, 5)
1168 box = curses.textpad.Textbox(win, insert_mode=True)
1169 lines, cols = win.getmaxyx()
1170 win.resize(lines-2, cols-2)
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001171 # this may cause infinite recursion, leading to a RuntimeError
1172 box._insert_printable_char('a')
1173
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001174
1175class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -05001176
Steve Dowerd2bc3892015-04-15 18:06:05 -04001177 def test_update_lines_cols(self):
Steve Dowerd2bc3892015-04-15 18:06:05 -04001178 curses.update_lines_cols()
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001179 lines, cols = curses.LINES, curses.COLS
1180 curses.LINES = curses.COLS = 0
1181 curses.update_lines_cols()
1182 self.assertEqual(curses.LINES, lines)
1183 self.assertEqual(curses.COLS, cols)
Steve Dowerd2bc3892015-04-15 18:06:05 -04001184
Serhiy Storchakab232df92018-10-30 13:22:42 +02001185 @requires_curses_func('ncurses_version')
1186 def test_ncurses_version(self):
1187 v = curses.ncurses_version
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +02001188 if verbose:
1189 print(f'ncurses_version = {curses.ncurses_version}', flush=True)
Serhiy Storchakab232df92018-10-30 13:22:42 +02001190 self.assertIsInstance(v[:], tuple)
1191 self.assertEqual(len(v), 3)
1192 self.assertIsInstance(v[0], int)
1193 self.assertIsInstance(v[1], int)
1194 self.assertIsInstance(v[2], int)
1195 self.assertIsInstance(v.major, int)
1196 self.assertIsInstance(v.minor, int)
1197 self.assertIsInstance(v.patch, int)
1198 self.assertEqual(v[0], v.major)
1199 self.assertEqual(v[1], v.minor)
1200 self.assertEqual(v[2], v.patch)
1201 self.assertGreaterEqual(v.major, 0)
1202 self.assertGreaterEqual(v.minor, 0)
1203 self.assertGreaterEqual(v.patch, 0)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001204
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001205 def test_has_extended_color_support(self):
1206 r = curses.has_extended_color_support()
1207 self.assertIsInstance(r, bool)
1208
1209
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001210class TestAscii(unittest.TestCase):
1211
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001212 def test_controlnames(self):
1213 for name in curses.ascii.controlnames:
1214 self.assertTrue(hasattr(curses.ascii, name), name)
1215
1216 def test_ctypes(self):
1217 def check(func, expected):
1218 with self.subTest(ch=c, func=func):
1219 self.assertEqual(func(i), expected)
1220 self.assertEqual(func(c), expected)
1221
1222 for i in range(256):
1223 c = chr(i)
1224 b = bytes([i])
1225 check(curses.ascii.isalnum, b.isalnum())
1226 check(curses.ascii.isalpha, b.isalpha())
1227 check(curses.ascii.isdigit, b.isdigit())
1228 check(curses.ascii.islower, b.islower())
1229 check(curses.ascii.isspace, b.isspace())
1230 check(curses.ascii.isupper, b.isupper())
1231
1232 check(curses.ascii.isascii, i < 128)
1233 check(curses.ascii.ismeta, i >= 128)
1234 check(curses.ascii.isctrl, i < 32)
1235 check(curses.ascii.iscntrl, i < 32 or i == 127)
1236 check(curses.ascii.isblank, c in ' \t')
1237 check(curses.ascii.isgraph, 32 < i <= 126)
1238 check(curses.ascii.isprint, 32 <= i <= 126)
1239 check(curses.ascii.ispunct, c in string.punctuation)
1240 check(curses.ascii.isxdigit, c in string.hexdigits)
1241
Serhiy Storchaka283de2b2016-12-28 10:04:27 +02001242 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
1243 self.assertFalse(curses.ascii.isalnum(i))
1244 self.assertFalse(curses.ascii.isalpha(i))
1245 self.assertFalse(curses.ascii.isdigit(i))
1246 self.assertFalse(curses.ascii.islower(i))
1247 self.assertFalse(curses.ascii.isspace(i))
1248 self.assertFalse(curses.ascii.isupper(i))
1249
1250 self.assertFalse(curses.ascii.isascii(i))
1251 self.assertFalse(curses.ascii.isctrl(i))
1252 self.assertFalse(curses.ascii.iscntrl(i))
1253 self.assertFalse(curses.ascii.isblank(i))
1254 self.assertFalse(curses.ascii.isgraph(i))
1255 self.assertFalse(curses.ascii.isprint(i))
1256 self.assertFalse(curses.ascii.ispunct(i))
1257 self.assertFalse(curses.ascii.isxdigit(i))
1258
1259 self.assertFalse(curses.ascii.ismeta(-1))
1260
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001261 def test_ascii(self):
1262 ascii = curses.ascii.ascii
1263 self.assertEqual(ascii('\xc1'), 'A')
1264 self.assertEqual(ascii('A'), 'A')
1265 self.assertEqual(ascii(ord('\xc1')), ord('A'))
1266
1267 def test_ctrl(self):
1268 ctrl = curses.ascii.ctrl
1269 self.assertEqual(ctrl('J'), '\n')
1270 self.assertEqual(ctrl('\n'), '\n')
1271 self.assertEqual(ctrl('@'), '\0')
1272 self.assertEqual(ctrl(ord('J')), ord('\n'))
1273
1274 def test_alt(self):
1275 alt = curses.ascii.alt
1276 self.assertEqual(alt('\n'), '\x8a')
1277 self.assertEqual(alt('A'), '\xc1')
1278 self.assertEqual(alt(ord('A')), 0xc1)
1279
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001280 def test_unctrl(self):
1281 unctrl = curses.ascii.unctrl
1282 self.assertEqual(unctrl('a'), 'a')
1283 self.assertEqual(unctrl('A'), 'A')
1284 self.assertEqual(unctrl(';'), ';')
1285 self.assertEqual(unctrl(' '), ' ')
1286 self.assertEqual(unctrl('\x7f'), '^?')
1287 self.assertEqual(unctrl('\n'), '^J')
1288 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001289 self.assertEqual(unctrl(ord('A')), 'A')
1290 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001291 # Meta-bit characters
1292 self.assertEqual(unctrl('\x8a'), '!^J')
1293 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001294 self.assertEqual(unctrl(ord('\x8a')), '!^J')
1295 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001296
1297
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001298def lorem_ipsum(win):
1299 text = [
1300 'Lorem ipsum',
1301 'dolor sit amet,',
1302 'consectetur',
1303 'adipiscing elit,',
1304 'sed do eiusmod',
1305 'tempor incididunt',
1306 'ut labore et',
1307 'dolore magna',
1308 'aliqua.',
1309 ]
1310 maxy, maxx = win.getmaxyx()
1311 for y, line in enumerate(text[:maxy]):
1312 win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
1313
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001314if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -05001315 unittest.main()