blob: 29286bce99e893691237c5c450d356612b938379 [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
243 # addch()
244 stdscr.refresh()
245 stdscr.move(0, 0)
246 stdscr.addch('A')
247 stdscr.addch(b'A')
248 stdscr.addch(65)
249 stdscr.addch('\u20ac')
250 stdscr.addch('A', curses.A_BOLD)
251 stdscr.addch(1, 2, 'A')
252 stdscr.addch(2, 3, 'A', curses.A_BOLD)
253 self.assertIs(stdscr.is_wintouched(), True)
254
255 # echochar()
256 stdscr.refresh()
257 stdscr.move(0, 0)
258 stdscr.echochar('A')
259 stdscr.echochar(b'A')
260 stdscr.echochar(65)
261 self.assertRaises(OverflowError, stdscr.echochar, '\u20ac')
262 stdscr.echochar('A', curses.A_BOLD)
263 self.assertIs(stdscr.is_wintouched(), False)
264
265 def test_output_string(self):
266 stdscr = self.stdscr
267 # addstr()/insstr()
268 for func in [stdscr.addstr, stdscr.insstr]:
269 with self.subTest(func.__qualname__):
270 stdscr.move(0, 0)
271 func('abcd')
272 func(b'abcd')
273 func('àßçđ')
274 func('abcd', curses.A_BOLD)
275 func(1, 2, 'abcd')
276 func(2, 3, 'abcd', curses.A_BOLD)
277
278 # addnstr()/insnstr()
279 for func in [stdscr.addnstr, stdscr.insnstr]:
280 with self.subTest(func.__qualname__):
281 stdscr.move(0, 0)
282 func('1234', 3)
283 func(b'1234', 3)
284 func('\u0661\u0662\u0663\u0664', 3)
285 func('1234', 5)
286 func('1234', 3, curses.A_BOLD)
287 func(1, 2, '1234', 3)
288 func(2, 3, '1234', 3, curses.A_BOLD)
289
290 def test_output_string_embedded_null_chars(self):
291 # reject embedded null bytes and characters
292 stdscr = self.stdscr
293 for arg in ['a\0', b'a\0']:
294 with self.subTest(arg=arg):
295 self.assertRaises(ValueError, stdscr.addstr, arg)
296 self.assertRaises(ValueError, stdscr.addnstr, arg, 1)
297 self.assertRaises(ValueError, stdscr.insstr, arg)
298 self.assertRaises(ValueError, stdscr.insnstr, arg, 1)
299
300 def test_read_from_window(self):
301 stdscr = self.stdscr
302 stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD)
303 # inch()
304 stdscr.move(0, 1)
305 self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD)
306 self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD)
307 stdscr.move(0, 0)
308 # instr()
309 self.assertEqual(stdscr.instr()[:6], b' ABCD ')
310 self.assertEqual(stdscr.instr(3)[:6], b' AB')
311 self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ')
312 self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ')
313 self.assertRaises(ValueError, stdscr.instr, -2)
314 self.assertRaises(ValueError, stdscr.instr, 0, 2, -2)
315
316 def test_getch(self):
317 win = curses.newwin(5, 12, 5, 2)
318
319 # TODO: Test with real input by writing to master fd.
320 for c in 'spam\n'[::-1]:
321 curses.ungetch(c)
322 self.assertEqual(win.getch(3, 1), b's'[0])
323 self.assertEqual(win.getyx(), (3, 1))
324 self.assertEqual(win.getch(3, 4), b'p'[0])
325 self.assertEqual(win.getyx(), (3, 4))
326 self.assertEqual(win.getch(), b'a'[0])
327 self.assertEqual(win.getyx(), (3, 4))
328 self.assertEqual(win.getch(), b'm'[0])
329 self.assertEqual(win.getch(), b'\n'[0])
330
331 def test_getstr(self):
332 win = curses.newwin(5, 12, 5, 2)
333 curses.echo()
334 self.addCleanup(curses.noecho)
335
336 self.assertRaises(ValueError, win.getstr, -400)
337 self.assertRaises(ValueError, win.getstr, 2, 3, -400)
338
339 # TODO: Test with real input by writing to master fd.
340 for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]:
341 curses.ungetch(c)
342 self.assertEqual(win.getstr(3, 1, 2), b'Lo')
343 self.assertEqual(win.instr(3, 0), b' Lo ')
344 self.assertEqual(win.getstr(3, 5, 10), b'ipsum')
345 self.assertEqual(win.instr(3, 0), b' Lo ipsum ')
346 self.assertEqual(win.getstr(1, 5), b'dolor')
347 self.assertEqual(win.instr(1, 0), b' dolor ')
348 self.assertEqual(win.getstr(2), b'si')
349 self.assertEqual(win.instr(1, 0), b'si dolor ')
350 self.assertEqual(win.getstr(), b'amet')
351 self.assertEqual(win.instr(1, 0), b'amet dolor ')
352
353 def test_clear(self):
354 win = curses.newwin(5, 15, 5, 2)
355 lorem_ipsum(win)
356
357 win.move(0, 8)
358 win.clrtoeol()
359 self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip')
360 self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,')
361
362 win.move(0, 3)
363 win.clrtobot()
364 self.assertEqual(win.instr(0, 0).rstrip(), b'Lor')
365 self.assertEqual(win.instr(1, 0).rstrip(), b'')
366
367 for func in [win.erase, win.clear]:
368 lorem_ipsum(win)
369 func()
370 self.assertEqual(win.instr(0, 0).rstrip(), b'')
371 self.assertEqual(win.instr(1, 0).rstrip(), b'')
372
373 def test_insert_delete(self):
374 win = curses.newwin(5, 15, 5, 2)
375 lorem_ipsum(win)
376
377 win.move(0, 2)
378 win.delch()
379 self.assertEqual(win.instr(0, 0), b'Loem ipsum ')
380 win.delch(0, 7)
381 self.assertEqual(win.instr(0, 0), b'Loem ipum ')
382
383 win.move(1, 5)
384 win.deleteln()
385 self.assertEqual(win.instr(0, 0), b'Loem ipum ')
386 self.assertEqual(win.instr(1, 0), b'consectetur ')
387 self.assertEqual(win.instr(2, 0), b'adipiscing elit')
388 self.assertEqual(win.instr(3, 0), b'sed do eiusmod ')
389 self.assertEqual(win.instr(4, 0), b' ')
390
391 win.move(1, 5)
392 win.insertln()
393 self.assertEqual(win.instr(0, 0), b'Loem ipum ')
394 self.assertEqual(win.instr(1, 0), b' ')
395 self.assertEqual(win.instr(2, 0), b'consectetur ')
396
397 win.clear()
398 lorem_ipsum(win)
399 win.move(1, 5)
400 win.insdelln(2)
401 self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
402 self.assertEqual(win.instr(1, 0), b' ')
403 self.assertEqual(win.instr(2, 0), b' ')
404 self.assertEqual(win.instr(3, 0), b'dolor sit amet,')
405
406 win.clear()
407 lorem_ipsum(win)
408 win.move(1, 5)
409 win.insdelln(-2)
410 self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
411 self.assertEqual(win.instr(1, 0), b'adipiscing elit')
412 self.assertEqual(win.instr(2, 0), b'sed do eiusmod ')
413 self.assertEqual(win.instr(3, 0), b' ')
414
415 def test_scroll(self):
416 win = curses.newwin(5, 15, 5, 2)
417 lorem_ipsum(win)
418 win.scrollok(True)
419 win.scroll()
420 self.assertEqual(win.instr(0, 0), b'dolor sit amet,')
421 win.scroll(2)
422 self.assertEqual(win.instr(0, 0), b'adipiscing elit')
423 win.scroll(-3)
424 self.assertEqual(win.instr(0, 0), b' ')
425 self.assertEqual(win.instr(2, 0), b' ')
426 self.assertEqual(win.instr(3, 0), b'adipiscing elit')
427 win.scrollok(False)
428
429 def test_attributes(self):
430 # TODO: attr_get(), attr_set(), ...
431 win = curses.newwin(5, 15, 5, 2)
432 win.attron(curses.A_BOLD)
433 win.attroff(curses.A_BOLD)
434 win.attrset(curses.A_BOLD)
435
436 win.standout()
437 win.standend()
438
439 @requires_curses_window_meth('chgat')
440 def test_chgat(self):
441 win = curses.newwin(5, 15, 5, 2)
442 win.addstr(2, 0, 'Lorem ipsum')
443 win.addstr(3, 0, 'dolor sit amet')
444
445 win.move(2, 8)
446 win.chgat(curses.A_BLINK)
447 self.assertEqual(win.inch(2, 7), b'p'[0])
448 self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK)
449 self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK)
450
451 win.move(2, 1)
452 win.chgat(3, curses.A_BOLD)
453 self.assertEqual(win.inch(2, 0), b'L'[0])
454 self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD)
455 self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD)
456 self.assertEqual(win.inch(2, 4), b'm'[0])
457
458 win.chgat(3, 2, curses.A_UNDERLINE)
459 self.assertEqual(win.inch(3, 1), b'o'[0])
460 self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE)
461 self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
462
463 win.chgat(3, 4, 7, curses.A_BLINK)
464 self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE)
465 self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK)
466 self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK)
467 self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE)
468 self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
469
470 def test_background(self):
471 win = curses.newwin(5, 15, 5, 2)
472 win.addstr(0, 0, 'Lorem ipsum')
473
474 self.assertEqual(win.getbkgd(), 0)
475
476 # bkgdset()
477 win.bkgdset('_')
478 self.assertEqual(win.getbkgd(), b'_'[0])
479 win.bkgdset(b'#')
480 self.assertEqual(win.getbkgd(), b'#'[0])
481 win.bkgdset(65)
482 self.assertEqual(win.getbkgd(), 65)
483 win.bkgdset(0)
484 self.assertEqual(win.getbkgd(), 32)
485
486 win.bkgdset('#', curses.A_REVERSE)
487 self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
488 self.assertEqual(win.inch(0, 0), b'L'[0])
489 self.assertEqual(win.inch(0, 5), b' '[0])
490 win.bkgdset(0)
491
492 # bkgd()
493 win.bkgd('_')
494 self.assertEqual(win.getbkgd(), b'_'[0])
495 self.assertEqual(win.inch(0, 0), b'L'[0])
496 self.assertEqual(win.inch(0, 5), b'_'[0])
497
498 win.bkgd('#', curses.A_REVERSE)
499 self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
500 self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE)
501 self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE)
502
503 def test_overlay(self):
504 srcwin = curses.newwin(5, 18, 3, 4)
505 lorem_ipsum(srcwin)
506 dstwin = curses.newwin(7, 17, 5, 7)
507 for i in range(6):
508 dstwin.addstr(i, 0, '_'*17)
509
510 srcwin.overlay(dstwin)
511 self.assertEqual(dstwin.instr(0, 0), b'sectetur_________')
512 self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____')
513 self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______')
514 self.assertEqual(dstwin.instr(3, 0), b'_________________')
515
516 srcwin.overwrite(dstwin)
517 self.assertEqual(dstwin.instr(0, 0), b'sectetur __')
518 self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __')
519 self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __')
520 self.assertEqual(dstwin.instr(3, 0), b'_________________')
521
522 srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11)
523 self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____')
524 self.assertEqual(dstwin.instr(4, 0), b'__ectetur________')
525 self.assertEqual(dstwin.instr(5, 0), b'_________________')
526
527 srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11)
528 self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____')
529 self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____')
530 self.assertEqual(dstwin.instr(5, 0), b'_________________')
531
532 def test_refresh(self):
533 win = curses.newwin(5, 15, 2, 5)
534 win.noutrefresh()
535 win.redrawln(1, 2)
536 win.redrawwin()
537 win.refresh()
538 curses.doupdate()
539
540 @requires_curses_window_meth('resize')
541 def test_resize(self):
542 win = curses.newwin(5, 15, 2, 5)
543 win.resize(4, 20)
544 self.assertEqual(win.getmaxyx(), (4, 20))
545 win.resize(5, 15)
546 self.assertEqual(win.getmaxyx(), (5, 15))
547
548 @requires_curses_window_meth('enclose')
549 def test_enclose(self):
550 win = curses.newwin(5, 15, 2, 5)
551 # TODO: Return bool instead of 1/0
552 self.assertTrue(win.enclose(2, 5))
553 self.assertFalse(win.enclose(1, 5))
554 self.assertFalse(win.enclose(2, 4))
555 self.assertTrue(win.enclose(6, 19))
556 self.assertFalse(win.enclose(7, 19))
557 self.assertFalse(win.enclose(6, 20))
558
559 def test_putwin(self):
560 win = curses.newwin(5, 12, 1, 2)
561 win.addstr(2, 1, 'Lorem ipsum')
562 with tempfile.TemporaryFile() as f:
563 win.putwin(f)
564 del win
565 f.seek(0)
566 win = curses.getwin(f)
567 self.assertEqual(win.getbegyx(), (1, 2))
568 self.assertEqual(win.getmaxyx(), (5, 12))
569 self.assertEqual(win.instr(2, 0), b' Lorem ipsum')
570
571 def test_borders_and_lines(self):
572 win = curses.newwin(5, 10, 5, 2)
Zachary Warebaf45c52014-10-17 13:59:18 -0500573 win.border('|', '!', '-', '_',
574 '+', '\\', '#', '/')
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200575 self.assertEqual(win.instr(0, 0), b'+--------\\')
576 self.assertEqual(win.instr(1, 0), b'| !')
577 self.assertEqual(win.instr(4, 0), b'#________/')
578 win.border(b'|', b'!', b'-', b'_',
579 b'+', b'\\', b'#', b'/')
580 win.border(65, 66, 67, 68,
581 69, 70, 71, 72)
582 self.assertRaises(TypeError, win.border,
583 65, 66, 67, 68, 69, [], 71, 72)
584 self.assertRaises(TypeError, win.border,
585 65, 66, 67, 68, 69, 70, 71, 72, 73)
586 self.assertRaises(TypeError, win.border,
587 65, 66, 67, 68, 69, 70, 71, 72, 73)
588 win.border(65, 66, 67, 68, 69, 70, 71)
589 win.border(65, 66, 67, 68, 69, 70)
590 win.border(65, 66, 67, 68, 69)
591 win.border(65, 66, 67, 68)
592 win.border(65, 66, 67)
593 win.border(65, 66)
594 win.border(65)
595 win.border()
Zachary Warebaf45c52014-10-17 13:59:18 -0500596
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200597 win.box(':', '~')
598 self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~')
599 self.assertEqual(win.instr(1, 0), b': :')
600 self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~')
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200601 win.box(b':', b'~')
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200602 win.box(65, 67)
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200603 self.assertRaises(TypeError, win.box, 65, 66, 67)
604 self.assertRaises(TypeError, win.box, 65)
605 win.box()
606
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200607 win.move(1, 2)
608 win.hline('-', 5)
609 self.assertEqual(win.instr(1, 1, 7), b' ----- ')
610 win.hline(b'-', 5)
611 win.hline(45, 5)
612 win.hline('-', 5, curses.A_BOLD)
613 win.hline(1, 1, '-', 5)
614 win.hline(1, 1, '-', 5, curses.A_BOLD)
Zachary Warebaf45c52014-10-17 13:59:18 -0500615
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200616 win.move(1, 2)
617 win.vline('a', 3)
618 win.vline(b'a', 3)
619 win.vline(97, 3)
620 win.vline('a', 3, curses.A_STANDOUT)
621 win.vline(1, 1, 'a', 3)
622 win.vline(1, 1, ';', 2, curses.A_STANDOUT)
623 self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT)
624 self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT)
625 self.assertEqual(win.inch(3, 1), b'a'[0])
Zachary Warebaf45c52014-10-17 13:59:18 -0500626
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200627 def test_unctrl(self):
628 # TODO: wunctrl()
629 self.assertEqual(curses.unctrl(b'A'), b'A')
630 self.assertEqual(curses.unctrl('A'), b'A')
631 self.assertEqual(curses.unctrl(65), b'A')
632 self.assertEqual(curses.unctrl(b'\n'), b'^J')
633 self.assertEqual(curses.unctrl('\n'), b'^J')
634 self.assertEqual(curses.unctrl(10), b'^J')
635 self.assertRaises(TypeError, curses.unctrl, b'')
636 self.assertRaises(TypeError, curses.unctrl, b'AB')
637 self.assertRaises(TypeError, curses.unctrl, '')
638 self.assertRaises(TypeError, curses.unctrl, 'AB')
639 self.assertRaises(OverflowError, curses.unctrl, 2**64)
Zachary Warebaf45c52014-10-17 13:59:18 -0500640
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200641 def test_endwin(self):
642 if not self.isatty:
643 self.skipTest('requires terminal')
644 self.assertIs(curses.isendwin(), False)
645 curses.endwin()
646 self.assertIs(curses.isendwin(), True)
647 curses.doupdate()
648 self.assertIs(curses.isendwin(), False)
Zachary Warebaf45c52014-10-17 13:59:18 -0500649
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200650 def test_terminfo(self):
651 self.assertIsInstance(curses.tigetflag('hc'), int)
652 self.assertEqual(curses.tigetflag('cols'), -1)
653 self.assertEqual(curses.tigetflag('cr'), -1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500654
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200655 self.assertIsInstance(curses.tigetnum('cols'), int)
656 self.assertEqual(curses.tigetnum('hc'), -2)
657 self.assertEqual(curses.tigetnum('cr'), -2)
Zachary Warebaf45c52014-10-17 13:59:18 -0500658
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200659 self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None)))
660 self.assertIsNone(curses.tigetstr('hc'))
661 self.assertIsNone(curses.tigetstr('cols'))
Zachary Warebaf45c52014-10-17 13:59:18 -0500662
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200663 cud = curses.tigetstr('cud')
664 if cud is not None:
665 # See issue10570.
666 self.assertIsInstance(cud, bytes)
667 curses.tparm(cud, 2)
668 cud_2 = curses.tparm(cud, 2)
669 self.assertIsInstance(cud_2, bytes)
670 curses.putp(cud_2)
Zachary Warebaf45c52014-10-17 13:59:18 -0500671
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200672 curses.putp(b'abc\n')
Zachary Warebaf45c52014-10-17 13:59:18 -0500673
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200674 def test_misc_module_funcs(self):
Zachary Warebaf45c52014-10-17 13:59:18 -0500675 curses.delay_output(1)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200676 curses.flushinp()
Zachary Warebaf45c52014-10-17 13:59:18 -0500677
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200678 curses.doupdate()
679 self.assertIs(curses.isendwin(), False)
680
Zachary Warebaf45c52014-10-17 13:59:18 -0500681 curses.napms(100)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200682
683 curses.newpad(50, 50)
684
685 def test_env_queries(self):
686 # TODO: term_attrs(), erasewchar(), killwchar()
687 self.assertIsInstance(curses.termname(), bytes)
688 self.assertIsInstance(curses.longname(), bytes)
689 self.assertIsInstance(curses.baudrate(), int)
690 self.assertIsInstance(curses.has_ic(), bool)
691 self.assertIsInstance(curses.has_il(), bool)
692 self.assertIsInstance(curses.termattrs(), int)
693
694 c = curses.killchar()
695 self.assertIsInstance(c, bytes)
696 self.assertEqual(len(c), 1)
697 c = curses.erasechar()
698 self.assertIsInstance(c, bytes)
699 self.assertEqual(len(c), 1)
700
701 def test_output_options(self):
702 stdscr = self.stdscr
703
704 stdscr.clearok(True)
705 stdscr.clearok(False)
706
707 stdscr.idcok(True)
708 stdscr.idcok(False)
709
710 stdscr.idlok(False)
711 stdscr.idlok(True)
712
713 if hasattr(stdscr, 'immedok'):
714 stdscr.immedok(True)
715 stdscr.immedok(False)
716
717 stdscr.leaveok(True)
718 stdscr.leaveok(False)
719
720 stdscr.scrollok(True)
721 stdscr.scrollok(False)
722
723 stdscr.setscrreg(5, 10)
724
725 curses.nonl()
726 curses.nl(True)
727 curses.nl(False)
728 curses.nl()
729
730
731 def test_input_options(self):
732 stdscr = self.stdscr
733
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200734 if self.isatty:
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200735 curses.nocbreak()
736 curses.cbreak()
737 curses.cbreak(False)
738 curses.cbreak(True)
739
740 curses.intrflush(True)
741 curses.intrflush(False)
742
743 curses.raw()
744 curses.raw(False)
745 curses.raw(True)
746 curses.noraw()
747
748 curses.noecho()
749 curses.echo()
750 curses.echo(False)
751 curses.echo(True)
752
753 curses.halfdelay(255)
754 curses.halfdelay(1)
755
756 stdscr.keypad(True)
757 stdscr.keypad(False)
758
759 curses.meta(True)
760 curses.meta(False)
761
762 stdscr.nodelay(True)
763 stdscr.nodelay(False)
764
765 curses.noqiflush()
766 curses.qiflush(True)
767 curses.qiflush(False)
768 curses.qiflush()
769
770 stdscr.notimeout(True)
771 stdscr.notimeout(False)
772
773 stdscr.timeout(-1)
774 stdscr.timeout(0)
775 stdscr.timeout(5)
776
777 @requires_curses_func('typeahead')
778 def test_typeahead(self):
779 curses.typeahead(sys.__stdin__.fileno())
780 curses.typeahead(-1)
781
782 def test_prog_mode(self):
783 if not self.isatty:
784 self.skipTest('requires terminal')
785 curses.def_prog_mode()
786 curses.reset_prog_mode()
787
788 def test_beep(self):
789 if (curses.tigetstr("bel") is not None
790 or curses.tigetstr("flash") is not None):
791 curses.beep()
792 else:
793 try:
794 curses.beep()
795 except curses.error:
796 self.skipTest('beep() failed')
797
798 def test_flash(self):
799 if (curses.tigetstr("bel") is not None
800 or curses.tigetstr("flash") is not None):
801 curses.flash()
802 else:
803 try:
804 curses.flash()
805 except curses.error:
806 self.skipTest('flash() failed')
807
808 def test_curs_set(self):
809 for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]:
810 if curses.tigetstr(cap) is not None:
811 curses.curs_set(vis)
812 else:
813 try:
814 curses.curs_set(vis)
815 except curses.error:
816 pass
817
818 @requires_curses_func('get_escdelay')
819 def test_escdelay(self):
820 escdelay = curses.get_escdelay()
821 self.assertIsInstance(escdelay, int)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700822 curses.set_escdelay(25)
823 self.assertEqual(curses.get_escdelay(), 25)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200824 curses.set_escdelay(escdelay)
825
826 @requires_curses_func('get_tabsize')
827 def test_tabsize(self):
828 tabsize = curses.get_tabsize()
829 self.assertIsInstance(tabsize, int)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700830 curses.set_tabsize(4)
831 self.assertEqual(curses.get_tabsize(), 4)
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200832 curses.set_tabsize(tabsize)
Zachary Warebaf45c52014-10-17 13:59:18 -0500833
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200834 @requires_curses_func('getsyx')
835 def test_getsyx(self):
836 y, x = curses.getsyx()
837 self.assertIsInstance(y, int)
838 self.assertIsInstance(x, int)
839 curses.setsyx(4, 5)
840 self.assertEqual(curses.getsyx(), (4, 5))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200841
842 def bad_colors(self):
843 return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
844
845 def bad_colors2(self):
846 return (curses.COLORS, 2**31, 2**63, 2**64)
847
848 def bad_pairs(self):
849 return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
850
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200851 def test_has_colors(self):
852 self.assertIsInstance(curses.has_colors(), bool)
853 self.assertIsInstance(curses.can_change_color(), bool)
854
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200855 def test_start_color(self):
856 if not curses.has_colors():
857 self.skipTest('requires colors support')
858 curses.start_color()
859 if verbose:
860 print(f'COLORS = {curses.COLORS}', file=sys.stderr)
861 print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
862
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200863 @requires_colors
864 def test_color_content(self):
865 self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
866 curses.color_content(0)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200867 maxcolor = curses.COLORS - 1
868 curses.color_content(maxcolor)
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200869
870 for color in self.bad_colors():
871 self.assertRaises(ValueError, curses.color_content, color)
872
873 @requires_colors
874 def test_init_color(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200875 if not curses.can_change_color():
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200876 self.skipTest('cannot change color')
877
878 old = curses.color_content(0)
879 try:
880 curses.init_color(0, *old)
881 except curses.error:
882 self.skipTest('cannot change color (init_color() failed)')
883 self.addCleanup(curses.init_color, 0, *old)
884 curses.init_color(0, 0, 0, 0)
885 self.assertEqual(curses.color_content(0), (0, 0, 0))
886 curses.init_color(0, 1000, 1000, 1000)
887 self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
888
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200889 maxcolor = curses.COLORS - 1
890 old = curses.color_content(maxcolor)
891 curses.init_color(maxcolor, *old)
892 self.addCleanup(curses.init_color, maxcolor, *old)
893 curses.init_color(maxcolor, 0, 500, 1000)
894 self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200895
896 for color in self.bad_colors():
897 self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
898 for comp in (-1, 1001):
899 self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
900 self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
901 self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
902
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200903 def get_pair_limit(self):
904 pair_limit = curses.COLOR_PAIRS
905 if hasattr(curses, 'ncurses_version'):
906 if curses.has_extended_color_support():
907 pair_limit += 2*curses.COLORS + 1
908 if (not curses.has_extended_color_support()
909 or (6, 1) <= curses.ncurses_version < (6, 2)):
910 pair_limit = min(pair_limit, SHORT_MAX)
911 return pair_limit
912
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200913 @requires_colors
914 def test_pair_content(self):
915 if not hasattr(curses, 'use_default_colors'):
916 self.assertEqual(curses.pair_content(0),
917 (curses.COLOR_WHITE, curses.COLOR_BLACK))
918 curses.pair_content(0)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200919 maxpair = self.get_pair_limit() - 1
920 if maxpair > 0:
921 curses.pair_content(maxpair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500922
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200923 for pair in self.bad_pairs():
924 self.assertRaises(ValueError, curses.pair_content, pair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500925
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200926 @requires_colors
927 def test_init_pair(self):
928 old = curses.pair_content(1)
929 curses.init_pair(1, *old)
930 self.addCleanup(curses.init_pair, 1, *old)
931
932 curses.init_pair(1, 0, 0)
933 self.assertEqual(curses.pair_content(1), (0, 0))
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200934 maxcolor = curses.COLORS - 1
935 curses.init_pair(1, maxcolor, 0)
936 self.assertEqual(curses.pair_content(1), (maxcolor, 0))
937 curses.init_pair(1, 0, maxcolor)
938 self.assertEqual(curses.pair_content(1), (0, maxcolor))
939 maxpair = self.get_pair_limit() - 1
940 if maxpair > 1:
941 curses.init_pair(maxpair, 0, 0)
942 self.assertEqual(curses.pair_content(maxpair), (0, 0))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200943
944 for pair in self.bad_pairs():
945 self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
946 for color in self.bad_colors2():
947 self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
948 self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
949
950 @requires_colors
951 def test_color_attrs(self):
952 for pair in 0, 1, 255:
953 attr = curses.color_pair(pair)
954 self.assertEqual(curses.pair_number(attr), pair, attr)
955 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
956 self.assertEqual(curses.color_pair(0), 0)
957 self.assertEqual(curses.pair_number(0), 0)
958
959 @requires_curses_func('use_default_colors')
960 @requires_colors
961 def test_use_default_colors(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200962 old = curses.pair_content(0)
963 try:
964 curses.use_default_colors()
965 except curses.error:
966 self.skipTest('cannot change color (use_default_colors() failed)')
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200967 self.assertEqual(curses.pair_content(0), (-1, -1))
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200968 self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)])
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500969
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300970 def test_keyname(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +0200971 # TODO: key_name()
972 self.assertEqual(curses.keyname(65), b'A')
973 self.assertEqual(curses.keyname(13), b'^M')
974 self.assertEqual(curses.keyname(127), b'^?')
975 self.assertEqual(curses.keyname(0), b'^@')
976 self.assertRaises(ValueError, curses.keyname, -1)
977 self.assertIsInstance(curses.keyname(256), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500978
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300979 @requires_curses_func('has_key')
980 def test_has_key(self):
981 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500982
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300983 @requires_curses_func('getmouse')
984 def test_getmouse(self):
985 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
986 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +0100987 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300988 curses.mouseinterval(10)
989 # just verify these don't cause errors
990 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
991 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500992
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200993 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500994 def test_userptr_without_set(self):
995 w = curses.newwin(10, 10)
996 p = curses.panel.new_panel(w)
997 # try to access userptr() before calling set_userptr() -- segfaults
998 with self.assertRaises(curses.panel.error,
999 msg='userptr should fail since not set'):
1000 p.userptr()
1001
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001002 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001003 def test_userptr_memory_leak(self):
1004 w = curses.newwin(10, 10)
1005 p = curses.panel.new_panel(w)
1006 obj = object()
1007 nrefs = sys.getrefcount(obj)
1008 for i in range(100):
1009 p.set_userptr(obj)
1010
1011 p.set_userptr(None)
1012 self.assertEqual(sys.getrefcount(obj), nrefs,
1013 "set_userptr leaked references")
1014
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001015 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -05001016 def test_userptr_segfault(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001017 w = curses.newwin(10, 10)
1018 panel = curses.panel.new_panel(w)
Zachary Warebaf45c52014-10-17 13:59:18 -05001019 class A:
1020 def __del__(self):
1021 panel.set_userptr(None)
1022 panel.set_userptr(A())
1023 panel.set_userptr(None)
1024
Serhiy Storchakabaac01e2017-10-31 13:56:44 +02001025 @requires_curses_func('panel')
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001026 def test_new_curses_panel(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +02001027 w = curses.newwin(10, 10)
1028 panel = curses.panel.new_panel(w)
Serhiy Storchakae3f1b092016-05-08 20:46:22 +03001029 self.assertRaises(TypeError, type(panel))
1030
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001031 @requires_curses_func('is_term_resized')
1032 def test_is_term_resized(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001033 lines, cols = curses.LINES, curses.COLS
1034 self.assertIs(curses.is_term_resized(lines, cols), False)
1035 self.assertIs(curses.is_term_resized(lines-1, cols-1), True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001036
1037 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -05001038 def test_resize_term(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001039 curses.update_lines_cols()
1040 lines, cols = curses.LINES, curses.COLS
1041 new_lines = lines - 1
1042 new_cols = cols + 1
1043 curses.resize_term(new_lines, new_cols)
1044 self.assertEqual(curses.LINES, new_lines)
1045 self.assertEqual(curses.COLS, new_cols)
1046
1047 curses.resize_term(lines, cols)
1048 self.assertEqual(curses.LINES, lines)
1049 self.assertEqual(curses.COLS, cols)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001050
1051 @requires_curses_func('resizeterm')
1052 def test_resizeterm(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001053 curses.update_lines_cols()
Zachary Warebaf45c52014-10-17 13:59:18 -05001054 lines, cols = curses.LINES, curses.COLS
1055 new_lines = lines - 1
1056 new_cols = cols + 1
1057 curses.resizeterm(new_lines, new_cols)
Zachary Warebaf45c52014-10-17 13:59:18 -05001058 self.assertEqual(curses.LINES, new_lines)
1059 self.assertEqual(curses.COLS, new_cols)
1060
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001061 curses.resizeterm(lines, cols)
1062 self.assertEqual(curses.LINES, lines)
1063 self.assertEqual(curses.COLS, cols)
1064
1065 def test_ungetch(self):
1066 curses.ungetch(b'A')
1067 self.assertEqual(self.stdscr.getkey(), 'A')
1068 curses.ungetch('B')
1069 self.assertEqual(self.stdscr.getkey(), 'B')
1070 curses.ungetch(67)
1071 self.assertEqual(self.stdscr.getkey(), 'C')
1072
Zachary Warebaf45c52014-10-17 13:59:18 -05001073 def test_issue6243(self):
1074 curses.ungetch(1025)
1075 self.stdscr.getkey()
1076
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001077 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +02001078 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
1079 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -05001080 def test_unget_wch(self):
1081 stdscr = self.stdscr
1082 encoding = stdscr.encoding
1083 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
1084 try:
1085 ch.encode(encoding)
1086 except UnicodeEncodeError:
1087 continue
1088 try:
1089 curses.unget_wch(ch)
1090 except Exception as err:
1091 self.fail("unget_wch(%a) failed with encoding %s: %s"
1092 % (ch, stdscr.encoding, err))
1093 read = stdscr.get_wch()
1094 self.assertEqual(read, ch)
1095
1096 code = ord(ch)
1097 curses.unget_wch(code)
1098 read = stdscr.get_wch()
1099 self.assertEqual(read, ch)
1100
Zachary Warebaf45c52014-10-17 13:59:18 -05001101 def test_encoding(self):
1102 stdscr = self.stdscr
1103 import codecs
1104 encoding = stdscr.encoding
1105 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -05001106 with self.assertRaises(TypeError):
1107 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -05001108 stdscr.encoding = encoding
1109 with self.assertRaises(TypeError):
1110 del stdscr.encoding
1111
1112 def test_issue21088(self):
1113 stdscr = self.stdscr
1114 #
1115 # http://bugs.python.org/issue21088
1116 #
1117 # the bug:
1118 # when converting curses.window.addch to Argument Clinic
1119 # the first two parameters were switched.
1120
1121 # if someday we can represent the signature of addch
1122 # we will need to rewrite this test.
1123 try:
1124 signature = inspect.signature(stdscr.addch)
1125 self.assertFalse(signature)
1126 except ValueError:
1127 # not generating a signature is fine.
1128 pass
1129
1130 # So. No signature for addch.
1131 # But Argument Clinic gave us a human-readable equivalent
1132 # as the first line of the docstring. So we parse that,
1133 # and ensure that the parameters appear in the correct order.
1134 # Since this is parsing output from Argument Clinic, we can
1135 # be reasonably certain the generated parsing code will be
1136 # correct too.
1137 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001138 self.assertIn("[y, x,]", human_readable_signature)
1139
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001140 @requires_curses_window_meth('resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001141 def test_issue13051(self):
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001142 win = curses.newwin(5, 15, 2, 5)
1143 box = curses.textpad.Textbox(win, insert_mode=True)
1144 lines, cols = win.getmaxyx()
1145 win.resize(lines-2, cols-2)
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +02001146 # this may cause infinite recursion, leading to a RuntimeError
1147 box._insert_printable_char('a')
1148
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001149
1150class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -05001151
Steve Dowerd2bc3892015-04-15 18:06:05 -04001152 def test_update_lines_cols(self):
Steve Dowerd2bc3892015-04-15 18:06:05 -04001153 curses.update_lines_cols()
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001154 lines, cols = curses.LINES, curses.COLS
1155 curses.LINES = curses.COLS = 0
1156 curses.update_lines_cols()
1157 self.assertEqual(curses.LINES, lines)
1158 self.assertEqual(curses.COLS, cols)
Steve Dowerd2bc3892015-04-15 18:06:05 -04001159
Serhiy Storchakab232df92018-10-30 13:22:42 +02001160 @requires_curses_func('ncurses_version')
1161 def test_ncurses_version(self):
1162 v = curses.ncurses_version
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +02001163 if verbose:
1164 print(f'ncurses_version = {curses.ncurses_version}', flush=True)
Serhiy Storchakab232df92018-10-30 13:22:42 +02001165 self.assertIsInstance(v[:], tuple)
1166 self.assertEqual(len(v), 3)
1167 self.assertIsInstance(v[0], int)
1168 self.assertIsInstance(v[1], int)
1169 self.assertIsInstance(v[2], int)
1170 self.assertIsInstance(v.major, int)
1171 self.assertIsInstance(v.minor, int)
1172 self.assertIsInstance(v.patch, int)
1173 self.assertEqual(v[0], v.major)
1174 self.assertEqual(v[1], v.minor)
1175 self.assertEqual(v[2], v.patch)
1176 self.assertGreaterEqual(v.major, 0)
1177 self.assertGreaterEqual(v.minor, 0)
1178 self.assertGreaterEqual(v.patch, 0)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001179
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001180 def test_has_extended_color_support(self):
1181 r = curses.has_extended_color_support()
1182 self.assertIsInstance(r, bool)
1183
1184
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001185class TestAscii(unittest.TestCase):
1186
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001187 def test_controlnames(self):
1188 for name in curses.ascii.controlnames:
1189 self.assertTrue(hasattr(curses.ascii, name), name)
1190
1191 def test_ctypes(self):
1192 def check(func, expected):
1193 with self.subTest(ch=c, func=func):
1194 self.assertEqual(func(i), expected)
1195 self.assertEqual(func(c), expected)
1196
1197 for i in range(256):
1198 c = chr(i)
1199 b = bytes([i])
1200 check(curses.ascii.isalnum, b.isalnum())
1201 check(curses.ascii.isalpha, b.isalpha())
1202 check(curses.ascii.isdigit, b.isdigit())
1203 check(curses.ascii.islower, b.islower())
1204 check(curses.ascii.isspace, b.isspace())
1205 check(curses.ascii.isupper, b.isupper())
1206
1207 check(curses.ascii.isascii, i < 128)
1208 check(curses.ascii.ismeta, i >= 128)
1209 check(curses.ascii.isctrl, i < 32)
1210 check(curses.ascii.iscntrl, i < 32 or i == 127)
1211 check(curses.ascii.isblank, c in ' \t')
1212 check(curses.ascii.isgraph, 32 < i <= 126)
1213 check(curses.ascii.isprint, 32 <= i <= 126)
1214 check(curses.ascii.ispunct, c in string.punctuation)
1215 check(curses.ascii.isxdigit, c in string.hexdigits)
1216
Serhiy Storchaka283de2b2016-12-28 10:04:27 +02001217 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
1218 self.assertFalse(curses.ascii.isalnum(i))
1219 self.assertFalse(curses.ascii.isalpha(i))
1220 self.assertFalse(curses.ascii.isdigit(i))
1221 self.assertFalse(curses.ascii.islower(i))
1222 self.assertFalse(curses.ascii.isspace(i))
1223 self.assertFalse(curses.ascii.isupper(i))
1224
1225 self.assertFalse(curses.ascii.isascii(i))
1226 self.assertFalse(curses.ascii.isctrl(i))
1227 self.assertFalse(curses.ascii.iscntrl(i))
1228 self.assertFalse(curses.ascii.isblank(i))
1229 self.assertFalse(curses.ascii.isgraph(i))
1230 self.assertFalse(curses.ascii.isprint(i))
1231 self.assertFalse(curses.ascii.ispunct(i))
1232 self.assertFalse(curses.ascii.isxdigit(i))
1233
1234 self.assertFalse(curses.ascii.ismeta(-1))
1235
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001236 def test_ascii(self):
1237 ascii = curses.ascii.ascii
1238 self.assertEqual(ascii('\xc1'), 'A')
1239 self.assertEqual(ascii('A'), 'A')
1240 self.assertEqual(ascii(ord('\xc1')), ord('A'))
1241
1242 def test_ctrl(self):
1243 ctrl = curses.ascii.ctrl
1244 self.assertEqual(ctrl('J'), '\n')
1245 self.assertEqual(ctrl('\n'), '\n')
1246 self.assertEqual(ctrl('@'), '\0')
1247 self.assertEqual(ctrl(ord('J')), ord('\n'))
1248
1249 def test_alt(self):
1250 alt = curses.ascii.alt
1251 self.assertEqual(alt('\n'), '\x8a')
1252 self.assertEqual(alt('A'), '\xc1')
1253 self.assertEqual(alt(ord('A')), 0xc1)
1254
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001255 def test_unctrl(self):
1256 unctrl = curses.ascii.unctrl
1257 self.assertEqual(unctrl('a'), 'a')
1258 self.assertEqual(unctrl('A'), 'A')
1259 self.assertEqual(unctrl(';'), ';')
1260 self.assertEqual(unctrl(' '), ' ')
1261 self.assertEqual(unctrl('\x7f'), '^?')
1262 self.assertEqual(unctrl('\n'), '^J')
1263 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001264 self.assertEqual(unctrl(ord('A')), 'A')
1265 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001266 # Meta-bit characters
1267 self.assertEqual(unctrl('\x8a'), '!^J')
1268 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +03001269 self.assertEqual(unctrl(ord('\x8a')), '!^J')
1270 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +03001271
1272
Serhiy Storchakad64fd4b2021-01-31 17:22:27 +02001273def lorem_ipsum(win):
1274 text = [
1275 'Lorem ipsum',
1276 'dolor sit amet,',
1277 'consectetur',
1278 'adipiscing elit,',
1279 'sed do eiusmod',
1280 'tempor incididunt',
1281 'ut labore et',
1282 'dolore magna',
1283 'aliqua.',
1284 ]
1285 maxy, maxx = win.getmaxyx()
1286 for y, line in enumerate(text[:maxy]):
1287 win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
1288
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +00001289if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -05001290 unittest.main()