Skip to content

Commit 9548e80

Browse files
TMHSDigitalclaude
andauthored
fix(examples): give gn-bundle-roundtrip a falsifier that can fail (#408)
The catalog falsifier expected exit 2 for --legacy-rna, but both outcomes of that branch returned 2 (5.x rejects the legacy node, and the legacy node unexpectedly exists), 2 also meant "carrier mesh rewritten", and argparse exits 2 for an unknown flag. The row passed whether or not the contract held, and would have passed with the flag deleted. Each outcome now has its own code: 6 carrier rewritten, 7 legacy node rejected (the catalog expectation), 8 legacy node accepted. The catalog lint now rejects expect_exit 2 for every row, since a usage error can always satisfy it. Closes #349 Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 350b75d commit 9548e80

5 files changed

Lines changed: 21 additions & 13 deletions

File tree

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ The host runner is `tests/smoke/run_example.py`. Shipped examples are listed in
177177
`$BDT_SMOKE_SIDECAR` (set by the runner). The harness asserts after Blender
178178
exits. Not a gallery still.
179179
- **Falsifiers in CI:** every catalog row carries `falsifiers`, at least one
180-
`{"args": [...], "expect_exit": N}` (N not 0, 1 or 77) or, for a contract
180+
`{"args": [...], "expect_exit": N}` (N not 0, 1, 2 or 77; 2 is argparse usage) or, for a contract
181181
the sidecar check owns, `{"args": [...], "expect_sidecar_fail": true}`.
182182
A falsifier that targets a version-specific API change takes
183183
`"min_version": "5.2"` and is recorded as SKIP below it (its README must

‎examples/gn-bundle-roundtrip/README.md‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ not read at thumbnail.
5353
**What failure each check would catch:**
5454

5555
- exit 77 — Blender &lt; 5.0 and not `--force-run`
56-
- exit 2 — `GeometryNodeCombineBundle` on 5.x (`--legacy-rna`)
56+
- exit 7 — `GeometryNodeCombineBundle` on 5.x (`--legacy-rna`); exit 8 if
57+
that legacy identifier is ever accepted again
5758
- exit 3 — item names do not pair, so Separate yields no geometry
5859
(`--mismatch` renames the geometry item → 0/0)
5960
- exit 4 — count matches but the unpacked Scale/Offset did not arrive
@@ -78,10 +79,13 @@ against it. `77` is the smoke skip protocol, not a product check.
7879
| --- | --- |
7980
| 0 | Success |
8081
| 1 | Uncaught exception (FATAL wrapper); `--force-run` on 4.5 lands here |
81-
| 2 | argparse / usage; carrier mesh rewritten; `--legacy-rna` on 5.x |
82+
| 2 | argparse / usage |
8283
| 3 | Evaluated vert/face count off closed form (`--mismatch`) |
8384
| 4 | Unpacked Scale/Offset x-extent off (`--bypass`, `--pack-scale 1`) |
8485
| 5 | `bundle_mark` missing or off closed form (`--mismatch-mark`) |
86+
| 6 | Carrier mesh rewritten |
87+
| 7 | `--legacy-rna`: 5.x RNA rejects `GeometryNodeCombineBundle` (the catalog falsifier) |
88+
| 8 | `--legacy-rna`: `GeometryNodeCombineBundle` was accepted (the rejection contract broke) |
8589
| 77 | `SMOKE_SKIP:` Bundles require Blender 5.0+ |
8690

8791
Measured on 4.5.11 / 5.0.1 / 5.1.2 / 5.2.1:
@@ -91,12 +95,12 @@ Measured on 4.5.11 / 5.0.1 / 5.1.2 / 5.2.1:
9195
| (none) | 77 | 0 | 0 | 0 |
9296
| `--force-run` | 1 | 0 | 0 | 0 |
9397
| `--bypass` | 77 | 4 | 4 | 4 |
94-
| `--legacy-rna` | 77 | 2 | 2 | 2 |
98+
| `--legacy-rna` | 77 | 7 | 7 | 7 |
9599
| `--mismatch` | 77 | 3 | 3 | 3 |
96100
| `--mismatch-mark` | 77 | 5 | 5 | 5 |
97101
| `--pack-scale 1` | 77 | 4 | 4 | 4 |
98102

99103
The `blender-smoke` workflow runs the check on Blender 5.2 LTS (5.1 on the
100104
weekly cron, the `needs-5.1` PR label, or manual dispatch) and skips on 4.5
101-
LTS. Smoke does not pass `--bypass`, `--legacy-rna`, `--mismatch`,
102-
`--mismatch-mark`, or `--force-run`.
105+
LTS. Its catalog falsifier is `--legacy-rna` (expects exit 7). Smoke does
106+
not pass `--bypass`, `--mismatch`, `--mismatch-mark`, or `--force-run`.

‎examples/gn-bundle-roundtrip/gn_bundle_roundtrip.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def build(pair, match_names, pack_scale, match_mark=True):
133133
def check(ob):
134134
if len(ob.data.vertices) != 1:
135135
print("ERROR: carrier mesh was rewritten", file=sys.stderr)
136-
return 2
136+
return 6
137137

138138
coords, faces, marks = eval_mesh(ob)
139139
xs = [c[0] for c in coords]
@@ -225,12 +225,14 @@ def main():
225225
tree.nodes.new("GeometryNodeCombineBundle")
226226
except RuntimeError as exc:
227227
print(f"ERROR: {type(exc).__name__}: {exc}", file=sys.stderr)
228-
return 2
228+
return 7
229+
# A distinct code so the catalog falsifier (expect 7) fails if the
230+
# legacy identifier ever comes back, instead of passing on any 2.
229231
print(
230232
"ERROR: GeometryNodeCombineBundle existed; 5.x RNA should reject it",
231233
file=sys.stderr,
232234
)
233-
return 2
235+
return 8
234236

235237
ob = build(
236238
pair=not args.bypass,

‎tests/check_smoke_catalog.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Schema: only the known row keys are accepted (a typo such as "falsifer" would
1010
otherwise be ignored and the falsifier silently never run), and every row
1111
carries at least one falsifier: {"args": [...], "expect_exit": N} with N not
12-
0, 1 or 77, or {"args": [...], "expect_sidecar_fail": true} on a row that
12+
0, 1, 2 (argparse usage) or 77, or {"args": [...], "expect_sidecar_fail": true} on a row that
1313
declares expect_sidecar.
1414
1515
python tests/check_smoke_catalog.py [--root PATH]
@@ -31,7 +31,9 @@
3131
ROW_KEYS = {"name", "script", "args", "min_version", "expect_file",
3232
"expect_sidecar", "sidecar_contains", "falsifiers"}
3333
FALSIFIER_KEYS = {"args", "expect_exit", "expect_sidecar_fail", "min_version"}
34-
FORBIDDEN_EXITS = {0, 1, 77}
34+
# 2 is argparse's usage-error exit: an unknown or deleted flag also exits 2,
35+
# so a falsifier expecting 2 passes even when its flag no longer exists.
36+
FORBIDDEN_EXITS = {0, 1, 2, 77}
3537

3638

3739
def check_schema(row: dict) -> list[str]:
@@ -60,7 +62,7 @@ def check_schema(row: dict) -> list[str]:
6062
elif has_exit:
6163
code = f["expect_exit"]
6264
if not isinstance(code, int) or isinstance(code, bool) or code in FORBIDDEN_EXITS:
63-
errors.append(f"{where}: expect_exit {code!r} must be an int other than 0, 1, 77")
65+
errors.append(f"{where}: expect_exit {code!r} must be an int other than 0, 1, 2, 77")
6466
elif not row.get("expect_sidecar"):
6567
errors.append(f"{where}: expect_sidecar_fail on a row with no expect_sidecar")
6668
mv = f.get("min_version")

‎tests/smoke/catalog.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"name": "gn-bundle-roundtrip",
6666
"script": "examples/gn-bundle-roundtrip/gn_bundle_roundtrip.py",
6767
"min_version": "5.0",
68-
"falsifiers": [{"args": ["--legacy-rna"], "expect_exit": 2}]
68+
"falsifiers": [{"args": ["--legacy-rna"], "expect_exit": 7}]
6969
},
7070
{
7171
"name": "exit-pre-sidecar",

0 commit comments

Comments
 (0)