- name
- gum-codegen-test-rigor
- description
- What actually proves a Gum-codegen fix works, vs. what only looks like it does. Triggers: "tests pass but it's still broken", CS1061/CS0200/NRE surviving a green suite, verifying StandardsCodeGenerator/StateCodeGenerator/GueDerivingClassCodeGenerator changes.
# Gum Codegen: What Actually Proves a Fix Works
Glue's Gum codegen (`gum-codegen` skill) emits C# source text with no compile-time link to the real
runtime types it targets. That's why weak verification repeatedly looks green while real bugs ship — this
skill is the record of that pattern recurring, so the next fix doesn't restart at the bottom of this list.
## Verification strength ladder — each level misses a different bug class
From weakest to strongest, all four were tried in sequence on the same fix before it actually worked:
1. **String-matching generated source** (`generatedSource.ShouldContain/ShouldNotContain(...)`). Proves
text presence only. Misses everything about correctness.
2. **Reflection-based member-contract checks** (`GumRuntimeMemberContractTests`). Proves a *named member*
exists on the *contained* type, but only for patterns the check explicitly matches (e.g. a
`ContainedXxx.Member` regex). Missed a base-class (`GraphicalUiElement.RenderableComponent`) assignment
entirely — wrong access shape, so the pattern never matched it, so it never looked.
3. **Real compile against real assemblies** (BuildSmoke `*CreationSmokeTests` /
`GumGeneratedCodeCompilesTests`). Proves the generated code *compiles*. Says nothing about runtime
behavior — code that compiles clean can still NRE the instant its constructor runs.
4. **Real instantiate-and-execute.** Actually construct the generated runtime the way production code
does, and run it. This is the only level that catches a construction-time `NullReferenceException`.
Nothing weaker than this can.
If a fix "passed" at one level and still broke in the field, the next debugging step is always "move up
this ladder," not "add another test at the same level."
## The sharper landmine: matching the real call path, not just reaching a level
Being at the top of the ladder isn't sufficient either — a test can compile-and-run and *still* miss the
bug if it doesn't reproduce the exact parameters/state production code actually uses:
- A BuildSmoke sweep silently defaulted `GumProjectSave.Version`, so it always exercised the v2 branch and
never touched the v3 code path it was supposedly covering.
- A fix worked when constructed with `fullInstantiation: true`, but every real screen/component instance
is constructed with `fullInstantiation: false` (`GumRuntime.ElementSaveExtensions.CreateGueForElement`'s
default) — a test using the wrong flag stayed green while the real path NREs.
**Before trusting any test at level 3 or 4, confirm it uses the same version flags, instantiation flags,
and call path a real generated game project actually hits — not just *some* valid input.** This is the
single most expensive-to-rediscover fact in this list; everything else follows from it.
## Two separate axes — don't conflate them when a run "looks stuck"
A test run that looks broken can be broken for two unrelated reasons: the test logic doesn't cover the
real bug (this skill), or the *environment* is jammed (orphaned `dotnet`/`testhost`/`MSBuild`/
`VBCSCompiler` processes piling up across a long session — see `glue-unit-test-bootstrap`'s standing
cleanup rule). Diagnose which one you're looking at before changing test code.
## Related
- [[gum-codegen]] — the two-pipeline skip-list mechanics this level of testing verifies.
- [[glue-unit-test-bootstrap]] — process hygiene (bootstrap init, BuildSmoke filtering, orphaned-process
cleanup) as opposed to test *coverage*.
GitHubで見る