bpo-42681: Fix range checks for color and pair numbers in curses (GH-… · python/cpython@1470edd
@@ -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, verbose, SaveSignals
@@ -37,6 +37,15 @@ def requires_curses_func(name):
3737return unittest.skipUnless(hasattr(curses, name),
3838'requires curses.%s' % name)
393940+def requires_colors(test):
41+@functools.wraps(test)
42+def wrapped(self, *args, **kwargs):
43+if not curses.has_colors():
44+self.skipTest('requires colors support')
45+curses.start_color()
46+test(self, *args, **kwargs)
47+return wrapped
48+4049term = os.environ.get('TERM')
41504251# If newterm was supported we could use it instead of initscr and not exit
@@ -48,6 +57,8 @@ class TestCurses(unittest.TestCase):
48574958@classmethod
5059def setUpClass(cls):
60+if verbose:
61+print(f'TERM={term}', file=sys.stderr, flush=True)
5162# testing setupterm() inside initscr/endwin
5263# causes terminal breakage
5364stdout_fd = sys.__stdout__.fileno()
@@ -306,31 +317,101 @@ def test_module_funcs(self):
306317curses.use_env(1)
307318308319# Functions only available on a few platforms
309-def test_colors_funcs(self):
310-if not curses.has_colors():
311-self.skipTest('requires colors support')
312-curses.start_color()
313-curses.init_pair(2, 1,1)
314-curses.color_content(1)
315-curses.color_pair(2)
320+321+def bad_colors(self):
322+return (-1, curses.COLORS, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
323+324+def bad_colors2(self):
325+return (curses.COLORS, 2**31, 2**63, 2**64)
326+327+def bad_pairs(self):
328+return (-1, -2**31 - 1, 2**31, -2**63 - 1, 2**63, 2**64)
329+330+@requires_colors
331+def test_color_content(self):
332+self.assertEqual(curses.color_content(curses.COLOR_BLACK), (0, 0, 0))
333+curses.color_content(0)
334+curses.color_content(curses.COLORS - 1)
335+336+for color in self.bad_colors():
337+self.assertRaises(ValueError, curses.color_content, color)
338+339+@requires_colors
340+def test_init_color(self):
341+if not curses.can_change_color:
342+self.skipTest('cannot change color')
343+344+old = curses.color_content(0)
345+try:
346+curses.init_color(0, *old)
347+except curses.error:
348+self.skipTest('cannot change color (init_color() failed)')
349+self.addCleanup(curses.init_color, 0, *old)
350+curses.init_color(0, 0, 0, 0)
351+self.assertEqual(curses.color_content(0), (0, 0, 0))
352+curses.init_color(0, 1000, 1000, 1000)
353+self.assertEqual(curses.color_content(0), (1000, 1000, 1000))
354+355+old = curses.color_content(curses.COLORS - 1)
356+curses.init_color(curses.COLORS - 1, *old)
357+self.addCleanup(curses.init_color, curses.COLORS - 1, *old)
358+curses.init_color(curses.COLORS - 1, 0, 500, 1000)
359+self.assertEqual(curses.color_content(curses.COLORS - 1), (0, 500, 1000))
360+361+for color in self.bad_colors():
362+self.assertRaises(ValueError, curses.init_color, color, 0, 0, 0)
363+for comp in (-1, 1001):
364+self.assertRaises(ValueError, curses.init_color, 0, comp, 0, 0)
365+self.assertRaises(ValueError, curses.init_color, 0, 0, comp, 0)
366+self.assertRaises(ValueError, curses.init_color, 0, 0, 0, comp)
367+368+@requires_colors
369+def test_pair_content(self):
370+if not hasattr(curses, 'use_default_colors'):
371+self.assertEqual(curses.pair_content(0),
372+ (curses.COLOR_WHITE, curses.COLOR_BLACK))
373+curses.pair_content(0)
316374curses.pair_content(curses.COLOR_PAIRS - 1)
317-curses.pair_number(0)
318-319-if hasattr(curses, 'use_default_colors'):
320-curses.use_default_colors()
321-322-self.assertRaises(ValueError, curses.color_content, -1)
323-self.assertRaises(ValueError, curses.color_content, curses.COLORS + 1)
324-self.assertRaises(ValueError, curses.color_content, -2**31 - 1)
325-self.assertRaises(ValueError, curses.color_content, 2**31)
326-self.assertRaises(ValueError, curses.color_content, -2**63 - 1)
327-self.assertRaises(ValueError, curses.color_content, 2**63 - 1)
328-self.assertRaises(ValueError, curses.pair_content, -1)
329-self.assertRaises(ValueError, curses.pair_content, curses.COLOR_PAIRS)
330-self.assertRaises(ValueError, curses.pair_content, -2**31 - 1)
331-self.assertRaises(ValueError, curses.pair_content, 2**31)
332-self.assertRaises(ValueError, curses.pair_content, -2**63 - 1)
333-self.assertRaises(ValueError, curses.pair_content, 2**63 - 1)
375+376+for pair in self.bad_pairs():
377+self.assertRaises(ValueError, curses.pair_content, pair)
378+379+@requires_colors
380+def test_init_pair(self):
381+old = curses.pair_content(1)
382+curses.init_pair(1, *old)
383+self.addCleanup(curses.init_pair, 1, *old)
384+385+curses.init_pair(1, 0, 0)
386+self.assertEqual(curses.pair_content(1), (0, 0))
387+curses.init_pair(1, curses.COLORS - 1, curses.COLORS - 1)
388+self.assertEqual(curses.pair_content(1),
389+ (curses.COLORS - 1, curses.COLORS - 1))
390+curses.init_pair(curses.COLOR_PAIRS - 1, 2, 3)
391+self.assertEqual(curses.pair_content(curses.COLOR_PAIRS - 1), (2, 3))
392+393+for pair in self.bad_pairs():
394+self.assertRaises(ValueError, curses.init_pair, pair, 0, 0)
395+for color in self.bad_colors2():
396+self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
397+self.assertRaises(ValueError, curses.init_pair, 1, 0, color)
398+399+@requires_colors
400+def test_color_attrs(self):
401+for pair in 0, 1, 255:
402+attr = curses.color_pair(pair)
403+self.assertEqual(curses.pair_number(attr), pair, attr)
404+self.assertEqual(curses.pair_number(attr | curses.A_BOLD), pair)
405+self.assertEqual(curses.color_pair(0), 0)
406+self.assertEqual(curses.pair_number(0), 0)
407+408+@requires_curses_func('use_default_colors')
409+@requires_colors
410+def test_use_default_colors(self):
411+self.assertIn(curses.pair_content(0),
412+ ((curses.COLOR_WHITE, curses.COLOR_BLACK), (-1, -1)))
413+curses.use_default_colors()
414+self.assertEqual(curses.pair_content(0), (-1, -1))
334415335416@requires_curses_func('keyname')
336417def test_keyname(self):