[3.9] bpo-42681: Fix range checks for color and pair numbers in curse… · python/cpython@b0ee2b4
@@ -4,15 +4,15 @@
44# This script doesn't actually display anything very coherent. but it
55# does call (nearly) every method and function.
66#
7-# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr(),
8-# init_color()
7+# Functions not tested: {def,reset}_{shell,prog}_mode, getch(), getstr()
98# Only called, not tested: getmouse(), ungetmouse()
109#
11101211import os
1312import string
1413import sys
1514import tempfile
15+import functools
1616import unittest
17171818from test.support import requires, import_module, verbose, SaveSignals
@@ -36,7 +36,17 @@ def requires_curses_func(name):
3636return unittest.skipUnless(hasattr(curses, name),
3737'requires curses.%s' % name)
383839+def requires_colors(test):
40+@functools.wraps(test)
41+def wrapped(self, *args, **kwargs):
42+if not curses.has_colors():
43+self.skipTest('requires colors support')
44+curses.start_color()
45+test(self, *args, **kwargs)
46+return wrapped
47+3948term = os.environ.get('TERM')
49+SHORT_MAX = 0x7fff
40504151# If newterm was supported we could use it instead of initscr and not exit
4252@unittest.skipIf(not term or term == 'unknown',
@@ -47,6 +57,8 @@ class TestCurses(unittest.TestCase):
47574858@classmethod
4959def setUpClass(cls):
60+if verbose:
61+print(f'TERM={term}', file=sys.stderr, flush=True)
5062# testing setupterm() inside initscr/endwin
5163# causes terminal breakage
5264stdout_fd = sys.__stdout__.fileno()
@@ -304,18 +316,111 @@ def test_module_funcs(self):
304316curses.use_env(1)
305317306318# Functions only available on a few platforms
307-def test_colors_funcs(self):
308-if not curses.has_colors():
309-self.skipTest('requires colors support')
310-curses.start_color()
311-curses.init_pair(2, 1,1)
312-curses.color_content(1)
313-curses.color_pair(2)
314-curses.pair_content(min(curses.COLOR_PAIRS - 1, 0x7fff))
315-curses.pair_number(0)
316-317-if hasattr(curses, 'use_default_colors'):
318-curses.use_default_colors()
319+320+def bad_colors(self):
321+return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
322+323+def bad_pairs(self):
324+return (-2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
325+326+@requires_colors
327+def test_color_content(self):
328+self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
329+curses.color_content(0)
330+curses.color_content(min(curses.COLORS - 1, SHORT_MAX))
331+332+for color in self.bad_colors():
333+self.assertRaises(OverflowError, curses.color_content, color)
334+if curses.COLORS <= SHORT_MAX:
335+self.assertRaises(curses.error, curses.color_content, curses.COLORS)
336+self.assertRaises(curses.error, curses.color_content, -1)
337+338+@requires_colors
339+def test_init_color(self):
340+if not curses.can_change_color:
341+self.skipTest('cannot change color')
342+343+old = curses.color_content(0)
344+try:
345+curses.init_color(0, *old)
346+except curses.error:
347+self.skipTest('cannot change color (init_color() failed)')
348+self.addCleanup(curses.init_color, 0, *old)
349+curses.init_color(0, 0, 0, 0)
350+self.assertEqual(curses.color_content(0), (0, 0, 0))
351+curses.init_color(0, 1000, 1000, 1000)
352+self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
353+354+maxcolor = min(curses.COLORS - 1, SHORT_MAX)
355+old = curses.color_content(maxcolor)
356+curses.init_color(maxcolor, *old)
357+self.addCleanup(curses.init_color, maxcolor, *old)
358+curses.init_color(maxcolor, 0, 500, 1000)
359+self.assertEqual(curses.color_content(maxcolor), (0, 500, 1000))
360+361+for color in self.bad_colors():
362+self.assertRaises(OverflowError, curses.init_color, color, 0, 0, 0)
363+if curses.COLORS <= SHORT_MAX:
364+self.assertRaises(curses.error, curses.init_color, curses.COLORS, 0, 0, 0)
365+self.assertRaises(curses.error, curses.init_color, -1, 0, 0, 0)
366+for comp in (-1, 1001):
367+self.assertRaises(curses.error, curses.init_color, 0, comp, 0, 0)
368+self.assertRaises(curses.error, curses.init_color, 0, 0, comp, 0)
369+self.assertRaises(curses.error, curses.init_color, 0, 0, 0, comp)
370+371+@requires_colors
372+def test_pair_content(self):
373+if not hasattr(curses, 'use_default_colors'):
374+self.assertEqual(curses.pair_content(0),
375+ (curses.COLOR_WHITE, curses.COLOR_BLACK))
376+curses.pair_content(0)
377+curses.pair_content(min(curses.COLOR_PAIRS - 1, SHORT_MAX))
378+379+for pair in self.bad_pairs():
380+self.assertRaises(OverflowError, curses.pair_content, pair)
381+self.assertRaises(curses.error, curses.pair_content, -1)
382+383+@requires_colors
384+def test_init_pair(self):
385+old = curses.pair_content(1)
386+curses.init_pair(1, *old)
387+self.addCleanup(curses.init_pair, 1, *old)
388+389+curses.init_pair(1, 0, 0)
390+self.assertEqual(curses.pair_content(1), (0, 0))
391+maxcolor = min(curses.COLORS - 1, SHORT_MAX)
392+curses.init_pair(1, maxcolor, maxcolor)
393+self.assertEqual(curses.pair_content(1), (maxcolor, maxcolor))
394+maxpair = min(curses.COLOR_PAIRS - 1, SHORT_MAX)
395+curses.init_pair(maxpair, 2, 3)
396+self.assertEqual(curses.pair_content(maxpair), (2, 3))
397+398+for pair in self.bad_pairs():
399+self.assertRaises(OverflowError, curses.init_pair, pair, 0, 0)
400+self.assertRaises(curses.error, curses.init_pair, -1, 0, 0)
401+for color in self.bad_colors():
402+self.assertRaises(OverflowError, curses.init_pair, 1, color, 0)
403+self.assertRaises(OverflowError, curses.init_pair, 1, 0, color)
404+if curses.COLORS <= SHORT_MAX:
405+self.assertRaises(curses.error, curses.init_pair, 1, curses.COLORS, 0)
406+self.assertRaises(curses.error, curses.init_pair, 1, 0, curses.COLORS)
407+408+@requires_colors
409+def test_color_attrs(self):
410+for pair in 0, 1, 255:
411+attr = curses.color_pair(pair)
412+self.assertEqual(curses.pair_number(attr), pair, attr)
413+self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
414+self.assertEqual(curses.color_pair(0), 0)
415+self.assertEqual(curses.pair_number(0), 0)
416+417+@requires_curses_func('use_default_colors')
418+@requires_colors
419+def test_use_default_colors(self):
420+self.assertIn(curses.pair_content(0),
421+ ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
422+curses.use_default_colors()
423+self.assertEqual(curses.pair_content(0), (-1, -1))
319424320425@requires_curses_func('keyname')
321426def test_keyname(self):