summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2021-01-04 08:58:09 (GMT)
committerGitHub <noreply@github.com>2021-01-04 08:58:09 (GMT)
commit82794cacc6073af9bc2b792a5ee21397f4c9037f (patch)
tree676d88d321459660f4fc0b415c3a9cb9bf626a62 /Lib
parent645174abe0d13cce2cb339cc80b095ad484428ea (diff)
downloadcpython-82794cacc6073af9bc2b792a5ee21397f4c9037f.zip
cpython-82794cacc6073af9bc2b792a5ee21397f4c9037f.tar.gz
cpython-82794cacc6073af9bc2b792a5ee21397f4c9037f.tar.bz2
[3.8] [3.9] bpo-42681: Fix range checks for color and pair numbers in curses (GH-23874). (GH-24077) (GH-24079)
(cherry picked from commit 1470edd6131c29b8a09ce012cdfee3afa269d553) (cherry picked from commit b0ee2b492dbf550fbd2a63b82de0a4dc9d67f32e)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_curses.py133
1 files changed, 119 insertions, 14 deletions
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
index b7349d9..e39161f 100644
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -4,8 +4,7 @@
# This script doesn't actually display anything very coherent. but it
# does call (nearly) every method and function.
#
-# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
-# init_color()
+# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr()
# Only called, not tested: getmouse(), ungetmouse()
#
@@ -13,6 +12,7 @@ import os
import string
import sys
import tempfile
+import functools
import unittest
from test.support import requires, import_module, verbose, SaveSignals
@@ -36,7 +36,17 @@ def requires_curses_func(name):
return unittest.skipUnless(hasattr(curses, name),
'requires curses.%s' % name)
+def requires_colors(test):
+ @functools.wraps(test)
+ def wrapped(self, *args, **kwargs):
+ if not curses.has_colors():
+ self.skipTest('requires colors support')
+ curses.start_color()
+ test(self, *args, **kwargs)
+ return wrapped
+
term = os.environ.get('TERM')
+SHORT_MAX = 0x7fff
# If newterm was supported we could use it instead of initscr and not exit
@unittest.skipIf(not term or term == 'unknown',
@@ -47,6 +57,8 @@ class TestCurses(unittest.TestCase):
@classmethod
def setUpClass(cls):
+ if verbose:
+ print(f'TERM={term}', file=sys.stderr, flush=True)
# testing setupterm() inside initscr/endwin
# causes terminal breakage
stdout_fd = sys.__stdout__.fileno()
@@ -300,18 +312,111 @@ class TestCurses(unittest.TestCase):
curses.use_env(1)
# Functions only available on a few platforms
- def test_colors_funcs(self):
- if not curses.has_colors():
- self.skipTest('requires colors support')
- curses.start_color()
- curses.init_pair(2, 1,1)
- curses.color_content(1)
- curses.color_pair(2)
- curses.pair_content(min(curses.COLOR_PAIRS - 1, 0x7fff))
- curses.pair_number(0)
-
- if hasattr(curses, 'use_default_colors'):
- curses.use_default_colors()
+
+ def bad_colors(self):
+ return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
+
+ def bad_pairs(self):
+ return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
+
+ @requires_colors
+ def test_color_content(self):
+ self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
+ curses.color_content(0)
+ curses.color_content(min(curses.COLORS - 1, SHORT_MAX))
+
+ for color in self.bad_colors():
+ self.assertRaises(OverflowError, curses.color_content, color)
+ if curses.COLORS <= SHORT_MAX:
+ self.assertRaises(curses.error, curses.color_content, curses.COLORS)
+ self.assertRaises(curses.error, curses.color_content, -1)
+
+ @requires_colors
+ def test_init_color(self):
+ if not curses.can_change_color:
+ self.skipTest('cannot change color')
+
+ old = curses.color_content(0)
+ try:
+ curses.init_color(0, *old)
+ except curses.error:
+ self.skipTest('cannot change color (init_color() failed)')
+ self.addCleanup(curses.init_color, 0, *old)
+ curses.init_color(0, 0, 0, 0)
+ self.assertEqual(curses.color_content(0), (0, 0, 0))
+ curses.init_color(0, 1000, 1000, 1000)
+ self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
+
+ maxcolor = min(curses.COLORS - 1, SHORT_MAX)
+ old = curses.color_content(maxcolor)
+ curses.init_color(maxcolor, *old)
+ self.addCleanup(curses.init_color, maxcolor, *old)
+ curses.init_color(maxcolor, 0, 500, 1000)
+ self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
+
+ for color in self.bad_colors():
+ self.assertRaises(OverflowError, curses.init_color, color, 0, 0, 0)
+ if curses.COLORS <= SHORT_MAX:
+ self.assertRaises(curses.error, curses.init_color, curses.COLORS, 0, 0, 0)
+ self.assertRaises(curses.error, curses.init_color, -1, 0, 0, 0)
+ for comp in (-1, 1001):
+ self.assertRaises(curses.error, curses.init_color, 0, comp, 0, 0)
+ self.assertRaises(curses.error, curses.init_color, 0, 0, comp, 0)
+ self.assertRaises(curses.error, curses.init_color, 0, 0, 0, comp)
+
+ @requires_colors
+ def test_pair_content(self):
+ if not hasattr(curses, 'use_default_colors'):
+ self.assertEqual(curses.pair_content(0),
+ (curses.COLOR_WHITE, curses.COLOR_BLACK))
+ curses.pair_content(0)
+ curses.pair_content(min(curses.COLOR_PAIRS - 1, SHORT_MAX))
+
+ for pair in self.bad_pairs():
+ self.assertRaises(OverflowError, curses.pair_content, pair)
+ self.assertRaises(curses.error, curses.pair_content, -1)
+
+ @requires_colors
+ def test_init_pair(self):
+ old = curses.pair_content(1)
+ curses.init_pair(1, *old)
+ self.addCleanup(curses.init_pair, 1, *old)
+
+ curses.init_pair(1, 0, 0)
+ self.assertEqual(curses.pair_content(1), (0, 0))
+ maxcolor = min(curses.COLORS - 1, SHORT_MAX)
+ curses.init_pair(1, maxcolor, maxcolor)
+ self.assertEqual(curses.pair_content(1), (maxcolor, maxcolor))
+ maxpair = min(curses.COLOR_PAIRS - 1, SHORT_MAX)
+ curses.init_pair(maxpair, 2, 3)
+ self.assertEqual(curses.pair_content(maxpair), (2, 3))
+
+ for pair in self.bad_pairs():
+ self.assertRaises(OverflowError, curses.init_pair, pair, 0, 0)
+ self.assertRaises(curses.error, curses.init_pair, -1, 0, 0)
+ for color in self.bad_colors():
+ self.assertRaises(OverflowError, curses.init_pair, 1, color, 0)
+ self.assertRaises(OverflowError, curses.init_pair, 1, 0, color)
+ if curses.COLORS <= SHORT_MAX:
+ self.assertRaises(curses.error, curses.init_pair, 1, curses.COLORS, 0)
+ self.assertRaises(curses.error, curses.init_pair, 1, 0, curses.COLORS)
+
+ @requires_colors
+ def test_color_attrs(self):
+ for pair in 0, 1, 255:
+ attr = curses.color_pair(pair)
+ self.assertEqual(curses.pair_number(attr), pair, attr)
+ self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
+ self.assertEqual(curses.color_pair(0), 0)
+ self.assertEqual(curses.pair_number(0), 0)
+
+ @requires_curses_func('use_default_colors')
+ @requires_colors
+ def test_use_default_colors(self):
+ self.assertIn(curses.pair_content(0),
+ ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
+ curses.use_default_colors()
+ self.assertEqual(curses.pair_content(0), (-1, -1))
@requires_curses_func('keyname')
def test_keyname(self):