blob: 3d8c50bcb6c6ae4aedd1a1159481ac3dc2002c66 [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')
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +020030import_module('curses.textpad')
R. David Murraya21e4ca2009-03-31 23:16:50 +000031
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030032def requires_curses_func(name):
33 return unittest.skipUnless(hasattr(curses, name),
34 'requires curses.%s' % name)
Mark Dickinson945e2422010-02-21 13:42:03 +000035
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030036term = os.environ.get('TERM')
37
38# If newterm was supported we could use it instead of initscr and not exit
39@unittest.skipIf(not term or term == 'unknown',
Zachary Warebaf45c52014-10-17 13:59:18 -050040 "$TERM=%r, calling initscr() may cause exit" % term)
41@unittest.skipIf(sys.platform == "cygwin",
42 "cygwin's curses mostly just hangs")
43class TestCurses(unittest.TestCase):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030044
Zachary Warebaf45c52014-10-17 13:59:18 -050045 @classmethod
46 def setUpClass(cls):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030047 if not sys.__stdout__.isatty():
Serhiy Storchaka53a00352016-05-22 18:16:20 +030048 # Temporary skip tests on non-tty
Serhiy Storchaka0a91e432016-05-22 18:23:36 +030049 raise unittest.SkipTest('sys.__stdout__ is not a tty')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030050 cls.tmp = tempfile.TemporaryFile()
51 fd = cls.tmp.fileno()
52 else:
53 cls.tmp = None
54 fd = sys.__stdout__.fileno()
55 # testing setupterm() inside initscr/endwin
56 # causes terminal breakage
57 curses.setupterm(fd=fd)
58
59 @classmethod
60 def tearDownClass(cls):
61 if cls.tmp:
62 cls.tmp.close()
63 del cls.tmp
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000064
Zachary Warebaf45c52014-10-17 13:59:18 -050065 def setUp(self):
66 if verbose:
67 # just to make the test output a little more readable
68 print()
69 self.stdscr = curses.initscr()
70 curses.savetty()
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000071
Zachary Warebaf45c52014-10-17 13:59:18 -050072 def tearDown(self):
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000073 curses.resetty()
Andrew M. Kuchling2158df02001-10-22 15:26:09 +000074 curses.endwin()
Zachary Warebaf45c52014-10-17 13:59:18 -050075
76 def test_window_funcs(self):
77 "Test the methods of windows"
78 stdscr = self.stdscr
79 win = curses.newwin(10,10)
80 win = curses.newwin(5,5, 5,5)
81 win2 = curses.newwin(15,15, 5,5)
82
83 for meth in [stdscr.addch, stdscr.addstr]:
84 for args in [('a'), ('a', curses.A_BOLD),
85 (4,4, 'a'), (5,5, 'a', curses.A_BOLD)]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030086 with self.subTest(meth=meth.__qualname__, args=args):
87 meth(*args)
Zachary Warebaf45c52014-10-17 13:59:18 -050088
89 for meth in [stdscr.box, stdscr.clear, stdscr.clrtobot,
90 stdscr.clrtoeol, stdscr.cursyncup, stdscr.delch,
91 stdscr.deleteln, stdscr.erase, stdscr.getbegyx,
92 stdscr.getbkgd, stdscr.getkey, stdscr.getmaxyx,
93 stdscr.getparyx, stdscr.getyx, stdscr.inch,
94 stdscr.insertln, stdscr.instr, stdscr.is_wintouched,
95 win.noutrefresh, stdscr.redrawwin, stdscr.refresh,
96 stdscr.standout, stdscr.standend, stdscr.syncdown,
97 stdscr.syncup, stdscr.touchwin, stdscr.untouchwin]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +030098 with self.subTest(meth=meth.__qualname__):
99 meth()
Zachary Warebaf45c52014-10-17 13:59:18 -0500100
101 stdscr.addnstr('1234', 3)
102 stdscr.addnstr('1234', 3, curses.A_BOLD)
103 stdscr.addnstr(4,4, '1234', 3)
104 stdscr.addnstr(5,5, '1234', 3, curses.A_BOLD)
105
106 stdscr.attron(curses.A_BOLD)
107 stdscr.attroff(curses.A_BOLD)
108 stdscr.attrset(curses.A_BOLD)
109 stdscr.bkgd(' ')
110 stdscr.bkgd(' ', curses.A_REVERSE)
111 stdscr.bkgdset(' ')
112 stdscr.bkgdset(' ', curses.A_REVERSE)
113
114 win.border(65, 66, 67, 68,
115 69, 70, 71, 72)
116 win.border('|', '!', '-', '_',
117 '+', '\\', '#', '/')
118 with self.assertRaises(TypeError,
119 msg="Expected win.border() to raise TypeError"):
120 win.border(65, 66, 67, 68,
121 69, [], 71, 72)
122
123 stdscr.clearok(1)
124
125 win4 = stdscr.derwin(2,2)
126 win4 = stdscr.derwin(1,1, 5,5)
127 win4.mvderwin(9,9)
128
129 stdscr.echochar('a')
130 stdscr.echochar('a', curses.A_BOLD)
131 stdscr.hline('-', 5)
132 stdscr.hline('-', 5, curses.A_BOLD)
133 stdscr.hline(1,1,'-', 5)
134 stdscr.hline(1,1,'-', 5, curses.A_BOLD)
135
136 stdscr.idcok(1)
137 stdscr.idlok(1)
138 stdscr.immedok(1)
139 stdscr.insch('c')
140 stdscr.insdelln(1)
141 stdscr.insnstr('abc', 3)
142 stdscr.insnstr('abc', 3, curses.A_BOLD)
143 stdscr.insnstr(5, 5, 'abc', 3)
144 stdscr.insnstr(5, 5, 'abc', 3, curses.A_BOLD)
145
146 stdscr.insstr('def')
147 stdscr.insstr('def', curses.A_BOLD)
148 stdscr.insstr(5, 5, 'def')
149 stdscr.insstr(5, 5, 'def', curses.A_BOLD)
150 stdscr.is_linetouched(0)
151 stdscr.keypad(1)
152 stdscr.leaveok(1)
153 stdscr.move(3,3)
154 win.mvwin(2,2)
155 stdscr.nodelay(1)
156 stdscr.notimeout(1)
157 win2.overlay(win)
158 win2.overwrite(win)
159 win2.overlay(win, 1, 2, 2, 1, 3, 3)
160 win2.overwrite(win, 1, 2, 2, 1, 3, 3)
161 stdscr.redrawln(1,2)
162
163 stdscr.scrollok(1)
164 stdscr.scroll()
165 stdscr.scroll(2)
166 stdscr.scroll(-3)
167
168 stdscr.move(12, 2)
169 stdscr.setscrreg(10,15)
170 win3 = stdscr.subwin(10,10)
171 win3 = stdscr.subwin(10,10, 5,5)
172 stdscr.syncok(1)
173 stdscr.timeout(5)
174 stdscr.touchline(5,5)
175 stdscr.touchline(5,5,0)
176 stdscr.vline('a', 3)
177 stdscr.vline('a', 3, curses.A_STANDOUT)
178 stdscr.chgat(5, 2, 3, curses.A_BLINK)
179 stdscr.chgat(3, curses.A_BOLD)
180 stdscr.chgat(5, 8, curses.A_UNDERLINE)
181 stdscr.chgat(curses.A_BLINK)
182 stdscr.refresh()
183
184 stdscr.vline(1,1, 'a', 3)
185 stdscr.vline(1,1, 'a', 3, curses.A_STANDOUT)
186
187 if hasattr(curses, 'resize'):
188 stdscr.resize()
189 if hasattr(curses, 'enclose'):
190 stdscr.enclose()
191
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700192 self.assertRaises(ValueError, stdscr.getstr, -400)
193 self.assertRaises(ValueError, stdscr.getstr, 2, 3, -400)
Benjamin Peterson432ea4f2016-08-15 21:40:14 -0700194 self.assertRaises(ValueError, stdscr.instr, -2)
195 self.assertRaises(ValueError, stdscr.instr, 2, 3, -2)
Benjamin Peterson40a77c32016-08-13 18:15:28 -0700196
Zachary Warebaf45c52014-10-17 13:59:18 -0500197
198 def test_module_funcs(self):
199 "Test module-level functions"
Zachary Warebaf45c52014-10-17 13:59:18 -0500200 for func in [curses.baudrate, curses.beep, curses.can_change_color,
201 curses.cbreak, curses.def_prog_mode, curses.doupdate,
202 curses.filter, curses.flash, curses.flushinp,
203 curses.has_colors, curses.has_ic, curses.has_il,
204 curses.isendwin, curses.killchar, curses.longname,
205 curses.nocbreak, curses.noecho, curses.nonl,
206 curses.noqiflush, curses.noraw,
207 curses.reset_prog_mode, curses.termattrs,
208 curses.termname, curses.erasechar, curses.getsyx]:
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300209 with self.subTest(func=func.__qualname__):
210 func()
Zachary Warebaf45c52014-10-17 13:59:18 -0500211
212 # Functions that actually need arguments
213 if curses.tigetstr("cnorm"):
214 curses.curs_set(1)
215 curses.delay_output(1)
216 curses.echo() ; curses.echo(1)
217
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300218 with tempfile.TemporaryFile() as f:
219 self.stdscr.putwin(f)
220 f.seek(0)
221 curses.getwin(f)
Zachary Warebaf45c52014-10-17 13:59:18 -0500222
223 curses.halfdelay(1)
224 curses.intrflush(1)
225 curses.meta(1)
226 curses.napms(100)
227 curses.newpad(50,50)
228 win = curses.newwin(5,5)
229 win = curses.newwin(5,5, 1,1)
230 curses.nl() ; curses.nl(1)
231 curses.putp(b'abc')
232 curses.qiflush()
233 curses.raw() ; curses.raw(1)
234 curses.setsyx(5,5)
235 curses.tigetflag('hc')
236 curses.tigetnum('co')
237 curses.tigetstr('cr')
238 curses.tparm(b'cr')
239 curses.typeahead(sys.__stdin__.fileno())
240 curses.unctrl('a')
241 curses.ungetch('a')
242 curses.use_env(1)
243
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300244 # Functions only available on a few platforms
245 def test_colors_funcs(self):
246 if not curses.has_colors():
Xavier de Gaye645bc802017-01-06 09:50:27 +0100247 self.skipTest('requires colors support')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300248 curses.start_color()
249 curses.init_pair(2, 1,1)
250 curses.color_content(1)
251 curses.color_pair(2)
252 curses.pair_content(curses.COLOR_PAIRS - 1)
253 curses.pair_number(0)
Zachary Warebaf45c52014-10-17 13:59:18 -0500254
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300255 if hasattr(curses, 'use_default_colors'):
256 curses.use_default_colors()
Zachary Warebaf45c52014-10-17 13:59:18 -0500257
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300258 @requires_curses_func('keyname')
259 def test_keyname(self):
260 curses.keyname(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500261
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300262 @requires_curses_func('has_key')
263 def test_has_key(self):
264 curses.has_key(13)
Zachary Warebaf45c52014-10-17 13:59:18 -0500265
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300266 @requires_curses_func('getmouse')
267 def test_getmouse(self):
268 (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
269 if availmask == 0:
Xavier de Gaye645bc802017-01-06 09:50:27 +0100270 self.skipTest('mouse stuff not available')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300271 curses.mouseinterval(10)
272 # just verify these don't cause errors
273 curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
274 m = curses.getmouse()
Zachary Warebaf45c52014-10-17 13:59:18 -0500275
276 def test_userptr_without_set(self):
277 w = curses.newwin(10, 10)
278 p = curses.panel.new_panel(w)
279 # try to access userptr() before calling set_userptr() -- segfaults
280 with self.assertRaises(curses.panel.error,
281 msg='userptr should fail since not set'):
282 p.userptr()
283
284 def test_userptr_memory_leak(self):
285 w = curses.newwin(10, 10)
286 p = curses.panel.new_panel(w)
287 obj = object()
288 nrefs = sys.getrefcount(obj)
289 for i in range(100):
290 p.set_userptr(obj)
291
292 p.set_userptr(None)
293 self.assertEqual(sys.getrefcount(obj), nrefs,
294 "set_userptr leaked references")
295
296 def test_userptr_segfault(self):
297 panel = curses.panel.new_panel(self.stdscr)
298 class A:
299 def __del__(self):
300 panel.set_userptr(None)
301 panel.set_userptr(A())
302 panel.set_userptr(None)
303
Serhiy Storchakae3f1b092016-05-08 20:46:22 +0300304 def test_new_curses_panel(self):
305 panel = curses.panel.new_panel(self.stdscr)
306 self.assertRaises(TypeError, type(panel))
307
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300308 @requires_curses_func('is_term_resized')
309 def test_is_term_resized(self):
310 curses.is_term_resized(*self.stdscr.getmaxyx())
311
312 @requires_curses_func('resize_term')
Zachary Warebaf45c52014-10-17 13:59:18 -0500313 def test_resize_term(self):
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300314 curses.resize_term(*self.stdscr.getmaxyx())
315
316 @requires_curses_func('resizeterm')
317 def test_resizeterm(self):
318 stdscr = self.stdscr
Zachary Warebaf45c52014-10-17 13:59:18 -0500319 lines, cols = curses.LINES, curses.COLS
320 new_lines = lines - 1
321 new_cols = cols + 1
322 curses.resizeterm(new_lines, new_cols)
323
324 self.assertEqual(curses.LINES, new_lines)
325 self.assertEqual(curses.COLS, new_cols)
326
327 def test_issue6243(self):
328 curses.ungetch(1025)
329 self.stdscr.getkey()
330
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300331 @requires_curses_func('unget_wch')
Zachary Warebaf45c52014-10-17 13:59:18 -0500332 def test_unget_wch(self):
333 stdscr = self.stdscr
334 encoding = stdscr.encoding
335 for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
336 try:
337 ch.encode(encoding)
338 except UnicodeEncodeError:
339 continue
340 try:
341 curses.unget_wch(ch)
342 except Exception as err:
343 self.fail("unget_wch(%a) failed with encoding %s: %s"
344 % (ch, stdscr.encoding, err))
345 read = stdscr.get_wch()
346 self.assertEqual(read, ch)
347
348 code = ord(ch)
349 curses.unget_wch(code)
350 read = stdscr.get_wch()
351 self.assertEqual(read, ch)
352
353 def test_issue10570(self):
354 b = curses.tparm(curses.tigetstr("cup"), 5, 3)
355 self.assertIs(type(b), bytes)
Zachary Warebaf45c52014-10-17 13:59:18 -0500356
357 def test_encoding(self):
358 stdscr = self.stdscr
359 import codecs
360 encoding = stdscr.encoding
361 codecs.lookup(encoding)
Zachary Warebaf45c52014-10-17 13:59:18 -0500362 with self.assertRaises(TypeError):
363 stdscr.encoding = 10
Zachary Warebaf45c52014-10-17 13:59:18 -0500364 stdscr.encoding = encoding
365 with self.assertRaises(TypeError):
366 del stdscr.encoding
367
368 def test_issue21088(self):
369 stdscr = self.stdscr
370 #
371 # http://bugs.python.org/issue21088
372 #
373 # the bug:
374 # when converting curses.window.addch to Argument Clinic
375 # the first two parameters were switched.
376
377 # if someday we can represent the signature of addch
378 # we will need to rewrite this test.
379 try:
380 signature = inspect.signature(stdscr.addch)
381 self.assertFalse(signature)
382 except ValueError:
383 # not generating a signature is fine.
384 pass
385
386 # So. No signature for addch.
387 # But Argument Clinic gave us a human-readable equivalent
388 # as the first line of the docstring. So we parse that,
389 # and ensure that the parameters appear in the correct order.
390 # Since this is parsing output from Argument Clinic, we can
391 # be reasonably certain the generated parsing code will be
392 # correct too.
393 human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300394 self.assertIn("[y, x,]", human_readable_signature)
395
Serhiy Storchakabdf9e0e2016-12-28 10:16:06 +0200396 def test_issue13051(self):
397 stdscr = self.stdscr
398 box = curses.textpad.Textbox(stdscr, insert_mode=True)
399 lines, cols = stdscr.getmaxyx()
400 stdscr.resize(lines-2, cols-2)
401 # this may cause infinite recursion, leading to a RuntimeError
402 box._insert_printable_char('a')
403
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300404
405class MiscTests(unittest.TestCase):
Zachary Warebaf45c52014-10-17 13:59:18 -0500406
Berker Peksag53926f12016-08-19 17:59:01 +0300407 @requires_curses_func('update_lines_cols')
Steve Dowerd2bc3892015-04-15 18:06:05 -0400408 def test_update_lines_cols(self):
409 # this doesn't actually test that LINES and COLS are updated,
410 # because we can't automate changing them. See Issue #4254 for
411 # a manual test script. We can only test that the function
412 # can be called.
413 curses.update_lines_cols()
414
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000415
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300416class TestAscii(unittest.TestCase):
417
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300418 def test_controlnames(self):
419 for name in curses.ascii.controlnames:
420 self.assertTrue(hasattr(curses.ascii, name), name)
421
422 def test_ctypes(self):
423 def check(func, expected):
424 with self.subTest(ch=c, func=func):
425 self.assertEqual(func(i), expected)
426 self.assertEqual(func(c), expected)
427
428 for i in range(256):
429 c = chr(i)
430 b = bytes([i])
431 check(curses.ascii.isalnum, b.isalnum())
432 check(curses.ascii.isalpha, b.isalpha())
433 check(curses.ascii.isdigit, b.isdigit())
434 check(curses.ascii.islower, b.islower())
435 check(curses.ascii.isspace, b.isspace())
436 check(curses.ascii.isupper, b.isupper())
437
438 check(curses.ascii.isascii, i < 128)
439 check(curses.ascii.ismeta, i >= 128)
440 check(curses.ascii.isctrl, i < 32)
441 check(curses.ascii.iscntrl, i < 32 or i == 127)
442 check(curses.ascii.isblank, c in ' \t')
443 check(curses.ascii.isgraph, 32 < i <= 126)
444 check(curses.ascii.isprint, 32 <= i <= 126)
445 check(curses.ascii.ispunct, c in string.punctuation)
446 check(curses.ascii.isxdigit, c in string.hexdigits)
447
Serhiy Storchaka283de2b2016-12-28 10:04:27 +0200448 for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
449 self.assertFalse(curses.ascii.isalnum(i))
450 self.assertFalse(curses.ascii.isalpha(i))
451 self.assertFalse(curses.ascii.isdigit(i))
452 self.assertFalse(curses.ascii.islower(i))
453 self.assertFalse(curses.ascii.isspace(i))
454 self.assertFalse(curses.ascii.isupper(i))
455
456 self.assertFalse(curses.ascii.isascii(i))
457 self.assertFalse(curses.ascii.isctrl(i))
458 self.assertFalse(curses.ascii.iscntrl(i))
459 self.assertFalse(curses.ascii.isblank(i))
460 self.assertFalse(curses.ascii.isgraph(i))
461 self.assertFalse(curses.ascii.isprint(i))
462 self.assertFalse(curses.ascii.ispunct(i))
463 self.assertFalse(curses.ascii.isxdigit(i))
464
465 self.assertFalse(curses.ascii.ismeta(-1))
466
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300467 def test_ascii(self):
468 ascii = curses.ascii.ascii
469 self.assertEqual(ascii('\xc1'), 'A')
470 self.assertEqual(ascii('A'), 'A')
471 self.assertEqual(ascii(ord('\xc1')), ord('A'))
472
473 def test_ctrl(self):
474 ctrl = curses.ascii.ctrl
475 self.assertEqual(ctrl('J'), '\n')
476 self.assertEqual(ctrl('\n'), '\n')
477 self.assertEqual(ctrl('@'), '\0')
478 self.assertEqual(ctrl(ord('J')), ord('\n'))
479
480 def test_alt(self):
481 alt = curses.ascii.alt
482 self.assertEqual(alt('\n'), '\x8a')
483 self.assertEqual(alt('A'), '\xc1')
484 self.assertEqual(alt(ord('A')), 0xc1)
485
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300486 def test_unctrl(self):
487 unctrl = curses.ascii.unctrl
488 self.assertEqual(unctrl('a'), 'a')
489 self.assertEqual(unctrl('A'), 'A')
490 self.assertEqual(unctrl(';'), ';')
491 self.assertEqual(unctrl(' '), ' ')
492 self.assertEqual(unctrl('\x7f'), '^?')
493 self.assertEqual(unctrl('\n'), '^J')
494 self.assertEqual(unctrl('\0'), '^@')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300495 self.assertEqual(unctrl(ord('A')), 'A')
496 self.assertEqual(unctrl(ord('\n')), '^J')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300497 # Meta-bit characters
498 self.assertEqual(unctrl('\x8a'), '!^J')
499 self.assertEqual(unctrl('\xc1'), '!A')
Serhiy Storchaka514f9732016-06-18 22:08:11 +0300500 self.assertEqual(unctrl(ord('\x8a')), '!^J')
501 self.assertEqual(unctrl(ord('\xc1')), '!A')
Serhiy Storchaka0eb39e72016-05-21 21:36:11 +0300502
503
Alexandre Vassalotti5ff02352009-07-22 21:27:53 +0000504if __name__ == '__main__':
Zachary Warebaf45c52014-10-17 13:59:18 -0500505 unittest.main()