From 8613f7d480ad5733020aa94074a70b1e665138cd Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:32:25 -0700 Subject: [PATCH] test(uocontent): skip tile-data-dependent tests when client files absent Now that CI actually runs the test projects (previous commits), it exposed that the UOContent.Tests bootstrap force-loaded UO client tile data (tiledata.mul) unconditionally in the collection-fixture constructor. On CI the copyrighted client files are absent, so TileData.Load() threw and xUnit failed every test in the collection with the same FileNotFoundException - 337 failures, most of them collateral (packet, scheduler, spawner tests that don't need tile data at all). Mirror the graceful pattern Server.Tests already uses: - TestServerInitializer now probes for tiledata.mul (Core.FindDataFile + File.Exists) and only force-loads tile data / runs the tile-dependent configure steps (MultiData.Configure, VerifyTrammelTileDataLoaded) when present. It exposes TileDataLoaded so the fixture constructor no longer throws when the files are missing. - Add a shared TileDataRequirement.SkipIfMissing() guard and apply it to exactly the 31 pathfinding/multi/AI tests that genuinely require real tile/multi data, converting them to [SkippableFact]/[SkippableTheory]. Result: with client data absent (CI) the suite is 0 failed / 32 skipped / 469 passed; with data present (dev) it is 0 failed / 0 skipped / 501 passed. The 306 collateral failures are gone because the fixture no longer throws. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Fixtures/TestServerInitializer.cs | 40 +++++++++++++++---- .../Fixtures/TileDataRequirement.cs | 18 +++++++++ .../Pathing/BitmapAStarAlgorithmTests.cs | 3 +- .../Engines/Pathing/Multi/BoatPathTests.cs | 6 ++- .../Pathing/Multi/FoundationRedesignTests.cs | 6 ++- .../Pathing/Multi/HousePathRoutingTests.cs | 6 ++- .../Pathing/Multi/MultiEdgeCaseTests.cs | 12 ++++-- .../Pathing/Multi/MultiMaskCacheTests.cs | 12 ++++-- .../Pathing/Multi/MultiMaskSynthesisTests.cs | 3 +- .../Pathing/Multi/MultiSplitRoutingTests.cs | 3 +- .../Pathing/Multi/MultiWalkabilityTests.cs | 6 ++- .../Engines/Pathing/StepCacheFileTests.cs | 6 ++- .../Pathing/StepCacheLifecycleTests.cs | 9 +++-- .../Engines/Pathing/StepCacheParityTests.cs | 3 +- .../Engines/Pathing/StepProbeParityTests.cs | 3 +- .../Tests/Engines/Pathing/StepProbeTests.cs | 9 +++-- .../Tests/Mobiles/AI/ApproachTargetTests.cs | 6 ++- 17 files changed, 112 insertions(+), 39 deletions(-) create mode 100644 Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs diff --git a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs b/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs index 33bdb3634..076f88141 100644 --- a/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs +++ b/Projects/UOContent.Tests/Fixtures/TestServerInitializer.cs @@ -30,6 +30,13 @@ internal static class TestServerInitializer private static bool _initialized; private static readonly Lock _lock = new(); + /// + /// True if the UO client tile data was found and loaded. When false (e.g. CI, where the + /// copyrighted client files are absent), tile/map/multi-dependent tests must skip rather than + /// fail. Guard such tests with Skip.If(!TestServerInitializer.TileDataLoaded, ...). + /// + public static bool TileDataLoaded { get; private set; } + public static void Initialize() { lock (_lock) @@ -62,7 +69,10 @@ internal static class TestServerInitializer // flags are populated before anything that reads TileData (MultiData, MovementImpl, // CheckMovement). Without this, TileData.MaxItemValue is 0 at MultiData.Configure() // time, causing every MCL tile ID to be masked to 0 and stored as ID=0 in Tiles[x][y]. - ForceLoadTileData(); + // The copyrighted client files are absent on CI; when tiledata.mul is missing we skip + // the tile/map/multi-dependent bootstrap and leave TileDataLoaded false so those tests + // skip instead of failing the whole collection from the fixture constructor. + TileDataLoaded = TryForceLoadTileData(); // Production runs every static Configure() via AssemblyHandler.Invoke("Configure"); // the fixture calls a curated subset, so configure the pathfinding singleton here so @@ -70,11 +80,15 @@ internal static class TestServerInitializer // calls Find. ServerConfiguration is already loaded above, so the setting resolves. BitmapAStarAlgorithm.Configure(); - // Multi component lists (multi.mul / MultiCollection.uop). Production invokes this via - // AssemblyHandler.Invoke("Configure"); the curated fixture subset must call it so that - // BaseMulti.Components (MultiData.GetComponents) returns real footprints instead of - // MultiComponentList.Empty. Required by the Multi pathfinding tests. - MultiData.Configure(); + if (TileDataLoaded) + { + // Multi component lists (multi.mul / MultiCollection.uop). Production invokes this via + // AssemblyHandler.Invoke("Configure"); the curated fixture subset must call it so that + // BaseMulti.Components (MultiData.GetComponents) returns real footprints instead of + // MultiComponentList.Empty. Required by the Multi pathfinding tests. Depends on the + // client files, so it only runs when tile data loaded. + MultiData.Configure(); + } World.Configure(); Timer.Init(0); @@ -86,14 +100,23 @@ internal static class TestServerInitializer DecayScheduler.Configure(); Server.Engines.Spawners.SpawnerJsonSerializer.Configure(); - VerifyTrammelTileDataLoaded(); + if (TileDataLoaded) + { + VerifyTrammelTileDataLoaded(); + } _initialized = true; } } - private static void ForceLoadTileData() + private static bool TryForceLoadTileData() { + var tileDataPath = Core.FindDataFile("tiledata.mul", false); + if (string.IsNullOrEmpty(tileDataPath) || !File.Exists(tileDataPath)) + { + return false; + } + var loadMethod = typeof(TileData).GetMethod( "Load", BindingFlags.Static | BindingFlags.NonPublic @@ -105,6 +128,7 @@ internal static class TestServerInitializer ); } loadMethod.Invoke(null, null); + return true; } private static void VerifyTrammelTileDataLoaded() diff --git a/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs b/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs new file mode 100644 index 000000000..7b9af1f27 --- /dev/null +++ b/Projects/UOContent.Tests/Fixtures/TileDataRequirement.cs @@ -0,0 +1,18 @@ +using Xunit; + +namespace Server.Tests; + +/// +/// Shared guard for tests that require the copyrighted UO client tile/map data (tiledata.mul and +/// friends), which is absent on CI. Call as the first statement of a +/// [SkippableFact]/[SkippableTheory] so the test is skipped — not failed — when the +/// data was not loaded. See . +/// +internal static class TileDataRequirement +{ + public static void SkipIfMissing() => + Skip.If( + !TestServerInitializer.TileDataLoaded, + "Requires UO client tile data (tiledata.mul); absent on CI." + ); +} diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs index ab9497729..20d729c0f 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/BitmapAStarAlgorithmTests.cs @@ -130,9 +130,10 @@ public class BitmapAStarAlgorithmTests blocker.Delete(); } - [Fact] + [SkippableFact] public void DynamicObstaclePass_RejectsCellOccupiedByImpassableItem() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[1]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs index a9796de7f..694374757 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/BoatPathTests.cs @@ -17,9 +17,10 @@ public class BoatPathTests private const int WaterX = 1450; private const int WaterY = 1770; - [Fact] + [SkippableFact] public void BoatDeck_HasWalkableSurfaceCells() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; @@ -44,9 +45,10 @@ public class BoatPathTests } } - [Fact] + [SkippableFact] public void BoatDeck_FootprintShape_IsPositionInvariant() { + TileDataRequirement.SkipIfMissing(); // The property Phase 2's local-frame, movement-invariant boat cache must preserve: // the deck's covered-cell shape (in local coords) is identical at two world positions. var map = Map.Maps[MapId]; diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs index 3fdb4fa3e..e82520ce9 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/FoundationRedesignTests.cs @@ -17,9 +17,10 @@ public class FoundationRedesignTests private const int PlaceX = 1500; private const int PlaceY = 1600; - [Fact] + [SkippableFact] public void SwappingComponents_ChangesFootprint() { + TileDataRequirement.SkipIfMissing(); var foundation = new SwappableFoundation(0x74); // GuildHouse footprint try { @@ -38,9 +39,10 @@ public class FoundationRedesignTests } } - [Fact] + [SkippableFact] public void RedesignReRegistered_RoutesNewFootprintToLivePath() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs index 41caa6255..8ecbd0591 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/HousePathRoutingTests.cs @@ -22,9 +22,10 @@ public class HousePathRoutingTests public WalkerStub() => Body = 0xC9; } - [Fact] + [SkippableFact] public void PathAround_NeverTraversesAWallCell() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var prevThreshold = StepCache.Instance.MissPromotionThreshold; StepCache.Instance.MissPromotionThreshold = 1; @@ -74,9 +75,10 @@ public class HousePathRoutingTests } } - [Fact] + [SkippableFact] public void Demolish_ReopensCoveredCells() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs index eb12df7d6..8aa0b8d19 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiEdgeCaseTests.cs @@ -25,9 +25,10 @@ public class MultiEdgeCaseTests /// GetStaticAndMultiTiles yields tiles from BOTH multis; the synthesizer must still /// agree with CheckMovement everywhere over the union footprint + halo. /// - [Fact] + [SkippableFact] public void OverlappingMultis_SynthesizerMatchesCheckMovement() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; @@ -75,9 +76,10 @@ public class MultiEdgeCaseTests /// blocks the rest — that vacuity is a fixture concern, not a synthesizer divergence (the /// synthesizer agrees with the oracle at every direction either way). /// - [Fact] + [SkippableFact] public void BoatOverWater_SynthesizerMatchesCheckMovement() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; @@ -98,9 +100,10 @@ public class MultiEdgeCaseTests /// re-registration pattern HouseFoundation uses on commit) and assert the synthesizer reads /// the LIVE, post-redesign Components — i.e. it matches CheckMovement on the NEW shape. /// - [Fact] + [SkippableFact] public void RedesignedFoundation_SynthesizerMatchesNewFootprint() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; @@ -136,10 +139,11 @@ public class MultiEdgeCaseTests /// dungeon cave-wall corner; add InlineData rows here — the assertion already covers them by /// construction. (Left intentionally unhunted: do not invent tree/dungeon coords.) /// - [Theory] + [SkippableTheory] [InlineData(MapId, HouseX, HouseY)] // known-good open Trammel placement (passes today) public void UserSuppliedScenarios_SynthesizerMatchesCheckMovement(int mapId, int x, int y) { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[mapId]; diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs index 8a544897c..7487ce8de 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskCacheTests.cs @@ -12,9 +12,10 @@ public class MultiMaskCacheTests private const int PlaceX = 1480; private const int PlaceY = 1620; - [Fact] + [SkippableFact] public void TryResolveCoveringMulti_FindsPlacedMulti_AndLocalIndices() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); @@ -38,9 +39,10 @@ public class MultiMaskCacheTests } } - [Fact] + [SkippableFact] public void IsInteriorLocalCell_TrueDeepInside_FalseAtEdge() { + TileDataRequirement.SkipIfMissing(); var multi = new TestMulti(GuildHouseId); try { @@ -122,9 +124,10 @@ public class MultiMaskCacheTests Assert.False(MultiMaskCache.TerrainTopBelow(map, PlaceX, PlaceY, (sbyte)(ground - 50))); } - [Fact] + [SkippableFact] public void PathThroughHouseInterior_IncrementsMultiMaskCacheHits() { + TileDataRequirement.SkipIfMissing(); // (PlaceX,PlaceY)=(1480,1620) is cluttered (footprint overlaps tall map statics → dirty), so it // would never serve the interior cache under the footprint-clean gate. Use a known flat/clear // spot so a house there is footprint-clean and its interior cells serve from the cache. @@ -242,9 +245,10 @@ public class MultiMaskCacheTests } } - [Fact] + [SkippableFact] public void ComputeFootprintClean_TrueAtNormalPlacement_FalseWhenSunk() { + TileDataRequirement.SkipIfMissing(); // (PlaceX,PlaceY) is a cluttered spot whose footprint overlaps tall map statics, so a guild // house there is never footprint-clean. Use a known flat/clear spot for the clean assertion. const int CleanX = 1560; diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs index 923784f4f..b415ec3d6 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiMaskSynthesisTests.cs @@ -11,9 +11,10 @@ public class MultiMaskSynthesisTests private const int PlaceX = 1480; private const int PlaceY = 1620; - [Fact] + [SkippableFact] public void ComputeMultiMaskAt_MatchesCheckMovement_OverFootprintAndHalo() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs index 4775b8fac..1103cdb83 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiSplitRoutingTests.cs @@ -17,9 +17,10 @@ public class MultiSplitRoutingTests private const int PlaceX = 1500; private const int PlaceY = 1600; - [Fact] + [SkippableFact] public void PlacedMulti_RoutesFootprintAndHalo_ToFallthroughMulti() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs index 59e583ac6..d8d46e7f8 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/Multi/MultiWalkabilityTests.cs @@ -23,9 +23,10 @@ public class MultiWalkabilityTests public WalkerStub() => Body = 0xC9; } - [Fact] + [SkippableFact] public void WallCell_CannotBeEnteredFromAnyAdjacentCell() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); @@ -86,9 +87,10 @@ public class MultiWalkabilityTests } } - [Fact] + [SkippableFact] public void FloorCell_IsStandable() { + TileDataRequirement.SkipIfMissing(); StepCache.Instance.Clear(); var map = Map.Maps[MapId]; map.GetAverageZ(PlaceX, PlaceY, out _, out var z, out _); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs index 0d865d08d..1ef3aa8ab 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheFileTests.cs @@ -347,9 +347,10 @@ public class StepCacheFileTests /// to which proves the /// default lazy behavior. /// - [Fact] + [SkippableFact] public void TryOpenLazyReader_WithPreloadFlag_MaterializesAllChunksImmediately() { + TileDataRequirement.SkipIfMissing(); var cache = StepCache.Instance; cache.Clear(); cache.MissPromotionThreshold = 1; @@ -401,9 +402,10 @@ public class StepCacheFileTests } } - [Fact] + [SkippableFact] public void LazyReaderHit_BypassesMissTrackerOnFirstTouch() { + TileDataRequirement.SkipIfMissing(); var cache = StepCache.Instance; cache.Clear(); cache.MissPromotionThreshold = 1; // eager build for save phase diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs index 244c51534..436f78c7e 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheLifecycleTests.cs @@ -54,9 +54,10 @@ public class StepCacheLifecycleTests Assert.Equal(1L, stats.FallthroughNotBuilt); } - [Fact] + [SkippableFact] public void TryGetMask_SecondTouchWithinWindow_PromotesAndBuilds() { + TileDataRequirement.SkipIfMissing(); var cache = StepCache.Instance; cache.Clear(); cache.MissPromotionThreshold = 2; @@ -112,9 +113,10 @@ public class StepCacheLifecycleTests Assert.Equal(2L, cache.GetStats().FallthroughNotBuilt); } - [Fact] + [SkippableFact] public void TryGetMask_MultipleCallsInSameFindGeneration_StayInFallthrough() { + TileDataRequirement.SkipIfMissing(); var cache = StepCache.Instance; cache.Clear(); cache.MissPromotionThreshold = 2; @@ -207,9 +209,10 @@ public class StepCacheLifecycleTests Assert.Equal((byte)0, lookup.WalkMask); } - [Fact] + [SkippableFact] public void MultiCoveredCell_AndHalo_RouteToFallthrough() { + TileDataRequirement.SkipIfMissing(); var cache = StepCache.Instance; cache.Clear(); cache.MissPromotionThreshold = 1; // eager build so a multi-free cell serves immediately diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs index 149a9fe13..0dc1eb12f 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepCacheParityTests.cs @@ -111,9 +111,10 @@ public class StepCacheParityTests /// (Atlantic coast) and asserts at least one cell has a non-zero WetMask. Catches the /// failure mode where StepProbe silently bakes zero swim output everywhere. /// - [Fact] + [SkippableFact] public void SwimBake_ProducesWetCells_OnKnownWaterRegion() { + TileDataRequirement.SkipIfMissing(); var map = Map.Maps[1]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeParityTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeParityTests.cs index df8ff5da5..429d60ca7 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeParityTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeParityTests.cs @@ -14,11 +14,12 @@ public class StaticWalkabilityParityTests _output = output; } - [Theory] + [SkippableTheory] [InlineData("britain_inn_dense", 1480, 1610, 32)] [InlineData("trammel_open_plain", 1500, 1600, 32)] public void BakerMatchesCheckMovement(string label, int xStart, int yStart, int size) { + TileDataRequirement.SkipIfMissing(); var map = Map.Maps[1]; Assert.NotNull(map); diff --git a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs index cfca235da..9a32d66bf 100644 --- a/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/Pathing/StepProbeTests.cs @@ -124,9 +124,10 @@ public class StepProbeTests Assert.True(found, "Expected at least one fully-flat open cell in (1500..1532, 1600..1632)"); } - [Fact] + [SkippableFact] public void ComputeMaskAt_BritainInnDense_HasCellWithBlockedDirections() { + TileDataRequirement.SkipIfMissing(); // Invariant: inside the dense Britain inn region, at least one cell must have // at least one direction blocked by a static. Protects against a regression // where the baker reports everything as walkable (the original false-pass bug). @@ -181,9 +182,10 @@ public class StepProbeTests } } - [Fact] + [SkippableFact] public void ComputeMaskAt_PinnedCell_TrammelOpenPlainOrigin() { + TileDataRequirement.SkipIfMissing(); // PINNING test: locks specific output for Trammel (1500, 1600). // Cell at z=10 with mask 0xC1 (N + W + NW only walkable). The other five // directions are blocked by water (E/SE/S/SW are wet tiles, NE is shore). @@ -214,9 +216,10 @@ public class StepProbeTests Assert.Equal((sbyte)10, result.GetWalkZ(Direction.Up)); } - [Fact] + [SkippableFact] public void ComputeMaskAt_PinnedCell_BritainInnDenseOrigin() { + TileDataRequirement.SkipIfMissing(); // PINNING test: locks specific output for Trammel (1480, 1610). // Cell at z=20 with mask 0x3F (N/NE/E/SE/S/SW walkable, W/NW blocked by // a wall to the west). All walkable directions stay flat at z=20. diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs index fa3eb23bd..923f4aff9 100644 --- a/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/ApproachTargetTests.cs @@ -176,9 +176,10 @@ public class ApproachTargetTests Assert.True(caught, "chaser must catch a target that walks away then stops"); } - [Fact] + [SkippableFact] public void UnreachableTarget_GivesUp_AndIdles() { + TileDataRequirement.SkipIfMissing(); var map = Map.Maps[1]; Assert.NotNull(map); map.GetAverageZ(1500, 1601, out _, out var z, out _); @@ -243,9 +244,10 @@ public class ApproachTargetTests Assert.True(stayedIdle, "after giving up, the creature must idle, not shuffle"); } - [Fact] + [SkippableFact] public void WallBetween_RoutesAround_ReachesTarget() { + TileDataRequirement.SkipIfMissing(); var map = Map.Maps[1]; Assert.NotNull(map); map.GetAverageZ(1500, 1600, out _, out var z, out _);