Files
TheriapolisV3/Theriapolis.Tests/Tactical/ChunkStreamerTests.cs
T
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:40:51 -07:00

84 lines
3.0 KiB
C#

using Theriapolis.Core;
using Theriapolis.Core.Tactical;
using Theriapolis.Core.Util;
using Xunit;
namespace Theriapolis.Tests.Tactical;
/// <summary>
/// Streamer-level invariants: caching, eviction, and delta round-trip.
/// </summary>
public sealed class ChunkStreamerTests : IClassFixture<WorldCache>
{
private const ulong TestSeed = 0xCAFEBABEUL;
private readonly WorldCache _cache;
public ChunkStreamerTests(WorldCache c) => _cache = c;
[Fact]
public void Get_CachesSubsequentCalls()
{
var w = _cache.Get(TestSeed).World;
var streamer = new ChunkStreamer(TestSeed, w, new InMemoryChunkDeltaStore());
var cc = new ChunkCoord(3, 3);
var first = streamer.Get(cc);
var second = streamer.Get(cc);
Assert.Same(first, second);
}
[Fact]
public void EnsureLoaded_PopulatesCacheNearPlayer()
{
var w = _cache.Get(TestSeed).World;
var streamer = new ChunkStreamer(TestSeed, w, new InMemoryChunkDeltaStore());
// Centre on world tile (50, 50) → tactical-pixel (1600, 1600).
var pos = new Vec2(50 * C.WORLD_TILE_PIXELS, 50 * C.WORLD_TILE_PIXELS);
streamer.EnsureLoadedAround(pos, worldTileRadius: C.TACTICAL_WINDOW_WORLD_TILES);
Assert.NotEmpty(streamer.Loaded);
}
[Fact]
public void DeltaRoundtrip_PreservesTileEdits()
{
var w = _cache.Get(TestSeed).World;
var deltas = new InMemoryChunkDeltaStore();
var streamer = new ChunkStreamer(TestSeed, w, deltas);
var cc = new ChunkCoord(8, 8);
var chunk = streamer.Get(cc);
// Pick a known tile, edit it, mark the chunk dirty.
ref var t = ref chunk.Tiles[10, 10];
var origSurface = t.Surface;
var newSurface = origSurface == TacticalSurface.Cobble ? TacticalSurface.Sand : TacticalSurface.Cobble;
t.Surface = newSurface;
t.Deco = TacticalDeco.None;
chunk.HasDelta = true;
// Force the streamer above the cache cap so the chunk gets evicted
// (and its delta flushed). Easiest way: load enough other chunks.
for (int i = 0; i < C.CHUNK_CACHE_SOFT_MAX + 2; i++)
streamer.Get(new ChunkCoord(100 + i, 100));
streamer.EnsureLoadedAround(new Vec2(100 * C.WORLD_TILE_PIXELS, 100 * C.WORLD_TILE_PIXELS),
worldTileRadius: 1);
// Now reload our edited chunk and verify the delta was reapplied.
var reloaded = streamer.Get(cc);
Assert.Equal(newSurface, reloaded.Tiles[10, 10].Surface);
}
[Fact]
public void FlushAll_PersistsModifiedChunks()
{
var w = _cache.Get(TestSeed).World;
var deltas = new InMemoryChunkDeltaStore();
var streamer = new ChunkStreamer(TestSeed, w, deltas);
var cc = new ChunkCoord(2, 2);
var chunk = streamer.Get(cc);
chunk.Tiles[5, 5].Deco = TacticalDeco.Boulder;
chunk.HasDelta = true;
streamer.FlushAll();
Assert.NotNull(deltas.Get(cc));
Assert.NotEmpty(deltas.Get(cc)!.TileMods);
}
}