archey4/archey/test/test_archey_colors.py

149 lines
6.1 KiB
Python

"""Test module for `archey.colors`"""
import unittest
from unittest.mock import patch
from archey.colors import ANSI_ECMA_REGEXP, Colors, Colors8Bit, Style
class TestColors(unittest.TestCase):
"""Test cases for the `Style` and `Colors` (enumeration / utility) classes"""
def setUp(self):
# Skip `Style.should_color_output` patching for its own testing method.
if self.id().endswith("should_color_output"):
return
# By default, colors won't be disabled.
self._should_color_output_patch = patch(
"archey.colors.Style.should_color_output",
return_value=True,
)
self._should_color_output_patch.start()
def tearDown(self):
# Skip `Style.should_color_output` patching for its own testing method.
if self.id().endswith("should_color_output"):
return
self._should_color_output_patch.stop()
def test_constant_values(self):
"""Test `Colors` enumeration member instantiation from value"""
self.assertEqual(Colors((1, 31)), Colors.RED_BRIGHT)
self.assertRaises(ValueError, Colors, (-1,))
def test_string_representation(self):
"""Test cases for `__str__` implementations"""
with self.subTest("`Colors` class"):
self.assertEqual(str(Colors.CLEAR), "\x1b[0m")
self.assertEqual(str(Colors.CYAN_BRIGHT), "\x1b[1;36m")
with self.subTest("`Colors8Bit` class"):
self.assertEqual(str(Colors8Bit(0, 0)), "\x1b[0;38;5;0m")
self.assertEqual(str(Colors8Bit(0, 127)), "\x1b[0;38;5;127m")
self.assertEqual(str(Colors8Bit(1, 202)), "\x1b[1;38;5;202m")
def test_8_bit_out_of_range(self):
"""Test for exception on colors outside the defined range in `Colors8Bit`"""
for test_values in [(0, 256), (2, 0), (0, -1), (-1, 0)]:
with self.assertRaises(ValueError):
Colors8Bit(*test_values)
def test_should_color_output(self):
"""Test for `Style.should_color_output`"""
# Clear cache filled by `functools.lru_cache` decorator.
Style.should_color_output.cache_clear()
with patch("archey.colors.Environment.CLICOLOR_FORCE", True):
self.assertTrue(Style.should_color_output())
Style.should_color_output.cache_clear()
with patch("archey.colors.Environment.CLICOLOR_FORCE", False), patch(
"archey.colors.Environment.NO_COLOR", True
):
self.assertFalse(Style.should_color_output())
Style.should_color_output.cache_clear()
with patch("archey.colors.Environment.CLICOLOR_FORCE", False), patch(
"archey.colors.Environment.NO_COLOR", False
):
with patch("archey.colors.sys.stdout.isatty", return_value=False):
with patch("archey.colors.Environment.CLICOLOR", True):
self.assertFalse(Style.should_color_output())
Style.should_color_output.cache_clear()
with patch("archey.colors.Environment.CLICOLOR", False):
self.assertFalse(Style.should_color_output())
Style.should_color_output.cache_clear()
with patch("archey.colors.sys.stdout.isatty", return_value=True):
# Default case : STDOUT is a TTY and `CLICOLOR` is (by default) set.
with patch("archey.colors.Environment.CLICOLOR", True):
self.assertTrue(Style.should_color_output())
Style.should_color_output.cache_clear()
with patch("archey.colors.Environment.CLICOLOR", False):
self.assertFalse(Style.should_color_output())
Style.should_color_output.cache_clear()
def test_escape_code_from_attrs(self):
"""Test for `Style.escape_code_from_attrs`"""
self.assertEqual(Style.escape_code_from_attrs("0;31"), "\x1b[0;31m")
self.assertEqual(Style.escape_code_from_attrs("0;31;45"), "\x1b[0;31;45m")
def test_get_level_color(self):
"""Test for `Colors.get_level_color`"""
# [25] (GREEN) < 50 < 75
self.assertEqual(Colors.get_level_color(25, 50, 75), Colors.GREEN_NORMAL)
# 33 < [34] (YELLOW) < 66
self.assertEqual(Colors.get_level_color(34, 33, 66), Colors.YELLOW_NORMAL)
# 33 < 66 < [90] (RED)
self.assertEqual(Colors.get_level_color(90, 33, 66), Colors.RED_NORMAL)
def test_ansi_ecma_regexp(self):
"""Test our ANSI/ECMA REGEXP compiled pattern"""
self.assertTrue(ANSI_ECMA_REGEXP.match(str(Colors.CLEAR)))
self.assertTrue(ANSI_ECMA_REGEXP.match(str(Colors.RED_NORMAL)))
self.assertTrue(ANSI_ECMA_REGEXP.match(str(Colors8Bit(0, 127))))
self.assertTrue(ANSI_ECMA_REGEXP.match(Colors.escape_code_from_attrs("0;31;45")))
self.assertFalse(ANSI_ECMA_REGEXP.match(""))
self.assertFalse(ANSI_ECMA_REGEXP.match("\x1b[m"))
self.assertFalse(ANSI_ECMA_REGEXP.match("\x1b[0M"))
# Check that matched groups contain the whole code (no capturing groups).
self.assertEqual(
len(
"".join(
ANSI_ECMA_REGEXP.findall(
str(Colors.GREEN_NORMAL) + "NOT_A_COLOR" + str(Colors.CLEAR)
)
)
),
len(str(Colors.GREEN_NORMAL) + str(Colors.CLEAR)),
)
def test_remove_colors(self):
"""Test our ANSI/ECMA REGEXP colors removal method"""
self.assertFalse(Style.remove_colors(str(Colors.CLEAR)))
self.assertEqual(Style.remove_colors("\x1b[0;31mTEST\x1b[0;0m"), "TEST") # 4-bit
self.assertEqual(Style.remove_colors("\x1b[0;38;5;127mTEST\x1b[0;0m"), "TEST") # 8-bit
self.assertEqual(
Style.remove_colors("\x1b[0nTEST\xde\xad\xbe\xaf"), "\x1b[0nTEST\xde\xad\xbe\xaf"
)
def test_color_disabling(self):
"""Test `Colors` internal behavior when coloration is disabled"""
with patch("archey.colors.Style.should_color_output", return_value=False):
self.assertFalse(str(Colors.CYAN_NORMAL))
self.assertFalse(str(Colors8Bit(0, 127)))
if __name__ == "__main__":
unittest.main()