blob: 897d738f57d601494aedcec558614c746934a5b7 [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#
7# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
Neal Norwitz88bbd732006-01-10 07:05:44 +00008# init_color()
9# Only called, not tested: getmouse(), ungetmouse()
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000010#
11
Zachary Warebaf45c52014-10-17 13:59:18 -050012import os
Serhiy Storchaka514f9732016-06-18 22:08:11 +030013import string
Zachary Warebaf45c52014-10-17 13:59:18 -050014import sys
15import tempfile
16import unittest
17
18from test.support import requires, import_module, verbose
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000019
20# Optionally test curses module. This currently requires that the
21# 'curses' resource be given on the regrtest command line using the -u
22# option. If not available, nothing after this line will be executed.
Larry Hastings9147a962014-05-04 04:41:18 -070023import inspect
Neal Norwitz9f39f682006-01-06 04:18:21 +000024requires('curses')
25
R. David Murraya21e4ca2009-03-31 23:16:50 +000026# If either of these don't exist, skip the tests.
27curses = import_module('curses')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030028import_module('curses.panel')
29import_module('curses.ascii')
R. David Murraya21e4ca2009-03-31 23:16:50 +000030
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030031def requires_curses_func(name):
32 return unittest.skipUnless(hasattr(curses, name),
33 'requires curses.%s' % name)
Mark Dickinson945e2422010-02-21 13:42:03 +000034
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030035term = os.environ.get('TERM')
36
37# If newterm was supported we could use it instead of initscr and not exit
38@unittest.skipIf(not term or term == 'unknown',
Zachary Warebaf45c52014-10-17 13:59:18 -050039 "$TERM=%r, calling initscr() may cause exit" % term)
40@unittest.skipIf(sys.platform == "cygwin",
41 "cygwin's curses mostly just hangs")
42class TestCurses(unittest.TestCase):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030043
Zachary Warebaf45c52014-10-17 13:59:18 -050044 @classmethod
45 def setUpClass(cls):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030046 if not sys.__stdout__.isatty():
Serhiy Storchaka53a00352016-05-22 18:16:20 +030047 # Temporary skip tests on non-tty
Serhiy Storchaka0a91e432016-05-22 18:23:36 +030048 raise unittest.SkipTest('sys.__stdout__ is not a tty')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030049 cls.tmp = tempfile.TemporaryFile()
50 fd = cls.tmp.fileno()
51 else:
52 cls.tmp = None
53 fd = sys.__stdout__.fileno()
54 # testing setupterm() inside initscr/endwin
55 # causes terminal breakage
56 curses.setupterm(fd=fd)
57
58 @classmethod
59 def tearDownClass(cls):
60 if cls.tmp:
61 cls.tmp.close()
62 del cls.tmp
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000063
Zachary Warebaf45c52014-10-17 13:59:18 -050064 def setUp(self):
65 if verbose:
66 # just to make the test output a little more readable
67 print()
68 self.stdscr = curses.initscr()
69 curses.savetty()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000070
Zachary Warebaf45c52014-10-17 13:59:18 -050071 def tearDown(self):
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000072 curses.resetty()
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000073 curses.endwin()
Zachary Warebaf45c52014-10-17 13:59:18 -050074
75 def test_window_funcs(self):
76 "Test the methods of windows"
77 stdscr = self.stdscr
78 win = curses.newwin(10,10)
79 win = curses.newwin(5,5, 5,5)
80 win2 = curses.newwin(15,15, 5,5)
81
82 for meth in [stdscr.addch, stdscr.addstr]:
83 for args in [('a'), ('a', curses.A_BOLD),
84 (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030085 with self.subTest(meth=meth.__qualname__, args=args):
86 meth(*args)
Zachary Warebaf45c52014-10-17 13:59:18 -050087
88 for meth in [stdscr.box, stdscr.clear, stdscr.clrtobot,
89 stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
90 stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
91 stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
92 stdscr.getparyx, stdscr.getyx, stdscr.inch,
93 stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
94 win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
95 stdscr.standout, stdscr.standend, stdscr.syncdown,
96 stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030097 with self.subTest(meth=meth.__qualname__):
98 meth()
Zachary Warebaf45c52014-10-17 13:59:18 -050099
100 stdscr.addnstr('1234', 3)
101 stdscr.addnstr('1234', 3, curses.A_BOLD)
102 stdscr.addnstr(4,4, '1234', 3)
103 stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
104
105 stdscr.attron(curses.A_BOLD)
106 stdscr.attroff(curses.A_BOLD)
107 stdscr.attrset(curses.A_BOLD)
108 stdscr.bkgd(' ')
109 stdscr.bkgd(' ', curses.A_REVERSE)
110 stdscr.bkgdset(' ')
111 stdscr.bkgdset(' ', curses.A_REVERSE)
112
113 win.border(65, 66, 67, 68,
114 69, 70, 71, 72)
115 win.border('|', '!', '-', '_',
116 '+', '\\', '#', '/')
117 with self.assertRaises(TypeError,
118 msg="Expected win.border() to raise TypeError"):
119 win.border(65, 66, 67, 68,
120 69, [], 71, 72)
121
122 stdscr.clearok(1)
123
124 win4 = stdscr.derwin(2,2)
125 win4 = stdscr.derwin(1,1, 5,5)
126 win4.mvderwin(9,9)
127
128 stdscr.echochar('a')
129 stdscr.echochar('a', curses.A_BOLD)
130 stdscr.hline('-', 5)
131 stdscr.hline('-', 5, curses.A_BOLD)
132 stdscr.hline(1,1,'-', 5)
133 stdscr.hline(1,1,'-', 5, curses.A_BOLD)
134
135 stdscr.idcok(1)
136 stdscr.idlok(1)
137 stdscr.immedok(1)
138 stdscr.insch('c')
139 stdscr.insdelln(1)
140 stdscr.insnstr('abc', 3)
141 stdscr.insnstr('abc', 3, curses.A_BOLD)
142 stdscr.insnstr(5, 5, 'abc', 3)
143 stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
144
145 stdscr.insstr('def')
146 stdscr.insstr('def', curses.A_BOLD)
147 stdscr.insstr(5, 5, 'def')
148 stdscr.insstr(5, 5, 'def', curses.A_BOLD)
149 stdscr.is_linetouched(0)
150 stdscr.keypad(1)
151 stdscr.leaveok(1)
152 stdscr.move(3,3)
153 win.mvwin(2,2)
154 stdscr.nodelay(1)
155 stdscr.notimeout(1)
156 win2.overlay(win)
157 win2.overwrite(win)
158 win2.overlay(win, 1, 2, 2, 1, 3, 3)
159 win2.overwrite(win, 1, 2, 2, 1, 3, 3)
160 stdscr.redrawln(1,2)
161
162 stdscr.scrollok(1)
163 stdscr.scroll()
164 stdscr.scroll(2)
165 stdscr.scroll(-3)
166
167 stdscr.move(12, 2)
168 stdscr.setscrreg(10,15)
169 win3 = stdscr.subwin(10,10)
170 win3 = stdscr.subwin(10,10, 5,5)
171 stdscr.syncok(1)
172 stdscr.timeout(5)
173 stdscr.touchline(5,5)
174 stdscr.touchline(5,5,0)
175 stdscr.vline('a', 3)
176 stdscr.vline('a', 3, curses.A_STANDOUT)
177 stdscr.chgat(5, 2, 3, curses.A_BLINK)
178 stdscr.chgat(3, curses.A_BOLD)
179 stdscr.chgat(5, 8, curses.A_UNDERLINE)
180 stdscr.chgat(curses.A_BLINK)
181 stdscr.refresh()
182
183 stdscr.vline(1,1, 'a', 3)
184 stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
185
186 if hasattr(curses, 'resize'):
187 stdscr.resize()
188 if hasattr(curses, 'enclose'):
189 stdscr.enclose()
190
191
192 def test_module_funcs(self):
193 "Test module-level functions"
Zachary Warebaf45c52014-10-17 13:59:18 -0500194 for func in [curses.baudrate, curses.beep, curses.can_change_color,
195 curses.cbreak, curses.def_prog_mode, curses.doupdate,
196 curses.filter, curses.flash, curses.flushinp,
197 curses.has_colors, curses.has_ic, curses.has_il,
198 curses.isendwin, curses.killchar, curses.longname,
199 curses.nocbreak, curses.noecho, curses.nonl,
200 curses.noqiflush, curses.noraw,
201 curses.reset_prog_mode, curses.termattrs,
202 curses.termname, curses.erasechar, curses.getsyx]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300203 with self.subTest(func=func.__qualname__):
204 func()
Zachary Warebaf45c52014-10-17 13:59:18 -0500205
206 # Functions that actually need arguments
207 if curses.tigetstr("cnorm"):
208 curses.curs_set(1)
209 curses.delay_output(1)
210 curses.echo() ; curses.echo(1)
211
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300212 with tempfile.TemporaryFile() as f:
213 self.stdscr.putwin(f)
214 f.seek(0)
215 curses.getwin(f)
Zachary Warebaf45c52014-10-17 13:59:18 -0500216
217 curses.halfdelay(1)
218 curses.intrflush(1)
219 curses.meta(1)
220 curses.napms(100)
221 curses.newpad(50,50)
222 win = curses.newwin(5,5)
223 win = curses.newwin(5,5, 1,1)
224 curses.nl() ; curses.nl(1)
225 curses.putp(b'abc')
226 curses.qiflush()
227 curses.raw() ; curses.raw(1)
228 curses.setsyx(5,5)
229 curses.tigetflag('hc')
230 curses.tigetnum('co')
231 curses.tigetstr('cr')
232 curses.tparm(b'cr')
233 curses.typeahead(sys.__stdin__.fileno())
234 curses.unctrl('a')
235 curses.ungetch('a')
236 curses.use_env(1)
237
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300238 # Functions only available on a few platforms
239 def test_colors_funcs(self):
240 if not curses.has_colors():
241 self.skip('requires colors support')
242 curses.start_color()
243 curses.init_pair(2, 1,1)
244 curses.color_content(1)
245 curses.color_pair(2)
246 curses.pair_content(curses.COLOR_PAIRS - 1)
247 curses.pair_number(0)
Zachary Warebaf45c52014-10-17 13:59:18 -0500248
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300249 if hasattr(curses, 'use_default_colors'):
250 curses.use_default_colors()
Zachary Warebaf45c52014-10-17 13:59:18 -0500251
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300252 @requires_curses_func('keyname')
253 def test_keyname(self):
254 curses.keyname(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500255
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300256 @requires_curses_func('has_key')
257 def test_has_key(self):
258 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500259
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300260 @requires_curses_func('getmouse')
261 def test_getmouse(self):
262 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
263 if availmask == 0:
264 self.skip('mouse stuff not available')
265 curses.mouseinterval(10)
266 # just verify these don't cause errors
267 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
268 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500269
270 def test_userptr_without_set(self):
271 w = curses.newwin(10, 10)
272 p = curses.panel.new_panel(w)
273 # try to access userptr() before calling set_userptr() -- segfaults
274 with self.assertRaises(curses.panel.error,
275 msg='userptr should fail since not set'):
276 p.userptr()
277
278 def test_userptr_memory_leak(self):
279 w = curses.newwin(10, 10)
280 p = curses.panel.new_panel(w)
281 obj = object()
282 nrefs = sys.getrefcount(obj)
283 for i in range(100):
284 p.set_userptr(obj)
285
286 p.set_userptr(None)
287 self.assertEqual(sys.getrefcount(obj), nrefs,
288 "set_userptr leaked references")
289
290 def test_userptr_segfault(self):
291 panel = curses.panel.new_panel(self.stdscr)
292 class A:
293 def __del__(self):
294 panel.set_userptr(None)
295 panel.set_userptr(A())
296 panel.set_userptr(None)
297
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300298 def test_new_curses_panel(self):
299 panel = curses.panel.new_panel(self.stdscr)
300 self.assertRaises(TypeError, type(panel))
301
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300302 @requires_curses_func('is_term_resized')
303 def test_is_term_resized(self):
304 curses.is_term_resized(*self.stdscr.getmaxyx())
305
306 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -0500307 def test_resize_term(self):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300308 curses.resize_term(*self.stdscr.getmaxyx())
309
310 @requires_curses_func('resizeterm')
311 def test_resizeterm(self):
312 stdscr = self.stdscr
Zachary Warebaf45c52014-10-17 13:59:18 -0500313 lines, cols = curses.LINES, curses.COLS
314 new_lines = lines - 1
315 new_cols = cols + 1
316 curses.resizeterm(new_lines, new_cols)
317
318 self.assertEqual(curses.LINES, new_lines)
319 self.assertEqual(curses.COLS, new_cols)
320
321 def test_issue6243(self):
322 curses.ungetch(1025)
323 self.stdscr.getkey()
324
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300325 @requires_curses_func('unget_wch')
Zachary Warebaf45c52014-10-17 13:59:18 -0500326 def test_unget_wch(self):
327 stdscr = self.stdscr
328 encoding = stdscr.encoding
329 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
330 try:
331 ch.encode(encoding)
332 except UnicodeEncodeError:
333 continue
334 try:
335 curses.unget_wch(ch)
336 except Exception as err:
337 self.fail("unget_wch(%a) failed with encoding %s: %s"
338 % (ch, stdscr.encoding, err))
339 read = stdscr.get_wch()
340 self.assertEqual(read, ch)
341
342 code = ord(ch)
343 curses.unget_wch(code)
344 read = stdscr.get_wch()
345 self.assertEqual(read, ch)
346
347 def test_issue10570(self):
348 b = curses.tparm(curses.tigetstr("cup"), 5, 3)
349 self.assertIs(type(b), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500350
351 def test_encoding(self):
352 stdscr = self.stdscr
353 import codecs
354 encoding = stdscr.encoding
355 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -0500356 with self.assertRaises(TypeError):
357 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -0500358 stdscr.encoding = encoding
359 with self.assertRaises(TypeError):
360 del stdscr.encoding
361
362 def test_issue21088(self):
363 stdscr = self.stdscr
364 #
365 # http://bugs.python.org/issue21088
366 #
367 # the bug:
368 # when converting curses.window.addch to Argument Clinic
369 # the first two parameters were switched.
370
371 # if someday we can represent the signature of addch
372 # we will need to rewrite this test.
373 try:
374 signature = inspect.signature(stdscr.addch)
375 self.assertFalse(signature)
376 except ValueError:
377 # not generating a signature is fine.
378 pass
379
380 # So. No signature for addch.
381 # But Argument Clinic gave us a human-readable equivalent
382 # as the first line of the docstring. So we parse that,
383 # and ensure that the parameters appear in the correct order.
384 # Since this is parsing output from Argument Clinic, we can
385 # be reasonably certain the generated parsing code will be
386 # correct too.
387 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300388 self.assertIn("[y, x,]", human_readable_signature)
389
390
391class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -0500392
Steve Dowerd2bc3892015-04-15 18:06:05 -0400393 def test_update_lines_cols(self):
394 # this doesn't actually test that LINES and COLS are updated,
395 # because we can't automate changing them. See Issue #4254 for
396 # a manual test script. We can only test that the function
397 # can be called.
398 curses.update_lines_cols()
399
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000400
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300401class TestAscii(unittest.TestCase):
402
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300403 def test_controlnames(self):
404 for name in curses.ascii.controlnames:
405 self.assertTrue(hasattr(curses.ascii, name), name)
406
407 def test_ctypes(self):
408 def check(func, expected):
409 with self.subTest(ch=c, func=func):
410 self.assertEqual(func(i), expected)
411 self.assertEqual(func(c), expected)
412
413 for i in range(256):
414 c = chr(i)
415 b = bytes([i])
416 check(curses.ascii.isalnum, b.isalnum())
417 check(curses.ascii.isalpha, b.isalpha())
418 check(curses.ascii.isdigit, b.isdigit())
419 check(curses.ascii.islower, b.islower())
420 check(curses.ascii.isspace, b.isspace())
421 check(curses.ascii.isupper, b.isupper())
422
423 check(curses.ascii.isascii, i < 128)
424 check(curses.ascii.ismeta, i >= 128)
425 check(curses.ascii.isctrl, i < 32)
426 check(curses.ascii.iscntrl, i < 32 or i == 127)
427 check(curses.ascii.isblank, c in ' \t')
428 check(curses.ascii.isgraph, 32 < i <= 126)
429 check(curses.ascii.isprint, 32 <= i <= 126)
430 check(curses.ascii.ispunct, c in string.punctuation)
431 check(curses.ascii.isxdigit, c in string.hexdigits)
432
433 def test_ascii(self):
434 ascii = curses.ascii.ascii
435 self.assertEqual(ascii('\xc1'), 'A')
436 self.assertEqual(ascii('A'), 'A')
437 self.assertEqual(ascii(ord('\xc1')), ord('A'))
438
439 def test_ctrl(self):
440 ctrl = curses.ascii.ctrl
441 self.assertEqual(ctrl('J'), '\n')
442 self.assertEqual(ctrl('\n'), '\n')
443 self.assertEqual(ctrl('@'), '\0')
444 self.assertEqual(ctrl(ord('J')), ord('\n'))
445
446 def test_alt(self):
447 alt = curses.ascii.alt
448 self.assertEqual(alt('\n'), '\x8a')
449 self.assertEqual(alt('A'), '\xc1')
450 self.assertEqual(alt(ord('A')), 0xc1)
451
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300452 def test_unctrl(self):
453 unctrl = curses.ascii.unctrl
454 self.assertEqual(unctrl('a'), 'a')
455 self.assertEqual(unctrl('A'), 'A')
456 self.assertEqual(unctrl(';'), ';')
457 self.assertEqual(unctrl(' '), ' ')
458 self.assertEqual(unctrl('\x7f'), '^?')
459 self.assertEqual(unctrl('\n'), '^J')
460 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300461 self.assertEqual(unctrl(ord('A')), 'A')
462 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300463 # Meta-bit characters
464 self.assertEqual(unctrl('\x8a'), '!^J')
465 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300466 self.assertEqual(unctrl(ord('\x8a')), '!^J')
467 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300468
469
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000470if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -0500471 unittest.main()