blob: f2cad05b2c92e6debbe2d38e12e4f5a5c90a0a2a [file] [log] [blame]
Andrew M. Kuchling2158df02001-10-22 15:26:09 +00001#
2# Test script for the curses module
3#
4# This script doesn't actually display anything very coherent. but it
Zachary Warebaf45c52014-10-17 13:59:18 -05005# does call (nearly) every method and function.
Andrew M. Kuchling2158df02001-10-22 15:26:09 +00006#
Serhiy Storchaka1470edd2021-01-03 22:51:11 +02007# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr()
Neal Norwitz88bbd732006-01-10 07:05:44 +00008# Only called, not tested: getmouse(), ungetmouse()
Andrew M. Kuchling2158df02001-10-22 15:26:09 +00009#
10
Zachary Warebaf45c52014-10-17 13:59:18 -050011import os
Serhiy Storchaka514f9732016-06-18 22:08:11 +030012import string
Zachary Warebaf45c52014-10-17 13:59:18 -050013import sys
14import tempfile
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020015import functools
Zachary Warebaf45c52014-10-17 13:59:18 -050016import unittest
17
Hai Shia7f5d932020-08-04 00:41:24 +080018from test.support import requires, verbose, SaveSignals
19from test.support.import_helper import import_module
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000020
21# Optionally test curses module. This currently requires that the
22# 'curses' resource be given on the regrtest command line using the -u
23# option. If not available, nothing after this line will be executed.
Larry Hastings9147a962014-05-04 04:41:18 -070024import inspect
Neal Norwitz9f39f682006-01-06 04:18:21 +000025requires('curses')
26
R. David Murraya21e4ca2009-03-31 23:16:50 +000027# If either of these don't exist, skip the tests.
28curses = import_module('curses')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030029import_module('curses.ascii')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +020030import_module('curses.textpad')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +020031try:
32 import curses.panel
33except ImportError:
34 pass
R. David Murraya21e4ca2009-03-31 23:16:50 +000035
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030036def requires_curses_func(name):
37 return unittest.skipUnless(hasattr(curses, name),
38 'requires curses.%s' % name)
Mark Dickinson945e2422010-02-21 13:42:03 +000039
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020040def requires_colors(test):
41 @functools.wraps(test)
42 def wrapped(self, *args, **kwargs):
43 if not curses.has_colors():
44 self.skipTest('requires colors support')
45 curses.start_color()
46 test(self, *args, **kwargs)
47 return wrapped
48
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030049term = os.environ.get('TERM')
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +020050SHORT_MAX = 0x7fff
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030051
52# If newterm was supported we could use it instead of initscr and not exit
53@unittest.skipIf(not term or term == 'unknown',
Zachary Warebaf45c52014-10-17 13:59:18 -050054 "$TERM=%r, calling initscr() may cause exit" % term)
55@unittest.skipIf(sys.platform == "cygwin",
56 "cygwin's curses mostly just hangs")
57class TestCurses(unittest.TestCase):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030058
Zachary Warebaf45c52014-10-17 13:59:18 -050059 @classmethod
60 def setUpClass(cls):
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020061 if verbose:
62 print(f'TERM={term}', file=sys.stderr, flush=True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030063 # testing setupterm() inside initscr/endwin
64 # causes terminal breakage
Serhiy Storchaka607501a2021-01-02 19:35:15 +020065 stdout_fd = sys.__stdout__.fileno()
66 curses.setupterm(fd=stdout_fd)
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000067
Zachary Warebaf45c52014-10-17 13:59:18 -050068 def setUp(self):
Serhiy Storchaka607501a2021-01-02 19:35:15 +020069 self.isatty = True
70 self.output = sys.__stdout__
71 stdout_fd = sys.__stdout__.fileno()
72 if not sys.__stdout__.isatty():
73 # initstr() unconditionally uses C stdout.
74 # If it is redirected to file or pipe, try to attach it
75 # to terminal.
76 # First, save a copy of the file descriptor of stdout, so it
77 # can be restored after finishing the test.
78 dup_fd = os.dup(stdout_fd)
79 self.addCleanup(os.close, dup_fd)
80 self.addCleanup(os.dup2, dup_fd, stdout_fd)
81
82 if sys.__stderr__.isatty():
83 # If stderr is connected to terminal, use it.
84 tmp = sys.__stderr__
85 self.output = sys.__stderr__
86 else:
87 try:
88 # Try to open the terminal device.
Serhiy Storchakab6fc0c42021-01-04 12:30:20 +020089 tmp = open('/dev/tty', 'wb', buffering=0)
Serhiy Storchaka607501a2021-01-02 19:35:15 +020090 except OSError:
91 # As a fallback, use regular file to write control codes.
92 # Some functions (like savetty) will not work, but at
93 # least the garbage control sequences will not be mixed
94 # with the testing report.
95 tmp = tempfile.TemporaryFile(mode='wb', buffering=0)
96 self.isatty = False
97 self.addCleanup(tmp.close)
98 self.output = None
99 os.dup2(tmp.fileno(), stdout_fd)
100
Victor Stinner19f68302017-10-31 03:14:01 -0700101 self.save_signals = SaveSignals()
102 self.save_signals.save()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200103 self.addCleanup(self.save_signals.restore)
104 if verbose and self.output is not None:
Zachary Warebaf45c52014-10-17 13:59:18 -0500105 # just to make the test output a little more readable
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200106 sys.stderr.flush()
107 sys.stdout.flush()
108 print(file=self.output, flush=True)
Zachary Warebaf45c52014-10-17 13:59:18 -0500109 self.stdscr = curses.initscr()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200110 if self.isatty:
111 curses.savetty()
112 self.addCleanup(curses.endwin)
113 self.addCleanup(curses.resetty)
Zachary Warebaf45c52014-10-17 13:59:18 -0500114
115 def test_window_funcs(self):
116 "Test the methods of windows"
117 stdscr = self.stdscr
118 win = curses.newwin(10,10)
119 win = curses.newwin(5,5, 5,5)
120 win2 = curses.newwin(15,15, 5,5)
121
122 for meth in [stdscr.addch, stdscr.addstr]:
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300123 for args in [('a',), ('a', curses.A_BOLD),
Zachary Warebaf45c52014-10-17 13:59:18 -0500124 (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300125 with self.subTest(meth=meth.__qualname__, args=args):
126 meth(*args)
Zachary Warebaf45c52014-10-17 13:59:18 -0500127
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200128 for meth in [stdscr.clear, stdscr.clrtobot,
Zachary Warebaf45c52014-10-17 13:59:18 -0500129 stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
130 stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200131 stdscr.getbkgd, stdscr.getmaxyx,
Zachary Warebaf45c52014-10-17 13:59:18 -0500132 stdscr.getparyx, stdscr.getyx, stdscr.inch,
133 stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
134 win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
135 stdscr.standout, stdscr.standend, stdscr.syncdown,
136 stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300137 with self.subTest(meth=meth.__qualname__):
138 meth()
Zachary Warebaf45c52014-10-17 13:59:18 -0500139
140 stdscr.addnstr('1234', 3)
141 stdscr.addnstr('1234', 3, curses.A_BOLD)
142 stdscr.addnstr(4,4, '1234', 3)
143 stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
144
145 stdscr.attron(curses.A_BOLD)
146 stdscr.attroff(curses.A_BOLD)
147 stdscr.attrset(curses.A_BOLD)
148 stdscr.bkgd(' ')
149 stdscr.bkgd(' ', curses.A_REVERSE)
150 stdscr.bkgdset(' ')
151 stdscr.bkgdset(' ', curses.A_REVERSE)
152
153 win.border(65, 66, 67, 68,
154 69, 70, 71, 72)
155 win.border('|', '!', '-', '_',
156 '+', '\\', '#', '/')
157 with self.assertRaises(TypeError,
158 msg="Expected win.border() to raise TypeError"):
159 win.border(65, 66, 67, 68,
160 69, [], 71, 72)
161
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200162 win.box(65, 67)
163 win.box('!', '_')
164 win.box(b':', b'~')
165 self.assertRaises(TypeError, win.box, 65, 66, 67)
166 self.assertRaises(TypeError, win.box, 65)
167 win.box()
168
Zachary Warebaf45c52014-10-17 13:59:18 -0500169 stdscr.clearok(1)
170
171 win4 = stdscr.derwin(2,2)
172 win4 = stdscr.derwin(1,1, 5,5)
173 win4.mvderwin(9,9)
174
175 stdscr.echochar('a')
176 stdscr.echochar('a', curses.A_BOLD)
177 stdscr.hline('-', 5)
178 stdscr.hline('-', 5, curses.A_BOLD)
179 stdscr.hline(1,1,'-', 5)
180 stdscr.hline(1,1,'-', 5, curses.A_BOLD)
181
182 stdscr.idcok(1)
183 stdscr.idlok(1)
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200184 if hasattr(stdscr, 'immedok'):
185 stdscr.immedok(1)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200186 stdscr.immedok(0)
Zachary Warebaf45c52014-10-17 13:59:18 -0500187 stdscr.insch('c')
188 stdscr.insdelln(1)
189 stdscr.insnstr('abc', 3)
190 stdscr.insnstr('abc', 3, curses.A_BOLD)
191 stdscr.insnstr(5, 5, 'abc', 3)
192 stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
193
194 stdscr.insstr('def')
195 stdscr.insstr('def', curses.A_BOLD)
196 stdscr.insstr(5, 5, 'def')
197 stdscr.insstr(5, 5, 'def', curses.A_BOLD)
198 stdscr.is_linetouched(0)
199 stdscr.keypad(1)
200 stdscr.leaveok(1)
201 stdscr.move(3,3)
202 win.mvwin(2,2)
203 stdscr.nodelay(1)
204 stdscr.notimeout(1)
205 win2.overlay(win)
206 win2.overwrite(win)
207 win2.overlay(win, 1, 2, 2, 1, 3, 3)
208 win2.overwrite(win, 1, 2, 2, 1, 3, 3)
209 stdscr.redrawln(1,2)
210
211 stdscr.scrollok(1)
212 stdscr.scroll()
213 stdscr.scroll(2)
214 stdscr.scroll(-3)
215
216 stdscr.move(12, 2)
217 stdscr.setscrreg(10,15)
218 win3 = stdscr.subwin(10,10)
219 win3 = stdscr.subwin(10,10, 5,5)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200220 if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200221 stdscr.syncok(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500222 stdscr.timeout(5)
223 stdscr.touchline(5,5)
224 stdscr.touchline(5,5,0)
225 stdscr.vline('a', 3)
226 stdscr.vline('a', 3, curses.A_STANDOUT)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200227 if hasattr(stdscr, 'chgat'):
228 stdscr.chgat(5, 2, 3, curses.A_BLINK)
229 stdscr.chgat(3, curses.A_BOLD)
230 stdscr.chgat(5, 8, curses.A_UNDERLINE)
231 stdscr.chgat(curses.A_BLINK)
Zachary Warebaf45c52014-10-17 13:59:18 -0500232 stdscr.refresh()
233
234 stdscr.vline(1,1, 'a', 3)
235 stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
236
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200237 if hasattr(stdscr, 'resize'):
238 stdscr.resize(25, 80)
239 if hasattr(stdscr, 'enclose'):
240 stdscr.enclose(10, 10)
Zachary Warebaf45c52014-10-17 13:59:18 -0500241
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200242 with tempfile.TemporaryFile() as f:
243 self.stdscr.putwin(f)
244 f.seek(0)
245 curses.getwin(f)
246
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700247 self.assertRaises(ValueError, stdscr.getstr, -400)
248 self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
Benjamin Peterson432ea4f2016-08-15 21:40:14 -0700249 self.assertRaises(ValueError, stdscr.instr, -2)
250 self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700251
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300252 def test_embedded_null_chars(self):
253 # reject embedded null bytes and characters
254 stdscr = self.stdscr
255 for arg in ['a', b'a']:
256 with self.subTest(arg=arg):
257 self.assertRaises(ValueError, stdscr.addstr, 'a\0')
258 self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1)
259 self.assertRaises(ValueError, stdscr.insstr, 'a\0')
260 self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500261
262 def test_module_funcs(self):
263 "Test module-level functions"
Zachary Warebaf45c52014-10-17 13:59:18 -0500264 for func in [curses.baudrate, curses.beep, curses.can_change_color,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200265 curses.doupdate, curses.flash, curses.flushinp,
Zachary Warebaf45c52014-10-17 13:59:18 -0500266 curses.has_colors, curses.has_ic, curses.has_il,
267 curses.isendwin, curses.killchar, curses.longname,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200268 curses.noecho, curses.nonl, curses.noqiflush,
269 curses.termattrs, curses.termname, curses.erasechar,
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500270 curses.has_extended_color_support]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300271 with self.subTest(func=func.__qualname__):
272 func()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200273 if self.isatty:
274 for func in [curses.cbreak, curses.def_prog_mode,
275 curses.nocbreak, curses.noraw,
276 curses.reset_prog_mode]:
277 with self.subTest(func=func.__qualname__):
278 func()
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200279 if hasattr(curses, 'filter'):
280 curses.filter()
281 if hasattr(curses, 'getsyx'):
282 curses.getsyx()
Zachary Warebaf45c52014-10-17 13:59:18 -0500283
284 # Functions that actually need arguments
285 if curses.tigetstr("cnorm"):
286 curses.curs_set(1)
287 curses.delay_output(1)
288 curses.echo() ; curses.echo(1)
289
Zachary Warebaf45c52014-10-17 13:59:18 -0500290 curses.halfdelay(1)
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200291 if self.isatty:
292 curses.intrflush(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500293 curses.meta(1)
294 curses.napms(100)
295 curses.newpad(50,50)
296 win = curses.newwin(5,5)
297 win = curses.newwin(5,5, 1,1)
298 curses.nl() ; curses.nl(1)
299 curses.putp(b'abc')
300 curses.qiflush()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200301 if self.isatty:
302 curses.raw() ; curses.raw(1)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700303 curses.set_escdelay(25)
304 self.assertEqual(curses.get_escdelay(), 25)
305 curses.set_tabsize(4)
306 self.assertEqual(curses.get_tabsize(), 4)
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200307 if hasattr(curses, 'setsyx'):
308 curses.setsyx(5,5)
Zachary Warebaf45c52014-10-17 13:59:18 -0500309 curses.tigetflag('hc')
310 curses.tigetnum('co')
311 curses.tigetstr('cr')
312 curses.tparm(b'cr')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200313 if hasattr(curses, 'typeahead'):
314 curses.typeahead(sys.__stdin__.fileno())
Zachary Warebaf45c52014-10-17 13:59:18 -0500315 curses.unctrl('a')
316 curses.ungetch('a')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200317 if hasattr(curses, 'use_env'):
318 curses.use_env(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500319
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300320 # Functions only available on a few platforms
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200321
322 def bad_colors(self):
323 return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
324
325 def bad_colors2(self):
326 return (curses.COLORS, 2**31, 2**63, 2**64)
327
328 def bad_pairs(self):
329 return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
330
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200331 def test_start_color(self):
332 if not curses.has_colors():
333 self.skipTest('requires colors support')
334 curses.start_color()
335 if verbose:
336 print(f'COLORS = {curses.COLORS}', file=sys.stderr)
337 print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
338
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200339 @requires_colors
340 def test_color_content(self):
341 self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
342 curses.color_content(0)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200343 maxcolor = curses.COLORS - 1
344 curses.color_content(maxcolor)
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200345
346 for color in self.bad_colors():
347 self.assertRaises(ValueError, curses.color_content, color)
348
349 @requires_colors
350 def test_init_color(self):
351 if not curses.can_change_color:
352 self.skipTest('cannot change color')
353
354 old = curses.color_content(0)
355 try:
356 curses.init_color(0, *old)
357 except curses.error:
358 self.skipTest('cannot change color (init_color() failed)')
359 self.addCleanup(curses.init_color, 0, *old)
360 curses.init_color(0, 0, 0, 0)
361 self.assertEqual(curses.color_content(0), (0, 0, 0))
362 curses.init_color(0, 1000, 1000, 1000)
363 self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
364
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200365 maxcolor = curses.COLORS - 1
366 old = curses.color_content(maxcolor)
367 curses.init_color(maxcolor, *old)
368 self.addCleanup(curses.init_color, maxcolor, *old)
369 curses.init_color(maxcolor, 0, 500, 1000)
370 self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200371
372 for color in self.bad_colors():
373 self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
374 for comp in (-1, 1001):
375 self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
376 self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
377 self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
378
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200379 def get_pair_limit(self):
380 pair_limit = curses.COLOR_PAIRS
381 if hasattr(curses, 'ncurses_version'):
382 if curses.has_extended_color_support():
383 pair_limit += 2*curses.COLORS + 1
384 if (not curses.has_extended_color_support()
385 or (6, 1) <= curses.ncurses_version < (6, 2)):
386 pair_limit = min(pair_limit, SHORT_MAX)
387 return pair_limit
388
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200389 @requires_colors
390 def test_pair_content(self):
391 if not hasattr(curses, 'use_default_colors'):
392 self.assertEqual(curses.pair_content(0),
393 (curses.COLOR_WHITE, curses.COLOR_BLACK))
394 curses.pair_content(0)
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200395 maxpair = self.get_pair_limit() - 1
396 if maxpair > 0:
397 curses.pair_content(maxpair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500398
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200399 for pair in self.bad_pairs():
400 self.assertRaises(ValueError, curses.pair_content, pair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500401
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200402 @requires_colors
403 def test_init_pair(self):
404 old = curses.pair_content(1)
405 curses.init_pair(1, *old)
406 self.addCleanup(curses.init_pair, 1, *old)
407
408 curses.init_pair(1, 0, 0)
409 self.assertEqual(curses.pair_content(1), (0, 0))
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200410 maxcolor = curses.COLORS - 1
411 curses.init_pair(1, maxcolor, 0)
412 self.assertEqual(curses.pair_content(1), (maxcolor, 0))
413 curses.init_pair(1, 0, maxcolor)
414 self.assertEqual(curses.pair_content(1), (0, maxcolor))
415 maxpair = self.get_pair_limit() - 1
416 if maxpair > 1:
417 curses.init_pair(maxpair, 0, 0)
418 self.assertEqual(curses.pair_content(maxpair), (0, 0))
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200419
420 for pair in self.bad_pairs():
421 self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
422 for color in self.bad_colors2():
423 self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
424 self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
425
426 @requires_colors
427 def test_color_attrs(self):
428 for pair in 0, 1, 255:
429 attr = curses.color_pair(pair)
430 self.assertEqual(curses.pair_number(attr), pair, attr)
431 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
432 self.assertEqual(curses.color_pair(0), 0)
433 self.assertEqual(curses.pair_number(0), 0)
434
435 @requires_curses_func('use_default_colors')
436 @requires_colors
437 def test_use_default_colors(self):
438 self.assertIn(curses.pair_content(0),
439 ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
440 curses.use_default_colors()
441 self.assertEqual(curses.pair_content(0), (-1, -1))
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500442
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300443 @requires_curses_func('keyname')
444 def test_keyname(self):
445 curses.keyname(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500446
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300447 @requires_curses_func('has_key')
448 def test_has_key(self):
449 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500450
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300451 @requires_curses_func('getmouse')
452 def test_getmouse(self):
453 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
454 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +0100455 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300456 curses.mouseinterval(10)
457 # just verify these don't cause errors
458 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
459 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500460
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200461 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500462 def test_userptr_without_set(self):
463 w = curses.newwin(10, 10)
464 p = curses.panel.new_panel(w)
465 # try to access userptr() before calling set_userptr() -- segfaults
466 with self.assertRaises(curses.panel.error,
467 msg='userptr should fail since not set'):
468 p.userptr()
469
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200470 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500471 def test_userptr_memory_leak(self):
472 w = curses.newwin(10, 10)
473 p = curses.panel.new_panel(w)
474 obj = object()
475 nrefs = sys.getrefcount(obj)
476 for i in range(100):
477 p.set_userptr(obj)
478
479 p.set_userptr(None)
480 self.assertEqual(sys.getrefcount(obj), nrefs,
481 "set_userptr leaked references")
482
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200483 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500484 def test_userptr_segfault(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +0200485 w = curses.newwin(10, 10)
486 panel = curses.panel.new_panel(w)
Zachary Warebaf45c52014-10-17 13:59:18 -0500487 class A:
488 def __del__(self):
489 panel.set_userptr(None)
490 panel.set_userptr(A())
491 panel.set_userptr(None)
492
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200493 @requires_curses_func('panel')
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300494 def test_new_curses_panel(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +0200495 w = curses.newwin(10, 10)
496 panel = curses.panel.new_panel(w)
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300497 self.assertRaises(TypeError, type(panel))
498
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300499 @requires_curses_func('is_term_resized')
500 def test_is_term_resized(self):
501 curses.is_term_resized(*self.stdscr.getmaxyx())
502
503 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -0500504 def test_resize_term(self):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300505 curses.resize_term(*self.stdscr.getmaxyx())
506
507 @requires_curses_func('resizeterm')
508 def test_resizeterm(self):
Zachary Warebaf45c52014-10-17 13:59:18 -0500509 lines, cols = curses.LINES, curses.COLS
510 new_lines = lines - 1
511 new_cols = cols + 1
512 curses.resizeterm(new_lines, new_cols)
513
514 self.assertEqual(curses.LINES, new_lines)
515 self.assertEqual(curses.COLS, new_cols)
516
517 def test_issue6243(self):
518 curses.ungetch(1025)
519 self.stdscr.getkey()
520
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300521 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +0200522 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
523 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -0500524 def test_unget_wch(self):
525 stdscr = self.stdscr
526 encoding = stdscr.encoding
527 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
528 try:
529 ch.encode(encoding)
530 except UnicodeEncodeError:
531 continue
532 try:
533 curses.unget_wch(ch)
534 except Exception as err:
535 self.fail("unget_wch(%a) failed with encoding %s: %s"
536 % (ch, stdscr.encoding, err))
537 read = stdscr.get_wch()
538 self.assertEqual(read, ch)
539
540 code = ord(ch)
541 curses.unget_wch(code)
542 read = stdscr.get_wch()
543 self.assertEqual(read, ch)
544
545 def test_issue10570(self):
546 b = curses.tparm(curses.tigetstr("cup"), 5, 3)
547 self.assertIs(type(b), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500548
549 def test_encoding(self):
550 stdscr = self.stdscr
551 import codecs
552 encoding = stdscr.encoding
553 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -0500554 with self.assertRaises(TypeError):
555 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -0500556 stdscr.encoding = encoding
557 with self.assertRaises(TypeError):
558 del stdscr.encoding
559
560 def test_issue21088(self):
561 stdscr = self.stdscr
562 #
563 # http://bugs.python.org/issue21088
564 #
565 # the bug:
566 # when converting curses.window.addch to Argument Clinic
567 # the first two parameters were switched.
568
569 # if someday we can represent the signature of addch
570 # we will need to rewrite this test.
571 try:
572 signature = inspect.signature(stdscr.addch)
573 self.assertFalse(signature)
574 except ValueError:
575 # not generating a signature is fine.
576 pass
577
578 # So. No signature for addch.
579 # But Argument Clinic gave us a human-readable equivalent
580 # as the first line of the docstring. So we parse that,
581 # and ensure that the parameters appear in the correct order.
582 # Since this is parsing output from Argument Clinic, we can
583 # be reasonably certain the generated parsing code will be
584 # correct too.
585 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300586 self.assertIn("[y, x,]", human_readable_signature)
587
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +0200588 def test_issue13051(self):
589 stdscr = self.stdscr
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200590 if not hasattr(stdscr, 'resize'):
591 raise unittest.SkipTest('requires curses.window.resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +0200592 box = curses.textpad.Textbox(stdscr, insert_mode=True)
593 lines, cols = stdscr.getmaxyx()
594 stdscr.resize(lines-2, cols-2)
595 # this may cause infinite recursion, leading to a RuntimeError
596 box._insert_printable_char('a')
597
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300598
599class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -0500600
Berker Peksag53926f12016-08-19 17:59:01 +0300601 @requires_curses_func('update_lines_cols')
Steve Dowerd2bc3892015-04-15 18:06:05 -0400602 def test_update_lines_cols(self):
603 # this doesn't actually test that LINES and COLS are updated,
604 # because we can't automate changing them. See Issue #4254 for
605 # a manual test script. We can only test that the function
606 # can be called.
607 curses.update_lines_cols()
608
Serhiy Storchakab232df92018-10-30 13:22:42 +0200609 @requires_curses_func('ncurses_version')
610 def test_ncurses_version(self):
611 v = curses.ncurses_version
Serhiy Storchaka59f9b4e2021-01-05 09:13:15 +0200612 if verbose:
613 print(f'ncurses_version = {curses.ncurses_version}', flush=True)
Serhiy Storchakab232df92018-10-30 13:22:42 +0200614 self.assertIsInstance(v[:], tuple)
615 self.assertEqual(len(v), 3)
616 self.assertIsInstance(v[0], int)
617 self.assertIsInstance(v[1], int)
618 self.assertIsInstance(v[2], int)
619 self.assertIsInstance(v.major, int)
620 self.assertIsInstance(v.minor, int)
621 self.assertIsInstance(v.patch, int)
622 self.assertEqual(v[0], v.major)
623 self.assertEqual(v[1], v.minor)
624 self.assertEqual(v[2], v.patch)
625 self.assertGreaterEqual(v.major, 0)
626 self.assertGreaterEqual(v.minor, 0)
627 self.assertGreaterEqual(v.patch, 0)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000628
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300629class TestAscii(unittest.TestCase):
630
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300631 def test_controlnames(self):
632 for name in curses.ascii.controlnames:
633 self.assertTrue(hasattr(curses.ascii, name), name)
634
635 def test_ctypes(self):
636 def check(func, expected):
637 with self.subTest(ch=c, func=func):
638 self.assertEqual(func(i), expected)
639 self.assertEqual(func(c), expected)
640
641 for i in range(256):
642 c = chr(i)
643 b = bytes([i])
644 check(curses.ascii.isalnum, b.isalnum())
645 check(curses.ascii.isalpha, b.isalpha())
646 check(curses.ascii.isdigit, b.isdigit())
647 check(curses.ascii.islower, b.islower())
648 check(curses.ascii.isspace, b.isspace())
649 check(curses.ascii.isupper, b.isupper())
650
651 check(curses.ascii.isascii, i < 128)
652 check(curses.ascii.ismeta, i >= 128)
653 check(curses.ascii.isctrl, i < 32)
654 check(curses.ascii.iscntrl, i < 32 or i == 127)
655 check(curses.ascii.isblank, c in ' \t')
656 check(curses.ascii.isgraph, 32 < i <= 126)
657 check(curses.ascii.isprint, 32 <= i <= 126)
658 check(curses.ascii.ispunct, c in string.punctuation)
659 check(curses.ascii.isxdigit, c in string.hexdigits)
660
Serhiy Storchaka283de2b2016-12-28 10:04:27 +0200661 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
662 self.assertFalse(curses.ascii.isalnum(i))
663 self.assertFalse(curses.ascii.isalpha(i))
664 self.assertFalse(curses.ascii.isdigit(i))
665 self.assertFalse(curses.ascii.islower(i))
666 self.assertFalse(curses.ascii.isspace(i))
667 self.assertFalse(curses.ascii.isupper(i))
668
669 self.assertFalse(curses.ascii.isascii(i))
670 self.assertFalse(curses.ascii.isctrl(i))
671 self.assertFalse(curses.ascii.iscntrl(i))
672 self.assertFalse(curses.ascii.isblank(i))
673 self.assertFalse(curses.ascii.isgraph(i))
674 self.assertFalse(curses.ascii.isprint(i))
675 self.assertFalse(curses.ascii.ispunct(i))
676 self.assertFalse(curses.ascii.isxdigit(i))
677
678 self.assertFalse(curses.ascii.ismeta(-1))
679
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300680 def test_ascii(self):
681 ascii = curses.ascii.ascii
682 self.assertEqual(ascii('\xc1'), 'A')
683 self.assertEqual(ascii('A'), 'A')
684 self.assertEqual(ascii(ord('\xc1')), ord('A'))
685
686 def test_ctrl(self):
687 ctrl = curses.ascii.ctrl
688 self.assertEqual(ctrl('J'), '\n')
689 self.assertEqual(ctrl('\n'), '\n')
690 self.assertEqual(ctrl('@'), '\0')
691 self.assertEqual(ctrl(ord('J')), ord('\n'))
692
693 def test_alt(self):
694 alt = curses.ascii.alt
695 self.assertEqual(alt('\n'), '\x8a')
696 self.assertEqual(alt('A'), '\xc1')
697 self.assertEqual(alt(ord('A')), 0xc1)
698
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300699 def test_unctrl(self):
700 unctrl = curses.ascii.unctrl
701 self.assertEqual(unctrl('a'), 'a')
702 self.assertEqual(unctrl('A'), 'A')
703 self.assertEqual(unctrl(';'), ';')
704 self.assertEqual(unctrl(' '), ' ')
705 self.assertEqual(unctrl('\x7f'), '^?')
706 self.assertEqual(unctrl('\n'), '^J')
707 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300708 self.assertEqual(unctrl(ord('A')), 'A')
709 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300710 # Meta-bit characters
711 self.assertEqual(unctrl('\x8a'), '!^J')
712 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300713 self.assertEqual(unctrl(ord('\x8a')), '!^J')
714 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300715
716
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000717if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -0500718 unittest.main()