summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--color.py44
-rw-r--r--tests/test_color.py82
2 files changed, 106 insertions, 20 deletions
diff --git a/color.py b/color.py
index 03fb65537..406863453 100644
--- a/color.py
+++ b/color.py
@@ -84,29 +84,39 @@ def _Color(fg=None, bg=None, attr=None):
84 84
85DEFAULT = None 85DEFAULT = None
86 86
87# Placholder value that indicates we need to check if the user is in an
88# interactive terminal session to determine if we turn on color or not.
89_CHECK_CONSOLE = object()
90
91# https://git-scm.com/docs/git-config#Documentation/git-config.txt-colorui
92_CONFIG_TO_COLOR_SETTING = {
93 "false": False,
94 "never": False,
95 "no": False,
96 "auto": _CHECK_CONSOLE,
97 "true": _CHECK_CONSOLE,
98 "yes": _CHECK_CONSOLE,
99 "always": True,
100}
101
87 102
88def SetDefaultColoring(state: Optional[str]) -> None: 103def SetDefaultColoring(state: Optional[str]) -> None:
89 """Set coloring behavior to |state|. 104 """Set coloring behavior to |state|.
90 105
91 This is useful for overriding config options via the command line. 106 This is useful for overriding config options via the command line.
92 """ 107 """
93 if state is None:
94 # Leave it alone -- return quick!
95 return
96 108
97 global DEFAULT 109 global DEFAULT
98 state = state.lower() 110
99 if state in ("auto",): 111 if isinstance(state, str):
112 state = state.lower()
113 if state in _CONFIG_TO_COLOR_SETTING:
100 DEFAULT = state 114 DEFAULT = state
101 elif state in ("always", "yes", "true"):
102 DEFAULT = "always"
103 elif state in ("never", "no", "false"):
104 DEFAULT = "never"
105 115
106 116
107class Coloring: 117class Coloring:
108 def __init__(self, config, section_type): 118 def __init__(self, config, section_type):
109 self._section = "color.%s" % section_type 119 self._section = f"color.{section_type}"
110 self._config = config 120 self._config = config
111 self._out = sys.stdout 121 self._out = sys.stdout
112 122
@@ -115,16 +125,12 @@ class Coloring:
115 on = self._config.GetString(self._section) 125 on = self._config.GetString(self._section)
116 if on is None: 126 if on is None:
117 on = self._config.GetString("color.ui") 127 on = self._config.GetString("color.ui")
128 if isinstance(on, str):
129 on = on.lower()
118 130
119 if on == "auto": 131 self._on = _CONFIG_TO_COLOR_SETTING.get(on, _CHECK_CONSOLE)
120 if pager.active or os.isatty(1): 132 if self._on is _CHECK_CONSOLE:
121 self._on = True 133 self._on = pager.active or os.isatty(1)
122 else:
123 self._on = False
124 elif on in ("true", "always"):
125 self._on = True
126 else:
127 self._on = False
128 134
129 def redirect(self, out): 135 def redirect(self, out):
130 self._out = out 136 self._out = out
diff --git a/tests/test_color.py b/tests/test_color.py
index 8b75d2199..72d74993d 100644
--- a/tests/test_color.py
+++ b/tests/test_color.py
@@ -14,6 +14,8 @@
14 14
15"""Unittests for the color.py module.""" 15"""Unittests for the color.py module."""
16 16
17from unittest import mock
18
17import pytest 19import pytest
18import utils_for_test 20import utils_for_test
19 21
@@ -24,9 +26,14 @@ import git_config
24@pytest.fixture 26@pytest.fixture
25def coloring() -> color.Coloring: 27def coloring() -> color.Coloring:
26 """Create a Coloring object for testing.""" 28 """Create a Coloring object for testing."""
29 return _make_coloring("always")
30
31
32def _make_coloring(default_state: str) -> color.Coloring:
33 """Set the default color mode and return a Coloring using test config."""
27 config_fixture = utils_for_test.FIXTURES_DIR / "test.gitconfig" 34 config_fixture = utils_for_test.FIXTURES_DIR / "test.gitconfig"
28 config = git_config.GitConfig(config_fixture) 35 config = git_config.GitConfig(config_fixture)
29 color.SetDefaultColoring("true") 36 color.SetDefaultColoring(default_state)
30 return color.Coloring(config, "status") 37 return color.Coloring(config, "status")
31 38
32 39
@@ -72,3 +79,76 @@ def test_Color_Parse_empty_entry(coloring: color.Coloring) -> None:
72 assert val == "\033[2;34;47m" 79 assert val == "\033[2;34;47m"
73 val = coloring._parse("empty", "green", "white", "bold") 80 val = coloring._parse("empty", "green", "white", "bold")
74 assert val == "\033[1;32;47m" 81 assert val == "\033[1;32;47m"
82
83
84class TestSetDefaultColoring:
85 """Tests for SetDefaultColoring."""
86
87 def test_none_leaves_default_unchanged(self) -> None:
88 color.DEFAULT = "auto"
89 color.SetDefaultColoring(None)
90 assert color.DEFAULT == "auto"
91
92 @pytest.mark.parametrize(
93 "value, expected",
94 (
95 # auto/true/yes all store their lowercase form.
96 ("auto", "auto"),
97 ("Auto", "auto"),
98 ("true", "true"),
99 ("True", "true"),
100 ("yes", "yes"),
101 ("Yes", "yes"),
102 # "always" stores as "always".
103 ("always", "always"),
104 ("Always", "always"),
105 # never/no/false store their lowercase form.
106 ("never", "never"),
107 ("no", "no"),
108 ("false", "false"),
109 ),
110 )
111 def test_maps_to_expected(self, value: str, expected: str) -> None:
112 color.SetDefaultColoring(value)
113 assert color.DEFAULT == expected
114
115 def test_unrecognised_leaves_default_unchanged(self) -> None:
116 color.DEFAULT = "auto"
117 color.SetDefaultColoring("garbage")
118 assert color.DEFAULT == "auto"
119
120
121class TestColoringInit:
122 """Tests for Coloring.__init__ color mode logic."""
123
124 @pytest.mark.parametrize(
125 "state, isatty, pager_active, expected",
126 (
127 # "always" enables color unconditionally.
128 ("always", False, False, True),
129 # "never" disables color unconditionally.
130 ("never", True, True, False),
131 # auto/true/yes enable color only on a TTY or active pager.
132 ("auto", True, False, True),
133 ("auto", False, False, False),
134 ("auto", False, True, True),
135 ("true", True, False, True),
136 ("true", False, False, False),
137 ("true", False, True, True),
138 ("yes", True, False, True),
139 ("yes", False, False, False),
140 ("yes", False, True, True),
141 ),
142 )
143 def test_color_mode(
144 self,
145 state: str,
146 isatty: bool,
147 pager_active: bool,
148 expected: bool,
149 ) -> None:
150 with mock.patch("os.isatty", return_value=isatty), mock.patch(
151 "pager.active", pager_active
152 ):
153 c = _make_coloring(state)
154 assert c.is_on is expected