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