blob: a9f7001d39f08d8428c5251f139f22d16b9afa92 [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)
929 return pair_limit
930
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200931 @requires_colors
932 def test_pair_content(self):
933 if not hasattr(curses, 'use_default_colors'):
934 self.assertEqual(curses.pair_content(0),
935 (curses.COLOR_WHITE, curses.COLOR_BLACK))
936 curses.pair_content(0)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200937 maxpair = self.get_pair_limit() - 1
938 if maxpair > 0:
939 curses.pair_content(maxpair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500940
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200941 for pair in self.bad_pairs():
942 self.assertRaises(ValueError, curses.pair_content, pair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500943
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200944 @requires_colors
945 def test_init_pair(self):
946 old = curses.pair_content(1)
947 curses.init_pair(1, *old)
948 self.addCleanup(curses.init_pair, 1, *old)
949
950 curses.init_pair(1, 0, 0)
951 self.assertEqual(curses.pair_content(1), (0, 0))
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200952 maxcolor = curses.COLORS - 1
953 curses.init_pair(1, maxcolor, 0)
954 self.assertEqual(curses.pair_content(1), (maxcolor, 0))
955 curses.init_pair(1, 0, maxcolor)
956 self.assertEqual(curses.pair_content(1), (0, maxcolor))
957 maxpair = self.get_pair_limit() - 1
958 if maxpair > 1:
959 curses.init_pair(maxpair, 0, 0)
960 self.assertEqual(curses.pair_content(maxpair), (0, 0))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200961
962 for pair in self.bad_pairs():
963 self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
964 for color in self.bad_colors2():
965 self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
966 self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
967
968 @requires_colors
969 def test_color_attrs(self):
970 for pair in 0, 1, 255:
971 attr = curses.color_pair(pair)
972 self.assertEqual(curses.pair_number(attr), pair, attr)
973 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
974 self.assertEqual(curses.color_pair(0), 0)
975 self.assertEqual(curses.pair_number(0), 0)
976
977 @requires_curses_func('use_default_colors')
978 @requires_colors
979 def test_use_default_colors(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200980 old = curses.pair_content(0)
981 try:
982 curses.use_default_colors()
983 except curses.error:
984 self.skipTest('cannot change color (use_default_colors() failed)')
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200985 self.assertEqual(curses.pair_content(0), (-1, -1))
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200986 self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)])
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500987
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300988 def test_keyname(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200989 # TODO: key_name()
990 self.assertEqual(curses.keyname(65), b'A')
991 self.assertEqual(curses.keyname(13), b'^M')
992 self.assertEqual(curses.keyname(127), b'^?')
993 self.assertEqual(curses.keyname(0), b'^@')
994 self.assertRaises(ValueError, curses.keyname, -1)
995 self.assertIsInstance(curses.keyname(256), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500996
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300997 @requires_curses_func('has_key')
998 def test_has_key(self):
999 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -05001000
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001001 @requires_curses_func('getmouse')
1002 def test_getmouse(self):
1003 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
1004 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +01001005 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001006 curses.mouseinterval(10)
1007 # just verify these don't cause errors
1008 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
1009 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -05001010
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001011 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001012 def test_userptr_without_set(self):
1013 w = curses.newwin(10, 10)
1014 p = curses.panel.new_panel(w)
1015 # try to access userptr() before calling set_userptr() -- segfaults
1016 with self.assertRaises(curses.panel.error,
1017 msg='userptr should fail since not set'):
1018 p.userptr()
1019
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001020 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001021 def test_userptr_memory_leak(self):
1022 w = curses.newwin(10, 10)
1023 p = curses.panel.new_panel(w)
1024 obj = object()
1025 nrefs = sys.getrefcount(obj)
1026 for i in range(100):
1027 p.set_userptr(obj)
1028
1029 p.set_userptr(None)
1030 self.assertEqual(sys.getrefcount(obj), nrefs,
1031 "set_userptr leaked references")
1032
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001033 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001034 def test_userptr_segfault(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001035 w = curses.newwin(10, 10)
1036 panel = curses.panel.new_panel(w)
Zachary Warebaf45c52014-10-17 13:59:18 -05001037 class A:
1038 def __del__(self):
1039 panel.set_userptr(None)
1040 panel.set_userptr(A())
1041 panel.set_userptr(None)
1042
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001043 @requires_curses_func('panel')
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001044 def test_new_curses_panel(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001045 w = curses.newwin(10, 10)
1046 panel = curses.panel.new_panel(w)
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001047 self.assertRaises(TypeError, type(panel))
1048
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001049 @requires_curses_func('is_term_resized')
1050 def test_is_term_resized(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001051 lines, cols = curses.LINES, curses.COLS
1052 self.assertIs(curses.is_term_resized(lines, cols), False)
1053 self.assertIs(curses.is_term_resized(lines-1, cols-1), True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001054
1055 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -05001056 def test_resize_term(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001057 curses.update_lines_cols()
1058 lines, cols = curses.LINES, curses.COLS
1059 new_lines = lines - 1
1060 new_cols = cols + 1
1061 curses.resize_term(new_lines, new_cols)
1062 self.assertEqual(curses.LINES, new_lines)
1063 self.assertEqual(curses.COLS, new_cols)
1064
1065 curses.resize_term(lines, cols)
1066 self.assertEqual(curses.LINES, lines)
1067 self.assertEqual(curses.COLS, cols)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001068
1069 @requires_curses_func('resizeterm')
1070 def test_resizeterm(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001071 curses.update_lines_cols()
Zachary Warebaf45c52014-10-17 13:59:18 -05001072 lines, cols = curses.LINES, curses.COLS
1073 new_lines = lines - 1
1074 new_cols = cols + 1
1075 curses.resizeterm(new_lines, new_cols)
Zachary Warebaf45c52014-10-17 13:59:18 -05001076 self.assertEqual(curses.LINES, new_lines)
1077 self.assertEqual(curses.COLS, new_cols)
1078
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001079 curses.resizeterm(lines, cols)
1080 self.assertEqual(curses.LINES, lines)
1081 self.assertEqual(curses.COLS, cols)
1082
1083 def test_ungetch(self):
1084 curses.ungetch(b'A')
1085 self.assertEqual(self.stdscr.getkey(), 'A')
1086 curses.ungetch('B')
1087 self.assertEqual(self.stdscr.getkey(), 'B')
1088 curses.ungetch(67)
1089 self.assertEqual(self.stdscr.getkey(), 'C')
1090
Zachary Warebaf45c52014-10-17 13:59:18 -05001091 def test_issue6243(self):
1092 curses.ungetch(1025)
1093 self.stdscr.getkey()
1094
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001095 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +02001096 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
1097 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -05001098 def test_unget_wch(self):
1099 stdscr = self.stdscr
1100 encoding = stdscr.encoding
1101 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
1102 try:
1103 ch.encode(encoding)
1104 except UnicodeEncodeError:
1105 continue
1106 try:
1107 curses.unget_wch(ch)
1108 except Exception as err:
1109 self.fail("unget_wch(%a) failed with encoding %s: %s"
1110 % (ch, stdscr.encoding, err))
1111 read = stdscr.get_wch()
1112 self.assertEqual(read, ch)
1113
1114 code = ord(ch)
1115 curses.unget_wch(code)
1116 read = stdscr.get_wch()
1117 self.assertEqual(read, ch)
1118
Zachary Warebaf45c52014-10-17 13:59:18 -05001119 def test_encoding(self):
1120 stdscr = self.stdscr
1121 import codecs
1122 encoding = stdscr.encoding
1123 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -05001124 with self.assertRaises(TypeError):
1125 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -05001126 stdscr.encoding = encoding
1127 with self.assertRaises(TypeError):
1128 del stdscr.encoding
1129
1130 def test_issue21088(self):
1131 stdscr = self.stdscr
1132 #
1133 # http://bugs.python.org/issue21088
1134 #
1135 # the bug:
1136 # when converting curses.window.addch to Argument Clinic
1137 # the first two parameters were switched.
1138
1139 # if someday we can represent the signature of addch
1140 # we will need to rewrite this test.
1141 try:
1142 signature = inspect.signature(stdscr.addch)
1143 self.assertFalse(signature)
1144 except ValueError:
1145 # not generating a signature is fine.
1146 pass
1147
1148 # So. No signature for addch.
1149 # But Argument Clinic gave us a human-readable equivalent
1150 # as the first line of the docstring. So we parse that,
1151 # and ensure that the parameters appear in the correct order.
1152 # Since this is parsing output from Argument Clinic, we can
1153 # be reasonably certain the generated parsing code will be
1154 # correct too.
1155 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001156 self.assertIn("[y, x,]", human_readable_signature)
1157
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001158 @requires_curses_window_meth('resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001159 def test_issue13051(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001160 win = curses.newwin(5, 15, 2, 5)
1161 box = curses.textpad.Textbox(win, insert_mode=True)
1162 lines, cols = win.getmaxyx()
1163 win.resize(lines-2, cols-2)
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001164 # this may cause infinite recursion, leading to a RuntimeError
1165 box._insert_printable_char('a')
1166
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001167
1168class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -05001169
Steve Dowerd2bc3892015-04-15 18:06:05 -04001170 def test_update_lines_cols(self):
Steve Dowerd2bc3892015-04-15 18:06:05 -04001171 curses.update_lines_cols()
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001172 lines, cols = curses.LINES, curses.COLS
1173 curses.LINES = curses.COLS = 0
1174 curses.update_lines_cols()
1175 self.assertEqual(curses.LINES, lines)
1176 self.assertEqual(curses.COLS, cols)
Steve Dowerd2bc3892015-04-15 18:06:05 -04001177
Serhiy Storchakab232df92018-10-30 13:22:42 +02001178 @requires_curses_func('ncurses_version')
1179 def test_ncurses_version(self):
1180 v = curses.ncurses_version
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +02001181 if verbose:
1182 print(f'ncurses_version = {curses.ncurses_version}', flush=True)
Serhiy Storchakab232df92018-10-30 13:22:42 +02001183 self.assertIsInstance(v[:], tuple)
1184 self.assertEqual(len(v), 3)
1185 self.assertIsInstance(v[0], int)
1186 self.assertIsInstance(v[1], int)
1187 self.assertIsInstance(v[2], int)
1188 self.assertIsInstance(v.major, int)
1189 self.assertIsInstance(v.minor, int)
1190 self.assertIsInstance(v.patch, int)
1191 self.assertEqual(v[0], v.major)
1192 self.assertEqual(v[1], v.minor)
1193 self.assertEqual(v[2], v.patch)
1194 self.assertGreaterEqual(v.major, 0)
1195 self.assertGreaterEqual(v.minor, 0)
1196 self.assertGreaterEqual(v.patch, 0)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001197
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001198 def test_has_extended_color_support(self):
1199 r = curses.has_extended_color_support()
1200 self.assertIsInstance(r, bool)
1201
1202
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001203class TestAscii(unittest.TestCase):
1204
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001205 def test_controlnames(self):
1206 for name in curses.ascii.controlnames:
1207 self.assertTrue(hasattr(curses.ascii, name), name)
1208
1209 def test_ctypes(self):
1210 def check(func, expected):
1211 with self.subTest(ch=c, func=func):
1212 self.assertEqual(func(i), expected)
1213 self.assertEqual(func(c), expected)
1214
1215 for i in range(256):
1216 c = chr(i)
1217 b = bytes([i])
1218 check(curses.ascii.isalnum, b.isalnum())
1219 check(curses.ascii.isalpha, b.isalpha())
1220 check(curses.ascii.isdigit, b.isdigit())
1221 check(curses.ascii.islower, b.islower())
1222 check(curses.ascii.isspace, b.isspace())
1223 check(curses.ascii.isupper, b.isupper())
1224
1225 check(curses.ascii.isascii, i < 128)
1226 check(curses.ascii.ismeta, i >= 128)
1227 check(curses.ascii.isctrl, i < 32)
1228 check(curses.ascii.iscntrl, i < 32 or i == 127)
1229 check(curses.ascii.isblank, c in ' \t')
1230 check(curses.ascii.isgraph, 32 < i <= 126)
1231 check(curses.ascii.isprint, 32 <= i <= 126)
1232 check(curses.ascii.ispunct, c in string.punctuation)
1233 check(curses.ascii.isxdigit, c in string.hexdigits)
1234
Serhiy Storchaka283de2b2016-12-28 10:04:27 +02001235 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
1236 self.assertFalse(curses.ascii.isalnum(i))
1237 self.assertFalse(curses.ascii.isalpha(i))
1238 self.assertFalse(curses.ascii.isdigit(i))
1239 self.assertFalse(curses.ascii.islower(i))
1240 self.assertFalse(curses.ascii.isspace(i))
1241 self.assertFalse(curses.ascii.isupper(i))
1242
1243 self.assertFalse(curses.ascii.isascii(i))
1244 self.assertFalse(curses.ascii.isctrl(i))
1245 self.assertFalse(curses.ascii.iscntrl(i))
1246 self.assertFalse(curses.ascii.isblank(i))
1247 self.assertFalse(curses.ascii.isgraph(i))
1248 self.assertFalse(curses.ascii.isprint(i))
1249 self.assertFalse(curses.ascii.ispunct(i))
1250 self.assertFalse(curses.ascii.isxdigit(i))
1251
1252 self.assertFalse(curses.ascii.ismeta(-1))
1253
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001254 def test_ascii(self):
1255 ascii = curses.ascii.ascii
1256 self.assertEqual(ascii('\xc1'), 'A')
1257 self.assertEqual(ascii('A'), 'A')
1258 self.assertEqual(ascii(ord('\xc1')), ord('A'))
1259
1260 def test_ctrl(self):
1261 ctrl = curses.ascii.ctrl
1262 self.assertEqual(ctrl('J'), '\n')
1263 self.assertEqual(ctrl('\n'), '\n')
1264 self.assertEqual(ctrl('@'), '\0')
1265 self.assertEqual(ctrl(ord('J')), ord('\n'))
1266
1267 def test_alt(self):
1268 alt = curses.ascii.alt
1269 self.assertEqual(alt('\n'), '\x8a')
1270 self.assertEqual(alt('A'), '\xc1')
1271 self.assertEqual(alt(ord('A')), 0xc1)
1272
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001273 def test_unctrl(self):
1274 unctrl = curses.ascii.unctrl
1275 self.assertEqual(unctrl('a'), 'a')
1276 self.assertEqual(unctrl('A'), 'A')
1277 self.assertEqual(unctrl(';'), ';')
1278 self.assertEqual(unctrl(' '), ' ')
1279 self.assertEqual(unctrl('\x7f'), '^?')
1280 self.assertEqual(unctrl('\n'), '^J')
1281 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001282 self.assertEqual(unctrl(ord('A')), 'A')
1283 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001284 # Meta-bit characters
1285 self.assertEqual(unctrl('\x8a'), '!^J')
1286 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001287 self.assertEqual(unctrl(ord('\x8a')), '!^J')
1288 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001289
1290
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001291def lorem_ipsum(win):
1292 text = [
1293 'Lorem ipsum',
1294 'dolor sit amet,',
1295 'consectetur',
1296 'adipiscing elit,',
1297 'sed do eiusmod',
1298 'tempor incididunt',
1299 'ut labore et',
1300 'dolore magna',
1301 'aliqua.',
1302 ]
1303 maxy, maxx = win.getmaxyx()
1304 for y, line in enumerate(text[:maxy]):
1305 win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
1306
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001307if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -05001308 unittest.main()