using Theriapolis.Core; using Theriapolis.Core.Tactical; using Theriapolis.Core.Util; using Xunit; namespace Theriapolis.Tests.Tactical; /// /// Streamer-level invariants: caching, eviction, and delta round-trip. /// public sealed class ChunkStreamerTests : IClassFixture { 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); } }