Skip to content

Commit 461a6e7

Browse files
Copilotoscarlevin
andauthored
Add tests for pretext build -g (force asset regeneration) (#1125)
* Initial plan * Add test_build_generate to test pretext build -g behavior Co-authored-by: oscarlevin <6504596+oscarlevin@users.noreply.github.com> Agent-Logs-Url: https://github.com/PreTeXtBook/pretext-cli/sessions/782c1d2c-2645-447a-9a8d-371f40e95808 * update core commit * update tests * remove passagemath as dev dependency * commit new test example files --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: oscarlevin <6504596+oscarlevin@users.noreply.github.com> Co-authored-by: Oscar Levin <oscar.levin@unco.edu>
1 parent c7cbb82 commit 461a6e7

File tree

5 files changed

+197
-6
lines changed

5 files changed

+197
-6
lines changed

pretext/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
VERSION = get_version("pretext", Path(__file__).parent.parent)
2020

2121

22-
CORE_COMMIT = "4e4ff13706654ca5eb4ff9936bc2acddbd4aa648"
22+
CORE_COMMIT = "8cc838f3fad87b51b2f3ba76ec9ca63fce8978ea"
2323

2424

2525
def activate() -> None:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- This file, the project manifest, provides the overall configuration for your PreTeXt project. -->
3+
<project ptx-version="2">
4+
<targets>
5+
<target name="web" format="html" />
6+
<target name="print" format="pdf" />
7+
</targets>
8+
</project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<pretext>
4+
<book>
5+
<title>Prefigure</title>
6+
<chapter xml:id="ch-prefigure">
7+
<title>prefigure</title>
8+
<image width="60%">
9+
<prefigure label="pftest"
10+
xmlns="https://prefigure.org">
11+
<diagram dimensions="(300,300)" margins="5">
12+
<definition>f(t,y) = (y[1], -pi*y[0]-0.3*y[1])</definition>
13+
<coordinates bbox="[-1,-3,6,3]">
14+
<grid-axes xlabel="t"/>
15+
<de-solve function="f" t0="0" t1="bbox[2]"
16+
y0="(0,2)" name="oscillator"
17+
N="200"/>
18+
<plot-de-solution at="x" solution="oscillator"
19+
axes="(t,y0)" />
20+
<plot-de-solution at="xprime" solution="oscillator"
21+
axes="(t,y1)" stroke="red"
22+
tactile-dash="9 9"/>
23+
<legend at="legend" anchor="(bbox[2], bbox[3])"
24+
alignment="sw" scale="0.9" opacity="0.5">
25+
<item ref="x"><m>x(t)</m></item>
26+
<item ref="xprime"><m>x'(t)</m></item>
27+
</legend>
28+
</coordinates>
29+
</diagram>
30+
</prefigure>
31+
</image>
32+
</chapter>
33+
</book>
34+
</pretext>

tests/test_cli.py

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
HAS_XELATEX = check_installed(["xelatex", "--version"])
2424
HAS_ASY = check_installed(["asy", "--version"])
25+
HAS_SAGE = check_installed(["sage", "--version"])
2526

2627

2728
@contextmanager
@@ -88,6 +89,10 @@ def test_devscript(script_runner: ScriptRunner) -> None:
8889
not HAS_XELATEX,
8990
reason="Skipped since xelatex isn't found.",
9091
)
92+
@pytest.mark.skipif(
93+
not HAS_SAGE,
94+
reason="Skipped since sage isn't found.",
95+
)
9196
@pytest.mark.skipif(
9297
not HAS_ASY,
9398
reason="Skipped since asy isn't found.",
@@ -131,7 +136,6 @@ def test_build(tmp_path: Path, script_runner: ScriptRunner) -> None:
131136
/ "fig_tikz-example-diagram.svg"
132137
).exists()
133138

134-
# Do a full build.
135139
assert script_runner.run(
136140
[PTX_CMD, "-v", "debug", "build", "web"], cwd=project_path
137141
).success
@@ -242,13 +246,137 @@ def test_init_and_update_with_git(tmp_path: Path, script_runner: ScriptRunner) -
242246
assert f"pretext == {pretext.VERSION}\n" in lines[1]
243247

244248

249+
def test_build_no_generate(tmp_path: Path, script_runner: ScriptRunner) -> None:
250+
"""
251+
Test the behavior of `pretext build -q` (no generate) and -g -q together.
252+
253+
Expected behaviors (per docs/asset-generation.md):
254+
- `pretext build -q`: no assets are generated, even if source has changed.
255+
- `pretext build -g -q`: warning issued; proceeds as if neither flag was set.
256+
"""
257+
prefigure_path = tmp_path / "prefigure"
258+
shutil.copytree(EXAMPLES_DIR / "projects" / "prefigure", prefigure_path)
259+
260+
prefigure_asset = prefigure_path / "generated-assets" / "prefigure" / "pftest.svg"
261+
262+
# --- Test -q: build without generating any assets ---
263+
result = script_runner.run(
264+
[PTX_CMD, "-v", "debug", "build", "-q"], cwd=prefigure_path
265+
)
266+
assert result.success
267+
assert (
268+
not prefigure_asset.exists()
269+
), "Prefigure assets should NOT be generated when using -q (no-generate)"
270+
271+
# --- Test -g -q together: warning issued, behaves like normal build ---
272+
result = script_runner.run(
273+
[PTX_CMD, "-v", "debug", "build", "-g", "-q"], cwd=prefigure_path
274+
)
275+
assert result.success
276+
# The warning message about conflicting flags should appear in the output.
277+
combined_output = result.stdout + result.stderr
278+
assert (
279+
"doesn't make sense" in combined_output
280+
), "A warning about using -g and -q together should be emitted"
281+
# Since the flags cancel each other, behavior is like a normal build.
282+
# On a clean project, that means the missing prefigure asset should be generated.
283+
assert (
284+
prefigure_asset.exists()
285+
), "With -g and -q together, prefigure assets should be generated on a clean project (flags cancel each other)"
286+
287+
288+
def test_build_force_generate_prefigure(
289+
tmp_path: Path, script_runner: ScriptRunner
290+
) -> None:
291+
"""
292+
Test the behavior of `pretext build -g` (force generate) and regular builds.
293+
294+
Expected behaviors (per docs/asset-generation.md):
295+
- `pretext build -g`: all assets generated regardless of whether source has changed.
296+
- Regular `pretext build` after a successful build: assets NOT regenerated when source is unchanged.
297+
"""
298+
prefigure_path = tmp_path / "prefigure"
299+
shutil.copytree(EXAMPLES_DIR / "projects" / "prefigure", prefigure_path)
300+
301+
prefigure_asset = prefigure_path / "generated-assets" / "prefigure" / "pftest.svg"
302+
303+
# --- Test -g: build with force-generate ---
304+
result = script_runner.run(
305+
[PTX_CMD, "-v", "debug", "build", "-g"], cwd=prefigure_path
306+
)
307+
assert result.success
308+
assert (
309+
prefigure_asset.exists()
310+
), "Prefigure asset should be generated when using -g (force generate)"
311+
assert (
312+
prefigure_path / "output" / "web"
313+
).exists(), "Build output should exist after `pretext build -g`"
314+
315+
# --- Test regular build does NOT regenerate when source is unchanged ---
316+
# Delete the generated asset to simulate a missing file after a previous build.
317+
prefigure_asset.unlink()
318+
assert not prefigure_asset.exists()
319+
320+
# A regular build (no -g flag) should NOT regenerate since source hash has not changed.
321+
result = script_runner.run([PTX_CMD, "-v", "debug", "build"], cwd=prefigure_path)
322+
assert result.success
323+
assert (
324+
not prefigure_asset.exists()
325+
), "Regular `pretext build` should NOT regenerate prefigure assets when source is unchanged"
326+
327+
# --- Test -g regenerates even though source is unchanged ---
328+
result = script_runner.run(
329+
[PTX_CMD, "-v", "debug", "build", "-g"], cwd=prefigure_path
330+
)
331+
assert result.success
332+
assert (
333+
prefigure_asset.exists()
334+
), "`pretext build -g` should regenerate prefigure assets even when source is unchanged"
335+
336+
245337
@pytest.mark.skipif(
246338
not HAS_ASY,
247339
reason="Skipped since asy isn't found.",
248340
)
249-
def test_generate_graphics(tmp_path: Path, script_runner: ScriptRunner) -> None:
341+
def test_build_force_generate_asymptote(
342+
tmp_path: Path, script_runner: ScriptRunner
343+
) -> None:
250344
graphics_path = tmp_path / "graphics"
251345
shutil.copytree(EXAMPLES_DIR / "projects" / "graphics", graphics_path)
346+
347+
asymptote_asset = graphics_path / "generated-assets" / "asymptote" / "test.html"
348+
349+
result = script_runner.run(
350+
[PTX_CMD, "-v", "debug", "build", "-g"], cwd=graphics_path
351+
)
352+
assert result.success
353+
assert (
354+
asymptote_asset.exists()
355+
), "Asymptote asset should be generated when using -g (force generate)"
356+
357+
358+
def test_generate_graphics_prefigure(
359+
tmp_path: Path, script_runner: ScriptRunner
360+
) -> None:
361+
prefigure_path = tmp_path / "prefigure"
362+
shutil.copytree(EXAMPLES_DIR / "projects" / "prefigure", prefigure_path)
363+
364+
assert script_runner.run(
365+
[PTX_CMD, "-v", "debug", "generate", "prefigure"], cwd=prefigure_path
366+
).success
367+
assert (prefigure_path / "generated-assets" / "prefigure" / "pftest.svg").exists()
368+
369+
370+
@pytest.mark.skipif(
371+
not HAS_ASY,
372+
reason="Skipped since asy isn't found.",
373+
)
374+
def test_generate_graphics_asymptote_local(
375+
tmp_path: Path, script_runner: ScriptRunner
376+
) -> None:
377+
graphics_path = tmp_path / "graphics"
378+
shutil.copytree(EXAMPLES_DIR / "projects" / "graphics", graphics_path)
379+
252380
assert script_runner.run(
253381
[PTX_CMD, "-v", "debug", "generate", "asymptote"], cwd=graphics_path
254382
).success
@@ -263,11 +391,27 @@ def test_generate_graphics(tmp_path: Path, script_runner: ScriptRunner) -> None:
263391
[PTX_CMD, "-v", "debug", "generate", "asymptote", "-t", "web"],
264392
cwd=graphics_path,
265393
).success
266-
os.remove(graphics_path / "generated-assets" / "asymptote" / "test.html")
394+
395+
396+
def test_generate_graphics_asymptote_server(
397+
tmp_path: Path, script_runner: ScriptRunner
398+
) -> None:
399+
graphics_path = tmp_path / "graphics"
400+
shutil.copytree(EXAMPLES_DIR / "projects" / "graphics", graphics_path)
401+
402+
# Force server-backed asymptote generation so this test is independent of local asy.
403+
project_manifest = graphics_path / "project.ptx"
404+
project_manifest.write_text(
405+
project_manifest.read_text().replace(
406+
'<project ptx-version="2">',
407+
'<project ptx-version="2" asy-method="server">',
408+
)
409+
)
410+
267411
assert script_runner.run(
268-
[PTX_CMD, "-v", "debug", "generate", "prefigure"], cwd=graphics_path
412+
[PTX_CMD, "-v", "debug", "generate", "asymptote"], cwd=graphics_path
269413
).success
270-
assert (graphics_path / "generated-assets" / "prefigure" / "pftest.svg").exists()
414+
assert (graphics_path / "generated-assets" / "asymptote" / "test.html").exists()
271415

272416

273417
# @pytest.mark.skip(

tests/test_sample_article.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
HAS_XELATEX = check_installed(["xelatex", "--version"])
1010
HAS_ASY = check_installed(["asy", "--version"])
11+
HAS_SAGE = check_installed(["sage", "--version"])
1112

1213

1314
@pytest.mark.skipif(
@@ -18,6 +19,10 @@
1819
not HAS_ASY,
1920
reason="Skipped since asy isn't found.",
2021
)
22+
@pytest.mark.skipif(
23+
not HAS_SAGE,
24+
reason="Skipped since sage isn't found.",
25+
)
2126
def test_sample_article(tmp_path: Path) -> None:
2227
error_checker = errorhandler.ErrorHandler(logger="ptxlogger")
2328
prj_path = tmp_path / "sample"

0 commit comments

Comments
 (0)