test: separate diagnostic apparatus from release gates
This commit is contained in:
parent
8f490240d4
commit
c1a905004a
33 changed files with 527 additions and 24 deletions
|
|
@ -10738,7 +10738,7 @@ user descended the spiral (`retail-spiral2.log`, 767 samples). **Retail's flood
|
||||||
and IDENTICAL in character to ours** — from the spiral cells it swings `num` 3→27 with gaze
|
and IDENTICAL in character to ours** — from the spiral cells it swings `num` 3→27 with gaze
|
||||||
and COLLAPSES to 3 cells at many poses (e.g. cam=015d eye(60.05,-28.51,-3.66) → just
|
and COLLAPSES to 3 cells at many poses (e.g. cam=015d eye(60.05,-28.51,-3.66) → just
|
||||||
{015d 015e 015f}; cam=014b → {014b 014c 014a}). Our flood does the same (3→43, headless
|
{015d 015e 015f}; cam=014b → {014b 014c 014a}). Our flood does the same (3→43, headless
|
||||||
`FloodDepthFrom015E_VsRetail26` = max 43 vs retail 26). So retail does NOT keep the spiral
|
`Diagnostic_FloodDepthFrom015E_VsRetail26` = max 43 vs retail 26). So retail does NOT keep the spiral
|
||||||
where we drop it — the flood is EXONERATED as the cause. **The vanish must be DOWNSTREAM of
|
where we drop it — the flood is EXONERATED as the cause. **The vanish must be DOWNSTREAM of
|
||||||
the flood** (unexplored): the steps in these spiral cells are STATIC objects (GfxObj
|
the flood** (unexplored): the steps in these spiral cells are STATIC objects (GfxObj
|
||||||
`0x010000DE` ×6/cell), drawn via the separate viewcone cull (`ViewconeCuller.SphereVisibleInCell`
|
`0x010000DE` ×6/cell), drawn via the separate viewcone cull (`ViewconeCuller.SphereVisibleInCell`
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ inline when ported.
|
||||||
### BR-1 — The surface gate — ✅ RESOLVED AS ALREADY-EQUIVALENT (2026-06-11, execution day 1)
|
### BR-1 — The surface gate — ✅ RESOLVED AS ALREADY-EQUIVALENT (2026-06-11, execution day 1)
|
||||||
|
|
||||||
**Premise falsified before implementation (the BR-1 pre-check,
|
**Premise falsified before implementation (the BR-1 pre-check,
|
||||||
`ReplicateProductionEmission_OnPortalFills`):** acdream **already suppresses
|
`Diagnostic_ReplicateProductionEmission_OnPortalFills`):** acdream **already suppresses
|
||||||
every portal fill** — all four extraction paths skip `Stippling.NoPos`
|
every portal fill** — all four extraction paths skip `Stippling.NoPos`
|
||||||
positive sides (`ObjectMeshManager.PrepareGfxObjMeshData:1046`,
|
positive sides (`ObjectMeshManager.PrepareGfxObjMeshData:1046`,
|
||||||
`PrepareCellStructMeshData:1394`, `CellMesh.Build:44`, `GfxObjMesh.Build:71`),
|
`PrepareCellStructMeshData:1394`, `CellMesh.Build:44`, `GfxObjMesh.Build:71`),
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,16 @@ expected until the linked defect is fixed. Diagnostic apparatus
|
||||||
(`Purpose=Diagnostic`) likewise reports separately and does not inflate the
|
(`Purpose=Diagnostic`) likewise reports separately and does not inflate the
|
||||||
contract-test pass count.
|
contract-test pass count.
|
||||||
|
|
||||||
|
The current diagnostic apparatus lives in App and Core. It is retained for
|
||||||
|
investigation output, and several methods require installed DATs:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet test tests/AcDream.App.Tests/AcDream.App.Tests.csproj -c Release `
|
||||||
|
--filter 'Purpose=Diagnostic'
|
||||||
|
dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Release `
|
||||||
|
--filter 'Purpose=Diagnostic'
|
||||||
|
```
|
||||||
|
|
||||||
## Updating dependencies
|
## Updating dependencies
|
||||||
|
|
||||||
Do not edit lock files by hand. To make an intentional dependency change:
|
Do not edit lock files by hand. To make an intentional dependency change:
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ area files.
|
||||||
> (`ObjectMeshManager.PrepareGfxObjMeshData:1046`,
|
> (`ObjectMeshManager.PrepareGfxObjMeshData:1046`,
|
||||||
> `PrepareCellStructMeshData:1394`, `CellMesh.Build:44`,
|
> `PrepareCellStructMeshData:1394`, `CellMesh.Build:44`,
|
||||||
> `GfxObjMesh.Build:71`), and the fills have no negative surface
|
> `GfxObjMesh.Build:71`), and the fills have no negative surface
|
||||||
> (`ReplicateProductionEmission_OnPortalFills`: pos=False/neg=False for every
|
> (`Diagnostic_ReplicateProductionEmission_OnPortalFills`: pos=False/neg=False for every
|
||||||
> fill). The equivalence pin (`StipplingSurfaceEquivalenceTests`, 2,607
|
> fill). The equivalence pin (`StipplingSurfaceEquivalenceTests`, 2,607
|
||||||
> polys, 0 violations) proves our build-time skip ⇔ retail's draw-time
|
> polys, 0 violations) proves our build-time skip ⇔ retail's draw-time
|
||||||
> `skipNoTexture` on this content. Consequences: the ledger rows
|
> `skipNoTexture` on this content. Consequences: the ledger rows
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ draw the purple lightning over the floor"):
|
||||||
3. **Striped floor z-fight-like artifact.** User's 2nd screenshot: regular
|
3. **Striped floor z-fight-like artifact.** User's 2nd screenshot: regular
|
||||||
magenta bands across one floor region, "like something is fighting to draw
|
magenta bands across one floor region, "like something is fighting to draw
|
||||||
the purple over the floor." **NOT attributed.** Ruled out: not coincident dat
|
the purple over the floor." **NOT attributed.** Ruled out: not coincident dat
|
||||||
geometry (the `CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs` sweep
|
geometry (the `Diagnostic_CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs` sweep
|
||||||
found only the legit z=−12 under-hall floor quad-fan, nothing near the −6
|
found only the legit z=−12 under-hall floor quad-fan, nothing near the −6
|
||||||
corridor floor); not a striped texture (all corridor surfaces are plain
|
corridor floor); not a striped texture (all corridor surfaces are plain
|
||||||
`Base1Image` stone 0x08000375/6/7/8). Leading guess: two draws of the same
|
`Base1Image` stone 0x08000375/6/7/8). Leading guess: two draws of the same
|
||||||
|
|
@ -116,7 +116,7 @@ draw the purple lightning over the floor"):
|
||||||
stair geometry owner (`0x8A020182`'s ramp shell, vertical portals, ZERO
|
stair geometry owner (`0x8A020182`'s ramp shell, vertical portals, ZERO
|
||||||
statics), CellBSP containment (partitions exactly at portal planes),
|
statics), CellBSP containment (partitions exactly at portal planes),
|
||||||
under-hall + corridor drawn-poly surface colors, DXT1 alpha histograms (0
|
under-hall + corridor drawn-poly surface colors, DXT1 alpha histograms (0
|
||||||
transparent texels), and `CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs`
|
transparent texels), and `Diagnostic_CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs`
|
||||||
(the stripe-geometry sweep — came back empty for the −6 floor).
|
(the stripe-geometry sweep — came back empty for the −6 floor).
|
||||||
- **`tests/AcDream.App.Tests/Rendering/Issue176177FacilityHubFloodReplayTests.cs`**
|
- **`tests/AcDream.App.Tests/Rendering/Issue176177FacilityHubFloodReplayTests.cs`**
|
||||||
— production-matched portal-flood replays (approach/descent/gaze-sweep/walk +
|
— production-matched portal-flood replays (approach/descent/gaze-sweep/walk +
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ NOT throw more lighting fixes at it (two already did nothing).
|
||||||
| Lighting **selection** | `SelectForCell` (all dynamic lights per cell, retail-exact) → **no visual change** |
|
| Lighting **selection** | `SelectForCell` (all dynamic lights per cell, retail-exact) → **no visual change** |
|
||||||
| Light-set **camera-cap churn** (the OLD "confirmed" #176 theory) | visible-cell scoping shipped (`c500912b`); probe-proven ~285 through-floor lights dropped/frame — symptom unchanged. **REFUTED.** |
|
| Light-set **camera-cap churn** (the OLD "confirmed" #176 theory) | visible-cell scoping shipped (`c500912b`); probe-proven ~285 through-floor lights dropped/frame — symptom unchanged. **REFUTED.** |
|
||||||
| **Membership / the "flap"** | `ACDREAM_PROBE_FLAP`: render cell = `0x8A020164` on **100%** of 188,732 frames across **526 distinct camera angles**, `res=None` always; `ACDREAM_PROBE_PVINPUT` flood is stable-per-angle (17/10/8…), never oscillates at a fixed view; 1 `[cell-transit]` (the spawn teleport) total |
|
| **Membership / the "flap"** | `ACDREAM_PROBE_FLAP`: render cell = `0x8A020164` on **100%** of 188,732 frames across **526 distinct camera angles**, `res=None` always; `ACDREAM_PROBE_PVINPUT` flood is stable-per-angle (17/10/8…), never oscillates at a fixed view; 1 `[cell-transit]` (the spawn teleport) total |
|
||||||
| **Dat-geometry z-fight** | `Issue176177DungeonSeamInspectionTests.CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs` seeded on the ACTUAL cell `0x8A020164` + neighbors → **zero** coplanar drawn pairs at the z=−6 corridor floor (only the benign same-cell z=−12 under-hall floor tiling in `0x011E`) |
|
| **Dat-geometry z-fight** | `Issue176177DungeonSeamInspectionTests.Diagnostic_CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs` seeded on the ACTUAL cell `0x8A020164` + neighbors → **zero** coplanar drawn pairs at the z=−6 corridor floor (only the benign same-cell z=−12 under-hall floor tiling in `0x011E`) |
|
||||||
| A2C / alpha-hole see-through | corridor floor surface `0x08000377` is **fully opaque** (`alpha0Texels=0`, `transl=0.00`) |
|
| A2C / alpha-hole see-through | corridor floor surface `0x08000377` is **fully opaque** (`alpha0Texels=0`, `transl=0.00`) |
|
||||||
| Translucent under-surface blend | the one translucent colored surface `0x08000034` is `NoPos` (not drawn) |
|
| Translucent under-surface blend | the one translucent colored surface `0x08000034` is `NoPos` (not drawn) |
|
||||||
| Flat (per-face) normals | corridor floor uses **smooth per-vertex dat normals** (center `(0,0,1)`, corners tilted ~27° — retail-style edge smoothing), NOT flat (`CellVertexNormals_SmoothOrFaceted_Dump`) |
|
| Flat (per-face) normals | corridor floor uses **smooth per-vertex dat normals** (center `(0,0,1)`, corners tilted ~27° — retail-style edge smoothing), NOT flat (`CellVertexNormals_SmoothOrFaceted_Dump`) |
|
||||||
|
|
@ -129,7 +129,7 @@ RenderDoc (do NOT assume — capture and read):
|
||||||
- `ACDREAM_PROBE_INDOOR_LIGHT=1` — `[indoor-light]` scoped-pool SET composition.
|
- `ACDREAM_PROBE_INDOOR_LIGHT=1` — `[indoor-light]` scoped-pool SET composition.
|
||||||
- `tools/cdb/issue176-floor-light.cdb` — retail light-setup trace.
|
- `tools/cdb/issue176-floor-light.cdb` — retail light-setup trace.
|
||||||
- `Issue176177DungeonSeamInspectionTests` — dat truth (coplanar sweep, floor
|
- `Issue176177DungeonSeamInspectionTests` — dat truth (coplanar sweep, floor
|
||||||
surfaces, vertex normals); `CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs`
|
surfaces, vertex normals); `Diagnostic_CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs`
|
||||||
seed = `{0164,0165,016E,017A}`+neighbors.
|
seed = `{0164,0165,016E,017A}`+neighbors.
|
||||||
|
|
||||||
## Repro + launch protocol
|
## Repro + launch protocol
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,50 @@ The six-case reduction from the earlier 14,748-pass headline is understood:
|
||||||
seven launcher editor-focus theory rows were consolidated into one fact that
|
seven launcher editor-focus theory rows were consolidated into one fact that
|
||||||
still executes all seven variants in a single Avalonia application session.
|
still executes all seven variants in a single Avalonia application session.
|
||||||
|
|
||||||
|
## Fresh reproducible syntax inventory
|
||||||
|
|
||||||
|
`tools/audit-test-inventory.ps1` now regenerates the complete attributed-method
|
||||||
|
manifest from tracked C# test sources with Roslyn:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
pwsh ./tools/audit-test-inventory.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated JSON lives under ignored `artifacts/test-audit/`; it is not a
|
||||||
|
second 6.8 MB checked-in source of truth. The script and this reviewed ledger
|
||||||
|
are durable, while paths and line numbers regenerate after every batch. The
|
||||||
|
inventory at commit `8f490240` reports:
|
||||||
|
|
||||||
|
| Syntax signal | Current count |
|
||||||
|
|---|---:|
|
||||||
|
| Tracked/parsed C# test files | 1,256 / 1,256 |
|
||||||
|
| Attributed test methods (not expanded theory cases) | 11,455 |
|
||||||
|
| Methods containing at least one empty `return;` | 399 |
|
||||||
|
| Empty-return sites | 426 |
|
||||||
|
| Prerequisite-return candidates | 282 |
|
||||||
|
| Mechanical output-only candidates | 47 |
|
||||||
|
| Methods directly using `Thread.Sleep` / `Task.Delay` | 20 / 23 |
|
||||||
|
| Methods directly reading environment variables | 47 |
|
||||||
|
| Methods directly reading `.cs` source text | 63 |
|
||||||
|
|
||||||
|
The 282 prerequisite-return count is intentionally a review candidate set, not
|
||||||
|
a claim that all 399 methods are broken. It comprises 109 App, 125 Core, 29
|
||||||
|
Content, 12 Launcher.Core, three Core.Net, three Bake, and one Headless method.
|
||||||
|
The classifier includes external-asset, opt-in, and platform gates; behavioral
|
||||||
|
early exits inside callback tests remain outside that candidate set. Every
|
||||||
|
candidate still requires source inspection because `Directory.Exists`, for
|
||||||
|
example, can also be the behavior under test.
|
||||||
|
|
||||||
|
The output-only classifier follows same-file helper calls, but it cannot decide
|
||||||
|
whether an assertion merely establishes probe setup. The reviewed diagnostic
|
||||||
|
set is therefore 51 methods: the 47 mechanical candidates plus
|
||||||
|
`OptionsPanelLiveMountProbeTests.ProbeLiveMountShapes`,
|
||||||
|
`DoorSetupGfxObjInspectionTests.HoltburgCottage_CellPortals_DatInspection`, and
|
||||||
|
the two already classified tower diagnostics. Relative to T-011's historical
|
||||||
|
51-method table, delete the removed PVS scaffold and add
|
||||||
|
`TowerAscentReplayTests.Diagnostic_TowerAscent_PerStepTable`; all other entries
|
||||||
|
remain. Those 51 methods expand to 70 discovered cases (28 App, 42 Core).
|
||||||
|
|
||||||
## Exact 77-skip reconciliation
|
## Exact 77-skip reconciliation
|
||||||
|
|
||||||
The latest clean-gate TRX files account for every reported skip:
|
The latest clean-gate TRX files account for every reported skip:
|
||||||
|
|
@ -83,11 +127,11 @@ identifiable in the lane report.
|
||||||
| T-004 warning mismatches | resolved in R2 | Clean rebuild is zero-warning with repository-wide warnings-as-errors. |
|
| T-004 warning mismatches | resolved in R2 | Clean rebuild is zero-warning with repository-wide warnings-as-errors. |
|
||||||
| T-005 unreachable panel stack | requires production reachability proof | Do not delete 52 meaningful tests until the dead presentation surface and any supported plugin compatibility promise are verified together. |
|
| T-005 unreachable panel stack | requires production reachability proof | Do not delete 52 meaningful tests until the dead presentation surface and any supported plugin compatibility promise are verified together. |
|
||||||
| T-006 misleading installed-DAT reason | resolved in batch B | Shared opt-in is now `ACDREAM_RUN_INSTALLED_DAT_TESTS=1` (legacy switch retained), the reason names the lane, and all nine owners carry `Lane=InstalledDat`. |
|
| T-006 misleading installed-DAT reason | resolved in batch B | Shared opt-in is now `ACDREAM_RUN_INSTALLED_DAT_TESTS=1` (legacy switch retained), the reason names the lane, and all nine owners carry `Lane=InstalledDat`. |
|
||||||
| T-007 271 silent passing gates | open | Re-run the syntax inventory against current source, then convert each to a hermetic fixture, explicit prerequisite skip, or non-default lane. |
|
| T-007 271 silent passing gates | active; current candidate set generated | Current syntax has 282 prerequisite-return candidates across seven projects. Inspect/classify from the generated manifest; the count changed with source growth and includes platform gates, so do not mechanically rewrite all 282. |
|
||||||
| T-008 incapable-of-failing diagnostics | batch A plus diagnostic lane work | Delete the literal wiring smoke test; repair the GPU contract tautology; later re-home output-only apparatus. |
|
| T-008 incapable-of-failing diagnostics | batch A plus diagnostic lane work | Delete the literal wiring smoke test; repair the GPU contract tautology; later re-home output-only apparatus. |
|
||||||
| T-009 wall-clock double-click tests | open | Introduce a behavior-preserving injectable monotonic clock and deterministic fake time. |
|
| T-009 wall-clock double-click tests | open | Introduce a behavior-preserving injectable monotonic clock and deterministic fake time. |
|
||||||
| T-010 two useless cases | high-confidence cleanup batch A | Delete `SmokeTest.TestProject_IsWired` and `ChaseCameraTests.ImplementsICamera`; compilation already proves both claims. |
|
| T-010 two useless cases | high-confidence cleanup batch A | Delete `SmokeTest.TestProject_IsWired` and `ChaseCameraTests.ImplementsICamera`; compilation already proves both claims. |
|
||||||
| T-011 51 output-only methods | open | Validate the historical 51-method list against current source; add stable oracles or move them outside default discovery. |
|
| T-011 51 output-only methods | classified in batch C | The reviewed current set is 51 methods / 70 cases. All carry `Purpose=Diagnostic`, preserving the apparatus while removing it from release pass totals. Contract-shaped names remain explicitly flagged until a stable oracle exists. |
|
||||||
| T-012 source-text freezes | requires semantic replacement map | Retain whole-tree dependency rules; remove exact-text freezes only when an equivalent semantic/behavioral guard is identified. |
|
| T-012 source-text freezes | requires semantic replacement map | Retain whole-tree dependency rules; remove exact-text freezes only when an equivalent semantic/behavioral guard is identified. |
|
||||||
| T-013 controller self-comparison | high-confidence cleanup batch A | Capture the first controller next to the first body and compare every retry with that reference. |
|
| T-013 controller self-comparison | high-confidence cleanup batch A | Capture the first controller next to the first body and compare every retry with that reference. |
|
||||||
| T-014 seven load-sensitive tests | active; first mechanism reproduced | `PortalProjectionTests.ClipToRegion_FrameOwnedStore_ReusesExactResultArray` reported 6,904 B only while three App test hosts ran concurrently, then passed in an isolated run. Preserve both observations and remove ambient process-allocation sensitivity rather than loosening the zero-allocation oracle. |
|
| T-014 seven load-sensitive tests | active; first mechanism reproduced | `PortalProjectionTests.ClipToRegion_FrameOwnedStore_ReusesExactResultArray` reported 6,904 B only while three App test hosts ran concurrently, then passed in an isolated run. Preserve both observations and remove ambient process-allocation sensitivity rather than loosening the zero-allocation oracle. |
|
||||||
|
|
@ -179,3 +223,31 @@ at 6,904 bytes while the Installed-DAT and discovery hosts ran beside it. The
|
||||||
same unmodified hermetic command then passed 5,505/5,505 in isolation. This is
|
same unmodified hermetic command then passed 5,505/5,505 in isolation. This is
|
||||||
evidence for removing cross-host allocation sensitivity; it is not grounds to
|
evidence for removing cross-host allocation sensitivity; it is not grounds to
|
||||||
weaken the assertion or claim a clean concurrent result.
|
weaken the assertion or claim a clean concurrent result.
|
||||||
|
|
||||||
|
## Batch C diagnostic classification
|
||||||
|
|
||||||
|
Batch C assigns the reviewed 51-method diagnostic set to
|
||||||
|
`Purpose=Diagnostic`. No body, output, historical comment, or fixture is
|
||||||
|
removed. This resolves the misleading release accounting without pretending
|
||||||
|
that every investigation has a stable regression oracle.
|
||||||
|
|
||||||
|
The 15 contract-shaped methods called out by T-011 now have a `Diagnostic_`
|
||||||
|
prefix: four facility-hub scenarios, six stair/camera sweeps, two ambient-slot
|
||||||
|
inventories, production-emission replication, and two drawn-polygon reports.
|
||||||
|
Their current output and historical method references are preserved, while the
|
||||||
|
new names no longer imply an asserted oracle. Promoting them back into the
|
||||||
|
hermetic lane requires an asserted expected result; R3 will not invent those
|
||||||
|
values.
|
||||||
|
|
||||||
|
Batch C verification:
|
||||||
|
|
||||||
|
- App Release build: 0 warnings, 0 errors;
|
||||||
|
- Core Release build, serialized after App: 0 warnings, 0 errors;
|
||||||
|
- hermetic App: 5,479 passed / 0 skipped / 0 failed;
|
||||||
|
- hermetic Core: 4,753 passed / 0 skipped / 0 failed; and
|
||||||
|
- diagnostic discovery: 28 App + 42 Core = 70 cases from 51 methods.
|
||||||
|
|
||||||
|
An attempted parallel App/Core build collided on the shared
|
||||||
|
`AcDream.Plugin.Abstractions` intermediate DLL. The repository gate serializes
|
||||||
|
projects, so the authoritative verification is the two isolated clean builds
|
||||||
|
above; no code or assertion was changed in response to that tooling collision.
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,7 @@ public class CornerFloodReplayTests
|
||||||
/// <summary>Scratch: the homogeneous reciprocal primitive in isolation, on the
|
/// <summary>Scratch: the homogeneous reciprocal primitive in isolation, on the
|
||||||
/// synthetic fixture geometry that Build_AppliesReciprocalOtherPortalClip uses.</summary>
|
/// synthetic fixture geometry that Build_AppliesReciprocalOtherPortalClip uses.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Scratch_ReciprocalPrimitive_SyntheticPair()
|
public void Scratch_ReciprocalPrimitive_SyntheticPair()
|
||||||
{
|
{
|
||||||
var view = Matrix4x4.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), Vector3.UnitY);
|
var view = Matrix4x4.CreateLookAt(Vector3.Zero, new Vector3(0, 0, -1), Vector3.UnitY);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace AcDream.App.Tests.Rendering;
|
||||||
/// candidate setup's parts + bounds from the dat. The portal's setup is the
|
/// candidate setup's parts + bounds from the dat. The portal's setup is the
|
||||||
/// translucent swirl; lamp posts / creatures / signs identify by part shape.
|
/// translucent swirl; lamp posts / creatures / signs identify by part shape.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public class Issue131SetupProbeTests
|
public class Issue131SetupProbeTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,8 @@ public class Issue176177FacilityHubFloodReplayTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ScenarioB_StairDescent_RampCellRetention()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_ScenarioB_StairDescent_RampCellRetention()
|
||||||
{
|
{
|
||||||
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -140,7 +141,8 @@ public class Issue176177FacilityHubFloodReplayTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ScenarioC_CorridorSeamGazeSweep_Bistability()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_ScenarioC_CorridorSeamGazeSweep_Bistability()
|
||||||
{
|
{
|
||||||
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -212,9 +214,10 @@ public class Issue176177FacilityHubFloodReplayTests
|
||||||
/// #177(a)/(c) at the stair transit.
|
/// #177(a)/(c) at the stair transit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Theory]
|
[Theory]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
[InlineData(0x016Eu, 0x017Au, 85.00f, -4.3f)] // corridor seam, plane x=85
|
[InlineData(0x016Eu, 0x017Au, 85.00f, -4.3f)] // corridor seam, plane x=85
|
||||||
[InlineData(0x0182u, 0x0183u, 98.333f, -7.3f)] // stair-bottom transit, plane x=98.33
|
[InlineData(0x0182u, 0x0183u, 98.333f, -7.3f)] // stair-bottom transit, plane x=98.33
|
||||||
public void ScenarioE_RootLagWindow_ForwardChainRetention(
|
public void Diagnostic_ScenarioE_RootLagWindow_ForwardChainRetention(
|
||||||
uint rootLow, uint forwardLow, float planeX, float eyeZ)
|
uint rootLow, uint forwardLow, float planeX, float eyeZ)
|
||||||
{
|
{
|
||||||
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
||||||
|
|
@ -239,7 +242,8 @@ public class Issue176177FacilityHubFloodReplayTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ScenarioD_CorridorWalk_PerStepChurn()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_ScenarioD_CorridorWalk_PerStepChurn()
|
||||||
{
|
{
|
||||||
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
=> string.Join(" ", ids.Select(id => $"{id & 0xFFFFu:X4}"));
|
=> string.Join(" ", ids.Select(id => $"{id & 0xFFFFu:X4}"));
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StairCellComposition_ShellVsStatics()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_StairCellComposition_ShellVsStatics()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -166,7 +167,8 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
/// locate the knife-edge and which cell flips.
|
/// locate the knife-edge and which cell flips.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RealStaircase_FineYawZoomSweep_FindKnifeEdge()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_RealStaircase_FineYawZoomSweep_FindKnifeEdge()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -228,7 +230,8 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
/// caps well below retail's 26, our flood truncates the spiral (the vanish).
|
/// caps well below retail's 26, our flood truncates the spiral (the vanish).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FloodDepthFrom015E_VsRetail26()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_FloodDepthFrom015E_VsRetail26()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -349,7 +352,8 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
/// contact leaving the eye on the surface vs free-space boom geometry.
|
/// contact leaving the eye on the surface vs free-space boom geometry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StaircaseSweep_EyeClearanceFromCeilingPortal()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_StaircaseSweep_EyeClearanceFromCeilingPortal()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -392,10 +396,11 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
// spot label, player x, player cell, description
|
// spot label, player x, player cell, description
|
||||||
[InlineData(90.0f, 0x0178u, "TOP corridor 0178 (approach view)")]
|
[InlineData(90.0f, 0x0178u, "TOP corridor 0178 (approach view)")]
|
||||||
[InlineData(100.0f, 0x0183u, "BOTTOM room 0183 (look-back view)")]
|
[InlineData(100.0f, 0x0183u, "BOTTOM room 0183 (look-back view)")]
|
||||||
public void ParkedYawZoomSweep_StairAdmission(float px, uint pcell, string label)
|
public void Diagnostic_ParkedYawZoomSweep_StairAdmission(float px, uint pcell, string label)
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -451,7 +456,8 @@ public class Issue177StairDescentCameraFloodTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Descent_RealCameraSweep_StairCellRetention()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_Descent_RealCameraSweep_StairCellRetention()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ public class Issue181VisFlapReplayTests
|
||||||
=> string.Join(" ", ids.Select(id => $"{id & 0xFFFFu:X4}"));
|
=> string.Join(" ", ids.Select(id => $"{id & 0xFFFFu:X4}"));
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Diagnostic_FlappingCellViewRegion_SliverOrLarge()
|
public void Diagnostic_FlappingCellViewRegion_SliverOrLarge()
|
||||||
{
|
{
|
||||||
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
var datDir = CornerFloodReplayTests.ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ namespace AcDream.App.Tests.Rendering;
|
||||||
/// numbers into hard pins.
|
/// numbers into hard pins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Collection(CameraDiagnosticsCollection.Name)]
|
[Collection(CameraDiagnosticsCollection.Name)]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public class Issue181WallPressEquilibriumTests
|
public class Issue181WallPressEquilibriumTests
|
||||||
{
|
{
|
||||||
private const uint FacilityHubLandblock = 0x8A020000u;
|
private const uint FacilityHubLandblock = 0x8A020000u;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace AcDream.App.Tests.UI.Layout;
|
||||||
/// identify the two FA slots definitively: 0x1000026F (fellowship name field)
|
/// identify the two FA slots definitively: 0x1000026F (fellowship name field)
|
||||||
/// and 0x10000263 (swear-allegiance button). Env-gated like the other probes.
|
/// and 0x10000263 (swear-allegiance button). Env-gated like the other probes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public sealed class FaPanelSlotProbeTests
|
public sealed class FaPanelSlotProbeTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ namespace AcDream.App.Tests.UI.Layout;
|
||||||
/// throwaway.
|
/// throwaway.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public sealed class MapHousePanelSlotProbeTests
|
public sealed class MapHousePanelSlotProbeTests
|
||||||
{
|
{
|
||||||
private const uint HostLayoutId = 0x2100006Eu;
|
private const uint HostLayoutId = 0x2100006Eu;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ namespace AcDream.App.Tests.UI.Layout;
|
||||||
public sealed class OptionsPanelLiveMountProbeTests
|
public sealed class OptionsPanelLiveMountProbeTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbeLiveMountShapes()
|
public void ProbeLiveMountShapes()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
||||||
|
|
@ -314,6 +315,7 @@ public sealed class OptionsPanelLiveMountProbeTests
|
||||||
/// FIXED 6-row + scrollbar popup in retail, user-gated during the vendor
|
/// FIXED 6-row + scrollbar popup in retail, user-gated during the vendor
|
||||||
/// campaign) as the contrast control.</summary>
|
/// campaign) as the contrast control.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbeMenuPopupSizingAndTextStyle()
|
public void ProbeMenuPopupSizingAndTextStyle()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
||||||
|
|
@ -479,6 +481,7 @@ public sealed class OptionsPanelLiveMountProbeTests
|
||||||
/// to resolve in table 0x23000003 — sweep the plausible tables and key
|
/// to resolve in table 0x23000003 — sweep the plausible tables and key
|
||||||
/// spellings against the live DAT to find their real home.</summary>
|
/// spellings against the live DAT to find their real home.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbeFilterLabelHome()
|
public void ProbeFilterLabelHome()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ namespace AcDream.App.Tests.UI.Layout;
|
||||||
public sealed class PowerbarLayoutProbeTests
|
public sealed class PowerbarLayoutProbeTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbePowerbarAuthoredStrings()
|
public void ProbePowerbarAuthoredStrings()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
||||||
|
|
@ -75,6 +76,7 @@ public sealed class PowerbarLayoutProbeTests
|
||||||
/// gmSecureTradeUI's element tree (lane A gave element ids 0x10000085-8B
|
/// gmSecureTradeUI's element tree (lane A gave element ids 0x10000085-8B
|
||||||
/// but not the layout id) and dump it.</summary>
|
/// but not the layout id) and dump it.</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbeSecureTradeLayout()
|
public void ProbeSecureTradeLayout()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
||||||
|
|
@ -107,6 +109,7 @@ public sealed class PowerbarLayoutProbeTests
|
||||||
/// ID_SecureTrade_TotalItemsLabel's fragments + variable hashes so the
|
/// ID_SecureTrade_TotalItemsLabel's fragments + variable hashes so the
|
||||||
/// count text can use ResolveTemplate if token-free (AD-95).</summary>
|
/// count text can use ResolveTemplate if token-free (AD-95).</summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void ProbeTotalItemsTemplate()
|
public void ProbeTotalItemsTemplate()
|
||||||
{
|
{
|
||||||
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_POWERBAR") != "1")
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ namespace AcDream.App.Tests.UI;
|
||||||
/// <c>States</c> dictionary was not walked) remain open.
|
/// <c>States</c> dictionary was not walked) remain open.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public sealed class SpewBoxLayoutDumpDiagnostic
|
public sealed class SpewBoxLayoutDumpDiagnostic
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,8 @@ public sealed class EnvCellSoundEmitterInventoryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PortalDat_SetupsWithAmbientSlotSoundTables()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_PortalDat_SetupsWithAmbientSlotSoundTables()
|
||||||
{
|
{
|
||||||
string? datDir = ConformanceDats.ResolveDatDir();
|
string? datDir = ConformanceDats.ResolveDatDir();
|
||||||
if (datDir is null)
|
if (datDir is null)
|
||||||
|
|
@ -184,7 +185,8 @@ public sealed class EnvCellSoundEmitterInventoryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SoundTables_WithAmbientSlots_ExistForWireBinding()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_SoundTables_WithAmbientSlots_ExistForWireBinding()
|
||||||
{
|
{
|
||||||
string? datDir = ConformanceDats.ResolveDatDir();
|
string? datDir = ConformanceDats.ResolveDatDir();
|
||||||
if (datDir is null)
|
if (datDir is null)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ public class CottageDoorwayCharacterizationTests
|
||||||
public CottageDoorwayCharacterizationTests(ITestOutputHelper output) => _out = output;
|
public CottageDoorwayCharacterizationTests(ITestOutputHelper output) => _out = output;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Characterize_CottageNeighborhood_PrintStructure()
|
public void Characterize_CottageNeighborhood_PrintStructure()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
@ -65,6 +66,7 @@ public class CottageDoorwayCharacterizationTests
|
||||||
public const uint OutdoorLandcell0031 = 0xA9B40031u;
|
public const uint OutdoorLandcell0031 = 0xA9B40031u;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Characterize_Doorway_FindInteriorPoints()
|
public void Characterize_Doorway_FindInteriorPoints()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ namespace AcDream.Core.Tests.Conformance;
|
||||||
/// landblock have a LandBlock (0xXXYYFFFF) terrain record at all, or only
|
/// landblock have a LandBlock (0xXXYYFFFF) terrain record at all, or only
|
||||||
/// LandBlockInfo + EnvCells? Output-only — no assertions.
|
/// LandBlockInfo + EnvCells? Output-only — no assertions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public sealed class DungeonLandblockDatProbeTests
|
public sealed class DungeonLandblockDatProbeTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ namespace AcDream.Core.Tests.Conformance;
|
||||||
/// these falloffs ARE what retail reads (modulo any load-time transform, settled
|
/// these falloffs ARE what retail reads (modulo any load-time transform, settled
|
||||||
/// separately in the decomp). Output-only — no assertions; read the log.
|
/// separately in the decomp). Output-only — no assertions; read the log.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public sealed class HoltburgTorchFalloffProbeTests
|
public sealed class HoltburgTorchFalloffProbeTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ public sealed class Issue113DoorVanishDiagnosticTests
|
||||||
/// vertical door-sized quads.
|
/// vertical door-sized quads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void DumpHoltburgBuildings_OrphanGeometry()
|
public void DumpHoltburgBuildings_OrphanGeometry()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
@ -191,7 +192,8 @@ public sealed class Issue113DoorVanishDiagnosticTests
|
||||||
/// must move.
|
/// must move.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ReplicateProductionEmission_OnPortalFills()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_ReplicateProductionEmission_OnPortalFills()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("dats unavailable — skipped"); return; }
|
if (datDir is null) { _out.WriteLine("dats unavailable — skipped"); return; }
|
||||||
|
|
@ -237,6 +239,7 @@ public sealed class Issue113DoorVanishDiagnosticTests
|
||||||
/// which is exactly why retail shows doors but not the ramp.
|
/// which is exactly why retail shows doors but not the ramp.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void DumpPortalFillSurfaceTypes()
|
public void DumpPortalFillSurfaceTypes()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
@ -282,6 +285,7 @@ public sealed class Issue113DoorVanishDiagnosticTests
|
||||||
/// the phantom geometry, for type-model comparison against the door.
|
/// the phantom geometry, for type-model comparison against the door.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void DumpControls_HallAndCottage()
|
public void DumpControls_HallAndCottage()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,7 @@ public sealed class Issue113PhantomStairsDumpTests
|
||||||
/// sphere STRADDLES this plane (|dist| < radius + 0.0002, pc:310090-310120).
|
/// sphere STRADDLES this plane (|dist| < radius + 0.0002, pc:310090-310120).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Dump_Cell104_ExteriorPortalPlane_Vs_GapPoint()
|
public void Dump_Cell104_ExteriorPortalPlane_Vs_GapPoint()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
@ -559,6 +560,7 @@ public sealed class Issue113PhantomStairsDumpTests
|
||||||
/// printed top→bottom DESCENDING (north up).
|
/// printed top→bottom DESCENDING (north up).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void DumpAAB3_Watchtower_TopDownMap()
|
public void DumpAAB3_Watchtower_TopDownMap()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
@ -679,6 +681,7 @@ public sealed class Issue113PhantomStairsDumpTests
|
||||||
/// retail skips and our building-mesh extraction mishandles?
|
/// retail skips and our building-mesh extraction mishandles?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void DumpHallModel_PolyFlagHistogram()
|
public void DumpHallModel_PolyFlagHistogram()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace AcDream.Core.Tests.Conformance;
|
||||||
/// FindCellList result — so the P0→P1 divergence is precisely characterized
|
/// FindCellList result — so the P0→P1 divergence is precisely characterized
|
||||||
/// (real pick-criterion divergence vs a test/sphere artifact).
|
/// (real pick-criterion divergence vs a test/sphere artifact).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public class ThresholdDivergenceDiagnosticTests
|
public class ThresholdDivergenceDiagnosticTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ public class CameraCornerSealReplayTests
|
||||||
/// shadow-query path is the half that failed).
|
/// shadow-query path is the half that failed).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Diagnostic_DispatchTrace_LeakPath_vs_Controls()
|
public void Diagnostic_DispatchTrace_LeakPath_vs_Controls()
|
||||||
{
|
{
|
||||||
var datDir = ConformanceDats.ResolveDatDir();
|
var datDir = ConformanceDats.ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,7 @@ public class DoorSetupGfxObjInspectionTests
|
||||||
// computes a different outdoor cell than 0xA9B40029
|
// computes a different outdoor cell than 0xA9B40029
|
||||||
// Findings drive the fix direction.
|
// Findings drive the fix direction.
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void HoltburgCottage_CellPortals_DatInspection()
|
public void HoltburgCottage_CellPortals_DatInspection()
|
||||||
{
|
{
|
||||||
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||||
|
|
@ -271,6 +272,7 @@ public class DoorSetupGfxObjInspectionTests
|
||||||
/// building wall sub-piece) lives at that XY.
|
/// building wall sub-piece) lives at that XY.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void HoltburgLandblockStatics_DatInspection()
|
public void HoltburgLandblockStatics_DatInspection()
|
||||||
{
|
{
|
||||||
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ public class Issue137CorridorSeamInspectionTests
|
||||||
/// hit point + normal.
|
/// hit point + normal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void CorridorSeam_FindPolygonMatchingLiveHit()
|
public void CorridorSeam_FindPolygonMatchingLiveHit()
|
||||||
{
|
{
|
||||||
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
var datDir = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ public class Issue186ConnectorCellGeometryInspectionTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void Dump_ConnectorCells_ShellAndCollision()
|
public void Dump_ConnectorCells_ShellAndCollision()
|
||||||
{
|
{
|
||||||
var datDir = DatDir();
|
var datDir = DatDir();
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ namespace AcDream.Core.Tests.Physics;
|
||||||
/// wired). Kept as a reusable decoder for any future "why doesn't this
|
/// wired). Kept as a reusable decoder for any future "why doesn't this
|
||||||
/// animate" question — swap the MotionTableId.
|
/// animate" question — swap the MotionTableId.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public class Issue188FadingDoorMotionTableInspectionTests
|
public class Issue188FadingDoorMotionTableInspectionTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _out;
|
private readonly ITestOutputHelper _out;
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,8 @@ public class Issue176177DungeonSeamInspectionTests
|
||||||
/// light exposed it.
|
/// light exposed it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs()
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_CorridorNeighborhood_CoplanarOverlappingDrawnPolyPairs()
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -470,7 +471,8 @@ public class Issue176177DungeonSeamInspectionTests
|
||||||
[InlineData(0x8A02016Eu)] // corridor cells — the striped-floor screenshot area
|
[InlineData(0x8A02016Eu)] // corridor cells — the striped-floor screenshot area
|
||||||
[InlineData(0x8A02017Au)]
|
[InlineData(0x8A02017Au)]
|
||||||
[InlineData(0x8A020165u)]
|
[InlineData(0x8A020165u)]
|
||||||
public void UnderHall_DrawnPolys_SurfaceColors(uint cellId)
|
[Trait("Purpose", "Diagnostic")]
|
||||||
|
public void Diagnostic_UnderHall_DrawnPolys_SurfaceColors(uint cellId)
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; }
|
||||||
|
|
@ -635,6 +637,7 @@ public class Issue176177DungeonSeamInspectionTests
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(0x8A02015Eu)] // #176 repro corridor
|
[InlineData(0x8A02015Eu)] // #176 repro corridor
|
||||||
[InlineData(0x8A02016Eu)] // corridor with floor-portals
|
[InlineData(0x8A02016Eu)] // corridor with floor-portals
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void CellVertexNormals_SmoothOrFaceted_Dump(uint cellId)
|
public void CellVertexNormals_SmoothOrFaceted_Dump(uint cellId)
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ public class Issue93TownNetworkFountainRoomLightInspectionTests
|
||||||
[InlineData(0x00070146u)]
|
[InlineData(0x00070146u)]
|
||||||
[InlineData(0x00070155u)]
|
[InlineData(0x00070155u)]
|
||||||
[InlineData(0x00070157u)]
|
[InlineData(0x00070157u)]
|
||||||
|
[Trait("Purpose", "Diagnostic")]
|
||||||
public void StaticObjects_SetupLightsCount_Dump(uint cellId)
|
public void StaticObjects_SetupLightsCount_Dump(uint cellId)
|
||||||
{
|
{
|
||||||
var datDir = ResolveDatDir();
|
var datDir = ResolveDatDir();
|
||||||
|
|
|
||||||
374
tools/audit-test-inventory.ps1
Normal file
374
tools/audit-test-inventory.ps1
Normal file
|
|
@ -0,0 +1,374 @@
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Produces a syntax-based inventory of every attributed C# test method.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Parses tracked C# files under tests/ with the Roslyn assemblies bundled in
|
||||||
|
the active .NET SDK. The report is evidence for the R3 test-truth audit: it
|
||||||
|
records test attributes and traits, static skips, empty-return sites,
|
||||||
|
assertion/throw signals (including same-file helper calls), diagnostic
|
||||||
|
output signals, wall-clock waits, environment-variable dependencies, and
|
||||||
|
source-text reads.
|
||||||
|
|
||||||
|
This is a candidate generator, not a semantic proof. In particular, an
|
||||||
|
output-only candidate can call a failure-capable helper declared in another
|
||||||
|
file or base class. Every removal or lane change still requires inspection.
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[string]$OutputPath = 'artifacts/test-audit/test-inventory.json'
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$repoRoot = [IO.Path]::GetFullPath((Split-Path -Parent $PSScriptRoot))
|
||||||
|
$resolvedOutput = if ([IO.Path]::IsPathRooted($OutputPath)) {
|
||||||
|
[IO.Path]::GetFullPath($OutputPath)
|
||||||
|
} else {
|
||||||
|
[IO.Path]::GetFullPath((Join-Path $repoRoot $OutputPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
$dotnetCommand = Get-Command dotnet -ErrorAction Stop
|
||||||
|
$dotnetRoot = Split-Path -Parent $dotnetCommand.Source
|
||||||
|
$sdkVersion = (& dotnet --version).Trim()
|
||||||
|
$roslynDirectory = Join-Path $dotnetRoot "sdk/$sdkVersion/Roslyn/bincore"
|
||||||
|
$loadedRoslyn = @([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object {
|
||||||
|
$_.GetName().Name -in @('Microsoft.CodeAnalysis', 'Microsoft.CodeAnalysis.CSharp')
|
||||||
|
})
|
||||||
|
if ($loadedRoslyn.Count -lt 2) {
|
||||||
|
foreach ($assemblyName in 'Microsoft.CodeAnalysis.dll', 'Microsoft.CodeAnalysis.CSharp.dll') {
|
||||||
|
$assemblyPath = Join-Path $roslynDirectory $assemblyName
|
||||||
|
if (-not (Test-Path -LiteralPath $assemblyPath -PathType Leaf)) {
|
||||||
|
throw "Roslyn assembly not found at '$assemblyPath'."
|
||||||
|
}
|
||||||
|
Add-Type -Path $assemblyPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$roslynVersion = [Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree].Assembly.GetName().Version.ToString()
|
||||||
|
|
||||||
|
function Get-ShortAttributeName {
|
||||||
|
param([Parameter(Mandatory)]$Attribute)
|
||||||
|
|
||||||
|
$name = $Attribute.Name.ToString().Split('.')[-1]
|
||||||
|
if ($name.EndsWith('Attribute', [StringComparison]::Ordinal)) {
|
||||||
|
$name = $name.Substring(0, $name.Length - 'Attribute'.Length)
|
||||||
|
}
|
||||||
|
return $name
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-NodeLine {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]$Tree,
|
||||||
|
[Parameter(Mandatory)]$Node
|
||||||
|
)
|
||||||
|
|
||||||
|
return $Tree.GetLineSpan($Node.Span).StartLinePosition.Line + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ContainingClassName {
|
||||||
|
param([Parameter(Mandatory)]$Method)
|
||||||
|
|
||||||
|
$typeNode = Get-ContainingTypeNode $Method
|
||||||
|
if ($null -ne $typeNode) {
|
||||||
|
return $typeNode.Identifier.Text
|
||||||
|
}
|
||||||
|
return '<global>'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ContainingTypeNode {
|
||||||
|
param([Parameter(Mandatory)]$Method)
|
||||||
|
|
||||||
|
$cursor = $Method.Parent
|
||||||
|
while ($null -ne $cursor) {
|
||||||
|
if ($cursor.GetType().Name -in @(
|
||||||
|
'ClassDeclarationSyntax',
|
||||||
|
'RecordDeclarationSyntax',
|
||||||
|
'StructDeclarationSyntax')) {
|
||||||
|
return $cursor
|
||||||
|
}
|
||||||
|
$cursor = $cursor.Parent
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ReturnGateKind {
|
||||||
|
param([Parameter(Mandatory)][string]$Condition)
|
||||||
|
|
||||||
|
if ($Condition -match '(?i)(GetEnvironmentVariable|ACDREAM_)') {
|
||||||
|
return 'OptIn'
|
||||||
|
}
|
||||||
|
if ($Condition -match '(?i)(OperatingSystem\.)') {
|
||||||
|
return 'Platform'
|
||||||
|
}
|
||||||
|
if ($Condition -match '(?i)(datDir|datDirectory|Directory\.Exists|File\.Exists|TryOpen|TryBuildScenario|steps is null|csvPath|\.pak)') {
|
||||||
|
return 'ExternalAsset'
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-InvocationName {
|
||||||
|
param([Parameter(Mandatory)]$Invocation)
|
||||||
|
|
||||||
|
$expression = $Invocation.Expression
|
||||||
|
if ($expression.GetType().Name -eq 'MemberAccessExpressionSyntax') {
|
||||||
|
return $expression.Name.Identifier.Text
|
||||||
|
}
|
||||||
|
if ($expression.GetType().Name -eq 'IdentifierNameSyntax') {
|
||||||
|
return $expression.Identifier.Text
|
||||||
|
}
|
||||||
|
if ($expression.GetType().Name -eq 'GenericNameSyntax') {
|
||||||
|
return $expression.Identifier.Text
|
||||||
|
}
|
||||||
|
return $expression.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-DirectFailureSignal {
|
||||||
|
param([Parameter(Mandatory)]$Method)
|
||||||
|
|
||||||
|
if (@($Method.DescendantNodes() | Where-Object {
|
||||||
|
$_.GetType().Name -in @('ThrowStatementSyntax', 'ThrowExpressionSyntax')
|
||||||
|
}).Count -gt 0) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($invocation in @($Method.DescendantNodes() | Where-Object {
|
||||||
|
$_.GetType().Name -eq 'InvocationExpressionSyntax'
|
||||||
|
})) {
|
||||||
|
$expression = $invocation.Expression.ToString()
|
||||||
|
if ($expression -match '(^|\.)Assert(\.|$)' -or
|
||||||
|
$expression -match '(^|\.)(Should|Shouldly)(\.|$)' -or
|
||||||
|
$expression -match '(^|\.)Verify($|\.)') {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-DirectOutputSignal {
|
||||||
|
param([Parameter(Mandatory)]$Method)
|
||||||
|
|
||||||
|
foreach ($invocation in @($Method.DescendantNodes() | Where-Object {
|
||||||
|
$_.GetType().Name -eq 'InvocationExpressionSyntax'
|
||||||
|
})) {
|
||||||
|
$expression = $invocation.Expression.ToString()
|
||||||
|
if ($expression -match '(^|\.)(_?out|output|Console|Debug|Trace)\.(Write|WriteLine)$' -or
|
||||||
|
$expression -match '(^|\.)(WriteDiagnostic|Dump|DumpSteps|Print)(\.|$)') {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-RecursiveSignal {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)][string]$MethodKey,
|
||||||
|
[Parameter(Mandatory)][hashtable]$MethodMap,
|
||||||
|
[Parameter(Mandatory)][hashtable]$DirectSignalMap,
|
||||||
|
[Parameter(Mandatory)][hashtable]$CallMap,
|
||||||
|
[Parameter(Mandatory)][hashtable]$Visited
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Visited.ContainsKey($MethodKey)) {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
$Visited[$MethodKey] = $true
|
||||||
|
if ($DirectSignalMap[$MethodKey]) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
foreach ($calledName in @($CallMap[$MethodKey])) {
|
||||||
|
foreach ($candidateKey in @($MethodMap.Keys | Where-Object {
|
||||||
|
$_.EndsWith("::$calledName", [StringComparison]::Ordinal)
|
||||||
|
})) {
|
||||||
|
if (Test-RecursiveSignal $candidateKey $MethodMap $DirectSignalMap $CallMap $Visited) {
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
$trackedFiles = @(& git -C $repoRoot ls-files tests | Where-Object {
|
||||||
|
$_.EndsWith('.cs', [StringComparison]::OrdinalIgnoreCase)
|
||||||
|
})
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw 'git ls-files failed while enumerating test sources.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$records = [Collections.Generic.List[object]]::new()
|
||||||
|
$parsedFileCount = 0
|
||||||
|
foreach ($relativePath in $trackedFiles) {
|
||||||
|
$absolutePath = Join-Path $repoRoot $relativePath
|
||||||
|
if (-not (Test-Path -LiteralPath $absolutePath -PathType Leaf)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = [IO.File]::ReadAllText($absolutePath)
|
||||||
|
$tree = [Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree]::ParseText($source)
|
||||||
|
$root = $tree.GetRoot()
|
||||||
|
$parsedFileCount++
|
||||||
|
|
||||||
|
$allMethods = @($root.DescendantNodes() | Where-Object {
|
||||||
|
$_.GetType().Name -eq 'MethodDeclarationSyntax'
|
||||||
|
})
|
||||||
|
$methodMap = @{}
|
||||||
|
$failureMap = @{}
|
||||||
|
$outputMap = @{}
|
||||||
|
$callMap = @{}
|
||||||
|
|
||||||
|
foreach ($method in $allMethods) {
|
||||||
|
$className = Get-ContainingClassName $method
|
||||||
|
$key = "$className::$($method.Identifier.Text)"
|
||||||
|
$methodMap[$key] = $method
|
||||||
|
$failureMap[$key] = Test-DirectFailureSignal $method
|
||||||
|
$outputMap[$key] = Test-DirectOutputSignal $method
|
||||||
|
$callMap[$key] = @($method.DescendantNodes() |
|
||||||
|
Where-Object { $_.GetType().Name -eq 'InvocationExpressionSyntax' } |
|
||||||
|
ForEach-Object { Get-InvocationName $_ } |
|
||||||
|
Sort-Object -Unique)
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($method in $allMethods) {
|
||||||
|
$methodAttributes = @($method.AttributeLists | ForEach-Object { $_.Attributes })
|
||||||
|
$attributeNames = @($methodAttributes | ForEach-Object {
|
||||||
|
Get-ShortAttributeName $_
|
||||||
|
})
|
||||||
|
$testAttributes = @($attributeNames | Where-Object {
|
||||||
|
$_ -in @('Fact', 'Theory', 'AvaloniaFact', 'InstalledDatFact')
|
||||||
|
})
|
||||||
|
if ($testAttributes.Count -eq 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = Get-ContainingClassName $method
|
||||||
|
$key = "$className::$($method.Identifier.Text)"
|
||||||
|
$hasFailureSignal = Test-RecursiveSignal `
|
||||||
|
$key $methodMap $failureMap $callMap @{}
|
||||||
|
$hasOutputSignal = Test-RecursiveSignal `
|
||||||
|
$key $methodMap $outputMap $callMap @{}
|
||||||
|
|
||||||
|
$emptyReturns = [Collections.Generic.List[object]]::new()
|
||||||
|
foreach ($returnNode in @($method.DescendantNodes() | Where-Object {
|
||||||
|
$_.GetType().Name -eq 'ReturnStatementSyntax' -and
|
||||||
|
$null -eq $_.Expression
|
||||||
|
})) {
|
||||||
|
$condition = '<unconditional>'
|
||||||
|
$cursor = $returnNode.Parent
|
||||||
|
while ($null -ne $cursor -and $cursor -ne $method) {
|
||||||
|
if ($cursor.GetType().Name -eq 'IfStatementSyntax') {
|
||||||
|
$condition = $cursor.Condition.ToString()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$cursor = $cursor.Parent
|
||||||
|
}
|
||||||
|
$emptyReturns.Add([ordered]@{
|
||||||
|
Line = Get-NodeLine $tree $returnNode
|
||||||
|
Condition = $condition
|
||||||
|
GateKind = Get-ReturnGateKind $condition
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$traits = [Collections.Generic.List[string]]::new()
|
||||||
|
$staticSkip = $null
|
||||||
|
$typeNode = Get-ContainingTypeNode $method
|
||||||
|
$traitAttributes = @($methodAttributes)
|
||||||
|
if ($null -ne $typeNode) {
|
||||||
|
$traitAttributes += @($typeNode.AttributeLists | ForEach-Object {
|
||||||
|
$_.Attributes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
foreach ($attribute in $traitAttributes) {
|
||||||
|
$attributeName = Get-ShortAttributeName $attribute
|
||||||
|
$arguments = if ($null -eq $attribute.ArgumentList) {
|
||||||
|
''
|
||||||
|
} else {
|
||||||
|
$attribute.ArgumentList.ToString()
|
||||||
|
}
|
||||||
|
if ($attributeName -eq 'Trait') {
|
||||||
|
$traits.Add($arguments.Trim('(', ')').Replace('"', ''))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($attribute in $methodAttributes) {
|
||||||
|
$arguments = if ($null -eq $attribute.ArgumentList) {
|
||||||
|
''
|
||||||
|
} else {
|
||||||
|
$attribute.ArgumentList.ToString()
|
||||||
|
}
|
||||||
|
if ($arguments -match '(^|[,\s])Skip\s*=') {
|
||||||
|
$staticSkip = $arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$bodyText = $method.ToString()
|
||||||
|
$environmentVariables = @([regex]::Matches(
|
||||||
|
$bodyText,
|
||||||
|
'GetEnvironmentVariable\s*\(\s*"([A-Za-z0-9_]+)"') |
|
||||||
|
ForEach-Object { $_.Groups[1].Value } |
|
||||||
|
Sort-Object -Unique)
|
||||||
|
|
||||||
|
$records.Add([ordered]@{
|
||||||
|
Path = $relativePath.Replace('\', '/')
|
||||||
|
Line = Get-NodeLine $tree $method
|
||||||
|
Class = $className
|
||||||
|
Method = $method.Identifier.Text
|
||||||
|
Attributes = $testAttributes
|
||||||
|
Traits = @($traits)
|
||||||
|
StaticSkip = $staticSkip
|
||||||
|
EmptyReturns = @($emptyReturns)
|
||||||
|
PrerequisiteReturnCandidate = @($emptyReturns | Where-Object {
|
||||||
|
$null -ne $_.GateKind
|
||||||
|
}).Count -gt 0
|
||||||
|
HasFailureSignal = $hasFailureSignal
|
||||||
|
HasOutputSignal = $hasOutputSignal
|
||||||
|
OutputOnlyCandidate = $hasOutputSignal -and -not $hasFailureSignal
|
||||||
|
EnvironmentVariables = $environmentVariables
|
||||||
|
HasThreadSleep = $bodyText -match '\bThread\.Sleep\s*\('
|
||||||
|
HasTaskDelay = $bodyText -match '\bTask\.Delay\s*\('
|
||||||
|
ReadsSourceText = $bodyText -match '(ReadAllText|ReadAllLines)\s*\(' -and
|
||||||
|
$bodyText -match '\.cs'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$orderedRecords = @($records | Sort-Object Path, Line, Method)
|
||||||
|
$workingTreeStatus = @(& git -C $repoRoot status --short --untracked-files=no)
|
||||||
|
$summary = [ordered]@{
|
||||||
|
GeneratedUtc = [DateTime]::UtcNow.ToString('O')
|
||||||
|
RepositoryCommit = (& git -C $repoRoot rev-parse HEAD).Trim()
|
||||||
|
WorkingTreeDirty = $workingTreeStatus.Count -gt 0
|
||||||
|
DotnetSdkVersion = $sdkVersion
|
||||||
|
RoslynAssemblyVersion = $roslynVersion
|
||||||
|
TrackedCSharpFiles = $trackedFiles.Count
|
||||||
|
ParsedCSharpFiles = $parsedFileCount
|
||||||
|
AttributedTestMethods = $orderedRecords.Count
|
||||||
|
StaticSkipMethods = @($orderedRecords | Where-Object { $null -ne $_.StaticSkip }).Count
|
||||||
|
EmptyReturnMethods = @($orderedRecords | Where-Object { $_.EmptyReturns.Count -gt 0 }).Count
|
||||||
|
EmptyReturnSites = @($orderedRecords | ForEach-Object { $_.EmptyReturns }).Count
|
||||||
|
PrerequisiteReturnCandidates = @($orderedRecords | Where-Object {
|
||||||
|
$_.PrerequisiteReturnCandidate
|
||||||
|
}).Count
|
||||||
|
OutputOnlyCandidates = @($orderedRecords | Where-Object { $_.OutputOnlyCandidate }).Count
|
||||||
|
DiagnosticMethods = @($orderedRecords | Where-Object {
|
||||||
|
$_.Traits -contains 'Purpose, Diagnostic'
|
||||||
|
}).Count
|
||||||
|
KnownFailureMethods = @($orderedRecords | Where-Object {
|
||||||
|
$_.Traits -contains 'Status, KnownFailure'
|
||||||
|
}).Count
|
||||||
|
ThreadSleepMethods = @($orderedRecords | Where-Object { $_.HasThreadSleep }).Count
|
||||||
|
TaskDelayMethods = @($orderedRecords | Where-Object { $_.HasTaskDelay }).Count
|
||||||
|
DirectEnvironmentVariableMethods = @($orderedRecords | Where-Object {
|
||||||
|
$_.EnvironmentVariables.Count -gt 0
|
||||||
|
}).Count
|
||||||
|
SourceTextReadMethods = @($orderedRecords | Where-Object { $_.ReadsSourceText }).Count
|
||||||
|
}
|
||||||
|
|
||||||
|
$report = [ordered]@{
|
||||||
|
Summary = $summary
|
||||||
|
Tests = $orderedRecords
|
||||||
|
}
|
||||||
|
$outputDirectory = Split-Path -Parent $resolvedOutput
|
||||||
|
[IO.Directory]::CreateDirectory($outputDirectory) | Out-Null
|
||||||
|
$report | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $resolvedOutput -Encoding utf8
|
||||||
|
|
||||||
|
$summary | Format-List
|
||||||
|
Write-Output "Inventory: $resolvedOutput"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue