Skip to content

Commit e805bf1

Browse files
Copilotxadupre
andauthored
Extend code coverage across sphinx-runpython utilities (#60)
* Initial plan * initial plan for code coverage extension 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> * Add tests to improve code coverage for multiple modules 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> * Add more tests for higher coverage: latex_functions, img_export, conf_helper, process_rst, readme 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> * 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> * black * Skip test_process_args_readme on Windows Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/6bff9a98-f076-48c0-b958-a41e73f5b704 Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> * Fix Windows CI failures in github_link, import_path, and run_cmd tests Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/673d9b92-6119-4171-8dc5-2c28bc233e20 Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> Co-authored-by: Xavier Dupré <xadupre@microsoft.com>
1 parent fd97dcb commit e805bf1

15 files changed

Lines changed: 1075 additions & 6 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,8 @@ _unittests/ut__main/*.html
2828
_unittests/ut_runpython/*.png
2929
_unittests/ut_runpython/*.html
3030
test_latex/*
31+
test_latex2/*
32+
test_api/*
33+
test_sphinx_api_func/*
34+
*.pdf
35+
_unittests/ut_runpython/exescript.py

_unittests/ut__main/test_cmd.py

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import platform
2+
import shutil
3+
import sys
4+
import tempfile
25
import unittest
36
import os
4-
from sphinx_runpython.ext_test_case import ExtTestCase, hide_stdout
5-
from sphinx_runpython._cmd_helper import get_parser, nb2py, latex_process
7+
from argparse import Namespace
8+
from sphinx_runpython.ext_test_case import ExtTestCase, hide_stdout, skipif_ci_windows
9+
from sphinx_runpython._cmd_helper import (
10+
get_parser,
11+
nb2py,
12+
latex_process,
13+
process_args,
14+
sphinx_api,
15+
)
616

717

818
class TestCmd(ExtTestCase):
@@ -30,6 +40,124 @@ def test_latex(self):
3040
expected = os.path.join(folder, "poulet.py")
3141
self.assertExists(expected)
3242

43+
def test_latex_inplace(self):
44+
data = os.path.join(os.path.dirname(__file__), "data")
45+
with tempfile.TemporaryDirectory() as tmpdir:
46+
shutil.copy(
47+
os.path.join(data, "strategie_avec_alea.rst"),
48+
os.path.join(tmpdir, "strategie_avec_alea.rst"),
49+
)
50+
latex_process(tmpdir, verbose=1)
51+
self.assertExists(os.path.join(tmpdir, "strategie_avec_alea.rst"))
52+
53+
def test_nb2py_not_found(self):
54+
self.assertRaise(lambda: nb2py("/nonexistent/path/xyz"), FileNotFoundError)
55+
56+
def test_latex_process_not_found(self):
57+
self.assertRaise(
58+
lambda: latex_process("/nonexistent/path/xyz"), FileNotFoundError
59+
)
60+
61+
def test_process_args_nb2py_empty(self):
62+
with tempfile.TemporaryDirectory() as tmpdir:
63+
args = Namespace(
64+
command="nb2py",
65+
path=tmpdir,
66+
recursive=False,
67+
verbose=0,
68+
)
69+
process_args(args)
70+
71+
@skipif_ci_windows("readme processing does not work on Windows")
72+
def test_process_args_readme(self):
73+
readme = os.path.join(os.path.dirname(__file__), "..", "..", "README.rst")
74+
args = Namespace(
75+
command="readme",
76+
path=readme,
77+
verbose=0,
78+
)
79+
process_args(args)
80+
81+
def test_process_args_unknown_command(self):
82+
args = Namespace(command="unknown_cmd", path=None, verbose=0)
83+
self.assertRaise(lambda: process_args(args), ValueError)
84+
85+
@hide_stdout()
86+
def test_process_args_latex(self):
87+
data = os.path.join(os.path.dirname(__file__), "data")
88+
folder = "test_latex2"
89+
if not os.path.exists(folder):
90+
os.mkdir(folder)
91+
args = Namespace(
92+
command="latex",
93+
path=data,
94+
recursive=False,
95+
verbose=0,
96+
output=folder,
97+
)
98+
process_args(args)
99+
100+
def test_process_args_api(self):
101+
data = os.path.join(os.path.dirname(__file__), "..", "..", "sphinx_runpython")
102+
folder = "test_api"
103+
if not os.path.exists(folder):
104+
os.mkdir(folder)
105+
args = Namespace(
106+
command="api",
107+
path=data,
108+
recursive=False,
109+
verbose=0,
110+
output=folder,
111+
hidden=False,
112+
)
113+
process_args(args)
114+
115+
def test_process_args_img2pdf(self):
116+
from PIL import Image
117+
118+
with tempfile.TemporaryDirectory() as tmpdir:
119+
img_path = os.path.join(tmpdir, "test.png")
120+
out_path = os.path.join(tmpdir, "out.pdf")
121+
Image.new("RGB", (100, 100), "white").save(img_path)
122+
args = Namespace(
123+
command="img2pdf",
124+
path=img_path,
125+
output=out_path,
126+
verbose=0,
127+
zoom=1.0,
128+
rotate=0.0,
129+
)
130+
process_args(args)
131+
self.assertExists(out_path)
132+
133+
def test_sphinx_api_function(self):
134+
data = os.path.join(os.path.dirname(__file__), "..", "..", "sphinx_runpython")
135+
folder = "test_sphinx_api_func"
136+
if not os.path.exists(folder):
137+
os.mkdir(folder)
138+
sphinx_api(data, folder, verbose=0)
139+
140+
def test_main_latex(self):
141+
with tempfile.TemporaryDirectory() as tmpdir:
142+
old_argv = sys.argv
143+
try:
144+
sys.argv = ["sphinx-runpython", "latex", "--path", tmpdir]
145+
from sphinx_runpython._cmd_helper import main
146+
147+
main()
148+
finally:
149+
sys.argv = old_argv
150+
151+
def test_main_help(self):
152+
old_argv = sys.argv
153+
try:
154+
sys.argv = ["sphinx-runpython", "--help"]
155+
from sphinx_runpython._cmd_helper import main
156+
157+
self.assertRaise(lambda: main(), SystemExit)
158+
finally:
159+
sys.argv = old_argv
160+
33161

34162
if __name__ == "__main__":
35163
unittest.main(verbosity=2)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import unittest
2+
from docutils import nodes
3+
from sphinx_runpython.ext_test_case import ExtTestCase
4+
from sphinx_runpython.ext_helper import (
5+
NodeEnter,
6+
NodeLeave,
7+
TinyNode,
8+
WrappedNode,
9+
traverse,
10+
sphinx_lang,
11+
)
12+
13+
14+
class TestExtHelper(ExtTestCase):
15+
def test_tiny_node(self):
16+
parent = object()
17+
node = TinyNode(parent)
18+
self.assertIs(node.parent, parent)
19+
20+
def test_node_enter(self):
21+
parent = object()
22+
node = NodeEnter(parent)
23+
self.assertIsInstance(node, TinyNode)
24+
self.assertIs(node.parent, parent)
25+
26+
def test_node_leave(self):
27+
parent = object()
28+
node = NodeLeave(parent)
29+
self.assertIsInstance(node, TinyNode)
30+
self.assertIs(node.parent, parent)
31+
32+
def test_wrapped_node(self):
33+
doc_node = nodes.section()
34+
wrapped = WrappedNode(doc_node)
35+
self.assertIs(wrapped.node, doc_node)
36+
37+
def test_traverse_simple(self):
38+
root = nodes.section()
39+
para = nodes.paragraph(text="hello")
40+
root += para
41+
42+
results = list(traverse(root))
43+
self.assertGreater(len(results), 0)
44+
depths = [d for d, n in results]
45+
node_types = [type(n) for d, n in results]
46+
self.assertIn(0, depths)
47+
self.assertIn(NodeEnter, node_types)
48+
self.assertIn(NodeLeave, node_types)
49+
self.assertIn(nodes.section, node_types)
50+
self.assertIn(nodes.paragraph, node_types)
51+
52+
def test_traverse_with_wrapped_node(self):
53+
root = nodes.paragraph(text="test")
54+
wrapped = WrappedNode(root)
55+
results = list(traverse(wrapped))
56+
self.assertGreater(len(results), 0)
57+
node_types = [type(n) for d, n in results]
58+
self.assertIn(NodeEnter, node_types)
59+
self.assertIn(NodeLeave, node_types)
60+
61+
def test_traverse_depth(self):
62+
root = nodes.section()
63+
child = nodes.paragraph(text="child")
64+
grandchild = nodes.Text("text")
65+
child += grandchild
66+
root += child
67+
68+
results = list(traverse(root))
69+
max_depth = max(d for d, n in results)
70+
self.assertGreaterEqual(max_depth, 2)
71+
72+
def test_sphinx_lang_no_settings(self):
73+
class FakeEnv:
74+
pass
75+
76+
lang = sphinx_lang(FakeEnv())
77+
self.assertEqual(lang, "en")
78+
79+
def test_sphinx_lang_with_settings_no_code(self):
80+
class FakeSettings:
81+
pass
82+
83+
class FakeEnv:
84+
settings = FakeSettings()
85+
86+
lang = sphinx_lang(FakeEnv())
87+
self.assertEqual(lang, "en")
88+
89+
def test_sphinx_lang_with_language_code(self):
90+
class FakeSettings:
91+
language_code = "fr"
92+
93+
class FakeEnv:
94+
settings = FakeSettings()
95+
96+
lang = sphinx_lang(FakeEnv())
97+
self.assertEqual(lang, "fr")
98+
99+
100+
if __name__ == "__main__":
101+
unittest.main(verbosity=2)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import os
2+
import unittest
3+
import tempfile
4+
from sphinx_runpython.ext_test_case import ExtTestCase
5+
from sphinx_runpython.ext_io_helper import (
6+
_get_file_url,
7+
ReadUrlException,
8+
InternetException,
9+
FileException,
10+
MONTH_DATE,
11+
get_url_content_timeout,
12+
)
13+
14+
15+
class TestExtIoHelper(ExtTestCase):
16+
def test_month_date_keys(self):
17+
self.assertEqual(MONTH_DATE["jan"], 1)
18+
self.assertEqual(MONTH_DATE["dec"], 12)
19+
self.assertEqual(len(MONTH_DATE), 12)
20+
21+
def test_get_file_url_basic(self):
22+
result = _get_file_url("http://example.com/file.html", "/tmp/cache")
23+
self.assertIn("/tmp/cache", result)
24+
self.assertIn("example", result)
25+
26+
def test_get_file_url_png(self):
27+
result = _get_file_url("http://example.com/image.png", "/tmp/cache")
28+
self.assertTrue(result.endswith(".png"))
29+
30+
def test_get_file_url_no_extension(self):
31+
result = _get_file_url("http://example.com/noext", "/tmp/cache")
32+
self.assertIn("/tmp/cache", result)
33+
34+
def test_get_file_url_query_params(self):
35+
result = _get_file_url("http://example.com/file?key=value.pdf", "/tmp/cache")
36+
self.assertIn("/tmp/cache", result)
37+
38+
def test_get_file_url_py(self):
39+
result = _get_file_url("http://example.com/script.py", "/tmp/cache")
40+
self.assertTrue(result.endswith(".py"))
41+
42+
def test_read_url_exception_custom(self):
43+
exc = ReadUrlException("test error")
44+
self.assertIsInstance(exc, Exception)
45+
46+
def test_internet_exception_custom(self):
47+
exc = InternetException("test error")
48+
self.assertIsInstance(exc, Exception)
49+
50+
def test_file_exception_custom(self):
51+
exc = FileException("test error")
52+
self.assertIsInstance(exc, Exception)
53+
54+
def test_get_url_content_timeout_invalid_url(self):
55+
url = "https://localhost:87777/nonexistent"
56+
result = get_url_content_timeout(
57+
url, timeout=2, raise_exception=False, encoding="utf-8"
58+
)
59+
self.assertIsNone(result)
60+
61+
def test_get_url_content_timeout_raises(self):
62+
url = "https://localhost:87777/nonexistent"
63+
self.assertRaise(
64+
lambda: get_url_content_timeout(url, timeout=2, raise_exception=True),
65+
InternetException,
66+
)
67+
68+
def test_get_url_content_timeout_save_to_file(self):
69+
url = "https://localhost:87777/nonexistent"
70+
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as f:
71+
outfile = f.name
72+
try:
73+
result = get_url_content_timeout(
74+
url,
75+
timeout=2,
76+
output=outfile,
77+
raise_exception=False,
78+
encoding="utf-8",
79+
)
80+
self.assertIsNone(result)
81+
finally:
82+
if os.path.exists(outfile):
83+
os.remove(outfile)
84+
85+
86+
if __name__ == "__main__":
87+
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)