2222
2323HAS_XELATEX = check_installed (["xelatex" , "--version" ])
2424HAS_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(
0 commit comments