test_curses.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323
  1. import functools
  2. import inspect
  3. import os
  4. import string
  5. import sys
  6. import tempfile
  7. import unittest
  8. from test.support import (requires, verbose, SaveSignals, cpython_only,
  9. check_disallow_instantiation)
  10. from test.support.import_helper import import_module
  11. # Optionally test curses module. This currently requires that the
  12. # 'curses' resource be given on the regrtest command line using the -u
  13. # option. If not available, nothing after this line will be executed.
  14. requires('curses')
  15. # If either of these don't exist, skip the tests.
  16. curses = import_module('curses')
  17. import_module('curses.ascii')
  18. import_module('curses.textpad')
  19. try:
  20. import curses.panel
  21. except ImportError:
  22. pass
  23. def requires_curses_func(name):
  24. return unittest.skipUnless(hasattr(curses, name),
  25. 'requires curses.%s' % name)
  26. def requires_curses_window_meth(name):
  27. def deco(test):
  28. @functools.wraps(test)
  29. def wrapped(self, *args, **kwargs):
  30. if not hasattr(self.stdscr, name):
  31. raise unittest.SkipTest('requires curses.window.%s' % name)
  32. test(self, *args, **kwargs)
  33. return wrapped
  34. return deco
  35. def requires_colors(test):
  36. @functools.wraps(test)
  37. def wrapped(self, *args, **kwargs):
  38. if not curses.has_colors():
  39. self.skipTest('requires colors support')
  40. curses.start_color()
  41. test(self, *args, **kwargs)
  42. return wrapped
  43. term = os.environ.get('TERM')
  44. SHORT_MAX = 0x7fff
  45. # If newterm was supported we could use it instead of initscr and not exit
  46. @unittest.skipIf(not term or term == 'unknown',
  47. "$TERM=%r, calling initscr() may cause exit" % term)
  48. @unittest.skipIf(sys.platform == "cygwin",
  49. "cygwin's curses mostly just hangs")
  50. class TestCurses(unittest.TestCase):
  51. @classmethod
  52. def setUpClass(cls):
  53. if verbose:
  54. print(f'TERM={term}', file=sys.stderr, flush=True)
  55. # testing setupterm() inside initscr/endwin
  56. # causes terminal breakage
  57. stdout_fd = sys.__stdout__.fileno()
  58. curses.setupterm(fd=stdout_fd)
  59. def setUp(self):
  60. self.isatty = True
  61. self.output = sys.__stdout__
  62. stdout_fd = sys.__stdout__.fileno()
  63. if not sys.__stdout__.isatty():
  64. # initstr() unconditionally uses C stdout.
  65. # If it is redirected to file or pipe, try to attach it
  66. # to terminal.
  67. # First, save a copy of the file descriptor of stdout, so it
  68. # can be restored after finishing the test.
  69. dup_fd = os.dup(stdout_fd)
  70. self.addCleanup(os.close, dup_fd)
  71. self.addCleanup(os.dup2, dup_fd, stdout_fd)
  72. if sys.__stderr__.isatty():
  73. # If stderr is connected to terminal, use it.
  74. tmp = sys.__stderr__
  75. self.output = sys.__stderr__
  76. else:
  77. try:
  78. # Try to open the terminal device.
  79. tmp = open('/dev/tty', 'wb', buffering=0)
  80. except OSError:
  81. # As a fallback, use regular file to write control codes.
  82. # Some functions (like savetty) will not work, but at
  83. # least the garbage control sequences will not be mixed
  84. # with the testing report.
  85. tmp = tempfile.TemporaryFile(mode='wb', buffering=0)
  86. self.isatty = False
  87. self.addCleanup(tmp.close)
  88. self.output = None
  89. os.dup2(tmp.fileno(), stdout_fd)
  90. self.save_signals = SaveSignals()
  91. self.save_signals.save()
  92. self.addCleanup(self.save_signals.restore)
  93. if verbose and self.output is not None:
  94. # just to make the test output a little more readable
  95. sys.stderr.flush()
  96. sys.stdout.flush()
  97. print(file=self.output, flush=True)
  98. self.stdscr = curses.initscr()
  99. if self.isatty:
  100. curses.savetty()
  101. self.addCleanup(curses.endwin)
  102. self.addCleanup(curses.resetty)
  103. self.stdscr.erase()
  104. @requires_curses_func('filter')
  105. def test_filter(self):
  106. # TODO: Should be called before initscr() or newterm() are called.
  107. # TODO: nofilter()
  108. curses.filter()
  109. @requires_curses_func('use_env')
  110. def test_use_env(self):
  111. # TODO: Should be called before initscr() or newterm() are called.
  112. # TODO: use_tioctl()
  113. curses.use_env(False)
  114. curses.use_env(True)
  115. def test_create_windows(self):
  116. win = curses.newwin(5, 10)
  117. self.assertEqual(win.getbegyx(), (0, 0))
  118. self.assertEqual(win.getparyx(), (-1, -1))
  119. self.assertEqual(win.getmaxyx(), (5, 10))
  120. win = curses.newwin(10, 15, 2, 5)
  121. self.assertEqual(win.getbegyx(), (2, 5))
  122. self.assertEqual(win.getparyx(), (-1, -1))
  123. self.assertEqual(win.getmaxyx(), (10, 15))
  124. win2 = win.subwin(3, 7)
  125. self.assertEqual(win2.getbegyx(), (3, 7))
  126. self.assertEqual(win2.getparyx(), (1, 2))
  127. self.assertEqual(win2.getmaxyx(), (9, 13))
  128. win2 = win.subwin(5, 10, 3, 7)
  129. self.assertEqual(win2.getbegyx(), (3, 7))
  130. self.assertEqual(win2.getparyx(), (1, 2))
  131. self.assertEqual(win2.getmaxyx(), (5, 10))
  132. win3 = win.derwin(2, 3)
  133. self.assertEqual(win3.getbegyx(), (4, 8))
  134. self.assertEqual(win3.getparyx(), (2, 3))
  135. self.assertEqual(win3.getmaxyx(), (8, 12))
  136. win3 = win.derwin(6, 11, 2, 3)
  137. self.assertEqual(win3.getbegyx(), (4, 8))
  138. self.assertEqual(win3.getparyx(), (2, 3))
  139. self.assertEqual(win3.getmaxyx(), (6, 11))
  140. win.mvwin(0, 1)
  141. self.assertEqual(win.getbegyx(), (0, 1))
  142. self.assertEqual(win.getparyx(), (-1, -1))
  143. self.assertEqual(win.getmaxyx(), (10, 15))
  144. self.assertEqual(win2.getbegyx(), (3, 7))
  145. self.assertEqual(win2.getparyx(), (1, 2))
  146. self.assertEqual(win2.getmaxyx(), (5, 10))
  147. self.assertEqual(win3.getbegyx(), (4, 8))
  148. self.assertEqual(win3.getparyx(), (2, 3))
  149. self.assertEqual(win3.getmaxyx(), (6, 11))
  150. win2.mvderwin(2, 1)
  151. self.assertEqual(win2.getbegyx(), (3, 7))
  152. self.assertEqual(win2.getparyx(), (2, 1))
  153. self.assertEqual(win2.getmaxyx(), (5, 10))
  154. win3.mvderwin(2, 1)
  155. self.assertEqual(win3.getbegyx(), (4, 8))
  156. self.assertEqual(win3.getparyx(), (2, 1))
  157. self.assertEqual(win3.getmaxyx(), (6, 11))
  158. def test_move_cursor(self):
  159. stdscr = self.stdscr
  160. win = stdscr.subwin(10, 15, 2, 5)
  161. stdscr.move(1, 2)
  162. win.move(2, 4)
  163. self.assertEqual(stdscr.getyx(), (1, 2))
  164. self.assertEqual(win.getyx(), (2, 4))
  165. win.cursyncup()
  166. self.assertEqual(stdscr.getyx(), (4, 9))
  167. def test_refresh_control(self):
  168. stdscr = self.stdscr
  169. # touchwin()/untouchwin()/is_wintouched()
  170. stdscr.refresh()
  171. self.assertIs(stdscr.is_wintouched(), False)
  172. stdscr.touchwin()
  173. self.assertIs(stdscr.is_wintouched(), True)
  174. stdscr.refresh()
  175. self.assertIs(stdscr.is_wintouched(), False)
  176. stdscr.touchwin()
  177. self.assertIs(stdscr.is_wintouched(), True)
  178. stdscr.untouchwin()
  179. self.assertIs(stdscr.is_wintouched(), False)
  180. # touchline()/untouchline()/is_linetouched()
  181. stdscr.touchline(5, 2)
  182. self.assertIs(stdscr.is_linetouched(5), True)
  183. self.assertIs(stdscr.is_linetouched(6), True)
  184. self.assertIs(stdscr.is_wintouched(), True)
  185. stdscr.touchline(5, 1, False)
  186. self.assertIs(stdscr.is_linetouched(5), False)
  187. # syncup()
  188. win = stdscr.subwin(10, 15, 2, 5)
  189. win2 = win.subwin(5, 10, 3, 7)
  190. win2.touchwin()
  191. stdscr.untouchwin()
  192. win2.syncup()
  193. self.assertIs(win.is_wintouched(), True)
  194. self.assertIs(stdscr.is_wintouched(), True)
  195. # syncdown()
  196. stdscr.touchwin()
  197. win.untouchwin()
  198. win2.untouchwin()
  199. win2.syncdown()
  200. self.assertIs(win2.is_wintouched(), True)
  201. # syncok()
  202. if hasattr(stdscr, 'syncok') and not sys.platform.startswith("sunos"):
  203. win.untouchwin()
  204. stdscr.untouchwin()
  205. for syncok in [False, True]:
  206. win2.syncok(syncok)
  207. win2.addch('a')
  208. self.assertIs(win.is_wintouched(), syncok)
  209. self.assertIs(stdscr.is_wintouched(), syncok)
  210. def test_output_character(self):
  211. stdscr = self.stdscr
  212. encoding = stdscr.encoding
  213. # addch()
  214. stdscr.refresh()
  215. stdscr.move(0, 0)
  216. stdscr.addch('A')
  217. stdscr.addch(b'A')
  218. stdscr.addch(65)
  219. c = '\u20ac'
  220. try:
  221. stdscr.addch(c)
  222. except UnicodeEncodeError:
  223. self.assertRaises(UnicodeEncodeError, c.encode, encoding)
  224. except OverflowError:
  225. encoded = c.encode(encoding)
  226. self.assertNotEqual(len(encoded), 1, repr(encoded))
  227. stdscr.addch('A', curses.A_BOLD)
  228. stdscr.addch(1, 2, 'A')
  229. stdscr.addch(2, 3, 'A', curses.A_BOLD)
  230. self.assertIs(stdscr.is_wintouched(), True)
  231. # echochar()
  232. stdscr.refresh()
  233. stdscr.move(0, 0)
  234. stdscr.echochar('A')
  235. stdscr.echochar(b'A')
  236. stdscr.echochar(65)
  237. with self.assertRaises((UnicodeEncodeError, OverflowError)):
  238. # Unicode is not fully supported yet, but at least it does
  239. # not crash.
  240. # It is supposed to fail because either the character is
  241. # not encodable with the current encoding, or it is encoded to
  242. # a multibyte sequence.
  243. stdscr.echochar('\u0114')
  244. stdscr.echochar('A', curses.A_BOLD)
  245. self.assertIs(stdscr.is_wintouched(), False)
  246. def test_output_string(self):
  247. stdscr = self.stdscr
  248. encoding = stdscr.encoding
  249. # addstr()/insstr()
  250. for func in [stdscr.addstr, stdscr.insstr]:
  251. with self.subTest(func.__qualname__):
  252. stdscr.move(0, 0)
  253. func('abcd')
  254. func(b'abcd')
  255. s = 'àßçđ'
  256. try:
  257. func(s)
  258. except UnicodeEncodeError:
  259. self.assertRaises(UnicodeEncodeError, s.encode, encoding)
  260. func('abcd', curses.A_BOLD)
  261. func(1, 2, 'abcd')
  262. func(2, 3, 'abcd', curses.A_BOLD)
  263. # addnstr()/insnstr()
  264. for func in [stdscr.addnstr, stdscr.insnstr]:
  265. with self.subTest(func.__qualname__):
  266. stdscr.move(0, 0)
  267. func('1234', 3)
  268. func(b'1234', 3)
  269. s = '\u0661\u0662\u0663\u0664'
  270. try:
  271. func(s, 3)
  272. except UnicodeEncodeError:
  273. self.assertRaises(UnicodeEncodeError, s.encode, encoding)
  274. func('1234', 5)
  275. func('1234', 3, curses.A_BOLD)
  276. func(1, 2, '1234', 3)
  277. func(2, 3, '1234', 3, curses.A_BOLD)
  278. def test_output_string_embedded_null_chars(self):
  279. # reject embedded null bytes and characters
  280. stdscr = self.stdscr
  281. for arg in ['a\0', b'a\0']:
  282. with self.subTest(arg=arg):
  283. self.assertRaises(ValueError, stdscr.addstr, arg)
  284. self.assertRaises(ValueError, stdscr.addnstr, arg, 1)
  285. self.assertRaises(ValueError, stdscr.insstr, arg)
  286. self.assertRaises(ValueError, stdscr.insnstr, arg, 1)
  287. def test_read_from_window(self):
  288. stdscr = self.stdscr
  289. stdscr.addstr(0, 1, 'ABCD', curses.A_BOLD)
  290. # inch()
  291. stdscr.move(0, 1)
  292. self.assertEqual(stdscr.inch(), 65 | curses.A_BOLD)
  293. self.assertEqual(stdscr.inch(0, 3), 67 | curses.A_BOLD)
  294. stdscr.move(0, 0)
  295. # instr()
  296. self.assertEqual(stdscr.instr()[:6], b' ABCD ')
  297. self.assertEqual(stdscr.instr(3)[:6], b' AB')
  298. self.assertEqual(stdscr.instr(0, 2)[:4], b'BCD ')
  299. self.assertEqual(stdscr.instr(0, 2, 4), b'BCD ')
  300. self.assertRaises(ValueError, stdscr.instr, -2)
  301. self.assertRaises(ValueError, stdscr.instr, 0, 2, -2)
  302. def test_getch(self):
  303. win = curses.newwin(5, 12, 5, 2)
  304. # TODO: Test with real input by writing to master fd.
  305. for c in 'spam\n'[::-1]:
  306. curses.ungetch(c)
  307. self.assertEqual(win.getch(3, 1), b's'[0])
  308. self.assertEqual(win.getyx(), (3, 1))
  309. self.assertEqual(win.getch(3, 4), b'p'[0])
  310. self.assertEqual(win.getyx(), (3, 4))
  311. self.assertEqual(win.getch(), b'a'[0])
  312. self.assertEqual(win.getyx(), (3, 4))
  313. self.assertEqual(win.getch(), b'm'[0])
  314. self.assertEqual(win.getch(), b'\n'[0])
  315. def test_getstr(self):
  316. win = curses.newwin(5, 12, 5, 2)
  317. curses.echo()
  318. self.addCleanup(curses.noecho)
  319. self.assertRaises(ValueError, win.getstr, -400)
  320. self.assertRaises(ValueError, win.getstr, 2, 3, -400)
  321. # TODO: Test with real input by writing to master fd.
  322. for c in 'Lorem\nipsum\ndolor\nsit\namet\n'[::-1]:
  323. curses.ungetch(c)
  324. self.assertEqual(win.getstr(3, 1, 2), b'Lo')
  325. self.assertEqual(win.instr(3, 0), b' Lo ')
  326. self.assertEqual(win.getstr(3, 5, 10), b'ipsum')
  327. self.assertEqual(win.instr(3, 0), b' Lo ipsum ')
  328. self.assertEqual(win.getstr(1, 5), b'dolor')
  329. self.assertEqual(win.instr(1, 0), b' dolor ')
  330. self.assertEqual(win.getstr(2), b'si')
  331. self.assertEqual(win.instr(1, 0), b'si dolor ')
  332. self.assertEqual(win.getstr(), b'amet')
  333. self.assertEqual(win.instr(1, 0), b'amet dolor ')
  334. def test_clear(self):
  335. win = curses.newwin(5, 15, 5, 2)
  336. lorem_ipsum(win)
  337. win.move(0, 8)
  338. win.clrtoeol()
  339. self.assertEqual(win.instr(0, 0).rstrip(), b'Lorem ip')
  340. self.assertEqual(win.instr(1, 0).rstrip(), b'dolor sit amet,')
  341. win.move(0, 3)
  342. win.clrtobot()
  343. self.assertEqual(win.instr(0, 0).rstrip(), b'Lor')
  344. self.assertEqual(win.instr(1, 0).rstrip(), b'')
  345. for func in [win.erase, win.clear]:
  346. lorem_ipsum(win)
  347. func()
  348. self.assertEqual(win.instr(0, 0).rstrip(), b'')
  349. self.assertEqual(win.instr(1, 0).rstrip(), b'')
  350. def test_insert_delete(self):
  351. win = curses.newwin(5, 15, 5, 2)
  352. lorem_ipsum(win)
  353. win.move(0, 2)
  354. win.delch()
  355. self.assertEqual(win.instr(0, 0), b'Loem ipsum ')
  356. win.delch(0, 7)
  357. self.assertEqual(win.instr(0, 0), b'Loem ipum ')
  358. win.move(1, 5)
  359. win.deleteln()
  360. self.assertEqual(win.instr(0, 0), b'Loem ipum ')
  361. self.assertEqual(win.instr(1, 0), b'consectetur ')
  362. self.assertEqual(win.instr(2, 0), b'adipiscing elit')
  363. self.assertEqual(win.instr(3, 0), b'sed do eiusmod ')
  364. self.assertEqual(win.instr(4, 0), b' ')
  365. win.move(1, 5)
  366. win.insertln()
  367. self.assertEqual(win.instr(0, 0), b'Loem ipum ')
  368. self.assertEqual(win.instr(1, 0), b' ')
  369. self.assertEqual(win.instr(2, 0), b'consectetur ')
  370. win.clear()
  371. lorem_ipsum(win)
  372. win.move(1, 5)
  373. win.insdelln(2)
  374. self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
  375. self.assertEqual(win.instr(1, 0), b' ')
  376. self.assertEqual(win.instr(2, 0), b' ')
  377. self.assertEqual(win.instr(3, 0), b'dolor sit amet,')
  378. win.clear()
  379. lorem_ipsum(win)
  380. win.move(1, 5)
  381. win.insdelln(-2)
  382. self.assertEqual(win.instr(0, 0), b'Lorem ipsum ')
  383. self.assertEqual(win.instr(1, 0), b'adipiscing elit')
  384. self.assertEqual(win.instr(2, 0), b'sed do eiusmod ')
  385. self.assertEqual(win.instr(3, 0), b' ')
  386. def test_scroll(self):
  387. win = curses.newwin(5, 15, 5, 2)
  388. lorem_ipsum(win)
  389. win.scrollok(True)
  390. win.scroll()
  391. self.assertEqual(win.instr(0, 0), b'dolor sit amet,')
  392. win.scroll(2)
  393. self.assertEqual(win.instr(0, 0), b'adipiscing elit')
  394. win.scroll(-3)
  395. self.assertEqual(win.instr(0, 0), b' ')
  396. self.assertEqual(win.instr(2, 0), b' ')
  397. self.assertEqual(win.instr(3, 0), b'adipiscing elit')
  398. win.scrollok(False)
  399. def test_attributes(self):
  400. # TODO: attr_get(), attr_set(), ...
  401. win = curses.newwin(5, 15, 5, 2)
  402. win.attron(curses.A_BOLD)
  403. win.attroff(curses.A_BOLD)
  404. win.attrset(curses.A_BOLD)
  405. win.standout()
  406. win.standend()
  407. @requires_curses_window_meth('chgat')
  408. def test_chgat(self):
  409. win = curses.newwin(5, 15, 5, 2)
  410. win.addstr(2, 0, 'Lorem ipsum')
  411. win.addstr(3, 0, 'dolor sit amet')
  412. win.move(2, 8)
  413. win.chgat(curses.A_BLINK)
  414. self.assertEqual(win.inch(2, 7), b'p'[0])
  415. self.assertEqual(win.inch(2, 8), b's'[0] | curses.A_BLINK)
  416. self.assertEqual(win.inch(2, 14), b' '[0] | curses.A_BLINK)
  417. win.move(2, 1)
  418. win.chgat(3, curses.A_BOLD)
  419. self.assertEqual(win.inch(2, 0), b'L'[0])
  420. self.assertEqual(win.inch(2, 1), b'o'[0] | curses.A_BOLD)
  421. self.assertEqual(win.inch(2, 3), b'e'[0] | curses.A_BOLD)
  422. self.assertEqual(win.inch(2, 4), b'm'[0])
  423. win.chgat(3, 2, curses.A_UNDERLINE)
  424. self.assertEqual(win.inch(3, 1), b'o'[0])
  425. self.assertEqual(win.inch(3, 2), b'l'[0] | curses.A_UNDERLINE)
  426. self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
  427. win.chgat(3, 4, 7, curses.A_BLINK)
  428. self.assertEqual(win.inch(3, 3), b'o'[0] | curses.A_UNDERLINE)
  429. self.assertEqual(win.inch(3, 4), b'r'[0] | curses.A_BLINK)
  430. self.assertEqual(win.inch(3, 10), b'a'[0] | curses.A_BLINK)
  431. self.assertEqual(win.inch(3, 11), b'm'[0] | curses.A_UNDERLINE)
  432. self.assertEqual(win.inch(3, 14), b' '[0] | curses.A_UNDERLINE)
  433. def test_background(self):
  434. win = curses.newwin(5, 15, 5, 2)
  435. win.addstr(0, 0, 'Lorem ipsum')
  436. self.assertIn(win.getbkgd(), (0, 32))
  437. # bkgdset()
  438. win.bkgdset('_')
  439. self.assertEqual(win.getbkgd(), b'_'[0])
  440. win.bkgdset(b'#')
  441. self.assertEqual(win.getbkgd(), b'#'[0])
  442. win.bkgdset(65)
  443. self.assertEqual(win.getbkgd(), 65)
  444. win.bkgdset(0)
  445. self.assertEqual(win.getbkgd(), 32)
  446. win.bkgdset('#', curses.A_REVERSE)
  447. self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
  448. self.assertEqual(win.inch(0, 0), b'L'[0])
  449. self.assertEqual(win.inch(0, 5), b' '[0])
  450. win.bkgdset(0)
  451. # bkgd()
  452. win.bkgd('_')
  453. self.assertEqual(win.getbkgd(), b'_'[0])
  454. self.assertEqual(win.inch(0, 0), b'L'[0])
  455. self.assertEqual(win.inch(0, 5), b'_'[0])
  456. win.bkgd('#', curses.A_REVERSE)
  457. self.assertEqual(win.getbkgd(), b'#'[0] | curses.A_REVERSE)
  458. self.assertEqual(win.inch(0, 0), b'L'[0] | curses.A_REVERSE)
  459. self.assertEqual(win.inch(0, 5), b'#'[0] | curses.A_REVERSE)
  460. def test_overlay(self):
  461. srcwin = curses.newwin(5, 18, 3, 4)
  462. lorem_ipsum(srcwin)
  463. dstwin = curses.newwin(7, 17, 5, 7)
  464. for i in range(6):
  465. dstwin.addstr(i, 0, '_'*17)
  466. srcwin.overlay(dstwin)
  467. self.assertEqual(dstwin.instr(0, 0), b'sectetur_________')
  468. self.assertEqual(dstwin.instr(1, 0), b'piscing_elit,____')
  469. self.assertEqual(dstwin.instr(2, 0), b'_do_eiusmod______')
  470. self.assertEqual(dstwin.instr(3, 0), b'_________________')
  471. srcwin.overwrite(dstwin)
  472. self.assertEqual(dstwin.instr(0, 0), b'sectetur __')
  473. self.assertEqual(dstwin.instr(1, 0), b'piscing elit, __')
  474. self.assertEqual(dstwin.instr(2, 0), b' do eiusmod __')
  475. self.assertEqual(dstwin.instr(3, 0), b'_________________')
  476. srcwin.overlay(dstwin, 1, 4, 3, 2, 4, 11)
  477. self.assertEqual(dstwin.instr(3, 0), b'__r_sit_amet_____')
  478. self.assertEqual(dstwin.instr(4, 0), b'__ectetur________')
  479. self.assertEqual(dstwin.instr(5, 0), b'_________________')
  480. srcwin.overwrite(dstwin, 1, 4, 3, 2, 4, 11)
  481. self.assertEqual(dstwin.instr(3, 0), b'__r sit amet_____')
  482. self.assertEqual(dstwin.instr(4, 0), b'__ectetur _____')
  483. self.assertEqual(dstwin.instr(5, 0), b'_________________')
  484. def test_refresh(self):
  485. win = curses.newwin(5, 15, 2, 5)
  486. win.noutrefresh()
  487. win.redrawln(1, 2)
  488. win.redrawwin()
  489. win.refresh()
  490. curses.doupdate()
  491. @requires_curses_window_meth('resize')
  492. def test_resize(self):
  493. win = curses.newwin(5, 15, 2, 5)
  494. win.resize(4, 20)
  495. self.assertEqual(win.getmaxyx(), (4, 20))
  496. win.resize(5, 15)
  497. self.assertEqual(win.getmaxyx(), (5, 15))
  498. @requires_curses_window_meth('enclose')
  499. def test_enclose(self):
  500. win = curses.newwin(5, 15, 2, 5)
  501. self.assertIs(win.enclose(2, 5), True)
  502. self.assertIs(win.enclose(1, 5), False)
  503. self.assertIs(win.enclose(2, 4), False)
  504. self.assertIs(win.enclose(6, 19), True)
  505. self.assertIs(win.enclose(7, 19), False)
  506. self.assertIs(win.enclose(6, 20), False)
  507. def test_putwin(self):
  508. win = curses.newwin(5, 12, 1, 2)
  509. win.addstr(2, 1, 'Lorem ipsum')
  510. with tempfile.TemporaryFile() as f:
  511. win.putwin(f)
  512. del win
  513. f.seek(0)
  514. win = curses.getwin(f)
  515. self.assertEqual(win.getbegyx(), (1, 2))
  516. self.assertEqual(win.getmaxyx(), (5, 12))
  517. self.assertEqual(win.instr(2, 0), b' Lorem ipsum')
  518. def test_borders_and_lines(self):
  519. win = curses.newwin(5, 10, 5, 2)
  520. win.border('|', '!', '-', '_',
  521. '+', '\\', '#', '/')
  522. self.assertEqual(win.instr(0, 0), b'+--------\\')
  523. self.assertEqual(win.instr(1, 0), b'| !')
  524. self.assertEqual(win.instr(4, 0), b'#________/')
  525. win.border(b'|', b'!', b'-', b'_',
  526. b'+', b'\\', b'#', b'/')
  527. win.border(65, 66, 67, 68,
  528. 69, 70, 71, 72)
  529. self.assertRaises(TypeError, win.border,
  530. 65, 66, 67, 68, 69, [], 71, 72)
  531. self.assertRaises(TypeError, win.border,
  532. 65, 66, 67, 68, 69, 70, 71, 72, 73)
  533. self.assertRaises(TypeError, win.border,
  534. 65, 66, 67, 68, 69, 70, 71, 72, 73)
  535. win.border(65, 66, 67, 68, 69, 70, 71)
  536. win.border(65, 66, 67, 68, 69, 70)
  537. win.border(65, 66, 67, 68, 69)
  538. win.border(65, 66, 67, 68)
  539. win.border(65, 66, 67)
  540. win.border(65, 66)
  541. win.border(65)
  542. win.border()
  543. win.box(':', '~')
  544. self.assertEqual(win.instr(0, 1, 8), b'~~~~~~~~')
  545. self.assertEqual(win.instr(1, 0), b': :')
  546. self.assertEqual(win.instr(4, 1, 8), b'~~~~~~~~')
  547. win.box(b':', b'~')
  548. win.box(65, 67)
  549. self.assertRaises(TypeError, win.box, 65, 66, 67)
  550. self.assertRaises(TypeError, win.box, 65)
  551. win.box()
  552. win.move(1, 2)
  553. win.hline('-', 5)
  554. self.assertEqual(win.instr(1, 1, 7), b' ----- ')
  555. win.hline(b'-', 5)
  556. win.hline(45, 5)
  557. win.hline('-', 5, curses.A_BOLD)
  558. win.hline(1, 1, '-', 5)
  559. win.hline(1, 1, '-', 5, curses.A_BOLD)
  560. win.move(1, 2)
  561. win.vline('a', 3)
  562. win.vline(b'a', 3)
  563. win.vline(97, 3)
  564. win.vline('a', 3, curses.A_STANDOUT)
  565. win.vline(1, 1, 'a', 3)
  566. win.vline(1, 1, ';', 2, curses.A_STANDOUT)
  567. self.assertEqual(win.inch(1, 1), b';'[0] | curses.A_STANDOUT)
  568. self.assertEqual(win.inch(2, 1), b';'[0] | curses.A_STANDOUT)
  569. self.assertEqual(win.inch(3, 1), b'a'[0])
  570. def test_unctrl(self):
  571. # TODO: wunctrl()
  572. self.assertEqual(curses.unctrl(b'A'), b'A')
  573. self.assertEqual(curses.unctrl('A'), b'A')
  574. self.assertEqual(curses.unctrl(65), b'A')
  575. self.assertEqual(curses.unctrl(b'\n'), b'^J')
  576. self.assertEqual(curses.unctrl('\n'), b'^J')
  577. self.assertEqual(curses.unctrl(10), b'^J')
  578. self.assertRaises(TypeError, curses.unctrl, b'')
  579. self.assertRaises(TypeError, curses.unctrl, b'AB')
  580. self.assertRaises(TypeError, curses.unctrl, '')
  581. self.assertRaises(TypeError, curses.unctrl, 'AB')
  582. self.assertRaises(OverflowError, curses.unctrl, 2**64)
  583. def test_endwin(self):
  584. if not self.isatty:
  585. self.skipTest('requires terminal')
  586. self.assertIs(curses.isendwin(), False)
  587. curses.endwin()
  588. self.assertIs(curses.isendwin(), True)
  589. curses.doupdate()
  590. self.assertIs(curses.isendwin(), False)
  591. def test_terminfo(self):
  592. self.assertIsInstance(curses.tigetflag('hc'), int)
  593. self.assertEqual(curses.tigetflag('cols'), -1)
  594. self.assertEqual(curses.tigetflag('cr'), -1)
  595. self.assertIsInstance(curses.tigetnum('cols'), int)
  596. self.assertEqual(curses.tigetnum('hc'), -2)
  597. self.assertEqual(curses.tigetnum('cr'), -2)
  598. self.assertIsInstance(curses.tigetstr('cr'), (bytes, type(None)))
  599. self.assertIsNone(curses.tigetstr('hc'))
  600. self.assertIsNone(curses.tigetstr('cols'))
  601. cud = curses.tigetstr('cud')
  602. if cud is not None:
  603. # See issue10570.
  604. self.assertIsInstance(cud, bytes)
  605. curses.tparm(cud, 2)
  606. cud_2 = curses.tparm(cud, 2)
  607. self.assertIsInstance(cud_2, bytes)
  608. curses.putp(cud_2)
  609. curses.putp(b'abc\n')
  610. def test_misc_module_funcs(self):
  611. curses.delay_output(1)
  612. curses.flushinp()
  613. curses.doupdate()
  614. self.assertIs(curses.isendwin(), False)
  615. curses.napms(100)
  616. curses.newpad(50, 50)
  617. def test_env_queries(self):
  618. # TODO: term_attrs(), erasewchar(), killwchar()
  619. self.assertIsInstance(curses.termname(), bytes)
  620. self.assertIsInstance(curses.longname(), bytes)
  621. self.assertIsInstance(curses.baudrate(), int)
  622. self.assertIsInstance(curses.has_ic(), bool)
  623. self.assertIsInstance(curses.has_il(), bool)
  624. self.assertIsInstance(curses.termattrs(), int)
  625. c = curses.killchar()
  626. self.assertIsInstance(c, bytes)
  627. self.assertEqual(len(c), 1)
  628. c = curses.erasechar()
  629. self.assertIsInstance(c, bytes)
  630. self.assertEqual(len(c), 1)
  631. def test_output_options(self):
  632. stdscr = self.stdscr
  633. stdscr.clearok(True)
  634. stdscr.clearok(False)
  635. stdscr.idcok(True)
  636. stdscr.idcok(False)
  637. stdscr.idlok(False)
  638. stdscr.idlok(True)
  639. if hasattr(stdscr, 'immedok'):
  640. stdscr.immedok(True)
  641. stdscr.immedok(False)
  642. stdscr.leaveok(True)
  643. stdscr.leaveok(False)
  644. stdscr.scrollok(True)
  645. stdscr.scrollok(False)
  646. stdscr.setscrreg(5, 10)
  647. curses.nonl()
  648. curses.nl(True)
  649. curses.nl(False)
  650. curses.nl()
  651. def test_input_options(self):
  652. stdscr = self.stdscr
  653. if self.isatty:
  654. curses.nocbreak()
  655. curses.cbreak()
  656. curses.cbreak(False)
  657. curses.cbreak(True)
  658. curses.intrflush(True)
  659. curses.intrflush(False)
  660. curses.raw()
  661. curses.raw(False)
  662. curses.raw(True)
  663. curses.noraw()
  664. curses.noecho()
  665. curses.echo()
  666. curses.echo(False)
  667. curses.echo(True)
  668. curses.halfdelay(255)
  669. curses.halfdelay(1)
  670. stdscr.keypad(True)
  671. stdscr.keypad(False)
  672. curses.meta(True)
  673. curses.meta(False)
  674. stdscr.nodelay(True)
  675. stdscr.nodelay(False)
  676. curses.noqiflush()
  677. curses.qiflush(True)
  678. curses.qiflush(False)
  679. curses.qiflush()
  680. stdscr.notimeout(True)
  681. stdscr.notimeout(False)
  682. stdscr.timeout(-1)
  683. stdscr.timeout(0)
  684. stdscr.timeout(5)
  685. @requires_curses_func('typeahead')
  686. def test_typeahead(self):
  687. curses.typeahead(sys.__stdin__.fileno())
  688. curses.typeahead(-1)
  689. def test_prog_mode(self):
  690. if not self.isatty:
  691. self.skipTest('requires terminal')
  692. curses.def_prog_mode()
  693. curses.reset_prog_mode()
  694. def test_beep(self):
  695. if (curses.tigetstr("bel") is not None
  696. or curses.tigetstr("flash") is not None):
  697. curses.beep()
  698. else:
  699. try:
  700. curses.beep()
  701. except curses.error:
  702. self.skipTest('beep() failed')
  703. def test_flash(self):
  704. if (curses.tigetstr("bel") is not None
  705. or curses.tigetstr("flash") is not None):
  706. curses.flash()
  707. else:
  708. try:
  709. curses.flash()
  710. except curses.error:
  711. self.skipTest('flash() failed')
  712. def test_curs_set(self):
  713. for vis, cap in [(0, 'civis'), (2, 'cvvis'), (1, 'cnorm')]:
  714. if curses.tigetstr(cap) is not None:
  715. curses.curs_set(vis)
  716. else:
  717. try:
  718. curses.curs_set(vis)
  719. except curses.error:
  720. pass
  721. @requires_curses_func('get_escdelay')
  722. def test_escdelay(self):
  723. escdelay = curses.get_escdelay()
  724. self.assertIsInstance(escdelay, int)
  725. curses.set_escdelay(25)
  726. self.assertEqual(curses.get_escdelay(), 25)
  727. curses.set_escdelay(escdelay)
  728. @requires_curses_func('get_tabsize')
  729. def test_tabsize(self):
  730. tabsize = curses.get_tabsize()
  731. self.assertIsInstance(tabsize, int)
  732. curses.set_tabsize(4)
  733. self.assertEqual(curses.get_tabsize(), 4)
  734. curses.set_tabsize(tabsize)
  735. @requires_curses_func('getsyx')
  736. def test_getsyx(self):
  737. y, x = curses.getsyx()
  738. self.assertIsInstance(y, int)
  739. self.assertIsInstance(x, int)
  740. curses.setsyx(4, 5)
  741. self.assertEqual(curses.getsyx(), (4, 5))
  742. def bad_colors(self):
  743. return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
  744. def bad_colors2(self):
  745. return (curses.COLORS, 2**31, 2**63, 2**64)
  746. def bad_pairs(self):
  747. return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
  748. def test_has_colors(self):
  749. self.assertIsInstance(curses.has_colors(), bool)
  750. self.assertIsInstance(curses.can_change_color(), bool)
  751. def test_start_color(self):
  752. if not curses.has_colors():
  753. self.skipTest('requires colors support')
  754. curses.start_color()
  755. if verbose:
  756. print(f'COLORS = {curses.COLORS}', file=sys.stderr)
  757. print(f'COLOR_PAIRS = {curses.COLOR_PAIRS}', file=sys.stderr)
  758. @requires_colors
  759. def test_color_content(self):
  760. self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
  761. curses.color_content(0)
  762. maxcolor = curses.COLORS - 1
  763. curses.color_content(maxcolor)
  764. for color in self.bad_colors():
  765. self.assertRaises(ValueError, curses.color_content, color)
  766. @requires_colors
  767. def test_init_color(self):
  768. if not curses.can_change_color():
  769. self.skipTest('cannot change color')
  770. old = curses.color_content(0)
  771. try:
  772. curses.init_color(0, *old)
  773. except curses.error:
  774. self.skipTest('cannot change color (init_color() failed)')
  775. self.addCleanup(curses.init_color, 0, *old)
  776. curses.init_color(0, 0, 0, 0)
  777. self.assertEqual(curses.color_content(0), (0, 0, 0))
  778. curses.init_color(0, 1000, 1000, 1000)
  779. self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
  780. maxcolor = curses.COLORS - 1
  781. old = curses.color_content(maxcolor)
  782. curses.init_color(maxcolor, *old)
  783. self.addCleanup(curses.init_color, maxcolor, *old)
  784. curses.init_color(maxcolor, 0, 500, 1000)
  785. self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
  786. for color in self.bad_colors():
  787. self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
  788. for comp in (-1, 1001):
  789. self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
  790. self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
  791. self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
  792. def get_pair_limit(self):
  793. pair_limit = curses.COLOR_PAIRS
  794. if hasattr(curses, 'ncurses_version'):
  795. if curses.has_extended_color_support():
  796. pair_limit += 2*curses.COLORS + 1
  797. if (not curses.has_extended_color_support()
  798. or (6, 1) <= curses.ncurses_version < (6, 2)):
  799. pair_limit = min(pair_limit, SHORT_MAX)
  800. # If use_default_colors() is called, the upper limit of the extended
  801. # range may be restricted, so we need to check if the limit is still
  802. # correct
  803. try:
  804. curses.init_pair(pair_limit - 1, 0, 0)
  805. except ValueError:
  806. pair_limit = curses.COLOR_PAIRS
  807. return pair_limit
  808. @requires_colors
  809. def test_pair_content(self):
  810. if not hasattr(curses, 'use_default_colors'):
  811. self.assertEqual(curses.pair_content(0),
  812. (curses.COLOR_WHITE, curses.COLOR_BLACK))
  813. curses.pair_content(0)
  814. maxpair = self.get_pair_limit() - 1
  815. if maxpair > 0:
  816. curses.pair_content(maxpair)
  817. for pair in self.bad_pairs():
  818. self.assertRaises(ValueError, curses.pair_content, pair)
  819. @requires_colors
  820. def test_init_pair(self):
  821. old = curses.pair_content(1)
  822. curses.init_pair(1, *old)
  823. self.addCleanup(curses.init_pair, 1, *old)
  824. curses.init_pair(1, 0, 0)
  825. self.assertEqual(curses.pair_content(1), (0, 0))
  826. maxcolor = curses.COLORS - 1
  827. curses.init_pair(1, maxcolor, 0)
  828. self.assertEqual(curses.pair_content(1), (maxcolor, 0))
  829. curses.init_pair(1, 0, maxcolor)
  830. self.assertEqual(curses.pair_content(1), (0, maxcolor))
  831. maxpair = self.get_pair_limit() - 1
  832. if maxpair > 1:
  833. curses.init_pair(maxpair, 0, 0)
  834. self.assertEqual(curses.pair_content(maxpair), (0, 0))
  835. for pair in self.bad_pairs():
  836. self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
  837. for color in self.bad_colors2():
  838. self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
  839. self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
  840. @requires_colors
  841. def test_color_attrs(self):
  842. for pair in 0, 1, 255:
  843. attr = curses.color_pair(pair)
  844. self.assertEqual(curses.pair_number(attr), pair, attr)
  845. self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
  846. self.assertEqual(curses.color_pair(0), 0)
  847. self.assertEqual(curses.pair_number(0), 0)
  848. @requires_curses_func('use_default_colors')
  849. @requires_colors
  850. def test_use_default_colors(self):
  851. old = curses.pair_content(0)
  852. try:
  853. curses.use_default_colors()
  854. except curses.error:
  855. self.skipTest('cannot change color (use_default_colors() failed)')
  856. self.assertEqual(curses.pair_content(0), (-1, -1))
  857. self.assertIn(old, [(curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1), (0, 0)])
  858. def test_keyname(self):
  859. # TODO: key_name()
  860. self.assertEqual(curses.keyname(65), b'A')
  861. self.assertEqual(curses.keyname(13), b'^M')
  862. self.assertEqual(curses.keyname(127), b'^?')
  863. self.assertEqual(curses.keyname(0), b'^@')
  864. self.assertRaises(ValueError, curses.keyname, -1)
  865. self.assertIsInstance(curses.keyname(256), bytes)
  866. @requires_curses_func('has_key')
  867. def test_has_key(self):
  868. curses.has_key(13)
  869. @requires_curses_func('getmouse')
  870. def test_getmouse(self):
  871. (availmask, oldmask) = curses.mousemask(curses.BUTTON1_PRESSED)
  872. if availmask == 0:
  873. self.skipTest('mouse stuff not available')
  874. curses.mouseinterval(10)
  875. # just verify these don't cause errors
  876. curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED)
  877. m = curses.getmouse()
  878. @requires_curses_func('panel')
  879. def test_userptr_without_set(self):
  880. w = curses.newwin(10, 10)
  881. p = curses.panel.new_panel(w)
  882. # try to access userptr() before calling set_userptr() -- segfaults
  883. with self.assertRaises(curses.panel.error,
  884. msg='userptr should fail since not set'):
  885. p.userptr()
  886. @requires_curses_func('panel')
  887. def test_userptr_memory_leak(self):
  888. w = curses.newwin(10, 10)
  889. p = curses.panel.new_panel(w)
  890. obj = object()
  891. nrefs = sys.getrefcount(obj)
  892. for i in range(100):
  893. p.set_userptr(obj)
  894. p.set_userptr(None)
  895. self.assertEqual(sys.getrefcount(obj), nrefs,
  896. "set_userptr leaked references")
  897. @requires_curses_func('panel')
  898. def test_userptr_segfault(self):
  899. w = curses.newwin(10, 10)
  900. panel = curses.panel.new_panel(w)
  901. class A:
  902. def __del__(self):
  903. panel.set_userptr(None)
  904. panel.set_userptr(A())
  905. panel.set_userptr(None)
  906. @cpython_only
  907. @requires_curses_func('panel')
  908. def test_disallow_instantiation(self):
  909. # Ensure that the type disallows instantiation (bpo-43916)
  910. w = curses.newwin(10, 10)
  911. panel = curses.panel.new_panel(w)
  912. check_disallow_instantiation(self, type(panel))
  913. @requires_curses_func('is_term_resized')
  914. def test_is_term_resized(self):
  915. lines, cols = curses.LINES, curses.COLS
  916. self.assertIs(curses.is_term_resized(lines, cols), False)
  917. self.assertIs(curses.is_term_resized(lines-1, cols-1), True)
  918. @requires_curses_func('resize_term')
  919. def test_resize_term(self):
  920. curses.update_lines_cols()
  921. lines, cols = curses.LINES, curses.COLS
  922. new_lines = lines - 1
  923. new_cols = cols + 1
  924. curses.resize_term(new_lines, new_cols)
  925. self.assertEqual(curses.LINES, new_lines)
  926. self.assertEqual(curses.COLS, new_cols)
  927. curses.resize_term(lines, cols)
  928. self.assertEqual(curses.LINES, lines)
  929. self.assertEqual(curses.COLS, cols)
  930. @requires_curses_func('resizeterm')
  931. def test_resizeterm(self):
  932. curses.update_lines_cols()
  933. lines, cols = curses.LINES, curses.COLS
  934. new_lines = lines - 1
  935. new_cols = cols + 1
  936. curses.resizeterm(new_lines, new_cols)
  937. self.assertEqual(curses.LINES, new_lines)
  938. self.assertEqual(curses.COLS, new_cols)
  939. curses.resizeterm(lines, cols)
  940. self.assertEqual(curses.LINES, lines)
  941. self.assertEqual(curses.COLS, cols)
  942. def test_ungetch(self):
  943. curses.ungetch(b'A')
  944. self.assertEqual(self.stdscr.getkey(), 'A')
  945. curses.ungetch('B')
  946. self.assertEqual(self.stdscr.getkey(), 'B')
  947. curses.ungetch(67)
  948. self.assertEqual(self.stdscr.getkey(), 'C')
  949. def test_issue6243(self):
  950. curses.ungetch(1025)
  951. self.stdscr.getkey()
  952. @requires_curses_func('unget_wch')
  953. @unittest.skipIf(getattr(curses, 'ncurses_version', (99,)) < (5, 8),
  954. "unget_wch is broken in ncurses 5.7 and earlier")
  955. def test_unget_wch(self):
  956. stdscr = self.stdscr
  957. encoding = stdscr.encoding
  958. for ch in ('a', '\xe9', '\u20ac', '\U0010FFFF'):
  959. try:
  960. ch.encode(encoding)
  961. except UnicodeEncodeError:
  962. continue
  963. try:
  964. curses.unget_wch(ch)
  965. except Exception as err:
  966. self.fail("unget_wch(%a) failed with encoding %s: %s"
  967. % (ch, stdscr.encoding, err))
  968. read = stdscr.get_wch()
  969. self.assertEqual(read, ch)
  970. code = ord(ch)
  971. curses.unget_wch(code)
  972. read = stdscr.get_wch()
  973. self.assertEqual(read, ch)
  974. def test_encoding(self):
  975. stdscr = self.stdscr
  976. import codecs
  977. encoding = stdscr.encoding
  978. codecs.lookup(encoding)
  979. with self.assertRaises(TypeError):
  980. stdscr.encoding = 10
  981. stdscr.encoding = encoding
  982. with self.assertRaises(TypeError):
  983. del stdscr.encoding
  984. def test_issue21088(self):
  985. stdscr = self.stdscr
  986. #
  987. # http://bugs.python.org/issue21088
  988. #
  989. # the bug:
  990. # when converting curses.window.addch to Argument Clinic
  991. # the first two parameters were switched.
  992. # if someday we can represent the signature of addch
  993. # we will need to rewrite this test.
  994. try:
  995. signature = inspect.signature(stdscr.addch)
  996. self.assertFalse(signature)
  997. except ValueError:
  998. # not generating a signature is fine.
  999. pass
  1000. # So. No signature for addch.
  1001. # But Argument Clinic gave us a human-readable equivalent
  1002. # as the first line of the docstring. So we parse that,
  1003. # and ensure that the parameters appear in the correct order.
  1004. # Since this is parsing output from Argument Clinic, we can
  1005. # be reasonably certain the generated parsing code will be
  1006. # correct too.
  1007. human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
  1008. self.assertIn("[y, x,]", human_readable_signature)
  1009. @requires_curses_window_meth('resize')
  1010. def test_issue13051(self):
  1011. win = curses.newwin(5, 15, 2, 5)
  1012. box = curses.textpad.Textbox(win, insert_mode=True)
  1013. lines, cols = win.getmaxyx()
  1014. win.resize(lines-2, cols-2)
  1015. # this may cause infinite recursion, leading to a RuntimeError
  1016. box._insert_printable_char('a')
  1017. class MiscTests(unittest.TestCase):
  1018. @requires_curses_func('update_lines_cols')
  1019. def test_update_lines_cols(self):
  1020. curses.update_lines_cols()
  1021. lines, cols = curses.LINES, curses.COLS
  1022. curses.LINES = curses.COLS = 0
  1023. curses.update_lines_cols()
  1024. self.assertEqual(curses.LINES, lines)
  1025. self.assertEqual(curses.COLS, cols)
  1026. @requires_curses_func('ncurses_version')
  1027. def test_ncurses_version(self):
  1028. v = curses.ncurses_version
  1029. if verbose:
  1030. print(f'ncurses_version = {curses.ncurses_version}', flush=True)
  1031. self.assertIsInstance(v[:], tuple)
  1032. self.assertEqual(len(v), 3)
  1033. self.assertIsInstance(v[0], int)
  1034. self.assertIsInstance(v[1], int)
  1035. self.assertIsInstance(v[2], int)
  1036. self.assertIsInstance(v.major, int)
  1037. self.assertIsInstance(v.minor, int)
  1038. self.assertIsInstance(v.patch, int)
  1039. self.assertEqual(v[0], v.major)
  1040. self.assertEqual(v[1], v.minor)
  1041. self.assertEqual(v[2], v.patch)
  1042. self.assertGreaterEqual(v.major, 0)
  1043. self.assertGreaterEqual(v.minor, 0)
  1044. self.assertGreaterEqual(v.patch, 0)
  1045. def test_has_extended_color_support(self):
  1046. r = curses.has_extended_color_support()
  1047. self.assertIsInstance(r, bool)
  1048. class TestAscii(unittest.TestCase):
  1049. def test_controlnames(self):
  1050. for name in curses.ascii.controlnames:
  1051. self.assertTrue(hasattr(curses.ascii, name), name)
  1052. def test_ctypes(self):
  1053. def check(func, expected):
  1054. with self.subTest(ch=c, func=func):
  1055. self.assertEqual(func(i), expected)
  1056. self.assertEqual(func(c), expected)
  1057. for i in range(256):
  1058. c = chr(i)
  1059. b = bytes([i])
  1060. check(curses.ascii.isalnum, b.isalnum())
  1061. check(curses.ascii.isalpha, b.isalpha())
  1062. check(curses.ascii.isdigit, b.isdigit())
  1063. check(curses.ascii.islower, b.islower())
  1064. check(curses.ascii.isspace, b.isspace())
  1065. check(curses.ascii.isupper, b.isupper())
  1066. check(curses.ascii.isascii, i < 128)
  1067. check(curses.ascii.ismeta, i >= 128)
  1068. check(curses.ascii.isctrl, i < 32)
  1069. check(curses.ascii.iscntrl, i < 32 or i == 127)
  1070. check(curses.ascii.isblank, c in ' \t')
  1071. check(curses.ascii.isgraph, 32 < i <= 126)
  1072. check(curses.ascii.isprint, 32 <= i <= 126)
  1073. check(curses.ascii.ispunct, c in string.punctuation)
  1074. check(curses.ascii.isxdigit, c in string.hexdigits)
  1075. for i in (-2, -1, 256, sys.maxunicode, sys.maxunicode+1):
  1076. self.assertFalse(curses.ascii.isalnum(i))
  1077. self.assertFalse(curses.ascii.isalpha(i))
  1078. self.assertFalse(curses.ascii.isdigit(i))
  1079. self.assertFalse(curses.ascii.islower(i))
  1080. self.assertFalse(curses.ascii.isspace(i))
  1081. self.assertFalse(curses.ascii.isupper(i))
  1082. self.assertFalse(curses.ascii.isascii(i))
  1083. self.assertFalse(curses.ascii.isctrl(i))
  1084. self.assertFalse(curses.ascii.iscntrl(i))
  1085. self.assertFalse(curses.ascii.isblank(i))
  1086. self.assertFalse(curses.ascii.isgraph(i))
  1087. self.assertFalse(curses.ascii.isprint(i))
  1088. self.assertFalse(curses.ascii.ispunct(i))
  1089. self.assertFalse(curses.ascii.isxdigit(i))
  1090. self.assertFalse(curses.ascii.ismeta(-1))
  1091. def test_ascii(self):
  1092. ascii = curses.ascii.ascii
  1093. self.assertEqual(ascii('\xc1'), 'A')
  1094. self.assertEqual(ascii('A'), 'A')
  1095. self.assertEqual(ascii(ord('\xc1')), ord('A'))
  1096. def test_ctrl(self):
  1097. ctrl = curses.ascii.ctrl
  1098. self.assertEqual(ctrl('J'), '\n')
  1099. self.assertEqual(ctrl('\n'), '\n')
  1100. self.assertEqual(ctrl('@'), '\0')
  1101. self.assertEqual(ctrl(ord('J')), ord('\n'))
  1102. def test_alt(self):
  1103. alt = curses.ascii.alt
  1104. self.assertEqual(alt('\n'), '\x8a')
  1105. self.assertEqual(alt('A'), '\xc1')
  1106. self.assertEqual(alt(ord('A')), 0xc1)
  1107. def test_unctrl(self):
  1108. unctrl = curses.ascii.unctrl
  1109. self.assertEqual(unctrl('a'), 'a')
  1110. self.assertEqual(unctrl('A'), 'A')
  1111. self.assertEqual(unctrl(';'), ';')
  1112. self.assertEqual(unctrl(' '), ' ')
  1113. self.assertEqual(unctrl('\x7f'), '^?')
  1114. self.assertEqual(unctrl('\n'), '^J')
  1115. self.assertEqual(unctrl('\0'), '^@')
  1116. self.assertEqual(unctrl(ord('A')), 'A')
  1117. self.assertEqual(unctrl(ord('\n')), '^J')
  1118. # Meta-bit characters
  1119. self.assertEqual(unctrl('\x8a'), '!^J')
  1120. self.assertEqual(unctrl('\xc1'), '!A')
  1121. self.assertEqual(unctrl(ord('\x8a')), '!^J')
  1122. self.assertEqual(unctrl(ord('\xc1')), '!A')
  1123. def lorem_ipsum(win):
  1124. text = [
  1125. 'Lorem ipsum',
  1126. 'dolor sit amet,',
  1127. 'consectetur',
  1128. 'adipiscing elit,',
  1129. 'sed do eiusmod',
  1130. 'tempor incididunt',
  1131. 'ut labore et',
  1132. 'dolore magna',
  1133. 'aliqua.',
  1134. ]
  1135. maxy, maxx = win.getmaxyx()
  1136. for y, line in enumerate(text[:maxy]):
  1137. win.addstr(y, 0, line[:maxx - (y == maxy - 1)])
  1138. if __name__ == '__main__':
  1139. unittest.main()