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

#

11101211

import os

1312

import string

1413

import sys

1514

import tempfile

15+

import functools

1616

import unittest

17171818

from test.support import requires, verbose, SaveSignals

@@ -37,6 +37,15 @@ def requires_curses_func(name):

3737

return 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+4049

term = 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

5059

def 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

5364

stdout_fd = sys.__stdout__.fileno()

@@ -306,31 +317,101 @@ def test_module_funcs(self):

306317

curses.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)

316374

curses.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')

336417

def test_keyname(self):