1#
2# Test script for the curses module
3#
4# This script doesn't actually display anything very coherent. but it
5# does call (nearly) every method and function.
6#
7# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
8# init_color()
9# Only called, not tested: getmouse(), ungetmouse()
10#
11
12import os
13import string
14import sys
15import tempfile
16import unittest
17
18from test.support import (requires, import_module, verbose, run_unittest,
19                          SaveSignals)
20
21
22# Optionally test curses module.  This currently requires that the
23# 'curses' resource be given on the regrtest command line using the -u
24# option.  If not available, nothing after this line will be executed.
25requires('curses')
26
27# If either of these don't exist, skip the tests.
28curses = import_module('curses')
29import_module('curses.ascii')
30import_module('curses.textpad')
31try:
32    import curses.panel
33except ImportError:
34    pass
35
36def requires_curses_func(name):
37    return unittest.skipUnless(hasattr(curses, name),
38                               'requires curses.%s' % name)
39
40term = os.environ.get('TERM')
41
42# If newterm was supported we could use it instead of initscr and not exit
43@unittest.skipIf(not term or term == 'unknown',
44                 "$TERM=%r, calling initscr() may cause exit" % term)
45@unittest.skipIf(sys.platform == "cygwin",
46                 "cygwin's curses mostly just hangs")
47class TestCurses(unittest.TestCase):
48
49    @classmethod
50    def setUpClass(cls):
51        if not sys.__stdout__.isatty():
52            # Temporary skip tests on non-tty
53            raise unittest.SkipTest('sys.__stdout__ is not a tty')
54            cls.tmp = tempfile.TemporaryFile()
55            fd = cls.tmp.fileno()
56        else:
57            cls.tmp = None
58            fd = sys.__stdout__.fileno()
59        # testing setupterm() inside initscr/endwin
60        # causes terminal breakage
61        curses.setupterm(fd=fd)
62
63    @classmethod
64    def tearDownClass(cls):
65        if cls.tmp:
66            cls.tmp.close()
67            del cls.tmp
68
69    def setUp(self):
70        self.save_signals = SaveSignals()
71        self.save_signals.save()
72        if verbose:
73            # just to make the test output a little more readable
74            print('')
75        self.stdscr = curses.initscr()
76        curses.savetty()
77
78    def tearDown(self):
79        curses.resetty()
80        curses.endwin()
81        self.save_signals.restore()
82
83    def test_window_funcs(self):
84        "Test the methods of windows"
85        stdscr = self.stdscr
86        win = curses.newwin(10,10)
87        win = curses.newwin(5,5, 5,5)
88        win2 = curses.newwin(15,15, 5,5)
89
90        for meth in [stdscr.addch, stdscr.addstr]:
91            for args in [('a'), ('a', curses.A_BOLD),
92                         (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
93                meth(*args)
94
95        for meth in [stdscr.clear, stdscr.clrtobot,
96                     stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
97                     stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
98                     stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
99                     stdscr.getparyx, stdscr.getyx, stdscr.inch,
100                     stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
101                     win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
102                     stdscr.standout, stdscr.standend, stdscr.syncdown,
103                     stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
104            meth()
105
106        stdscr.addnstr('1234', 3)
107        stdscr.addnstr('1234', 3, curses.A_BOLD)
108        stdscr.addnstr(4,4, '1234', 3)
109        stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
110
111        stdscr.attron(curses.A_BOLD)
112        stdscr.attroff(curses.A_BOLD)
113        stdscr.attrset(curses.A_BOLD)
114        stdscr.bkgd(' ')
115        stdscr.bkgd(' ', curses.A_REVERSE)
116        stdscr.bkgdset(' ')
117        stdscr.bkgdset(' ', curses.A_REVERSE)
118
119        win.border(65, 66, 67, 68,
120                   69, 70, 71, 72)
121        win.border('|', '!', '-', '_',
122                   '+', '\\', '#', '/')
123        with self.assertRaises(TypeError,
124                               msg="Expected win.border() to raise TypeError"):
125            win.border(65, 66, 67, 68,
126                       69, [], 71, 72)
127
128        win.box(65, 67)
129        win.box('!', '_')
130        win.box(b':', b'~')
131        self.assertRaises(TypeError, win.box, 65, 66, 67)
132        self.assertRaises(TypeError, win.box, 65)
133        win.box()
134
135        stdscr.clearok(1)
136
137        win4 = stdscr.derwin(2,2)
138        win4 = stdscr.derwin(1,1, 5,5)
139        win4.mvderwin(9,9)
140
141        stdscr.echochar('a')
142        stdscr.echochar('a', curses.A_BOLD)
143        stdscr.hline('-', 5)
144        stdscr.hline('-', 5, curses.A_BOLD)
145        stdscr.hline(1,1,'-', 5)
146        stdscr.hline(1,1,'-', 5, curses.A_BOLD)
147
148        stdscr.idcok(1)
149        stdscr.idlok(1)
150        if hasattr(stdscr, 'immedok'):
151            stdscr.immedok(1)
152            stdscr.immedok(0)
153        stdscr.insch('c')
154        stdscr.insdelln(1)
155        stdscr.insnstr('abc', 3)
156        stdscr.insnstr('abc', 3, curses.A_BOLD)
157        stdscr.insnstr(5, 5, 'abc', 3)
158        stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
159
160        stdscr.insstr('def')
161        stdscr.insstr('def', curses.A_BOLD)
162        stdscr.insstr(5, 5, 'def')
163        stdscr.insstr(5, 5, 'def', curses.A_BOLD)
164        stdscr.is_linetouched(0)
165        stdscr.keypad(1)
166        stdscr.leaveok(1)
167        stdscr.move(3,3)
168        win.mvwin(2,2)
169        stdscr.nodelay(1)
170        stdscr.notimeout(1)
171        win2.overlay(win)
172        win2.overwrite(win)
173        win2.overlay(win, 1, 2, 2, 1, 3, 3)
174        win2.overwrite(win, 1, 2, 2, 1, 3, 3)
175        stdscr.redrawln(1,2)
176
177        stdscr.scrollok(1)
178        stdscr.scroll()
179        stdscr.scroll(2)
180        stdscr.scroll(-3)
181
182        stdscr.move(12, 2)
183        stdscr.setscrreg(10,15)
184        win3 = stdscr.subwin(10,10)
185        win3 = stdscr.subwin(10,10, 5,5)
186        if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
187            stdscr.syncok(1)
188        stdscr.timeout(5)
189        stdscr.touchline(5,5)
190        stdscr.touchline(5,5,0)
191        stdscr.vline('a', 3)
192        stdscr.vline('a', 3, curses.A_STANDOUT)
193        if hasattr(stdscr, 'chgat'):
194            stdscr.chgat(5, 2, 3, curses.A_BLINK)
195            stdscr.chgat(3, curses.A_BOLD)
196            stdscr.chgat(5, 8, curses.A_UNDERLINE)
197            stdscr.chgat(curses.A_BLINK)
198        stdscr.refresh()
199
200        stdscr.vline(1,1, 'a', 3)
201        stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
202
203        if hasattr(stdscr, 'resize'):
204            stdscr.resize(25, 80)
205        if hasattr(stdscr, 'enclose'):
206            stdscr.enclose(10, 10)
207
208        self.assertRaises(ValueError, stdscr.getstr, -400)
209        self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
210        self.assertRaises(ValueError, stdscr.instr, -2)
211        self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
212
213
214    def test_module_funcs(self):
215        "Test module-level functions"
216        for func in [curses.baudrate, curses.beep, curses.can_change_color,
217                     curses.cbreak, curses.def_prog_mode, curses.doupdate,
218                     curses.flash, curses.flushinp,
219                     curses.has_colors, curses.has_ic, curses.has_il,
220                     curses.isendwin, curses.killchar, curses.longname,
221                     curses.nocbreak, curses.noecho, curses.nonl,
222                     curses.noqiflush, curses.noraw,
223                     curses.reset_prog_mode, curses.termattrs,
224                     curses.termname, curses.erasechar]:
225            func()
226        if hasattr(curses, 'filter'):
227            curses.filter()
228        if hasattr(curses, 'getsyx'):
229            curses.getsyx()
230
231        # Functions that actually need arguments
232        if curses.tigetstr("cnorm"):
233            curses.curs_set(1)
234        curses.delay_output(1)
235        curses.echo() ; curses.echo(1)
236
237        with tempfile.TemporaryFile() as f:
238            self.stdscr.putwin(f)
239            f.seek(0)
240            curses.getwin(f)
241
242        curses.halfdelay(1)
243        curses.intrflush(1)
244        curses.meta(1)
245        curses.napms(100)
246        curses.newpad(50,50)
247        win = curses.newwin(5,5)
248        win = curses.newwin(5,5, 1,1)
249        curses.nl() ; curses.nl(1)
250        curses.putp(b'abc')
251        curses.qiflush()
252        curses.raw() ; curses.raw(1)
253        if hasattr(curses, 'setsyx'):
254            curses.setsyx(5,5)
255        curses.tigetflag('hc')
256        curses.tigetnum('co')
257        curses.tigetstr('cr')
258        curses.tparm(b'cr')
259        if hasattr(curses, 'typeahead'):
260            curses.typeahead(sys.__stdin__.fileno())
261        curses.unctrl('a')
262        curses.ungetch('a')
263        if hasattr(curses, 'use_env'):
264            curses.use_env(1)
265
266    # Functions only available on a few platforms
267    def test_colors_funcs(self):
268        if not curses.has_colors():
269            self.skipTest('requires colors support')
270        curses.start_color()
271        curses.init_pair(2, 1,1)
272        curses.color_content(1)
273        curses.color_pair(2)
274        curses.pair_content(curses.COLOR_PAIRS - 1)
275        curses.pair_number(0)
276
277        if hasattr(curses, 'use_default_colors'):
278            curses.use_default_colors()
279
280    @requires_curses_func('keyname')
281    def test_keyname(self):
282        curses.keyname(13)
283
284    @requires_curses_func('has_key')
285    def test_has_key(self):
286        curses.has_key(13)
287
288    @requires_curses_func('getmouse')
289    def test_getmouse(self):
290        (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
291        if availmask == 0:
292            self.skipTest('mouse stuff not available')
293        curses.mouseinterval(10)
294        # just verify these don't cause errors
295        curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
296        m = curses.getmouse()
297
298    @requires_curses_func('panel')
299    def test_userptr_without_set(self):
300        w = curses.newwin(10, 10)
301        p = curses.panel.new_panel(w)
302        # try to access userptr() before calling set_userptr() -- segfaults
303        with self.assertRaises(curses.panel.error,
304                               msg='userptr should fail since not set'):
305            p.userptr()
306
307    @requires_curses_func('panel')
308    def test_userptr_memory_leak(self):
309        w = curses.newwin(10, 10)
310        p = curses.panel.new_panel(w)
311        obj = object()
312        nrefs = sys.getrefcount(obj)
313        for i in range(100):
314            p.set_userptr(obj)
315
316        p.set_userptr(None)
317        self.assertEqual(sys.getrefcount(obj), nrefs,
318                         "set_userptr leaked references")
319
320    @requires_curses_func('panel')
321    def test_userptr_segfault(self):
322        w = curses.newwin(10, 10)
323        panel = curses.panel.new_panel(w)
324        class A:
325            def __del__(self):
326                panel.set_userptr(None)
327        panel.set_userptr(A())
328        panel.set_userptr(None)
329
330    @requires_curses_func('panel')
331    def test_new_curses_panel(self):
332        w = curses.newwin(10, 10)
333        panel = curses.panel.new_panel(w)
334        self.assertRaises(TypeError, type(panel))
335
336    @requires_curses_func('is_term_resized')
337    def test_is_term_resized(self):
338        curses.is_term_resized(*self.stdscr.getmaxyx())
339
340    @requires_curses_func('resize_term')
341    def test_resize_term(self):
342        curses.resize_term(*self.stdscr.getmaxyx())
343
344    @requires_curses_func('resizeterm')
345    def test_resizeterm(self):
346        stdscr = self.stdscr
347        lines, cols = curses.LINES, curses.COLS
348        new_lines = lines - 1
349        new_cols = cols + 1
350        curses.resizeterm(new_lines, new_cols)
351
352        self.assertEqual(curses.LINES, new_lines)
353        self.assertEqual(curses.COLS, new_cols)
354
355    def test_issue6243(self):
356        curses.ungetch(1025)
357        self.stdscr.getkey()
358
359    def test_issue10570(self):
360        b = curses.tparm(curses.tigetstr("cup"), 5, 3)
361        self.assertIs(type(b), bytes)
362
363    def test_issue13051(self):
364        stdscr = self.stdscr
365        if not hasattr(stdscr, 'resize'):
366            raise unittest.SkipTest('requires curses.window.resize')
367        box = curses.textpad.Textbox(stdscr, insert_mode=True)
368        lines, cols = stdscr.getmaxyx()
369        stdscr.resize(lines-2, cols-2)
370        # this may cause infinite recursion, leading to a RuntimeError
371        box._insert_printable_char('a')
372
373
374class TestAscii(unittest.TestCase):
375
376    def test_controlnames(self):
377        for name in curses.ascii.controlnames:
378            self.assertTrue(hasattr(curses.ascii, name), name)
379
380    def test_ctypes(self):
381        def check(func, expected):
382            self.assertEqual(func(i), expected)
383            self.assertEqual(func(c), expected)
384
385        for i in range(256):
386            c = b = chr(i)
387            check(curses.ascii.isalnum, b.isalnum())
388            check(curses.ascii.isalpha, b.isalpha())
389            check(curses.ascii.isdigit, b.isdigit())
390            check(curses.ascii.islower, b.islower())
391            check(curses.ascii.isspace, b.isspace())
392            check(curses.ascii.isupper, b.isupper())
393
394            check(curses.ascii.isascii, i < 128)
395            check(curses.ascii.ismeta, i >= 128)
396            check(curses.ascii.isctrl, i < 32)
397            check(curses.ascii.iscntrl, i < 32 or i == 127)
398            check(curses.ascii.isblank, c in ' \t')
399            check(curses.ascii.isgraph, 32 < i <= 126)
400            check(curses.ascii.isprint, 32 <= i <= 126)
401            check(curses.ascii.ispunct, c in string.punctuation)
402            check(curses.ascii.isxdigit, c in string.hexdigits)
403
404        for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
405            self.assertFalse(curses.ascii.isalnum(i))
406            self.assertFalse(curses.ascii.isalpha(i))
407            self.assertFalse(curses.ascii.isdigit(i))
408            self.assertFalse(curses.ascii.islower(i))
409            self.assertFalse(curses.ascii.isspace(i))
410            self.assertFalse(curses.ascii.isupper(i))
411
412            self.assertFalse(curses.ascii.isascii(i))
413            self.assertFalse(curses.ascii.isctrl(i))
414            self.assertFalse(curses.ascii.iscntrl(i))
415            self.assertFalse(curses.ascii.isblank(i))
416            self.assertFalse(curses.ascii.isgraph(i))
417            self.assertFalse(curses.ascii.isprint(i))
418            self.assertFalse(curses.ascii.ispunct(i))
419            self.assertFalse(curses.ascii.isxdigit(i))
420
421        self.assertFalse(curses.ascii.ismeta(-1))
422
423    def test_ascii(self):
424        ascii = curses.ascii.ascii
425        self.assertEqual(ascii('\xc1'), 'A')
426        self.assertEqual(ascii('A'), 'A')
427        self.assertEqual(ascii(ord('\xc1')), ord('A'))
428
429    def test_ctrl(self):
430        ctrl = curses.ascii.ctrl
431        self.assertEqual(ctrl('J'), '\n')
432        self.assertEqual(ctrl('\n'), '\n')
433        self.assertEqual(ctrl('@'), '\0')
434        self.assertEqual(ctrl(ord('J')), ord('\n'))
435
436    def test_alt(self):
437        alt = curses.ascii.alt
438        self.assertEqual(alt('\n'), '\x8a')
439        self.assertEqual(alt('A'), '\xc1')
440        self.assertEqual(alt(ord('A')), 0xc1)
441
442    def test_unctrl(self):
443        unctrl = curses.ascii.unctrl
444        self.assertEqual(unctrl('a'), 'a')
445        self.assertEqual(unctrl('A'), 'A')
446        self.assertEqual(unctrl(';'), ';')
447        self.assertEqual(unctrl(' '), ' ')
448        self.assertEqual(unctrl('\x7f'), '^?')
449        self.assertEqual(unctrl('\n'), '^J')
450        self.assertEqual(unctrl('\0'), '^@')
451        self.assertEqual(unctrl(ord('A')), 'A')
452        self.assertEqual(unctrl(ord('\n')), '^J')
453        # Meta-bit characters
454        self.assertEqual(unctrl('\x8a'), '!^J')
455        self.assertEqual(unctrl('\xc1'), '!A')
456        self.assertEqual(unctrl(ord('\x8a')), '!^J')
457        self.assertEqual(unctrl(ord('\xc1')), '!A')
458
459
460def test_main():
461    run_unittest(TestCurses, TestAscii)
462
463
464if __name__ == "__main__":
465    unittest.main()
466