Skip to content

Commit fa9c4ee

Browse files
Copilotxadupre
andauthored
Add tests for run_cmd, ext_test_case utilities improving coverage further
Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/5f7ddf50-ca98-4180-bfc7-e949b3b79b24 Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com>
1 parent 08b7d7e commit fa9c4ee

2 files changed

Lines changed: 238 additions & 0 deletions

File tree

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import os
2+
import sys
3+
import unittest
4+
import warnings
5+
import numpy
6+
from sphinx_runpython.ext_test_case import (
7+
ExtTestCase,
8+
unit_test_going,
9+
ignore_warnings,
10+
hide_stdout,
11+
sys_path_append,
12+
is_windows,
13+
is_apple,
14+
is_linux,
15+
skipif_ci_windows,
16+
skipif_ci_linux,
17+
skipif_ci_apple,
18+
)
19+
20+
21+
class TestExtTestCase(ExtTestCase):
22+
def test_unit_test_going_default(self):
23+
old = os.environ.get("UNITTEST_GOING", None)
24+
try:
25+
if "UNITTEST_GOING" in os.environ:
26+
del os.environ["UNITTEST_GOING"]
27+
result = unit_test_going()
28+
self.assertFalse(result)
29+
finally:
30+
if old is not None:
31+
os.environ["UNITTEST_GOING"] = old
32+
33+
def test_unit_test_going_set(self):
34+
old = os.environ.get("UNITTEST_GOING", None)
35+
try:
36+
os.environ["UNITTEST_GOING"] = "1"
37+
result = unit_test_going()
38+
self.assertTrue(result)
39+
finally:
40+
if old is not None:
41+
os.environ["UNITTEST_GOING"] = old
42+
elif "UNITTEST_GOING" in os.environ:
43+
del os.environ["UNITTEST_GOING"]
44+
45+
def test_ignore_warnings_none(self):
46+
def dummy():
47+
pass
48+
49+
wrapper = ignore_warnings([UserWarning])
50+
decorated = wrapper(dummy)
51+
self.assertTrue(callable(decorated))
52+
53+
def test_ignore_warnings_warns_none_raises(self):
54+
self.assertRaise(
55+
lambda: ignore_warnings(None)(lambda: None)(),
56+
AssertionError,
57+
)
58+
59+
def test_hide_stdout_basic(self):
60+
@hide_stdout()
61+
def my_func(self):
62+
print("hidden output")
63+
64+
my_func(self)
65+
66+
def test_hide_stdout_with_callback(self):
67+
captured = []
68+
69+
@hide_stdout(lambda s: captured.append(s))
70+
def my_func(self):
71+
print("captured text")
72+
73+
my_func(self)
74+
self.assertEqual(len(captured), 1)
75+
self.assertIn("captured text", captured[0])
76+
77+
def test_sys_path_append_front(self):
78+
old_path = sys.path.copy()
79+
test_path = "/tmp/test_path_prepend"
80+
with sys_path_append([test_path], position=0):
81+
self.assertIn(test_path, sys.path)
82+
self.assertEqual(sys.path[0], test_path)
83+
self.assertEqual(sys.path, old_path)
84+
85+
def test_sys_path_append_end(self):
86+
old_path = sys.path.copy()
87+
test_path = "/tmp/test_path_append"
88+
with sys_path_append(test_path):
89+
self.assertIn(test_path, sys.path)
90+
self.assertEqual(sys.path, old_path)
91+
92+
def test_is_windows(self):
93+
self.assertIn(is_windows(), {True, False})
94+
95+
def test_is_apple(self):
96+
self.assertIn(is_apple(), {True, False})
97+
98+
def test_is_linux(self):
99+
self.assertIn(is_linux(), {True, False})
100+
101+
def test_platform_skip_decorators(self):
102+
decorator_win = skipif_ci_windows("test")
103+
decorator_linux = skipif_ci_linux("test")
104+
decorator_apple = skipif_ci_apple("test")
105+
self.assertTrue(callable(decorator_win))
106+
self.assertTrue(callable(decorator_linux))
107+
self.assertTrue(callable(decorator_apple))
108+
109+
def test_assert_exists_passes(self):
110+
self.assertExists(__file__)
111+
112+
def test_assert_exists_fails(self):
113+
self.assertRaise(
114+
lambda: self.assertExists("/nonexistent/path/file.txt"),
115+
AssertionError,
116+
)
117+
118+
def test_assert_equal_array(self):
119+
a = numpy.array([1.0, 2.0, 3.0])
120+
b = numpy.array([1.0, 2.0, 3.0])
121+
self.assertEqualArray(a, b)
122+
123+
def test_assert_almost_equal(self):
124+
a = numpy.array([1.0, 2.0, 3.0])
125+
b = [1.0, 2.0, 3.0]
126+
self.assertAlmostEqual(a, b)
127+
128+
def test_assert_raise_passes(self):
129+
self.assertRaise(lambda: 1 / 0, ZeroDivisionError)
130+
131+
def test_assert_raise_wrong_type_propagates(self):
132+
# When assertRaise is called with wrong exc_type, the exception propagates
133+
with self.assertRaises(ZeroDivisionError):
134+
self.assertRaise(lambda: 1 / 0, ValueError)
135+
136+
def test_assert_raise_no_exception(self):
137+
self.assertRaise(
138+
lambda: self.assertRaise(lambda: None, ValueError),
139+
AssertionError,
140+
)
141+
142+
def test_assert_empty_none(self):
143+
self.assertEmpty(None)
144+
145+
def test_assert_empty_list(self):
146+
self.assertEmpty([])
147+
148+
def test_assert_empty_fails(self):
149+
self.assertRaise(lambda: self.assertEmpty([1, 2, 3]), AssertionError)
150+
151+
def test_assert_not_empty_none_fails(self):
152+
self.assertRaise(lambda: self.assertNotEmpty(None), AssertionError)
153+
154+
def test_assert_not_empty_empty_list_fails(self):
155+
self.assertRaise(lambda: self.assertNotEmpty([]), AssertionError)
156+
157+
def test_assert_not_empty_passes(self):
158+
self.assertNotEmpty([1, 2])
159+
self.assertNotEmpty("abc")
160+
161+
def test_assert_starts_with_passes(self):
162+
self.assertStartsWith("hello", "hello world")
163+
164+
def test_assert_starts_with_fails(self):
165+
self.assertRaise(
166+
lambda: self.assertStartsWith("world", "hello world"),
167+
AssertionError,
168+
)
169+
170+
def test_capture(self):
171+
def my_func():
172+
print("output text")
173+
return 42
174+
175+
result, stdout, stderr = self.capture(my_func)
176+
self.assertEqual(result, 42)
177+
self.assertIn("output text", stdout)
178+
179+
def test_teardown_with_warns(self):
180+
# Test that tearDownClass works with stored warnings
181+
class TempTestCase(ExtTestCase):
182+
_warns = []
183+
184+
TempTestCase.tearDownClass()
185+
186+
187+
if __name__ == "__main__":
188+
unittest.main(verbosity=2)

_unittests/ut_runpython/test_run_cmd.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,53 @@ def test_run_cmd_exception_class(self):
136136

137137
if __name__ == "__main__":
138138
unittest.main(verbosity=2)
139+
140+
141+
class TestRunCmdExtra(ExtTestCase):
142+
def test_decode_outerr_unicode_fallback(self):
143+
# Bytes that fail ASCII but succeed with utf8 fallback
144+
# 0xc3 0xa9 is the UTF-8 encoding of 'é'
145+
result = decode_outerr(b"\xc3\xa9 hello", "ascii", "strict", "test")
146+
self.assertIn("hello", result)
147+
148+
def test_decode_outerr_unicode_error(self):
149+
# Bytes that fail both ASCII and UTF-8 strict decoding
150+
# 0x80 is not valid in ascii strict or utf-8 strict
151+
self.assertRaise(
152+
lambda: decode_outerr(b"\x80\x81\x82", "ascii", "strict", "test"),
153+
RuntimeError,
154+
)
155+
156+
def test_run_cmd_with_logf_list(self):
157+
logs = []
158+
159+
def logf(prefix, msg):
160+
logs.append((prefix, msg))
161+
162+
out, err = run_cmd(["echo", "hello"], wait=True, logf=logf)
163+
self.assertGreater(len(logs), 0)
164+
self.assertIn("hello", out)
165+
166+
def test_run_cmd_catch_exit(self):
167+
out, err = run_cmd("echo hello", wait=True, catch_exit=True)
168+
self.assertIn("hello", out)
169+
170+
def test_run_cmd_with_prefix_log(self):
171+
logs = []
172+
173+
def logf(prefix, msg):
174+
logs.append((prefix, msg))
175+
176+
out, err = run_cmd("echo hello", wait=True, logf=logf, prefix_log="[test] ")
177+
self.assertGreater(len(logs), 0)
178+
self.assertTrue(any("[test]" in str(log) for log in logs))
179+
180+
def test_run_cmd_nowait(self):
181+
# run_cmd with wait=False returns (pproc, None)
182+
result = run_cmd("echo hello", wait=False)
183+
pproc, _ = result
184+
pproc.__exit__(None, None, None)
185+
186+
187+
if __name__ == "__main__":
188+
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)