test: separate non-hermetic release lanes

This commit is contained in:
Erik 2026-08-18 10:49:22 +02:00
parent 52015f5052
commit 8f490240d4
17 changed files with 131 additions and 114 deletions

View file

@ -36,6 +36,7 @@ namespace AcDream.App.Tests.Rendering;
/// those runs as Skipped so the counts show "ran" separately from "no-op'd".
/// </para>
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class ChargenPreviewControllerTests
{
private readonly ITestOutputHelper _out;

View file

@ -29,6 +29,7 @@ namespace AcDream.App.Tests.Rendering;
/// the vanish; root/visibility instability across adjacent steps pins the
/// flap.
/// </summary>
[Trait("Lane", "InstalledDat")]
public class TowerAscentReplayTests
{
private readonly ITestOutputHelper _out;
@ -96,10 +97,9 @@ public class TowerAscentReplayTests
}
}
private List<AscentStep>? RunAscent()
private List<AscentStep> RunAscent()
{
var datDir = CornerFloodReplayTests.ResolveDatDir();
if (datDir is null) { _out.WriteLine("SKIP: dats unavailable"); return null; }
var datDir = RequireDatDir();
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cells = Issue120ReciprocalPingPongTests.LoadAllInteriorCells(dats, Landblock);
@ -203,12 +203,11 @@ public class TowerAscentReplayTests
/// through this band (curr_cell keep-curr above open-top cells), or can
/// ConstructView(CBldPortal) seed with an in-plane eye?
/// </summary>
[Fact(Skip = "#119-residual: pins the roof-lip flood gap (steps 195-201) — un-skip with the fix; see the doc comment")]
[Fact]
[Trait("Status", "KnownFailure")]
public void TowerAscent_StaircaseStaysConeVisible_EveryStep()
{
var steps = RunAscent();
if (steps is null) return;
var failures = steps.FindAll(s => !s.StairsConeVisible);
if (failures.Count > 0)
{
@ -229,8 +228,6 @@ public class TowerAscentReplayTests
public void TowerAscent_RootDoesNotPingPong()
{
var steps = RunAscent();
if (steps is null) return;
var flips = new List<int>();
for (int i = 2; i < steps.Count; i++)
{
@ -265,8 +262,7 @@ public class TowerAscentReplayTests
public void CapturedTopOfStairs_MainCellStaysInFlood(
float ex, float ey, float ez, float fx, float fy, float fz)
{
var datDir = CornerFloodReplayTests.ResolveDatDir();
if (datDir is null) { _out.WriteLine("SKIP: dats unavailable"); return; }
var datDir = RequireDatDir();
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cells = Issue120ReciprocalPingPongTests.LoadAllInteriorCells(dats, Landblock);
@ -336,10 +332,10 @@ public class TowerAscentReplayTests
/// footprint containment).
/// </summary>
[Fact]
[Trait("Purpose", "Diagnostic")]
public void Diagnostic_TopOfStairs_GateByGate()
{
var datDir = CornerFloodReplayTests.ResolveDatDir();
if (datDir is null) { _out.WriteLine("SKIP: dats unavailable"); return; }
var datDir = RequireDatDir();
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cells = Issue120ReciprocalPingPongTests.LoadAllInteriorCells(dats, Landblock);
@ -419,10 +415,15 @@ public class TowerAscentReplayTests
/// <summary>Full per-step table for the investigation record.</summary>
[Fact]
[Trait("Purpose", "Diagnostic")]
public void Diagnostic_TowerAscent_PerStepTable()
{
var steps = RunAscent();
if (steps is null) return;
DumpSteps(steps);
}
private static string RequireDatDir()
=> CornerFloodReplayTests.ResolveDatDir()
?? throw new InvalidOperationException(
"Installed client_portal.dat is required for the tower-ascent lane.");
}

View file

@ -17,6 +17,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// authored master-shell and page ids the campaign plan and CC4's own
/// decomp research cite, pinning them against the real installed layout.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class CharacterCreationLiveDatTests
{
private static string DatDirectory =>

View file

@ -16,6 +16,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// <c>ACDREAM_PROBE_LIVE_MOUNT=1</c>; <c>ACDREAM_DAT_DIR</c> can override the
/// ordinary Documents/Asheron's Call location. Reads the DATs read-only.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class CharacterManagementLiveDatTests
{
[InstalledDatFact]
@ -368,9 +369,14 @@ internal sealed class InstalledDatFactAttribute : FactAttribute
{
public InstalledDatFactAttribute()
{
if (Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") != "1")
bool requested = Environment.GetEnvironmentVariable(
"ACDREAM_RUN_INSTALLED_DAT_TESTS") == "1"
|| Environment.GetEnvironmentVariable("ACDREAM_PROBE_LIVE_MOUNT") == "1";
if (!requested)
{
Skip = "Set ACDREAM_PROBE_LIVE_MOUNT=1 to run the installed-DAT LA8 gate.";
Skip = "Installed-DAT lane not requested; set "
+ "ACDREAM_RUN_INSTALLED_DAT_TESTS=1. The legacy "
+ "ACDREAM_PROBE_LIVE_MOUNT=1 switch is also accepted.";
return;
}

View file

@ -235,7 +235,7 @@ public class ChatLayoutConformanceTests
/// §2.5/§2.6): the chat transcript's base style (<c>0x10000372</c> in layout
/// <c>0x2100003F</c>) authors NO property 0x21 (Outline) anywhere in its
/// inheritance chain — regenerated straight from the real installed DAT
/// (<c>ChatLayoutFixtureGenerator</c>), proving the property-0x21/0x22
/// (<c>RetailLayoutFixtureGenerator</c>), proving the property-0x21/0x22
/// importer (<see cref="ElementReader.ApplyCanonicalLegacyProjection"/>)
/// resolves this correctly end to end, not merely by a missing-field JSON
/// default.

View file

@ -1,50 +0,0 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text.Json;
using AcDream.App.UI.Layout;
using DatReaderWriter;
using DatReaderWriter.Options;
namespace AcDream.App.Tests.UI.Layout;
/// <summary>
/// One-off generator for the committed chat golden fixture. Skipped by default —
/// run manually with the real dats present (set ACDREAM_DAT_DIR) to regenerate
/// chat_2100006f.json, then commit it. Mirrors how vitals_2100006C.json was made.
///
/// <para>
/// Campaign CH slice CH6a swapped the imported main-chat LayoutDesc from the
/// wrong <c>0x21000006</c> (a different, unrelated chat layout whose root
/// <c>0x1000000E</c> and 800px resize bar <c>0x1000000F</c> appear nowhere in
/// the EoR gameplay UI) to retail's actual main chat window,
/// <c>0x2100006F</c> (window element <c>0x10000601</c>, layout root
/// <c>0x10000600</c>, authored 410x100 — see
/// docs/research/2026-08-09-chat-retail-window-shell.md §2.1 and §5). The old
/// <c>chat_21000006.json</c> fixture is retired with it.
/// </para>
/// </summary>
public class ChatLayoutFixtureGenerator
{
[Fact(Skip = "manual: regenerates the committed chat fixture; needs the real dats (ACDREAM_DAT_DIR)")]
public void GenerateChatFixture()
{
var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Documents", "Asheron's Call");
using var dats = new DatCollection(datDir, DatAccessType.Read);
var info = LayoutImporter.ImportInfos(dats, 0x2100006Fu);
Assert.NotNull(info);
var json = JsonSerializer.Serialize(info, new JsonSerializerOptions
{
IncludeFields = true,
WriteIndented = true,
});
File.WriteAllText(FixturePath(), json);
}
// Resolve the SOURCE fixtures dir (not bin/) from this file's compile-time path.
private static string FixturePath([CallerFilePath] string thisFile = "")
=> Path.Combine(Path.GetDirectoryName(thisFile)!, "fixtures", "chat_2100006f.json");
}

View file

@ -17,6 +17,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// proves the escape class genuinely exists in shipping data and prints
/// which tables carry it.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class DatStringEscapeSweepTests
{
[InstalledDatFact]

View file

@ -24,6 +24,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// elements (not a brittle exact global total) so the gate survives a
/// future DAT revision without going red on an unrelated content change.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class LayoutImporterMediaBearingChildSweepTests
{
private static string DatDirectory =>

View file

@ -34,6 +34,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// Documents/Asheron's Call location.
/// </para>
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class MapHousePanelLiveDatMountTests
{
private static string DatDirectory =>

View file

@ -30,6 +30,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// A=FF R=00 G=FF B=00) via per-state <c>P0x3B</c>.
/// </para>
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class MapNoteLiveDatTests
{
private static string DatDirectory =>

View file

@ -1,36 +0,0 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text.Json;
using AcDream.App.UI.Layout;
using DatReaderWriter;
using DatReaderWriter.Options;
namespace AcDream.App.Tests.UI.Layout;
/// <summary>One-off generator for the committed retail radar LayoutDesc fixture.</summary>
public sealed class RadarLayoutFixtureGenerator
{
[Fact(Skip = "manual: regenerates the committed radar fixture; needs the real dats (ACDREAM_DAT_DIR)")]
public void GenerateRadarFixture()
{
var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
?? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"Documents",
"Asheron's Call");
using var dats = new DatCollection(datDir, DatAccessType.Read);
var info = LayoutImporter.ImportInfos(dats, RadarController.LayoutId);
Assert.NotNull(info);
var json = JsonSerializer.Serialize(info, new JsonSerializerOptions
{
IncludeFields = true,
WriteIndented = true,
});
File.WriteAllText(FixturePath(), json);
}
private static string FixturePath([CallerFilePath] string thisFile = "")
=> Path.Combine(Path.GetDirectoryName(thisFile)!, "fixtures", "radar_21000074.json");
}

View file

@ -9,9 +9,10 @@ namespace AcDream.App.Tests.UI.Layout;
/// <summary>
/// Regenerates every committed retail layout fixture from production portal.dat.
/// The test is inert unless <c>ACDREAM_REGENERATE_UI_FIXTURES=1</c> is set, keeping
/// The manual lane requires <c>ACDREAM_REGENERATE_UI_FIXTURES=1</c>, keeping
/// normal test runs deterministic and dat-independent.
/// </summary>
[Trait("Lane", "Manual")]
public sealed class RetailLayoutFixtureGenerator
{
private static readonly (uint Id, string FileName)[] Layouts =
@ -46,7 +47,10 @@ public sealed class RetailLayoutFixtureGenerator
"1",
StringComparison.Ordinal))
{
return;
throw new InvalidOperationException(
"Manual fixture generation was not requested; set "
+ "ACDREAM_REGENERATE_UI_FIXTURES=1 and ACDREAM_DAT_DIR, "
+ "then run the Manual lane explicitly.");
}
var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")

View file

@ -19,6 +19,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// sweep landmark, mirroring <see cref="CharacterManagementLiveDatTests"/>'s
/// pattern.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class TooltipLiveDatTests
{
private static string DatDirectory =>

View file

@ -21,6 +21,7 @@ namespace AcDream.App.Tests.UI.Layout;
/// enumeration for the commit message, pins landmark counts rather than a
/// brittle exact global total.
/// </summary>
[Trait("Lane", "InstalledDat")]
public sealed class UiButtonCorpusSweepTests
{
private static string DatDirectory =>