From b7bed627e06851f019b2e881c1ffef2ee69ec77c Mon Sep 17 00:00:00 2001 From: apotema Date: Fri, 18 Sep 2026 14:47:33 -0300 Subject: [PATCH] ci: build the bgfx examples from their pins, and run the shader pipeline e2e (#732, #735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two coverage gaps in one workflow, grouped because they rewire the same lanes of `.github/workflows/ci.yml` and would conflict as separate PRs. ── #732: the bgfx examples were generated, never built ────────────────── `generate` proves the manifest parsed. It does not compile a line of backend source, so every defect that lives in a DEPENDENCY — an exhaustive switch, a moved contract type, a raised floor — passed. That is how core v1.32.0's `MaterialEffect.pixel_water` reached main: `material-demo` and `pack-city` both pinned a bgfx with no arm for it and CI said nothing. Only `sprite-runner` (sokol) was built, so the signal read "one backend broken" instead of "three examples broken, two untested" (#731). The same lane also FABRICATES breaks: it clones core/engine/gfx/null at `main` as siblings, and the assembler's auto-discovery builds those instead of the pins, so mid-release-train the four mains disagree and an unrelated PR goes red (#730, `backend_contract has no member named 'PixelWaterRgba'`; a re-run of the identical commit passed). * New job `bgfx examples (PINNED resolution)` — ubuntu, scratch LABELLE_HOME, NO runtime sibling checkouts — GENERATES AND BUILDS both bgfx examples against their declared `project.labelle` pins. It refuses to start if a runtime sibling exists, asserts each pinned version was fetched into the scratch home, and greps the install/generate logs for the assembler's own "using LOCAL sources" / "built from LOCAL sources" warnings — a mechanism assertion, not a value one. * The sibling lane is RENAMED "Examples integration (siblings @ main — cross-repo, may be mid-train)" and prints a banner of every sibling SHA/ref/describe before anything builds, so a mid-train red is recognised at a glance instead of debugged from scratch. Linux only (one platform catches a compile break in backend source), and on every PR rather than path-gated: the v1.32.0 break arrived from a dependency's release with no file in this repo changed, so a path filter would have skipped the job exactly when the break landed. Proof, on a mirrored checkout outside the toolkit tree with a scratch LABELLE_HOME (so pins really resolve from the registry): material-demo @ its own pins (core 1.32.0 / bgfx 0.20.0) install: fetching core 1.32.0, bgfx 0.20.0 (remote) generate: ok zig build -> exit 0, zig-out/bin/material_demo (36s) pack-city @ its own pins -> zig build exit 0 (25s) material-demo with bgfx re-pinned to the known-bad 0.13.1 (the #731 break) $ labelle-assembler generate -> exit 0 <-- old job: GREEN $ zig build -> exit 1 <-- new job: RED labelle-bgfx/src/gfx/texture.zig:469:12: error: switch must handle all possibilities labelle-core/src/backend_contract.zig:308:5: note: unhandled enumeration value: 'pixel_water' And the local-sources assertion, verified against a real sibling checkout: install: "core: using LOCAL sources from '…' — the pinned 1.32.0 will NOT be built (#685)" generate: "labelle: core is built from LOCAL sources, not the pinned 1.32.0 — source '…' (rev …)" ── #735: nothing ran the shader BUILD pipeline ────────────────────────── `test/material_pipeline_e2e.py` is the only test covering the PRODUCER side of game-owned materials (descriptor -> shaderc -> packaged variants -> a material that validates against core's contract). It was opt-in behind three `-D` flags because CI had no shaderc, so it never ran. Everything else covers CONSUMERS of already-compiled shaders, and the fixtures are pre-compiled — structurally unable to notice a pipeline regression. * New job `Material shader pipeline e2e (real shaderc)` provisions shaderc by REUSING the existing toolchain source rather than adding a second one: it reads the builtin bgfx provider version out of `src/config.zig`, reads that release's `zbgfx` archive URL out of labelle-bgfx's `build.zig.zon`, and builds `-Dwith_shaderc=true` from exactly that archive (cached on the URL; ~2.5 min cold locally). `--core` comes from the curated `core_version` default in `build.zig`, so there is no second pin to drift. * Nobody passes a flag by hand — the job derives all three. * Already independent of `examples/condenser`: the driver synthesizes its own three-material fixture under `.zig-cache/`. * `test/material_pipeline_e2e.py`: the missing-target assertion hard-coded `target requires 'spv'`, which is the LINUX answer. `material_build` picks the required variant per host (darwin -> mtl), so the test could not pass on macOS at all. It now follows the host. The driver already exercises five REJECTION paths beside the happy one, so it proves the pipeline fails closed: an include escaping the material dir, an uncompilable `.sc`, a descriptor missing the host's required variant, and two component-collision cases in the emitted comptime guard. Proof it catches a real producer-side regression that nothing else does — disabled include-tree tracking in `material_build.zig` so a changed `color.sh` no longer invalidates the shader cache: zig build test-materials -> exit 0 (448 tests, all green) zig build test-material-integration -> exit 1 AssertionError: shader fog (spv) (fog.spv.bin) success [not rebuilt] and, with the escaping-include rejection disabled: zig build test-material-integration -> exit 1 at build(False, "EscapingShaderInclude") Clean run on this branch: "PASS: 12 real shader variants, generated core descriptors, cached rebuild, include invalidation, escaping include rejection, compiler failure, missing target diagnostic". Closes #732 Closes #735 Co-authored-by: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XBGRLojiysEr5zy1U5mbP6 --- .github/workflows/ci.yml | 372 +++++++++++++++++++++++++++++++++- test/material_pipeline_e2e.py | 10 +- 2 files changed, 376 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb539adc..484bcb20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,23 @@ jobs: # dep doesn't fetch cleanly from the macos GitHub runner (see the # slot-allocator gate above for context). examples-integration: - name: Examples integration test + # NAMED for what it actually resolves (#732 half 2). This lane clones + # labelle-core / labelle-engine / labelle-gfx / labelle-null at `main` as + # SIBLINGS, and the assembler's monorepo auto-discovery (findRepoRoot walks + # up from the EXECUTABLE) builds those siblings INSTEAD of each example's + # declared pins. That is deliberate — it is the cross-repo integration + # signal — but it means a red here does NOT necessarily mean this PR is + # broken: during a multi-repo release train the four mains disagree for the + # minutes between merges, and an example fails on a contract that exists on + # one main and not another. PR #730 went red in a null-backend example on + # `backend_contract has no member named 'PixelWaterRgba'`, and a re-run of + # the IDENTICAL commit passed once the mains agreed. + # + # So: read a failure here as "this PR vs the toolkit mains, right now". + # The `bgfx-examples-pinned` job below is the lane that tests a PR against + # what it DECLARES; if that one is green and this one is red, check the SHA + # banner this job prints first — you are probably mid-train. + name: Examples integration (siblings @ main — cross-repo, may be mid-train) runs-on: ubuntu-latest # Builds several example projects end-to-end; normally a few minutes. # 15 gives headroom for the multi-repo checkout + apt install while @@ -204,6 +220,36 @@ jobs: ref: main path: labelle-gfx + # Print WHICH sibling revisions this run resolved, before anything is + # built (#732). The examples' own pins are overridden by these + # checkouts, so these SHAs — not the `project.labelle` version fields — + # are what a failure below was actually compiled against. Without this + # banner a mid-release-train red is indistinguishable from a real break + # and gets debugged from scratch; with it, one glance at the top of the + # log says "core is 40 commits ahead of engine, wait for the train". + - name: Sibling revisions under test (READ THIS FIRST on a failure) + run: | + set -uo pipefail + echo "::group::Sibling checkouts resolved for this run" + printf '%-22s %-46s %s\n' REPO REVISION DESCRIBE + for d in labelle-core labelle-engine labelle-gfx labelle-null \ + labelle-cli labelle-gui labelle-engine-727; do + dir="$GITHUB_WORKSPACE/$d" + if [ ! -d "$dir/.git" ]; then + printf '%-22s %-46s %s\n' "$d" "(not checked out)" "-" + continue + fi + sha=$(git -C "$dir" rev-parse HEAD) + ref=$(git -C "$dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "?") + desc=$(git -C "$dir" describe --tags --always 2>/dev/null || echo "?") + printf '%-22s %-46s %s\n' "$d" "$sha ($ref)" "$desc" + done + echo "::endgroup::" + echo "NOTE: these siblings OVERRIDE each example's project.labelle pins." + echo " A failure here may be a mid-release-train disagreement between" + echo " the mains above rather than a defect in this PR. The" + echo " 'bgfx examples (PINNED resolution)' job tests the declared pins." + - uses: mlugg/setup-zig@v2 with: version: 0.16.0 @@ -888,9 +934,11 @@ jobs: # build is run for the raylib + sokol games (their system libs are # installed above, and sprite-runner is the regression guard for the #611 # sokol-desktop material-seam fix). The two bgfx games (pack-city, - # material-demo) are generate-only here — the bgfx desktop C++ compile is - # heavy and their full build is covered locally; wiring it into CI is a - # follow-up. Screenshots are NOT part of CI: each game ships a committed + # material-demo) stay generate-only IN THIS LANE on purpose: here their + # pins are overridden by the sibling `main` checkouts, so a build would + # be a moving target. They are BUILT — not just generated — against their + # DECLARED pins in the `bgfx examples (PINNED resolution)` job below + # (#732). Screenshots are NOT part of CI: each game ships a committed # deterministic preview.png and documents manual `--screenshot` capture # (windowed backends need a GUI session / xvfb). See each game's README. - name: Generate + build the #611 showcase games (released runtime pins) @@ -1318,3 +1366,319 @@ jobs: run: | .\zig-out\bin\labelle-assembler.exe --protocol-version .\zig-out\bin\labelle-assembler.exe --help + + # ── bgfx examples, resolved from the PINS they declare (#732) ──────── + # + # Two defects, one job. + # + # 1. CI GENERATED the bgfx examples and never BUILT them, so a break that + # only appears at compile time — which is most of them, and all of the + # ones that live in a DEPENDENCY — passed. labelle-core v1.32.0 shipped + # `MaterialEffect.pixel_water`; `material-demo` and `pack-city` both + # pinned a bgfx whose exhaustive switches had no arm for it. Both were + # broken and CI said nothing, because `generate` succeeds — the error is + # in backend source that only a real build compiles. The signal read as + # "one backend (sokol) is broken" rather than "three examples are broken, + # two of them untested" (#731). + # + # 2. The examples lane clones core/engine/gfx/null at `main` as SIBLINGS, + # and the assembler's auto-discovery builds those INSTEAD of the pins. + # Nothing tested a PR against what it DECLARES. During a release train + # the mains disagree for the minutes between merges and CI fabricates + # failures: #730 went red on `backend_contract has no member named + # 'PixelWaterRgba'` and a re-run of the identical commit passed. + # + # This job fixes both: it checks out NOTHING but the assembler (+ the + # labelle-gui path-dep that `build.zig.zon` needs), points LABELLE_HOME at a + # scratch dir so nothing is inherited from a warm cache, and BUILDS both + # bgfx examples against the exact `project.labelle` pins. It then ASSERTS the + # pins are what got used, rather than trusting a green build: the assembler + # warns `is built from LOCAL sources, not the pinned ` whenever + # auto-discovery wins, and this job fails on that string. + # + # WHY EVERY PR, AND WHY LINUX ONLY: + # * Linux only — one platform is enough to catch a compile break in + # backend source, and it is the cheapest runner. macOS would re-prove + # the same sema against Metal instead of Vulkan for ~2x the minutes. + # * Every PR, NOT gated on `examples/**` / `src/config.zig` / backend + # pins. That gate was the tempting option and it does not cover the + # defect that motivated the issue: the v1.32.0 break arrived from a + # DEPENDENCY's release. No file in this repo changed, so a path filter + # would have skipped the job on every PR while main stayed broken. A + # check that skips exactly when the break lands is the same class of + # defect as the one being fixed here. + # * Cost is bounded by sharing one zig cache between the two examples — + # the bgfx C++ objects compile once and pack-city links against them. + bgfx-examples-pinned: + name: bgfx examples (PINNED resolution) + runs-on: ubuntu-latest + # The first example pays a cold bgfx C++ compile; the second reuses it. + # 45 bounds a wedged runner far under GitHub's 6h default while leaving + # room for a cold package fetch on top. + timeout-minutes: 45 + + env: + # Scratch package home. NOT the default `~/.labelle` — a pinned lane + # must prove the pins resolve from the registry on a cold machine, and + # a warm shared cache would hide a pin that no longer exists. + LABELLE_HOME: ${{ github.workspace }}/pinned-labelle-home + + steps: + - name: Checkout labelle-assembler + uses: actions/checkout@v4 + with: + path: labelle-assembler + + # The ONLY sibling checkout in this job. `build.zig.zon` has a + # `../labelle-gui/flow-codegen` path-dep, so the assembler cannot build + # without it. It is not part of the runtime trio, so it cannot shadow a + # pin — deliberately, NO labelle-core / labelle-engine / labelle-gfx / + # labelle-null sibling lives here. That absence IS the test. + - name: Checkout labelle-gui (flow_codegen path-dep) + uses: actions/checkout@v4 + with: + repository: labelle-toolkit/labelle-gui + ref: main + path: labelle-gui + + - uses: mlugg/setup-zig@v2 + with: + version: 0.16.0 + + # bgfx desktop on Linux: X11/Wayland + Vulkan headers. Mirrors + # labelle-bgfx's own CI so the two lanes compile the same backend. + - name: Install bgfx desktop system libs + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libgl1-mesa-dev libx11-dev libxext-dev libxrandr-dev \ + libxinerama-dev libxcursor-dev libxi-dev \ + libwayland-dev libxkbcommon-dev \ + libvulkan-dev + + - name: Build labelle-assembler + run: cd labelle-assembler && zig build + + - name: Assert no runtime sibling can shadow the pins + run: | + set -euo pipefail + for repo in labelle-core labelle-engine labelle-gfx labelle-null; do + if [ -d "$GITHUB_WORKSPACE/$repo" ]; then + echo "::error::$repo is checked out as a sibling — the assembler's" + echo "::error::auto-discovery would build it INSTEAD of the declared pin," + echo "::error::which is exactly what this job exists to avoid." + exit 1 + fi + done + echo "No runtime siblings present; pins will resolve from the registry." + + - name: Generate + BUILD the bgfx examples against their declared pins + run: | + set -euo pipefail + ASM="$GITHUB_WORKSPACE/labelle-assembler/zig-out/bin/labelle-assembler" + mkdir -p "$LABELLE_HOME" + + for g in material-demo pack-city; do + echo "::group::$g — declared pins" + grep -E '_version|backend_package' \ + "$GITHUB_WORKSPACE/labelle-assembler/examples/$g/project.labelle" \ + | grep -v '^\s*//' || true + echo "::endgroup::" + + cd "$GITHUB_WORKSPACE/labelle-assembler/examples/$g" + "$ASM" install --project-root . 2>&1 | tee install.log + "$ASM" generate --project-root . 2>&1 | tee generate.log + + # MECHANISM assertion, not a value one. The assembler's monorepo + # auto-discovery symlinks a sibling checkout into the cache's local + # slot in preference to the pin, and SAYS SO when it does — two + # distinct strings, one per phase (both verified against a real + # sibling checkout while writing this job): + # install: core: using LOCAL sources from '' — the pinned + # 1.32.0 will NOT be built (#685) + # generate: labelle: core is built from LOCAL sources, not the + # pinned 1.32.0 — source '' (rev ) + # A green build only proves the example compiles against SOMETHING. + # This is what proves it compiled against what the example declares. + if grep -Eq 'built from LOCAL sources|using LOCAL sources' install.log generate.log; then + echo "::error::$g resolved a LOCAL checkout instead of its pins:" + grep -E 'built from LOCAL sources|using LOCAL sources' install.log generate.log + exit 1 + fi + + # And the pinned versions really were fetched into the scratch home. + for pkg in core engine gfx; do + want=$(sed -n "s/.*\.${pkg}_version = \"\([^\"]*\)\".*/\1/p" project.labelle | head -1) + if [ ! -d "$LABELLE_HOME/packages/${pkg}/${want}" ]; then + echo "::error::$g pins ${pkg} ${want} but it was never fetched into the scratch LABELLE_HOME" + exit 1 + fi + echo "$g: ${pkg} ${want} resolved from the registry" + done + + # THE POINT OF THIS JOB. `generate` alone would already be green + # here; the backend source is only compiled by a real build. + echo "== $g: zig build (bgfx desktop) ==" + cd .labelle/bgfx_desktop + # labelle-cli-equivalent fixFingerprint dance — the assembler emits + # a placeholder fingerprint (see the showcase step for context). + set +e + ERR=$(zig build 2>&1) + RC=$? + set -e + if [ $RC -ne 0 ]; then + BAD=$(printf '%s\n' "$ERR" | grep -oE 'invalid fingerprint: 0x[0-9a-f]+' | head -1 | awk '{print $3}') + GOOD=$(printf '%s\n' "$ERR" | grep -oE 'use this value: 0x[0-9a-f]+' | head -1 | awk '{print $4}') + if [ -n "$BAD" ] && [ -n "$GOOD" ]; then + sed -i "s/${BAD}/${GOOD}/" build.zig.zon + else + printf '%s\n' "$ERR" + echo "::error::$g failed to build against its declared pins" + exit 1 + fi + fi + zig build + echo "PASS: $g BUILT against its declared pins" + done + + echo "PASS: both bgfx examples built from the registry-resolved pins" + + # ── The shader BUILD pipeline, end to end, with a real shaderc (#735) ─ + # + # `test/material_pipeline_e2e.py` is the only test that covers the PRODUCER + # side of game-owned materials: descriptor -> shaderc -> packaged variants -> + # a material that compiles and validates against core's contract. Every other + # material check in CI covers CONSUMERS of already-compiled shaders (the + # goldens, the bgfx probe with checked-in `.bin` fixtures) — and pre-compiled + # fixtures are structurally incapable of noticing a pipeline regression. + # + # It was opt-in behind three `-D` flags because CI had no shaderc, so it + # never ran anywhere. This job provisions one and un-gates it. Nobody passes + # a flag by hand: the job DERIVES all three from the repo's own pins. + # + # The toolchain is REUSED, not duplicated: labelle-bgfx already builds + # shaderc from its pinned `zbgfx` for the golden harnesses, so this job reads + # the builtin bgfx provider version out of `src/config.zig`, reads that + # release's `zbgfx` archive URL out of its `build.zig.zon`, and builds + # `shaderc` from exactly that source. No second toolchain, and no separate + # pin to drift. + # + # Independent of `examples/condenser` by construction — the python driver + # synthesizes its own three-material fixture (shared includes, an escaping + # include, a deliberately broken shader) under `.zig-cache/`. + # + # FAILS CLOSED, not just open: the driver asserts five REJECTION paths as + # well as the happy one — an include that escapes the material dir + # (`EscapingShaderInclude`), an uncompilable `.sc`, a descriptor missing the + # variant the host renderer requires, and two component-collision cases in + # the emitted comptime guard. A pipeline that silently accepted any of those + # turns this job red. + material-pipeline-e2e: + name: Material shader pipeline e2e (real shaderc) + runs-on: ubuntu-latest + # Cold shaderc build (glslang + spirv-tools) dominates; ~2.5 min of it on + # a warm local machine, and it is cached below. 40 leaves room for a cold + # miss plus the fixture's 12 shader compiles. + timeout-minutes: 40 + + steps: + - name: Checkout labelle-assembler + uses: actions/checkout@v4 + with: + path: labelle-assembler + + - name: Checkout labelle-gui (flow_codegen path-dep) + uses: actions/checkout@v4 + with: + repository: labelle-toolkit/labelle-gui + ref: main + path: labelle-gui + + - uses: mlugg/setup-zig@v2 + with: + version: 0.16.0 + + # zbgfx's default build step touches its glfw/bgfx targets alongside the + # shaderc host tool, so the X11/GL headers have to be present even + # though shaderc itself is a pure host compiler. + - name: Install zbgfx build deps + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libgl1-mesa-dev libx11-dev libxext-dev libxrandr-dev \ + libxinerama-dev libxcursor-dev libxi-dev \ + libwayland-dev libxkbcommon-dev libvulkan-dev + + # Resolve the shaderc SOURCE from this repo's own pins, so the compiler + # under test is the one the builtin bgfx provider actually ships with. + # Chain: src/config.zig builtinProvider(.bgfx).version + # -> labelle-bgfx@v/build.zig.zon .zbgfx.url + # -> that archive, built with -Dwith_shaderc=true. + - name: Resolve the pinned zbgfx (shaderc source) + id: zbgfx + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE" + BGFX_VER=$(sed -n 's/.*\.bgfx => .*\.repo = "github\.com\/labelle-toolkit\/labelle-bgfx".*\.version = "\([^"]*\)".*/\1/p' \ + labelle-assembler/src/config.zig | head -1) + test -n "$BGFX_VER" || { echo "::error::could not read the builtin bgfx provider version from src/config.zig"; exit 1; } + echo "builtin bgfx provider: $BGFX_VER" + + git clone --quiet --depth 1 --branch "v$BGFX_VER" \ + https://github.com/labelle-toolkit/labelle-bgfx.git labelle-bgfx + ZBGFX_URL=$(sed -n 's#.*\.url = "\(https://[^"]*zbgfx/archive/[^"]*\)".*#\1#p' \ + labelle-bgfx/build.zig.zon | head -1) + test -n "$ZBGFX_URL" || { echo "::error::labelle-bgfx v$BGFX_VER declares no zbgfx archive url"; exit 1; } + echo "pinned zbgfx: $ZBGFX_URL" + + mkdir -p zbgfx + curl -sSfL "$ZBGFX_URL" -o zbgfx.tar.gz + tar xzf zbgfx.tar.gz -C zbgfx --strip-components=1 + test -f zbgfx/build.zig + + echo "url=$ZBGFX_URL" >> "$GITHUB_OUTPUT" + echo "key=$(printf '%s' "$ZBGFX_URL" | sha256sum | cut -c1-16)" >> "$GITHUB_OUTPUT" + + # The build is deterministic in the pinned source, so cache it on the + # zbgfx URL. A cold miss costs a few minutes; a hit costs seconds. + - name: Cache the built shaderc + uses: actions/cache@v4 + with: + path: | + zbgfx/zig-out + zbgfx/.zig-cache + key: shaderc-${{ runner.os }}-zig0.16.0-${{ steps.zbgfx.outputs.key }} + + - name: Build shaderc from the pinned zbgfx + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE/zbgfx" + zig build -Dwith_shaderc=true + test -x zig-out/bin/shaderc + ./zig-out/bin/shaderc --help >/dev/null 2>&1 || true + echo "shaderc ready: $PWD/zig-out/bin/shaderc" + + # The fixture compiles `core.shader_material` descriptors, so it needs a + # core checkout. Take the CURATED default out of build.zig — the same + # version a fresh `labelle init` scaffolds — so this lane tracks the set + # the assembler ships rather than a second, drifting pin. + - name: Check out the curated labelle-core + id: core + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE" + CORE_VER=$(sed -n 's/.*"core_version", "Default core library version") orelse "\([^"]*\)".*/\1/p' \ + labelle-assembler/build.zig | head -1) + test -n "$CORE_VER" || { echo "::error::could not read the curated core_version from build.zig"; exit 1; } + echo "curated core: $CORE_VER" + git clone --quiet --depth 1 --branch "v$CORE_VER" \ + https://github.com/labelle-toolkit/labelle-core.git labelle-core-e2e + + - name: Run the material pipeline e2e (descriptor -> shaderc -> material) + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE/labelle-assembler" + zig build test-material-integration \ + -Dshaderc="$GITHUB_WORKSPACE/zbgfx/zig-out/bin/shaderc" \ + -Dmaterial-test-core="$GITHUB_WORKSPACE/labelle-core-e2e" \ + -Dmaterial-test-zbgfx="$GITHUB_WORKSPACE/zbgfx" diff --git a/test/material_pipeline_e2e.py b/test/material_pipeline_e2e.py index 561be6e2..c22a9585 100644 --- a/test/material_pipeline_e2e.py +++ b/test/material_pipeline_e2e.py @@ -5,6 +5,7 @@ from pathlib import Path import shutil import subprocess +import sys import tempfile import zlib @@ -84,8 +85,13 @@ def build(ok=True, diagnostic=None): (root / "materials/fog/fs.sc").write_text(shader + "\ninvalid shader syntax!\n", encoding="utf-8") build(False, "shader fog") (root / "materials/fog/fs.sc").write_text(shader, encoding="utf-8") -descriptor["targets"] = ["glsl"] +# Fails closed when the descriptor drops the variant the HOST needs. +# `material_build.create` picks that per host (darwin -> mtl, else spv), so the +# expectation follows the host rather than hard-coding a Linux-only answer — +# this assertion used to make the whole test unrunnable on macOS. +required_target = "mtl" if sys.platform == "darwin" else "spv" +descriptor["targets"] = [t for t in ("glsl", "essl") if t != required_target] (root / "materials/fog/material.json").write_text(json.dumps(descriptor), encoding="utf-8") -build(False, "target requires 'spv'") +build(False, "target requires '%s'" % required_target) print("PASS: 12 real shader variants, generated core descriptors, cached rebuild, include invalidation, escaping include rejection, compiler failure, missing target diagnostic") print("Evidence fixture:", root)