Skip to content

Commit 31229ab

Browse files
committed
Split termui and utils tests into separate files. Fix some mispaced tests.
1 parent 04ef3a6 commit 31229ab

13 files changed

Lines changed: 1023 additions & 980 deletions
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import os
2+
import subprocess
3+
import sys
4+
from collections import namedtuple
5+
from contextlib import nullcontext
6+
from functools import partial
7+
from pathlib import Path
8+
from tempfile import tempdir
9+
from unittest.mock import patch
10+
11+
import pytest
12+
13+
import click
14+
from click import echo
15+
from click import echo_via_pager
16+
from click._compat import WIN
17+
18+
19+
def _test_gen_func():
20+
yield "a"
21+
yield "b"
22+
yield "c"
23+
yield "abc"
24+
25+
26+
def _test_gen_func_fails():
27+
yield "test"
28+
raise RuntimeError("This is a test.")
29+
30+
31+
def _test_gen_func_echo(file=None):
32+
yield "test"
33+
echo("hello", file=file)
34+
yield "test"
35+
36+
37+
def _test_simulate_keyboard_interrupt(file=None):
38+
yield "output_before_keyboard_interrupt"
39+
raise KeyboardInterrupt()
40+
41+
42+
EchoViaPagerTest = namedtuple(
43+
"EchoViaPagerTest",
44+
(
45+
"description",
46+
"test_input",
47+
"expected_pager",
48+
"expected_stdout",
49+
"expected_stderr",
50+
"expected_error",
51+
),
52+
)
53+
54+
55+
@pytest.mark.skipif(WIN, reason="Different behavior on windows.")
56+
@pytest.mark.parametrize(
57+
"pager_cmd", ["cat", "cat ", " cat ", "less", " less", " less "]
58+
)
59+
@pytest.mark.parametrize(
60+
"test",
61+
[
62+
# We need to pass a parameter function instead of a plain param
63+
# as pytest.mark.parametrize will reuse the parameters causing the
64+
# generators to be used up so they will not yield anymore
65+
EchoViaPagerTest(
66+
description="Plain string argument",
67+
test_input=lambda: "just text",
68+
expected_pager="just text\n",
69+
expected_stdout="",
70+
expected_stderr="",
71+
expected_error=None,
72+
),
73+
EchoViaPagerTest(
74+
description="Iterable argument",
75+
test_input=lambda: ["itera", "ble"],
76+
expected_pager="iterable\n",
77+
expected_stdout="",
78+
expected_stderr="",
79+
expected_error=None,
80+
),
81+
EchoViaPagerTest(
82+
description="Generator function argument",
83+
test_input=lambda: _test_gen_func,
84+
expected_pager="abcabc\n",
85+
expected_stdout="",
86+
expected_stderr="",
87+
expected_error=None,
88+
),
89+
EchoViaPagerTest(
90+
description="String generator argument",
91+
test_input=lambda: _test_gen_func(),
92+
expected_pager="abcabc\n",
93+
expected_stdout="",
94+
expected_stderr="",
95+
expected_error=None,
96+
),
97+
EchoViaPagerTest(
98+
description="Number generator expression argument",
99+
test_input=lambda: (c for c in range(6)),
100+
expected_pager="012345\n",
101+
expected_stdout="",
102+
expected_stderr="",
103+
expected_error=None,
104+
),
105+
EchoViaPagerTest(
106+
description="Exception in generator function argument",
107+
test_input=lambda: _test_gen_func_fails,
108+
# Because generator throws early on, the pager did not have
109+
# a chance yet to write the file.
110+
expected_pager="",
111+
expected_stdout="",
112+
expected_stderr="",
113+
expected_error=RuntimeError,
114+
),
115+
EchoViaPagerTest(
116+
description="Exception in generator argument",
117+
test_input=lambda: _test_gen_func_fails,
118+
# Because generator throws early on, the pager did not have a
119+
# chance yet to write the file.
120+
expected_pager="",
121+
expected_stdout="",
122+
expected_stderr="",
123+
expected_error=RuntimeError,
124+
),
125+
EchoViaPagerTest(
126+
description="Keyboard interrupt should not terminate the pager",
127+
test_input=lambda: _test_simulate_keyboard_interrupt(),
128+
# Due to the keyboard interrupt during pager execution, click program
129+
# should abort, but the pager should stay open.
130+
# This allows users to cancel the program and search in the pager
131+
# output, before they decide to terminate the pager.
132+
expected_pager="output_before_keyboard_interrupt",
133+
expected_stdout="",
134+
expected_stderr="",
135+
expected_error=KeyboardInterrupt,
136+
),
137+
EchoViaPagerTest(
138+
description="Writing to stdout during generator execution",
139+
test_input=lambda: _test_gen_func_echo(),
140+
expected_pager="testtest\n",
141+
expected_stdout="hello\n",
142+
expected_stderr="",
143+
expected_error=None,
144+
),
145+
EchoViaPagerTest(
146+
description="Writing to stderr during generator execution",
147+
test_input=lambda: _test_gen_func_echo(file=sys.stderr),
148+
expected_pager="testtest\n",
149+
expected_stdout="",
150+
expected_stderr="hello\n",
151+
expected_error=None,
152+
),
153+
],
154+
)
155+
def test_echo_via_pager(monkeypatch, capfd, pager_cmd, test):
156+
monkeypatch.setitem(os.environ, "PAGER", pager_cmd)
157+
monkeypatch.setattr(click._termui_impl, "isatty", lambda x: True)
158+
159+
test_input = test.test_input()
160+
expected_pager = test.expected_pager
161+
expected_stdout = test.expected_stdout
162+
expected_stderr = test.expected_stderr
163+
expected_error = test.expected_error
164+
165+
check_raise = pytest.raises(expected_error) if expected_error else nullcontext()
166+
167+
pager_out_tmp = Path(tempdir) / "pager_out.txt"
168+
pager_out_tmp.unlink(missing_ok=True)
169+
with pager_out_tmp.open("w") as f:
170+
force_subprocess_stdout = patch.object(
171+
subprocess,
172+
"Popen",
173+
partial(subprocess.Popen, stdout=f),
174+
)
175+
with force_subprocess_stdout:
176+
with check_raise:
177+
echo_via_pager(test_input)
178+
179+
out, err = capfd.readouterr()
180+
181+
pager = pager_out_tmp.read_text()
182+
183+
assert pager == expected_pager, (
184+
f"Unexpected pager output in test case '{test.description}'"
185+
)
186+
assert out == expected_stdout, (
187+
f"Unexpected stdout in test case '{test.description}'"
188+
)
189+
assert err == expected_stderr, (
190+
f"Unexpected stderr in test case '{test.description}'"
191+
)

0 commit comments

Comments
 (0)