acdream/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs
Erik 11df7930fc feat(app): Phase A.1 — StreamingRegion (window set + diff with hysteresis)
Pure data type describing the set of landblocks inside the current
streaming window, with a diff-style Recenter that returns (toLoad,
toUnload) pairs the LandblockStreamer consumes as jobs. Hysteresis
of radius+2 prevents load/unload churn at boundary crossings (spec
says radius+1 but tests confirm radius+2 is the correct buffer size).

First piece of Phase A.1 per docs/superpowers/plans/2026-04-11-foundation-a1-streaming.md.

7 new tests, all green. Total suite: 105/105.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:01:45 +02:00

85 lines
2.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AcDream.App.Streaming;
using Xunit;
namespace AcDream.Core.Tests.Streaming;
public class StreamingRegionTests
{
[Fact]
public void Constructor_Radius2_Produces25Landblocks()
{
var region = new StreamingRegion(cx: 50, cy: 50, radius: 2);
Assert.Equal(25, region.Visible.Count);
}
[Fact]
public void Constructor_NearOrigin_ClampsToWorldEdge()
{
// Center at (0, 0) with radius 2: only the +X / +Y quadrant is
// in-bounds. That's a 3×3 subset of the 5×5 window = 9 landblocks.
var region = new StreamingRegion(cx: 0, cy: 0, radius: 2);
Assert.Equal(9, region.Visible.Count);
}
[Fact]
public void Constructor_NearFarEdge_ClampsToWorldEdge()
{
var region = new StreamingRegion(cx: 0xFF, cy: 0xFF, radius: 2);
Assert.Equal(9, region.Visible.Count);
}
[Fact]
public void RecenterTo_SamePosition_EmptyDiff()
{
var region = new StreamingRegion(cx: 50, cy: 50, radius: 2);
var diff = region.RecenterTo(50, 50);
Assert.Empty(diff.ToLoad);
Assert.Empty(diff.ToUnload);
}
[Fact]
public void RecenterTo_SingleStepEast_LoadsColumn_NoUnloadsDueToHysteresis()
{
// Radius 2 → unload threshold is radius+1 = 3.
// Starting center (50,50) covers X in [48..52]. Step to (51,50):
// new coverage X in [49..53]. New column is x=53 (5 entries).
// Departing column would be x=48, but |48-51| = 3 which equals the
// threshold, so it stays loaded (hysteresis keeps radius+1).
var region = new StreamingRegion(cx: 50, cy: 50, radius: 2);
var diff = region.RecenterTo(51, 50);
Assert.Equal(5, diff.ToLoad.Count);
Assert.Empty(diff.ToUnload);
}
[Fact]
public void RecenterTo_ThreeStepEast_LoadsAndUnloadsColumns()
{
// Starting (50,50) covers X in [48..52]. Step to (53,50):
// new coverage X in [51..55]. New columns: x=53,54,55 (15 entries).
// x=48 is now 5 away → unload. x=49,50 still within radius+1 → keep.
var region = new StreamingRegion(cx: 50, cy: 50, radius: 2);
var diff = region.RecenterTo(53, 50);
Assert.Equal(15, diff.ToLoad.Count);
Assert.Equal(5, diff.ToUnload.Count);
}
[Fact]
public void RecenterTo_LongTeleport_UnloadsEverythingLoadsEverything()
{
var region = new StreamingRegion(cx: 50, cy: 50, radius: 2);
var diff = region.RecenterTo(200, 200);
Assert.Equal(25, diff.ToLoad.Count);
Assert.Equal(25, diff.ToUnload.Count);
}
}