Files
benilla-octo/scripts/tests/test_water_shots.py
T
gromandClaude Opus 5 624ccec808 water: graft deepthought84's Improved Water onto this fork
The whole workspace compiles with his water in place of ours. Nothing is
verified beyond that yet -- no capture, no gates -- so this lands on
`water-port`, not on main.

Method: scripts/water-port-merge.sh stitches the common ancestor our squashed
history hides (both forks descend from samwhosung/benilla) and runs a real
three-way merge. 134 of his 168 files land clean; the 34 that conflict were
resolved by hand, 82 hunks, and the log is in docs/WATER_PORT.md.

What went, per grom's call (full swap, drop the extras): enhanced_water.wgsl,
liquid/{reflect,scene_depth,waves,lod}.rs, water_fx/bob.rs and the Water Quality
dropdown, with our LiquidExt bindings and the WaterDepth prepass they fed.

What was kept and re-applied onto his tree: the Ironforge magma palette
(MagmaTone, now multiplying the body before his stylised grade so a tinted pool's
crust and glow are its own colour) and our shader hooks -- fog, emissive,
monkey_frame, wind, wet, moonlight -- which his liquid.wgsl knew nothing about.

Four things needed real work rather than a side:
  * static_gx now keys its pipelines per VIEW, not per world camera, because his
    mirror and probe faces carry their own samples and format; our torch and
    light groups and the shadow-filter def ride along, and the depth-only twin
    his Hi-Z prepass needs is built beside each colour pipeline.
  * the booth layer ladder moved past the liquid render layers. His seam claims
    30 and 31; our pane block grew from UI_MODELS_LAYER with nothing stopping it
    reaching them, and the const assert fired exactly as the doc predicted.
  * two SystemParam tuples were already at the 16 ceiling (the minimap's blip
    inputs, the capture driver) and had to nest to take his.
  * sky.rs grew `dome_uniforms`, the one law his water march and the dome now
    share, without losing our Enhanced sky inputs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-11-09 10:57:18 -07:00

184 lines
8.6 KiB
Python

"""Fixtures for scripts/water-shots.sh: where it writes and what reaches the client, run in a
throwaway repo with a stub `cargo` on PATH and a stub client that lists one scene, `water-a`, echoes
the capture knobs it receives into its log and writes the files a capture would.
python3 -m unittest discover -s scripts/tests
"""
import os
import pathlib
import shutil
import subprocess
import tempfile
import unittest
SCRIPT = pathlib.Path(__file__).resolve().parents[1] / "water-shots.sh"
CLIENT = """#!/bin/bash
if [ "${WOW_CAPTURE:-}" = list ]; then echo water-a; exit 0; fi
[ -z "${WOW_CAPTURE_STABLE+x}" ] || echo "WOW_CAPTURE_STABLE is set: $WOW_CAPTURE_STABLE"
[ -z "${WOW_CAPTURE_AGE+x}" ] || echo "WOW_CAPTURE_AGE is set: $WOW_CAPTURE_AGE"
echo "WOW_CAPTURE_PAN=${WOW_CAPTURE_PAN:-}"
echo "WOW_MSAA=${WOW_MSAA:-}"
echo "WOW_NO_LIQUID=${WOW_NO_LIQUID:-}"
if [ -n "${STUB_ABORT:-}" ]; then echo "wgpu error: Validation Error"; exit 134; fi
if [ "${WOW_CAPTURE_BURST:-0}" -gt 0 ]; then
for i in $(seq 0 $((WOW_CAPTURE_BURST - 1))); do
: > "${WOW_CAPTURE_OUT%.png}-$(printf %03d "$i").png"
done
else
: > "$WOW_CAPTURE_OUT"
fi
"""
@unittest.skipUnless(shutil.which("bash"), "water-shots.sh is a bash script")
class Shots(unittest.TestCase):
def setUp(self):
tmp = tempfile.TemporaryDirectory()
self.addCleanup(tmp.cleanup)
self.tmp = pathlib.Path(tmp.name)
self.repo = self.tmp / "repo"
self.caller = self.tmp / "caller"
for d in ("scripts", "target/release", "docs"):
(self.repo / d).mkdir(parents=True)
(self.tmp / "bin").mkdir()
self.caller.mkdir()
shutil.copy(SCRIPT, self.repo / "scripts" / "water-shots.sh")
(self.tmp / "bin" / "cargo").write_text("#!/bin/sh\nexit 0\n")
(self.repo / "target" / "release" / "benilla").write_text(CLIENT)
for p in ("scripts/water-shots.sh", "target/release/benilla"):
(self.repo / p).chmod(0o755)
(self.tmp / "bin" / "cargo").chmod(0o755)
def shots(self, cwd, *args, **env):
"""water-shots.sh run from `cwd`, with only the knobs given set."""
base = {k: v for k, v in os.environ.items()
if not k.startswith(("WATER_", "WOW_CAPTURE"))}
base["PATH"] = f"{self.tmp / 'bin'}:{base['PATH']}"
return subprocess.run([str(self.repo / "scripts" / "water-shots.sh"), *map(str, args)],
cwd=cwd, env=base | env, capture_output=True, text=True)
def test_the_default_directory_is_target_visual_water(self):
r = self.shots(self.caller)
self.assertEqual(r.returncode, 0, r.stderr)
self.assertTrue((self.repo / "target/visual/water/water-a.png").exists())
self.assertTrue((self.repo / "target/visual/water/water-a-ref.png").exists())
def test_a_relative_directory_resolves_against_the_caller(self):
r = self.shots(self.caller, "rel")
self.assertEqual(r.returncode, 0, r.stderr)
self.assertTrue((self.caller / "rel" / "water-a.png").exists())
def test_a_fresh_directory_in_the_repo_is_refused_and_removed(self):
r = self.shots(self.caller, self.repo / "foo")
self.assertEqual(r.returncode, 2)
self.assertFalse((self.repo / "foo").exists())
self.assertIn("is in the repo outside target/", r.stderr)
def test_an_existing_directory_in_the_repo_is_refused_and_kept(self):
r = self.shots(self.caller, self.repo / "docs")
self.assertEqual(r.returncode, 2)
self.assertTrue((self.repo / "docs").is_dir())
self.assertEqual(list((self.repo / "docs").iterdir()), [])
def test_a_relative_directory_from_the_repo_root_is_refused(self):
r = self.shots(self.repo, "docs/sub")
self.assertEqual(r.returncode, 2)
self.assertFalse((self.repo / "docs" / "sub").exists())
def test_every_level_a_refusal_made_is_removed_and_no_other(self):
r = self.shots(self.caller, self.repo / "shots" / "deep" / "er")
self.assertEqual(r.returncode, 2)
self.assertFalse((self.repo / "shots").exists())
(self.repo / "keep").mkdir()
r = self.shots(self.caller, self.repo / "keep" / "new")
self.assertEqual(r.returncode, 2)
self.assertTrue((self.repo / "keep").is_dir())
self.assertFalse((self.repo / "keep" / "new").exists())
def test_a_directory_under_target_is_accepted(self):
r = self.shots(self.caller, self.repo / "target" / "shots")
self.assertEqual(r.returncode, 0, r.stderr)
self.assertTrue((self.repo / "target" / "shots" / "water-a.png").exists())
def test_a_burst_writes_and_names_its_shots(self):
r = self.shots(self.caller, self.tmp / "burst", 3)
self.assertEqual(r.returncode, 0, r.stderr)
for i in range(3):
self.assertTrue((self.tmp / "burst" / f"water-a-{i:03d}.png").exists())
self.assertFalse((self.tmp / "burst" / "water-a-ref-000.png").exists())
self.assertIn("water-a-000.png … water-a-002.png", r.stdout)
def test_the_settle_and_age_stay_the_scenes_own_and_other_knobs_pass(self):
r = self.shots(self.caller, self.tmp / "env", WOW_CAPTURE_STABLE="30",
WOW_CAPTURE_AGE="5", WOW_CAPTURE_PAN="20,10")
self.assertEqual(r.returncode, 0, r.stderr)
log = (self.tmp / "env" / "water-a.log").read_text()
self.assertNotIn("WOW_CAPTURE_STABLE", log)
self.assertNotIn("WOW_CAPTURE_AGE", log)
self.assertIn("WOW_CAPTURE_PAN=20,10", log)
def test_water_noliquid_adds_the_still_with_no_liquid(self):
r = self.shots(self.caller, self.tmp / "dry", WATER_NOLIQUID="1")
self.assertEqual(r.returncode, 0, r.stderr)
for stem, dry in (("water-a", ""), ("water-a~noliquid", "1"), ("water-a-ref", "")):
self.assertTrue((self.tmp / "dry" / f"{stem}.png").exists())
log = (self.tmp / "dry" / f"{stem}.log").read_text()
self.assertIn(f"WOW_NO_LIQUID={dry}\n", log)
r = self.shots(self.caller, self.tmp / "wet")
self.assertFalse((self.tmp / "wet" / "water-a~noliquid.png").exists())
def test_msaa_is_off_whatever_the_caller_exported(self):
r = self.shots(self.caller, self.tmp / "msaa", WOW_MSAA="4")
self.assertEqual(r.returncode, 0, r.stderr)
for stem in ("water-a", "water-a-ref"):
log = (self.tmp / "msaa" / f"{stem}.log").read_text()
self.assertIn("WOW_MSAA=off\n", log)
def test_water_msaa_multisamples_both_lanes(self):
r = self.shots(self.caller, self.tmp / "msaa", WATER_MSAA="4", WOW_MSAA="off")
self.assertEqual(r.returncode, 0, r.stderr)
for stem in ("water-a", "water-a-ref"):
log = (self.tmp / "msaa" / f"{stem}.log").read_text()
self.assertIn("WOW_MSAA=4\n", log)
def test_water_msaa_refuses_the_default_directory(self):
r = self.shots(self.caller, WATER_MSAA="4")
self.assertEqual(r.returncode, 2)
self.assertIn("give it a <dir> of its own", r.stderr)
self.assertFalse((self.repo / "target" / "visual").exists())
def test_every_log_names_the_msaa_level(self):
for level, sub in (("4", "m4"), (None, "off")):
env = {"WATER_MSAA": level} if level else {}
r = self.shots(self.caller, self.tmp / sub, **env)
self.assertEqual(r.returncode, 0, r.stderr)
for stem in ("water-a", "water-a-ref"):
log = (self.tmp / sub / f"{stem}.log").read_text()
self.assertTrue(log.startswith(f"water-shots: msaa {level or 'off'}\n"), log)
self.assertIn(f"WOW_MSAA={level or 'off'}\n", log)
self.assertIn(f"(msaa {level or 'off'})", r.stdout)
def test_an_msaa_level_wgpu_does_not_express_is_refused(self):
r = self.shots(self.caller, self.tmp / "msaa", WATER_MSAA="3")
self.assertEqual(r.returncode, 2)
self.assertIn("not 3", r.stderr)
self.assertFalse((self.tmp / "msaa").exists())
def test_a_client_that_aborts_fails_the_run(self):
r = self.shots(self.caller, self.tmp / "abort", WATER_MSAA="4", STUB_ABORT="1")
self.assertEqual(r.returncode, 1)
self.assertIn("capture water-a FAILED", r.stderr)
self.assertIn("Validation Error", (self.tmp / "abort" / "water-a.log").read_text())
self.assertFalse((self.tmp / "abort" / "water-a-ref.log").exists())
def test_a_perf_run_takes_no_burst(self):
r = self.shots(self.caller, self.tmp / "perf", 3, WATER_PERF="1200")
self.assertEqual(r.returncode, 2)
self.assertIn("not a burst or a view", r.stderr)
if __name__ == "__main__":
unittest.main()