Skip to content

Commit c7cbb82

Browse files
Copilotoscarlevin
andauthored
Add tests for previously untested utility functions (#1124)
* Initial plan * Add tests for utility functions: working_directory, format_docstring_as_help_str, is_earlier_version, hash_path, xml_syntax_is_valid Co-authored-by: oscarlevin <6504596+oscarlevin@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: oscarlevin <6504596+oscarlevin@users.noreply.github.com>
1 parent 62acbed commit c7cbb82

1 file changed

Lines changed: 95 additions & 1 deletion

File tree

tests/test_utils.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import pytest
23
from pathlib import Path
34
from pretext import utils
45

@@ -10,7 +11,8 @@ def test_working_directory(tmp_path: Path) -> None:
1011
assert Path().resolve() == tmp_path.resolve()
1112
with utils.working_directory(subdir):
1213
assert Path().resolve().parent == tmp_path.resolve()
13-
# TODO check path returns afterward
14+
# After exiting context manager, the directory should have returned to the original
15+
assert Path().resolve() == tmp_path.resolve()
1416

1517

1618
def test_project_path(tmp_path: Path) -> None:
@@ -73,3 +75,95 @@ def test_requirements_version(tmp_path: Path) -> None:
7375
assert (
7476
utils.requirements_version(tmp_path) is None
7577
), f"Should not match: {line!r}"
78+
79+
80+
def test_format_docstring_as_help_str() -> None:
81+
# Leading/trailing whitespace on each line is stripped, and single newlines
82+
# within a paragraph are collapsed to a single space.
83+
docstring = """
84+
First line of text
85+
that continues here.
86+
87+
Second paragraph.
88+
"""
89+
result = utils.format_docstring_as_help_str(docstring)
90+
assert "First line of text that continues here." in result
91+
assert "Second paragraph." in result
92+
# Double newlines (paragraph breaks) are preserved as "\n\n".
93+
assert "\n\n" in result
94+
95+
# A single-line docstring has no newlines.
96+
assert "\n" not in utils.format_docstring_as_help_str("Single line.")
97+
assert utils.format_docstring_as_help_str(" spaced ") == "spaced"
98+
99+
# An empty string produces an empty string.
100+
assert utils.format_docstring_as_help_str("") == ""
101+
102+
103+
def test_is_earlier_version() -> None:
104+
assert utils.is_earlier_version("1.0.0", "2.0.0")
105+
assert utils.is_earlier_version("2.0.0", "2.1.0")
106+
assert utils.is_earlier_version("2.1.0", "2.1.1")
107+
assert not utils.is_earlier_version("2.0.0", "1.0.0")
108+
assert not utils.is_earlier_version("2.1.0", "2.0.0")
109+
assert not utils.is_earlier_version("2.1.1", "2.1.0")
110+
# Equal versions are not earlier.
111+
assert not utils.is_earlier_version("1.2.3", "1.2.3")
112+
# When the primary digits are equal but the strings differ only in length,
113+
# the shorter version string is treated as earlier. In pretext-cli, dev
114+
# builds (e.g. "2.11.5.dev0") are nightly POST-release builds produced
115+
# *after* the stable release, so "2.11.5" is earlier than "2.11.5.dev0".
116+
assert utils.is_earlier_version("2.11.5", "2.11.5.dev0")
117+
assert not utils.is_earlier_version("2.11.5.dev0", "2.11.5")
118+
119+
120+
def test_hash_path(tmp_path: Path) -> None:
121+
# hash_path should return a 10-character hex string.
122+
result = utils.hash_path(tmp_path)
123+
assert isinstance(result, str)
124+
assert len(result) == 10
125+
assert all(c in "0123456789abcdef" for c in result)
126+
# The same path should always produce the same hash.
127+
assert utils.hash_path(tmp_path) == utils.hash_path(tmp_path)
128+
# Different paths should (almost certainly) produce different hashes.
129+
other_path = tmp_path / "subdir"
130+
assert utils.hash_path(tmp_path) != utils.hash_path(other_path)
131+
132+
133+
def test_xml_syntax_is_valid(tmp_path: Path) -> None:
134+
# A well-formed PreTeXt file should pass validation.
135+
valid_xml = tmp_path / "valid.ptx"
136+
valid_xml.write_text(
137+
"<?xml version='1.0' encoding='utf-8'?>\n"
138+
"<pretext>\n"
139+
" <article xml:id='article-id'>\n"
140+
" <title>Hello</title>\n"
141+
" <p>Content.</p>\n"
142+
" </article>\n"
143+
"</pretext>\n"
144+
)
145+
assert utils.xml_syntax_is_valid(valid_xml)
146+
147+
# A file whose root tag is not <pretext> should fail.
148+
wrong_root = tmp_path / "wrong_root.ptx"
149+
wrong_root.write_text(
150+
"<?xml version='1.0' encoding='utf-8'?>\n"
151+
"<notpretext>\n"
152+
" <p>Content.</p>\n"
153+
"</notpretext>\n"
154+
)
155+
assert not utils.xml_syntax_is_valid(wrong_root)
156+
157+
# A file with malformed XML should fail.
158+
bad_xml = tmp_path / "bad.ptx"
159+
bad_xml.write_text(
160+
"<?xml version='1.0' encoding='utf-8'?>\n"
161+
"<pretext>\n"
162+
" <unclosed-tag>\n"
163+
"</pretext>\n"
164+
)
165+
assert not utils.xml_syntax_is_valid(bad_xml)
166+
167+
# A nonexistent file should raise IOError.
168+
with pytest.raises(IOError):
169+
utils.xml_syntax_is_valid(tmp_path / "nonexistent.ptx")

0 commit comments

Comments
 (0)