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>
85 lines
2.6 KiB
C#
85 lines
2.6 KiB
C#
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);
|
||
}
|
||
}
|