ModernUO/Projects/UOContent.Tests/Tests/Engines/Pathing/PathingTestSupport.cs
Kamron Batman 64643dbc7e
test(pathing): consolidate the parity and lifecycle tests
Three files tested "parity" and none of them said which parity. They were in fact
three different layers, and the seams between them were the useful part, so they are
now one StepCacheParityTests that names them:

  ProbeMatchesSlowPath        StepProbe vs MovementImpl - is the bake right?
  CacheMatchesProbe           StepCache vs StepProbe    - is it stored and returned intact?
  CacheServesReachableWalkStates  StepCache vs MovementImpl - end to end, over the states A* visits

Merging them removed a duplicated stub Mobile, duplicated region seeds, and a
filename/class mismatch (StepProbeParityTests.cs declared StaticWalkabilityParityTests).
SwimBake_ProducesWetCells moved with it: it lived in the cache parity file but never
touched the cache - it is a probe test.

The layering is load-bearing, not cosmetic. Offsetting the cache's cell index by one
fails 7 of the 10 parity cases, and the 3 that stay green are exactly the ones that
don't touch the cache - so a failure localizes the fault instead of just reporting one.

Reflection: tests reached into StepCache._chunks via GetField in 9 places to inspect
or inject chunk state, each rebuilding the key and the cell-index arithmetic by hand.
StepCache now exposes GetResidentChunk and ResidentIndexInSync alongside the existing
internal test hooks (LazyReaderHasChunk, CurrentFindGeneration), and the shared
arithmetic moved to PathingTestSupport. All 9 reflection blocks are gone.

StepCacheLifecycleTests is regrouped by what it actually covers - promotion gate,
fallthrough routes, strata, swim layer, eviction - with the Tier4* names dropped
(same internal jargon already removed from the comments) and the repeated
offsets/strata byte-array construction replaced by builders.

Dropped Singleton_IsAvailable: it asserted that an inline-initialized static property
was not null, which it cannot be.

Test count 123 -> 122, entirely from that one removal; every parity case and every
behavioral assertion is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:48:57 -07:00

69 lines
2.4 KiB
C#

using System;
using Server.Engines.Pathing.Cache;
namespace Server.Tests.Pathfinding;
/// <summary>
/// Shared fixtures for the step-cache tests: the walker the parity tests measure against, the
/// cell-index arithmetic, and builders for the chunk state several tests inject by hand.
/// </summary>
internal static class PathingTestSupport
{
/// <summary>
/// Trammel. Every seed coordinate below is a real location on it, so these tests need the
/// client's map files; they skip when those are absent.
/// </summary>
public static Map TestMap => Map.Maps[1];
/// <summary>
/// A cell in open Britain countryside — flat, walkable in all directions, no statics. The
/// default subject when a test needs a chunk to exist and doesn't care what's in it.
/// </summary>
public const int PlainX = 1500;
public const int PlainY = 1600;
/// <summary>Index of world cell (x, y) within its own chunk.</summary>
public static int CellIndex(int x, int y) => ((y & 15) << 4) | (x & 15);
/// <summary>A strata offset table with every cell marked single-Z.</summary>
public static ushort[] NoStrataOffsets()
{
var offsets = new ushort[StepChunk.CellsPerChunk];
Array.Fill(offsets, StepChunk.NoStrata);
return offsets;
}
/// <summary>
/// Packs a one-stratum record: a count byte, then the stratum itself. Directions not named in
/// <paramref name="walkZs"/> stay at 0. Mirrors the layout StepCache.WriteStratum produces.
/// </summary>
public static byte[] OneStratum(sbyte zCenter, byte walkMask = 0, byte wetMask = 0, params sbyte[] walkZs)
{
var data = new byte[1 + StepChunk.StratumByteLength];
data[0] = 1; // stratum count
data[1] = (byte)zCenter;
data[2] = walkMask;
data[3] = wetMask;
// walkZ_N..NW occupy bytes 4..11; swimZ_N..NW follow at 12..19.
for (var i = 0; i < walkZs.Length && i < 8; i++)
{
data[4 + i] = (byte)walkZs[i];
}
return data;
}
/// <summary>
/// The default static walker. Deriving straight from <see cref="Mobile"/> rather than
/// BaseCreature is the point: MovementImpl then sees no creature capabilities (no swim, no fly,
/// no door-opening), which is exactly the walker the cache bakes for.
/// </summary>
public sealed class StaticWalker : Mobile
{
public StaticWalker()
{
Body = 0xC9;
}
}
}