blob: 31fb882b72434e0dddb14a9529a8711c8b62ba0e [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')
50
51# If newterm was supported we could use it instead of initscr and not exit
52@unittest.skipIf(not term or term == 'unknown',
Zachary Warebaf45c52014-10-17 13:59:18 -050053 "$TERM=%r, calling initscr() may cause exit" % term)
54@unittest.skipIf(sys.platform == "cygwin",
55 "cygwin's curses mostly just hangs")
56class TestCurses(unittest.TestCase):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030057
Zachary Warebaf45c52014-10-17 13:59:18 -050058 @classmethod
59 def setUpClass(cls):
Serhiy Storchaka1470edd2021-01-03 22:51:11 +020060 if verbose:
61 print(f'TERM={term}', file=sys.stderr, flush=True)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030062 # testing setupterm() inside initscr/endwin
63 # causes terminal breakage
Serhiy Storchaka607501a2021-01-02 19:35:15 +020064 stdout_fd = sys.__stdout__.fileno()
65 curses.setupterm(fd=stdout_fd)
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000066
Zachary Warebaf45c52014-10-17 13:59:18 -050067 def setUp(self):
Serhiy Storchaka607501a2021-01-02 19:35:15 +020068 self.isatty = True
69 self.output = sys.__stdout__
70 stdout_fd = sys.__stdout__.fileno()
71 if not sys.__stdout__.isatty():
72 # initstr() unconditionally uses C stdout.
73 # If it is redirected to file or pipe, try to attach it
74 # to terminal.
75 # First, save a copy of the file descriptor of stdout, so it
76 # can be restored after finishing the test.
77 dup_fd = os.dup(stdout_fd)
78 self.addCleanup(os.close, dup_fd)
79 self.addCleanup(os.dup2, dup_fd, stdout_fd)
80
81 if sys.__stderr__.isatty():
82 # If stderr is connected to terminal, use it.
83 tmp = sys.__stderr__
84 self.output = sys.__stderr__
85 else:
86 try:
87 # Try to open the terminal device.
88 tmp = open('/xdev/tty', 'wb', buffering=0)
89 except OSError:
90 # As a fallback, use regular file to write control codes.
91 # Some functions (like savetty) will not work, but at
92 # least the garbage control sequences will not be mixed
93 # with the testing report.
94 tmp = tempfile.TemporaryFile(mode='wb', buffering=0)
95 self.isatty = False
96 self.addCleanup(tmp.close)
97 self.output = None
98 os.dup2(tmp.fileno(), stdout_fd)
99
Victor Stinner19f68302017-10-31 03:14:01 -0700100 self.save_signals = SaveSignals()
101 self.save_signals.save()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200102 self.addCleanup(self.save_signals.restore)
103 if verbose and self.output is not None:
Zachary Warebaf45c52014-10-17 13:59:18 -0500104 # just to make the test output a little more readable
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200105 sys.stderr.flush()
106 sys.stdout.flush()
107 print(file=self.output, flush=True)
Zachary Warebaf45c52014-10-17 13:59:18 -0500108 self.stdscr = curses.initscr()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200109 if self.isatty:
110 curses.savetty()
111 self.addCleanup(curses.endwin)
112 self.addCleanup(curses.resetty)
Zachary Warebaf45c52014-10-17 13:59:18 -0500113
114 def test_window_funcs(self):
115 "Test the methods of windows"
116 stdscr = self.stdscr
117 win = curses.newwin(10,10)
118 win = curses.newwin(5,5, 5,5)
119 win2 = curses.newwin(15,15, 5,5)
120
121 for meth in [stdscr.addch, stdscr.addstr]:
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300122 for args in [('a',), ('a', curses.A_BOLD),
Zachary Warebaf45c52014-10-17 13:59:18 -0500123 (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300124 with self.subTest(meth=meth.__qualname__, args=args):
125 meth(*args)
Zachary Warebaf45c52014-10-17 13:59:18 -0500126
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200127 for meth in [stdscr.clear, stdscr.clrtobot,
Zachary Warebaf45c52014-10-17 13:59:18 -0500128 stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
129 stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200130 stdscr.getbkgd, stdscr.getmaxyx,
Zachary Warebaf45c52014-10-17 13:59:18 -0500131 stdscr.getparyx, stdscr.getyx, stdscr.inch,
132 stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
133 win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
134 stdscr.standout, stdscr.standend, stdscr.syncdown,
135 stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300136 with self.subTest(meth=meth.__qualname__):
137 meth()
Zachary Warebaf45c52014-10-17 13:59:18 -0500138
139 stdscr.addnstr('1234', 3)
140 stdscr.addnstr('1234', 3, curses.A_BOLD)
141 stdscr.addnstr(4,4, '1234', 3)
142 stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
143
144 stdscr.attron(curses.A_BOLD)
145 stdscr.attroff(curses.A_BOLD)
146 stdscr.attrset(curses.A_BOLD)
147 stdscr.bkgd(' ')
148 stdscr.bkgd(' ', curses.A_REVERSE)
149 stdscr.bkgdset(' ')
150 stdscr.bkgdset(' ', curses.A_REVERSE)
151
152 win.border(65, 66, 67, 68,
153 69, 70, 71, 72)
154 win.border('|', '!', '-', '_',
155 '+', '\\', '#', '/')
156 with self.assertRaises(TypeError,
157 msg="Expected win.border() to raise TypeError"):
158 win.border(65, 66, 67, 68,
159 69, [], 71, 72)
160
Serhiy Storchaka4f469c02017-11-01 20:48:49 +0200161 win.box(65, 67)
162 win.box('!', '_')
163 win.box(b':', b'~')
164 self.assertRaises(TypeError, win.box, 65, 66, 67)
165 self.assertRaises(TypeError, win.box, 65)
166 win.box()
167
Zachary Warebaf45c52014-10-17 13:59:18 -0500168 stdscr.clearok(1)
169
170 win4 = stdscr.derwin(2,2)
171 win4 = stdscr.derwin(1,1, 5,5)
172 win4.mvderwin(9,9)
173
174 stdscr.echochar('a')
175 stdscr.echochar('a', curses.A_BOLD)
176 stdscr.hline('-', 5)
177 stdscr.hline('-', 5, curses.A_BOLD)
178 stdscr.hline(1,1,'-', 5)
179 stdscr.hline(1,1,'-', 5, curses.A_BOLD)
180
181 stdscr.idcok(1)
182 stdscr.idlok(1)
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200183 if hasattr(stdscr, 'immedok'):
184 stdscr.immedok(1)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200185 stdscr.immedok(0)
Zachary Warebaf45c52014-10-17 13:59:18 -0500186 stdscr.insch('c')
187 stdscr.insdelln(1)
188 stdscr.insnstr('abc', 3)
189 stdscr.insnstr('abc', 3, curses.A_BOLD)
190 stdscr.insnstr(5, 5, 'abc', 3)
191 stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
192
193 stdscr.insstr('def')
194 stdscr.insstr('def', curses.A_BOLD)
195 stdscr.insstr(5, 5, 'def')
196 stdscr.insstr(5, 5, 'def', curses.A_BOLD)
197 stdscr.is_linetouched(0)
198 stdscr.keypad(1)
199 stdscr.leaveok(1)
200 stdscr.move(3,3)
201 win.mvwin(2,2)
202 stdscr.nodelay(1)
203 stdscr.notimeout(1)
204 win2.overlay(win)
205 win2.overwrite(win)
206 win2.overlay(win, 1, 2, 2, 1, 3, 3)
207 win2.overwrite(win, 1, 2, 2, 1, 3, 3)
208 stdscr.redrawln(1,2)
209
210 stdscr.scrollok(1)
211 stdscr.scroll()
212 stdscr.scroll(2)
213 stdscr.scroll(-3)
214
215 stdscr.move(12, 2)
216 stdscr.setscrreg(10,15)
217 win3 = stdscr.subwin(10,10)
218 win3 = stdscr.subwin(10,10, 5,5)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200219 if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200220 stdscr.syncok(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500221 stdscr.timeout(5)
222 stdscr.touchline(5,5)
223 stdscr.touchline(5,5,0)
224 stdscr.vline('a', 3)
225 stdscr.vline('a', 3, curses.A_STANDOUT)
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200226 if hasattr(stdscr, 'chgat'):
227 stdscr.chgat(5, 2, 3, curses.A_BLINK)
228 stdscr.chgat(3, curses.A_BOLD)
229 stdscr.chgat(5, 8, curses.A_UNDERLINE)
230 stdscr.chgat(curses.A_BLINK)
Zachary Warebaf45c52014-10-17 13:59:18 -0500231 stdscr.refresh()
232
233 stdscr.vline(1,1, 'a', 3)
234 stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
235
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200236 if hasattr(stdscr, 'resize'):
237 stdscr.resize(25, 80)
238 if hasattr(stdscr, 'enclose'):
239 stdscr.enclose(10, 10)
Zachary Warebaf45c52014-10-17 13:59:18 -0500240
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200241 with tempfile.TemporaryFile() as f:
242 self.stdscr.putwin(f)
243 f.seek(0)
244 curses.getwin(f)
245
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700246 self.assertRaises(ValueError, stdscr.getstr, -400)
247 self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
Benjamin Peterson432ea4f2016-08-15 21:40:14 -0700248 self.assertRaises(ValueError, stdscr.instr, -2)
249 self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700250
Serhiy Storchakaf7eae0a2017-06-28 08:30:06 +0300251 def test_embedded_null_chars(self):
252 # reject embedded null bytes and characters
253 stdscr = self.stdscr
254 for arg in ['a', b'a']:
255 with self.subTest(arg=arg):
256 self.assertRaises(ValueError, stdscr.addstr, 'a\0')
257 self.assertRaises(ValueError, stdscr.addnstr, 'a\0', 1)
258 self.assertRaises(ValueError, stdscr.insstr, 'a\0')
259 self.assertRaises(ValueError, stdscr.insnstr, 'a\0', 1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500260
261 def test_module_funcs(self):
262 "Test module-level functions"
Zachary Warebaf45c52014-10-17 13:59:18 -0500263 for func in [curses.baudrate, curses.beep, curses.can_change_color,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200264 curses.doupdate, curses.flash, curses.flushinp,
Zachary Warebaf45c52014-10-17 13:59:18 -0500265 curses.has_colors, curses.has_ic, curses.has_il,
266 curses.isendwin, curses.killchar, curses.longname,
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200267 curses.noecho, curses.nonl, curses.noqiflush,
268 curses.termattrs, curses.termname, curses.erasechar,
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500269 curses.has_extended_color_support]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300270 with self.subTest(func=func.__qualname__):
271 func()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200272 if self.isatty:
273 for func in [curses.cbreak, curses.def_prog_mode,
274 curses.nocbreak, curses.noraw,
275 curses.reset_prog_mode]:
276 with self.subTest(func=func.__qualname__):
277 func()
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200278 if hasattr(curses, 'filter'):
279 curses.filter()
280 if hasattr(curses, 'getsyx'):
281 curses.getsyx()
Zachary Warebaf45c52014-10-17 13:59:18 -0500282
283 # Functions that actually need arguments
284 if curses.tigetstr("cnorm"):
285 curses.curs_set(1)
286 curses.delay_output(1)
287 curses.echo() ; curses.echo(1)
288
Zachary Warebaf45c52014-10-17 13:59:18 -0500289 curses.halfdelay(1)
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200290 if self.isatty:
291 curses.intrflush(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500292 curses.meta(1)
293 curses.napms(100)
294 curses.newpad(50,50)
295 win = curses.newwin(5,5)
296 win = curses.newwin(5,5, 1,1)
297 curses.nl() ; curses.nl(1)
298 curses.putp(b'abc')
299 curses.qiflush()
Serhiy Storchaka607501a2021-01-02 19:35:15 +0200300 if self.isatty:
301 curses.raw() ; curses.raw(1)
Anthony Sottileb32cb972019-10-31 02:13:48 -0700302 curses.set_escdelay(25)
303 self.assertEqual(curses.get_escdelay(), 25)
304 curses.set_tabsize(4)
305 self.assertEqual(curses.get_tabsize(), 4)
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200306 if hasattr(curses, 'setsyx'):
307 curses.setsyx(5,5)
Zachary Warebaf45c52014-10-17 13:59:18 -0500308 curses.tigetflag('hc')
309 curses.tigetnum('co')
310 curses.tigetstr('cr')
311 curses.tparm(b'cr')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200312 if hasattr(curses, 'typeahead'):
313 curses.typeahead(sys.__stdin__.fileno())
Zachary Warebaf45c52014-10-17 13:59:18 -0500314 curses.unctrl('a')
315 curses.ungetch('a')
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200316 if hasattr(curses, 'use_env'):
317 curses.use_env(1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500318
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300319 # Functions only available on a few platforms
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200320
321 def bad_colors(self):
322 return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
323
324 def bad_colors2(self):
325 return (curses.COLORS, 2**31, 2**63, 2**64)
326
327 def bad_pairs(self):
328 return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
329
330 @requires_colors
331 def test_color_content(self):
332 self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
333 curses.color_content(0)
334 curses.color_content(curses.COLORS - 1)
335
336 for color in self.bad_colors():
337 self.assertRaises(ValueError, curses.color_content, color)
338
339 @requires_colors
340 def test_init_color(self):
341 if not curses.can_change_color:
342 self.skipTest('cannot change color')
343
344 old = curses.color_content(0)
345 try:
346 curses.init_color(0, *old)
347 except curses.error:
348 self.skipTest('cannot change color (init_color() failed)')
349 self.addCleanup(curses.init_color, 0, *old)
350 curses.init_color(0, 0, 0, 0)
351 self.assertEqual(curses.color_content(0), (0, 0, 0))
352 curses.init_color(0, 1000, 1000, 1000)
353 self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
354
355 old = curses.color_content(curses.COLORS - 1)
356 curses.init_color(curses.COLORS - 1, *old)
357 self.addCleanup(curses.init_color, curses.COLORS - 1, *old)
358 curses.init_color(curses.COLORS - 1, 0, 500, 1000)
359 self.assertEqual(curses.color_content(curses.COLORS - 1), (0, 500, 1000))
360
361 for color in self.bad_colors():
362 self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
363 for comp in (-1, 1001):
364 self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
365 self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
366 self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
367
368 @requires_colors
369 def test_pair_content(self):
370 if not hasattr(curses, 'use_default_colors'):
371 self.assertEqual(curses.pair_content(0),
372 (curses.COLOR_WHITE, curses.COLOR_BLACK))
373 curses.pair_content(0)
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300374 curses.pair_content(curses.COLOR_PAIRS - 1)
Zachary Warebaf45c52014-10-17 13:59:18 -0500375
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200376 for pair in self.bad_pairs():
377 self.assertRaises(ValueError, curses.pair_content, pair)
Zachary Warebaf45c52014-10-17 13:59:18 -0500378
Serhiy Storchaka1470edd2021-01-03 22:51:11 +0200379 @requires_colors
380 def test_init_pair(self):
381 old = curses.pair_content(1)
382 curses.init_pair(1, *old)
383 self.addCleanup(curses.init_pair, 1, *old)
384
385 curses.init_pair(1, 0, 0)
386 self.assertEqual(curses.pair_content(1), (0, 0))
387 curses.init_pair(1, curses.COLORS - 1, curses.COLORS - 1)
388 self.assertEqual(curses.pair_content(1),
389 (curses.COLORS - 1, curses.COLORS - 1))
390 curses.init_pair(curses.COLOR_PAIRS - 1, 2, 3)
391 self.assertEqual(curses.pair_content(curses.COLOR_PAIRS - 1), (2, 3))
392
393 for pair in self.bad_pairs():
394 self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
395 for color in self.bad_colors2():
396 self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
397 self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
398
399 @requires_colors
400 def test_color_attrs(self):
401 for pair in 0, 1, 255:
402 attr = curses.color_pair(pair)
403 self.assertEqual(curses.pair_number(attr), pair, attr)
404 self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
405 self.assertEqual(curses.color_pair(0), 0)
406 self.assertEqual(curses.pair_number(0), 0)
407
408 @requires_curses_func('use_default_colors')
409 @requires_colors
410 def test_use_default_colors(self):
411 self.assertIn(curses.pair_content(0),
412 ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
413 curses.use_default_colors()
414 self.assertEqual(curses.pair_content(0), (-1, -1))
Hans Petter Janssonda4e09f2020-08-03 22:51:33 -0500415
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300416 @requires_curses_func('keyname')
417 def test_keyname(self):
418 curses.keyname(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500419
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300420 @requires_curses_func('has_key')
421 def test_has_key(self):
422 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500423
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300424 @requires_curses_func('getmouse')
425 def test_getmouse(self):
426 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
427 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +0100428 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300429 curses.mouseinterval(10)
430 # just verify these don't cause errors
431 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
432 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500433
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200434 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500435 def test_userptr_without_set(self):
436 w = curses.newwin(10, 10)
437 p = curses.panel.new_panel(w)
438 # try to access userptr() before calling set_userptr() -- segfaults
439 with self.assertRaises(curses.panel.error,
440 msg='userptr should fail since not set'):
441 p.userptr()
442
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200443 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500444 def test_userptr_memory_leak(self):
445 w = curses.newwin(10, 10)
446 p = curses.panel.new_panel(w)
447 obj = object()
448 nrefs = sys.getrefcount(obj)
449 for i in range(100):
450 p.set_userptr(obj)
451
452 p.set_userptr(None)
453 self.assertEqual(sys.getrefcount(obj), nrefs,
454 "set_userptr leaked references")
455
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200456 @requires_curses_func('panel')
Zachary Warebaf45c52014-10-17 13:59:18 -0500457 def test_userptr_segfault(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +0200458 w = curses.newwin(10, 10)
459 panel = curses.panel.new_panel(w)
Zachary Warebaf45c52014-10-17 13:59:18 -0500460 class A:
461 def __del__(self):
462 panel.set_userptr(None)
463 panel.set_userptr(A())
464 panel.set_userptr(None)
465
Serhiy Storchakabaac01e2017-10-31 13:56:44 +0200466 @requires_curses_func('panel')
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300467 def test_new_curses_panel(self):
Serhiy Storchakaa7723d82017-11-03 20:29:33 +0200468 w = curses.newwin(10, 10)
469 panel = curses.panel.new_panel(w)
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300470 self.assertRaises(TypeError, type(panel))
471
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300472 @requires_curses_func('is_term_resized')
473 def test_is_term_resized(self):
474 curses.is_term_resized(*self.stdscr.getmaxyx())
475
476 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -0500477 def test_resize_term(self):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300478 curses.resize_term(*self.stdscr.getmaxyx())
479
480 @requires_curses_func('resizeterm')
481 def test_resizeterm(self):
Zachary Warebaf45c52014-10-17 13:59:18 -0500482 lines, cols = curses.LINES, curses.COLS
483 new_lines = lines - 1
484 new_cols = cols + 1
485 curses.resizeterm(new_lines, new_cols)
486
487 self.assertEqual(curses.LINES, new_lines)
488 self.assertEqual(curses.COLS, new_cols)
489
490 def test_issue6243(self):
491 curses.ungetch(1025)
492 self.stdscr.getkey()
493
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300494 @requires_curses_func('unget_wch')
Serhiy Storchakab232df92018-10-30 13:22:42 +0200495 @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
496 "unget_wch is broken in ncurses 5.7 and earlier")
Zachary Warebaf45c52014-10-17 13:59:18 -0500497 def test_unget_wch(self):
498 stdscr = self.stdscr
499 encoding = stdscr.encoding
500 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
501 try:
502 ch.encode(encoding)
503 except UnicodeEncodeError:
504 continue
505 try:
506 curses.unget_wch(ch)
507 except Exception as err:
508 self.fail("unget_wch(%a) failed with encoding %s: %s"
509 % (ch, stdscr.encoding, err))
510 read = stdscr.get_wch()
511 self.assertEqual(read, ch)
512
513 code = ord(ch)
514 curses.unget_wch(code)
515 read = stdscr.get_wch()
516 self.assertEqual(read, ch)
517
518 def test_issue10570(self):
519 b = curses.tparm(curses.tigetstr("cup"), 5, 3)
520 self.assertIs(type(b), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500521
522 def test_encoding(self):
523 stdscr = self.stdscr
524 import codecs
525 encoding = stdscr.encoding
526 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -0500527 with self.assertRaises(TypeError):
528 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -0500529 stdscr.encoding = encoding
530 with self.assertRaises(TypeError):
531 del stdscr.encoding
532
533 def test_issue21088(self):
534 stdscr = self.stdscr
535 #
536 # http://bugs.python.org/issue21088
537 #
538 # the bug:
539 # when converting curses.window.addch to Argument Clinic
540 # the first two parameters were switched.
541
542 # if someday we can represent the signature of addch
543 # we will need to rewrite this test.
544 try:
545 signature = inspect.signature(stdscr.addch)
546 self.assertFalse(signature)
547 except ValueError:
548 # not generating a signature is fine.
549 pass
550
551 # So. No signature for addch.
552 # But Argument Clinic gave us a human-readable equivalent
553 # as the first line of the docstring. So we parse that,
554 # and ensure that the parameters appear in the correct order.
555 # Since this is parsing output from Argument Clinic, we can
556 # be reasonably certain the generated parsing code will be
557 # correct too.
558 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300559 self.assertIn("[y, x,]", human_readable_signature)
560
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +0200561 def test_issue13051(self):
562 stdscr = self.stdscr
Serhiy Storchaka894ebd02017-11-01 14:34:20 +0200563 if not hasattr(stdscr, 'resize'):
564 raise unittest.SkipTest('requires curses.window.resize')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +0200565 box = curses.textpad.Textbox(stdscr, insert_mode=True)
566 lines, cols = stdscr.getmaxyx()
567 stdscr.resize(lines-2, cols-2)
568 # this may cause infinite recursion, leading to a RuntimeError
569 box._insert_printable_char('a')
570
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300571
572class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -0500573
Berker Peksag53926f12016-08-19 17:59:01 +0300574 @requires_curses_func('update_lines_cols')
Steve Dowerd2bc3892015-04-15 18:06:05 -0400575 def test_update_lines_cols(self):
576 # this doesn't actually test that LINES and COLS are updated,
577 # because we can't automate changing them. See Issue #4254 for
578 # a manual test script. We can only test that the function
579 # can be called.
580 curses.update_lines_cols()
581
Serhiy Storchakab232df92018-10-30 13:22:42 +0200582 @requires_curses_func('ncurses_version')
583 def test_ncurses_version(self):
584 v = curses.ncurses_version
585 self.assertIsInstance(v[:], tuple)
586 self.assertEqual(len(v), 3)
587 self.assertIsInstance(v[0], int)
588 self.assertIsInstance(v[1], int)
589 self.assertIsInstance(v[2], int)
590 self.assertIsInstance(v.major, int)
591 self.assertIsInstance(v.minor, int)
592 self.assertIsInstance(v.patch, int)
593 self.assertEqual(v[0], v.major)
594 self.assertEqual(v[1], v.minor)
595 self.assertEqual(v[2], v.patch)
596 self.assertGreaterEqual(v.major, 0)
597 self.assertGreaterEqual(v.minor, 0)
598 self.assertGreaterEqual(v.patch, 0)
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000599
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300600class TestAscii(unittest.TestCase):
601
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300602 def test_controlnames(self):
603 for name in curses.ascii.controlnames:
604 self.assertTrue(hasattr(curses.ascii, name), name)
605
606 def test_ctypes(self):
607 def check(func, expected):
608 with self.subTest(ch=c, func=func):
609 self.assertEqual(func(i), expected)
610 self.assertEqual(func(c), expected)
611
612 for i in range(256):
613 c = chr(i)
614 b = bytes([i])
615 check(curses.ascii.isalnum, b.isalnum())
616 check(curses.ascii.isalpha, b.isalpha())
617 check(curses.ascii.isdigit, b.isdigit())
618 check(curses.ascii.islower, b.islower())
619 check(curses.ascii.isspace, b.isspace())
620 check(curses.ascii.isupper, b.isupper())
621
622 check(curses.ascii.isascii, i < 128)
623 check(curses.ascii.ismeta, i >= 128)
624 check(curses.ascii.isctrl, i < 32)
625 check(curses.ascii.iscntrl, i < 32 or i == 127)
626 check(curses.ascii.isblank, c in ' \t')
627 check(curses.ascii.isgraph, 32 < i <= 126)
628 check(curses.ascii.isprint, 32 <= i <= 126)
629 check(curses.ascii.ispunct, c in string.punctuation)
630 check(curses.ascii.isxdigit, c in string.hexdigits)
631
Serhiy Storchaka283de2b2016-12-28 10:04:27 +0200632 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
633 self.assertFalse(curses.ascii.isalnum(i))
634 self.assertFalse(curses.ascii.isalpha(i))
635 self.assertFalse(curses.ascii.isdigit(i))
636 self.assertFalse(curses.ascii.islower(i))
637 self.assertFalse(curses.ascii.isspace(i))
638 self.assertFalse(curses.ascii.isupper(i))
639
640 self.assertFalse(curses.ascii.isascii(i))
641 self.assertFalse(curses.ascii.isctrl(i))
642 self.assertFalse(curses.ascii.iscntrl(i))
643 self.assertFalse(curses.ascii.isblank(i))
644 self.assertFalse(curses.ascii.isgraph(i))
645 self.assertFalse(curses.ascii.isprint(i))
646 self.assertFalse(curses.ascii.ispunct(i))
647 self.assertFalse(curses.ascii.isxdigit(i))
648
649 self.assertFalse(curses.ascii.ismeta(-1))
650
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300651 def test_ascii(self):
652 ascii = curses.ascii.ascii
653 self.assertEqual(ascii('\xc1'), 'A')
654 self.assertEqual(ascii('A'), 'A')
655 self.assertEqual(ascii(ord('\xc1')), ord('A'))
656
657 def test_ctrl(self):
658 ctrl = curses.ascii.ctrl
659 self.assertEqual(ctrl('J'), '\n')
660 self.assertEqual(ctrl('\n'), '\n')
661 self.assertEqual(ctrl('@'), '\0')
662 self.assertEqual(ctrl(ord('J')), ord('\n'))
663
664 def test_alt(self):
665 alt = curses.ascii.alt
666 self.assertEqual(alt('\n'), '\x8a')
667 self.assertEqual(alt('A'), '\xc1')
668 self.assertEqual(alt(ord('A')), 0xc1)
669
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300670 def test_unctrl(self):
671 unctrl = curses.ascii.unctrl
672 self.assertEqual(unctrl('a'), 'a')
673 self.assertEqual(unctrl('A'), 'A')
674 self.assertEqual(unctrl(';'), ';')
675 self.assertEqual(unctrl(' '), ' ')
676 self.assertEqual(unctrl('\x7f'), '^?')
677 self.assertEqual(unctrl('\n'), '^J')
678 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300679 self.assertEqual(unctrl(ord('A')), 'A')
680 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300681 # Meta-bit characters
682 self.assertEqual(unctrl('\x8a'), '!^J')
683 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300684 self.assertEqual(unctrl(ord('\x8a')), '!^J')
685 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300686
687
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000688if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -0500689 unittest.main()