[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

#

11101211

import os

1312

import string

1413

import sys

1514

import tempfile

15+

import functools

1616

import unittest

17171818

from test.support import requires, import_module, verbose, SaveSignals

@@ -36,7 +36,17 @@ def requires_curses_func(name):

3636

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

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

4959

def 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

5264

stdout_fd = sys.__stdout__.fileno()

@@ -304,18 +316,111 @@ def test_module_funcs(self):

304316

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

321426

def test_keyname(self):