acdream/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs
Erik 449c2caf8b fix(app): Phase A.1 — separate Visible from Resident in StreamingRegion
Review follow-up from commit 11df793. Three fixes:

1. Visible semantics: StreamingRegion.Visible now strictly describes the
   current (2r+1)×(2r+1) window, not window + hysteresis retainees.
   Added a parallel Resident property exposing the actual loaded set
   (window + hysteresis buffer). This matters because StreamingController
   (next task) reads these to decide what to render vs what to unload;
   conflating them in one set would have forced awkward post-processing
   downstream.

2. Doc/code disagreement: updated the RecenterTo and RegionDiff doc
   comments from "radius + 1" to "radius + 2" to match the actual
   implementation (which is what the tests require). Also updated the
   plan doc so future readers don't hit the same contradiction.

3. Edge-clamping test coverage: added a single-axis edge test
   (cx=0, cy=50 → 15 entries) and an ID-encoding test (radius=0 at
   0x12,0x34 → 0x1234FFFE) so a swapped-shift bug in EncodeLandblockId
   or an asymmetric off-by-one would fail a test instead of passing
   silently.

9 tests green, full suite regressions-free.

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

108 lines
3.3 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);
}
[Fact]
public void Constructor_NearXEdgeOnly_ClampsOnlyXAxis()
{
// cx=0, cy=50, radius=2:
// X is clamped to [0..2] (3 entries)
// Y is unclamped [48..52] (5 entries)
// Total = 3 × 5 = 15 landblocks.
var region = new StreamingRegion(cx: 0, cy: 50, radius: 2);
Assert.Equal(15, region.Visible.Count);
}
[Fact]
public void Constructor_SmallRadius_IDsMatchEncodingRule()
{
// Verify EncodeLandblockId is correct (not swapped shifts).
// radius=0 at (0x12, 0x34) → exactly one entry, id = 0x1234FFFE.
var region = new StreamingRegion(cx: 0x12, cy: 0x34, radius: 0);
Assert.Single(region.Visible);
Assert.Contains(0x1234FFFEu, region.Visible);
}
}