test: own Avalonia application session

This commit is contained in:
Erik 2026-08-18 13:41:23 +02:00
parent 631ecd24e3
commit 088add2fac
2 changed files with 94 additions and 11 deletions

View file

@ -52,7 +52,7 @@ inventory refreshed for Batch L reports:
| Mechanical output-only candidates | 2 |
| Constant-truth assertion methods / sites | 0 / 0 |
| Reviewed syntactic self-comparison methods / sites | 4 / 5 |
| Reviewed diagnostic methods | 57 |
| Reviewed diagnostic methods | 82 |
| Methods directly using `Thread.Sleep` / `Task.Delay` | 15 / 14 |
| Cancellable-infinite-only / elapsed-time methods | 5 / 24 |
| Methods directly reading environment variables | 47 |
@ -68,10 +68,10 @@ the three platform branches; they are not missing-prerequisite passes.
The output-only classifier follows same-file helper calls, so an assertion used
only while constructing a probe can prevent an otherwise diagnostic method from
appearing in its two-candidate result. The reviewed `Purpose=Diagnostic` set is
therefore the authority: Batch C's 51 methods plus six artifact/report writers
reconciled in Batch L, for 57 methods and 76 discovered cases (28 App, 48 Core).
Their investigative output and historical rationale remain executable outside
the release total.
therefore the authority: Batch C's 51 methods, six artifact/report writers from
Batch L, and 25 fixture-sanity-only investigations from Batch R, for 82 methods
and 103 discovered cases (35 App, 68 Core). Their investigative output and
historical rationale remain executable outside the release total.
The five syntactic self-comparisons are reviewed, not vacuous: two call a hash
twice to assert determinism, one reads the singleton twice, and two read the
@ -139,13 +139,13 @@ identifiable in the lane report.
| T-008 incapable-of-failing diagnostics | resolved in batches A, C, and L | The literal wiring smoke test was deleted, the GPU contract tautology was repaired, and the six remaining constant-truth diagnostic assertions were removed after their output was preserved under `Purpose=Diagnostic`. |
| T-009 wall-clock double-click tests | resolved in batch E | Four sleeps were replaced by a deterministic test clock behind an internal factory overload. The production factory still reads `Environment.TickCount64` exactly as before. |
| T-010 two useless cases | high-confidence cleanup batch A | Delete `SmokeTest.TestProject_IsWired` and `ChaseCameraTests.ImplementsICamera`; compilation already proves both claims. |
| T-011 output-only methods | classified in batches C and L | The reviewed current set is 57 methods / 76 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-011 diagnostic-only methods | resolved in batches C, L, and R | The reviewed current set is 82 methods / 103 cases. All carry `Purpose=Diagnostic`, preserving the apparatus while removing it from release pass totals. Batch R catches investigations whose only assertion validates fixture/DAT availability, which the original mechanical output-only scan could not distinguish from an oracle. |
| T-012 source-text freezes | replacement map complete in batch I; 46 implementation-shape facts remain staged | Twelve whole-tree architecture rules and five cross-artifact contracts stay. The other 46 are campaign-era literal implementation freezes; retire each only beside its semantic/behavioral replacement, with user approval for owner/lifecycle assertions that have no equivalent yet. |
| 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 | six mechanisms repaired in batch F; one product defect classified | Virtual/wall-clock mixing, tiered-JIT allocation noise, a live controller clock, and a ThreadPool-start timing oracle were removed without weakening behavioral contracts. `DatSoundCache` #321 is a real in-flight-entry race and now runs as `Status=KnownFailure` pending a product fix. |
| T-015 four non-prerequisite skips | resolved in batches A/B | PVS scaffold deleted with rationale preserved; redundant chat/radar generators deleted in favor of the comprehensive Manual lane; tower oracle is `Status=KnownFailure`. |
| T-016 historical test taxonomy | first descriptive-identity batch active in batch J | Remove opaque AP/R/J/K/Slice codes from current test/type names while preserving provenance in comments and ledgers. The 47 `Issue###` files remain an explicit user-decision set because their IDs still connect tests to retail evidence. |
| T-017 Avalonia ownership | reopened and closed in batch D | The full gate exposed the same compositor ownership class between six newer `MainWindowViewTests` facts. Their six named assertion phases now run in one owned Avalonia application session; 25 fresh-process stress iterations and the complete gate pass. |
| T-016 historical test taxonomy | descriptive identities repaired in batches J and R; issue-prefix decision remains | Opaque AP/R/J/K/Slice and `SCRATCH` labels have been removed from maintained regression identities while provenance stays in comments and ledgers. The 47 `Issue###` files remain an explicit user-decision set because their IDs still connect tests to retail evidence. |
| T-017 Avalonia ownership | batch D's window cleanup was necessary but incomplete; runner lifetime fixed in batch S | Batch R's clean gate reproduced a compositor thread-affinity cleanup failure. The sole combined Avalonia fact still used the package's default per-test application isolation. Batch S explicitly owns one application/dispatcher for the assembly's one UI fact; 25 focused processes, 25 complete Launcher assemblies, and the complete gate pass. |
| T-018 stderr observer race | resolved in R2 | Live reader shares writes/deletes; 25 focused repetitions plus the complete gate. |
| T-019 remaining explicit waits | resolved in batches MO | All nine fixed-delay negative oracles now use observed monitor/thread/lease/process state. The remaining 24 elapsed-time methods are the reviewed cancellation, real-time protocol, cooperative-yield, bounded-polling, and positive-timeout categories from Batch H. |
| T-020 exact duplicate bodies/data | resolved in batch K | Four redundant executions were removed. The remaining 11 body-equivalent groups are intentionally split theories with disjoint, meaningfully named datasets; the inventory enforces zero repeated rows within a theory or across body-equivalent theories. |
@ -793,8 +793,8 @@ cooperative yields, bounded integration polling, or a positive timeout guard.
The 47 `Issue###` files contain 155 attributed methods. They are not a hidden
duplicate or source-freeze family: none reads production source text and none
contains a direct timing wait. Twenty-nine methods are explicitly classified
`Purpose=Diagnostic`; the other 126 are executable regression contracts.
contains a direct timing wait. Forty-six methods are explicitly classified
`Purpose=Diagnostic`; the other 109 are executable regression contracts.
Ninety-four methods belong to the installed-DAT lane, overlapping both groups
where retail assets are the evidence prerequisite.
@ -807,8 +807,85 @@ contracts materially easier to understand.
R3's recommendation is therefore to retain the current `Issue###` class/file
prefixes. Continue correcting individual method names whose oracle overclaims,
and keep diagnostic methods outside the release count, but do not re-home 126
and keep diagnostic methods outside the release count, but do not re-home 109
useful regressions merely to erase their provenance. If the issue archive is
later split into component-owned records, a class-level `Issue` trait can
preserve filtering before any component-based file rename. This recommendation
remains a user approval item because it closes T-016 without code churn.
## Batch R fixture-sanity-only diagnostics
The original output-only scan intentionally treated any assertion as a failure
signal. A final name/comment review found 25 more investigation methods whose
only assertions establish that the DAT, fixture, setup, or synthetic replay
loaded; none compares the report, sweep, or characterization named by the
method. Their own names or comments say `Diagnostic`, `Dump`, `Measure`,
`Characterize`, “always passes,” or “not a regression.”
The 25 methods (27 expanded cases) divide into:
- seven visibility/flood measurement reports across corner flood, house exit,
issue #181, and issue #95;
- ten raw building/geometry dumps across issues #113, #119, #147, and #337;
and
- eight physics transition traces across issue #112, cellar ascent, door
collision, issue #108, and issue #265.
Batch R adds only `Purpose=Diagnostic`. It does not remove a body, fixture,
assertion, output line, retail/ACE citation, or historical comment. The
diagnostic inventory is now 82 methods / 103 cases: 35 App and 68 Core. The
explicit non-Manual diagnostic filters pass 27/27 App and 68/68 Core; the other
eight App cases remain in their separately selected Manual tasks.
The same pass corrected two inverse naming errors without changing their
oracles:
- `SCRATCH_BuyShapedFreshGuidCreateObjectThenContainId_FinalOrder` is a durable
vendor-order regression and is now
`BuyShapedFreshGuidCreateThenPlacementZeroEcho_InsertsAtRetailListHead`; and
- `Diagnostic_ExitDoorTopEdge_GateVsAperture` already enforces hard pixel
bounds and is now
`ExitDoorTopEdge_GateCoversLiftedApertureWithinPixelTolerance`.
Both focused regressions pass. Their detailed queue-order and doorway/scissor
rationale remains beside the tests. The only remaining unclassified method
names beginning `Reflect_` or `Dump` are real asserted collision/reflection or
environment-default contracts, not output apparatus.
Only three of Batch R's 27 newly classified cases had still been hermetic;
the other 24 already belonged to the installed-DAT lane. After Batch S's
ownership correction, the no-retry complete hermetic Release gate therefore
passes 14,379/14,379 with zero skips or failures, exactly three below Batch O.
## Batch S Avalonia application-session ownership
The first clean complete gate after Batch R did not pass: every non-Launcher
assembly passed, but `MainWindowViewTests` failed during Avalonia test cleanup
with `DefaultRenderLoop.Add` calling `Dispatcher.VerifyAccess` from a thread
that did not own the compositor. The gate reported 14,378 passes and one
failure. This was recorded and fixed rather than retried unchanged.
Batch D had closed and pumped every shown window and consolidated the six UI
assertion phases into one `[AvaloniaFact]`. That removed leaked-window overlap,
but it did not change Avalonia 12.1.1's default
`AvaloniaTestIsolationLevel.PerTest`: the runner still recreated and disposed
the application, dispatcher, and compositor around that one fact. The failure
occurred in that isolated application setup/cleanup path, before any product
assertion could explain it.
`AcDream.Launcher.Tests` contains exactly one Avalonia fact, and that fact owns
the complete real-window matrix. Batch S declares
`AvaloniaTestIsolationLevel.PerAssembly`, so the runner creates one
application/dispatcher on its dedicated session thread and lets the fresh test
process own its terminal cleanup. All individual windows still close and pump
their dispatcher in `finally`; no suite serialization, retry, product code, or
assertion was changed.
Verification:
- the focused Avalonia fact passes in 25/25 fresh processes;
- the complete 56-case Launcher assembly passes in 25/25 fresh processes;
- the complete 44-project Release build reports zero warnings and zero errors;
and
- the no-retry complete hermetic Release gate passes 14,379/14,379 with zero
skips or failures across all 12 test assemblies.

View file

@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Headless;
[assembly: AvaloniaTestApplication(typeof(AcDream.Launcher.Tests.TestAppBuilder))]
[assembly: AvaloniaTestIsolation(AvaloniaTestIsolationLevel.PerAssembly)]
namespace AcDream.Launcher.Tests;
@ -12,6 +13,11 @@ namespace AcDream.Launcher.Tests;
/// live for every test — <see cref="MainWindow"/>'s controls (TreeView,
/// Button, TextBox, ...) need a real control theme to template and focus
/// correctly, not just an unstyled visual tree.
/// The assembly contains one Avalonia test that owns the complete window
/// matrix. Keep one application/dispatcher for that process: Avalonia 12's
/// per-test teardown can recreate the compositor after its render loop has
/// become owned by a different worker, producing a thread-affinity cleanup
/// failure after an otherwise passing test.
/// </summary>
public static class TestAppBuilder
{