From 6b148362c00084c474541a88165fa52558f8181e Mon Sep 17 00:00:00 2001 From: MDietermann Date: Mon, 1 Jun 2026 13:24:20 +0200 Subject: [PATCH] test: end-to-end streaming + modification-store roundtrip --- .../Streaming/StreamingIntegrationTests.cs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs diff --git a/VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs b/VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs new file mode 100644 index 0000000..66935a1 --- /dev/null +++ b/VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs @@ -0,0 +1,84 @@ +using OpenTK.Mathematics; +using VoxelEngine.Core; +using VoxelEngine.World; +using VoxelEngine.World.Blocks; +using VoxelEngine.World.Chunk; +using VoxelEngine.World.Persistence; +using VoxelEngine.World.Streaming; +using VoxelEngine.World.WorldGen; +using Xunit; + +namespace VoxelEngine.Tests.Streaming; + +public class StreamingIntegrationTests +{ + private static (ChunkStreamer streamer, World.World world, InMemoryChunkModificationStore store) + BuildRealStack(Configuration cfg) + { + var registry = new BlockRegistry(); + var store = new InMemoryChunkModificationStore(); + var world = new World.World(registry, store); + var gen = new WorldGenerator(seed: 1337, blocks: registry); + var mesher = new ChunkMesher(atlasWidthPixels: 128, atlasHeightPixels: 32); + var executor = new SynchronousJobExecutor(); + Func factory = m => new FakeRenderer(m); + var streamer = new ChunkStreamer(cfg, world, gen.Generate, mesher, registry, executor, factory); + return (streamer, world, store); + } + + [Fact] + public void PlayerWalksAcrossWorld_LoadBoxFollowsPlayer() + { + var cfg = new Configuration + { + RenderDistanceHorizontal = 1, + RenderDistanceVertical = 0, + MaxGenerateJobsInFlight = 100, + MaxMeshUploadsPerFrame = 100, + }; + var (streamer, world, _) = BuildRealStack(cfg); + + for (int step = 0; step < 5; step++) + { + streamer.Tick(new Vector3i(step, 0, 0)); + + for (int dx = -1; dx <= 1; dx++) + for (int dz = -1; dz <= 1; dz++) + { + var pos = new Vector3i(step + dx, 0, dz); + Assert.NotNull(world.GetChunk(pos)); + Assert.True(streamer.IsReady(pos)); + } + } + } + + [Fact] + public void ModificationStore_SurvivesUnloadReload() + { + var cfg = new Configuration + { + RenderDistanceHorizontal = 0, + RenderDistanceVertical = 0, + MaxGenerateJobsInFlight = 100, + MaxMeshUploadsPerFrame = 100, + }; + var (streamer, world, store) = BuildRealStack(cfg); + + // Use a block id that differs from what WorldGenerator places at + // world (0,0,0). At y=0 every biome's "Under" block is Stone (id 1), + // so SetBlock(..., Stone) would hit World.SetBlock's no-op guard and + // skip the diff record. Dirt (id 2) is always a real change. + var pos = new Vector3i(0, 0, 0); + streamer.Tick(pos); + Assert.True(streamer.IsReady(pos)); + Assert.True(world.SetBlock(0, 0, 0, /*Dirt*/ (ushort)2)); + + streamer.Tick(new Vector3i(50, 0, 0)); + Assert.Null(world.GetChunk(pos)); + Assert.True(store.HasDiff(pos)); + + streamer.Tick(pos); + Assert.NotNull(world.GetChunk(pos)); + Assert.Equal((ushort)2, world.GetBlock(0, 0, 0).Id); + } +}