blob: d020fd2ccabde22051696388587280b26854b016 [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
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700191 self.assertRaises(ValueError, stdscr.getstr, -400)
192 self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
Benjamin Peterson432ea4f2016-08-15 21:40:14 -0700193 self.assertRaises(ValueError, stdscr.instr, -2)
194 self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700195
Zachary Warebaf45c52014-10-17 13:59:18 -0500196
197 def test_module_funcs(self):
198 "Test module-level functions"
Zachary Warebaf45c52014-10-17 13:59:18 -0500199 for func in [curses.baudrate, curses.beep, curses.can_change_color,
200 curses.cbreak, curses.def_prog_mode, curses.doupdate,
201 curses.filter, curses.flash, curses.flushinp,
202 curses.has_colors, curses.has_ic, curses.has_il,
203 curses.isendwin, curses.killchar, curses.longname,
204 curses.nocbreak, curses.noecho, curses.nonl,
205 curses.noqiflush, curses.noraw,
206 curses.reset_prog_mode, curses.termattrs,
207 curses.termname, curses.erasechar, curses.getsyx]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300208 with self.subTest(func=func.__qualname__):
209 func()
Zachary Warebaf45c52014-10-17 13:59:18 -0500210
211 # Functions that actually need arguments
212 if curses.tigetstr("cnorm"):
213 curses.curs_set(1)
214 curses.delay_output(1)
215 curses.echo() ; curses.echo(1)
216
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300217 with tempfile.TemporaryFile() as f:
218 self.stdscr.putwin(f)
219 f.seek(0)
220 curses.getwin(f)
Zachary Warebaf45c52014-10-17 13:59:18 -0500221
222 curses.halfdelay(1)
223 curses.intrflush(1)
224 curses.meta(1)
225 curses.napms(100)
226 curses.newpad(50,50)
227 win = curses.newwin(5,5)
228 win = curses.newwin(5,5, 1,1)
229 curses.nl() ; curses.nl(1)
230 curses.putp(b'abc')
231 curses.qiflush()
232 curses.raw() ; curses.raw(1)
233 curses.setsyx(5,5)
234 curses.tigetflag('hc')
235 curses.tigetnum('co')
236 curses.tigetstr('cr')
237 curses.tparm(b'cr')
238 curses.typeahead(sys.__stdin__.fileno())
239 curses.unctrl('a')
240 curses.ungetch('a')
241 curses.use_env(1)
242
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300243 # Functions only available on a few platforms
244 def test_colors_funcs(self):
245 if not curses.has_colors():
246 self.skip('requires colors support')
247 curses.start_color()
248 curses.init_pair(2, 1,1)
249 curses.color_content(1)
250 curses.color_pair(2)
251 curses.pair_content(curses.COLOR_PAIRS - 1)
252 curses.pair_number(0)
Zachary Warebaf45c52014-10-17 13:59:18 -0500253
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300254 if hasattr(curses, 'use_default_colors'):
255 curses.use_default_colors()
Zachary Warebaf45c52014-10-17 13:59:18 -0500256
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300257 @requires_curses_func('keyname')
258 def test_keyname(self):
259 curses.keyname(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500260
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300261 @requires_curses_func('has_key')
262 def test_has_key(self):
263 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500264
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300265 @requires_curses_func('getmouse')
266 def test_getmouse(self):
267 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
268 if availmask == 0:
269 self.skip('mouse stuff not available')
270 curses.mouseinterval(10)
271 # just verify these don't cause errors
272 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
273 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500274
275 def test_userptr_without_set(self):
276 w = curses.newwin(10, 10)
277 p = curses.panel.new_panel(w)
278 # try to access userptr() before calling set_userptr() -- segfaults
279 with self.assertRaises(curses.panel.error,
280 msg='userptr should fail since not set'):
281 p.userptr()
282
283 def test_userptr_memory_leak(self):
284 w = curses.newwin(10, 10)
285 p = curses.panel.new_panel(w)
286 obj = object()
287 nrefs = sys.getrefcount(obj)
288 for i in range(100):
289 p.set_userptr(obj)
290
291 p.set_userptr(None)
292 self.assertEqual(sys.getrefcount(obj), nrefs,
293 "set_userptr leaked references")
294
295 def test_userptr_segfault(self):
296 panel = curses.panel.new_panel(self.stdscr)
297 class A:
298 def __del__(self):
299 panel.set_userptr(None)
300 panel.set_userptr(A())
301 panel.set_userptr(None)
302
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300303 def test_new_curses_panel(self):
304 panel = curses.panel.new_panel(self.stdscr)
305 self.assertRaises(TypeError, type(panel))
306
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300307 @requires_curses_func('is_term_resized')
308 def test_is_term_resized(self):
309 curses.is_term_resized(*self.stdscr.getmaxyx())
310
311 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -0500312 def test_resize_term(self):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300313 curses.resize_term(*self.stdscr.getmaxyx())
314
315 @requires_curses_func('resizeterm')
316 def test_resizeterm(self):
317 stdscr = self.stdscr
Zachary Warebaf45c52014-10-17 13:59:18 -0500318 lines, cols = curses.LINES, curses.COLS
319 new_lines = lines - 1
320 new_cols = cols + 1
321 curses.resizeterm(new_lines, new_cols)
322
323 self.assertEqual(curses.LINES, new_lines)
324 self.assertEqual(curses.COLS, new_cols)
325
326 def test_issue6243(self):
327 curses.ungetch(1025)
328 self.stdscr.getkey()
329
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300330 @requires_curses_func('unget_wch')
Zachary Warebaf45c52014-10-17 13:59:18 -0500331 def test_unget_wch(self):
332 stdscr = self.stdscr
333 encoding = stdscr.encoding
334 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
335 try:
336 ch.encode(encoding)
337 except UnicodeEncodeError:
338 continue
339 try:
340 curses.unget_wch(ch)
341 except Exception as err:
342 self.fail("unget_wch(%a) failed with encoding %s: %s"
343 % (ch, stdscr.encoding, err))
344 read = stdscr.get_wch()
345 self.assertEqual(read, ch)
346
347 code = ord(ch)
348 curses.unget_wch(code)
349 read = stdscr.get_wch()
350 self.assertEqual(read, ch)
351
352 def test_issue10570(self):
353 b = curses.tparm(curses.tigetstr("cup"), 5, 3)
354 self.assertIs(type(b), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500355
356 def test_encoding(self):
357 stdscr = self.stdscr
358 import codecs
359 encoding = stdscr.encoding
360 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -0500361 with self.assertRaises(TypeError):
362 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -0500363 stdscr.encoding = encoding
364 with self.assertRaises(TypeError):
365 del stdscr.encoding
366
367 def test_issue21088(self):
368 stdscr = self.stdscr
369 #
370 # http://bugs.python.org/issue21088
371 #
372 # the bug:
373 # when converting curses.window.addch to Argument Clinic
374 # the first two parameters were switched.
375
376 # if someday we can represent the signature of addch
377 # we will need to rewrite this test.
378 try:
379 signature = inspect.signature(stdscr.addch)
380 self.assertFalse(signature)
381 except ValueError:
382 # not generating a signature is fine.
383 pass
384
385 # So. No signature for addch.
386 # But Argument Clinic gave us a human-readable equivalent
387 # as the first line of the docstring. So we parse that,
388 # and ensure that the parameters appear in the correct order.
389 # Since this is parsing output from Argument Clinic, we can
390 # be reasonably certain the generated parsing code will be
391 # correct too.
392 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300393 self.assertIn("[y, x,]", human_readable_signature)
394
395
396class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -0500397
Berker Peksag53926f12016-08-19 17:59:01 +0300398 @requires_curses_func('update_lines_cols')
Steve Dowerd2bc3892015-04-15 18:06:05 -0400399 def test_update_lines_cols(self):
400 # this doesn't actually test that LINES and COLS are updated,
401 # because we can't automate changing them. See Issue #4254 for
402 # a manual test script. We can only test that the function
403 # can be called.
404 curses.update_lines_cols()
405
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000406
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300407class TestAscii(unittest.TestCase):
408
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300409 def test_controlnames(self):
410 for name in curses.ascii.controlnames:
411 self.assertTrue(hasattr(curses.ascii, name), name)
412
413 def test_ctypes(self):
414 def check(func, expected):
415 with self.subTest(ch=c, func=func):
416 self.assertEqual(func(i), expected)
417 self.assertEqual(func(c), expected)
418
419 for i in range(256):
420 c = chr(i)
421 b = bytes([i])
422 check(curses.ascii.isalnum, b.isalnum())
423 check(curses.ascii.isalpha, b.isalpha())
424 check(curses.ascii.isdigit, b.isdigit())
425 check(curses.ascii.islower, b.islower())
426 check(curses.ascii.isspace, b.isspace())
427 check(curses.ascii.isupper, b.isupper())
428
429 check(curses.ascii.isascii, i < 128)
430 check(curses.ascii.ismeta, i >= 128)
431 check(curses.ascii.isctrl, i < 32)
432 check(curses.ascii.iscntrl, i < 32 or i == 127)
433 check(curses.ascii.isblank, c in ' \t')
434 check(curses.ascii.isgraph, 32 < i <= 126)
435 check(curses.ascii.isprint, 32 <= i <= 126)
436 check(curses.ascii.ispunct, c in string.punctuation)
437 check(curses.ascii.isxdigit, c in string.hexdigits)
438
439 def test_ascii(self):
440 ascii = curses.ascii.ascii
441 self.assertEqual(ascii('\xc1'), 'A')
442 self.assertEqual(ascii('A'), 'A')
443 self.assertEqual(ascii(ord('\xc1')), ord('A'))
444
445 def test_ctrl(self):
446 ctrl = curses.ascii.ctrl
447 self.assertEqual(ctrl('J'), '\n')
448 self.assertEqual(ctrl('\n'), '\n')
449 self.assertEqual(ctrl('@'), '\0')
450 self.assertEqual(ctrl(ord('J')), ord('\n'))
451
452 def test_alt(self):
453 alt = curses.ascii.alt
454 self.assertEqual(alt('\n'), '\x8a')
455 self.assertEqual(alt('A'), '\xc1')
456 self.assertEqual(alt(ord('A')), 0xc1)
457
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300458 def test_unctrl(self):
459 unctrl = curses.ascii.unctrl
460 self.assertEqual(unctrl('a'), 'a')
461 self.assertEqual(unctrl('A'), 'A')
462 self.assertEqual(unctrl(';'), ';')
463 self.assertEqual(unctrl(' '), ' ')
464 self.assertEqual(unctrl('\x7f'), '^?')
465 self.assertEqual(unctrl('\n'), '^J')
466 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300467 self.assertEqual(unctrl(ord('A')), 'A')
468 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300469 # Meta-bit characters
470 self.assertEqual(unctrl('\x8a'), '!^J')
471 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300472 self.assertEqual(unctrl(ord('\x8a')), '!^J')
473 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300474
475
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000476if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -0500477 unittest.main()