diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-21 23:12:20 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-21 23:12:20 +0800 |
| commit | 8bc60b461c5ec90ccf2820b7a3d8b1fc74b526a1 (patch) | |
| tree | 7338a21b22667de59bb6d056c276a5e6e2f11315 /src | |
| parent | d94ec2dc875c6c45973c6d6d6730b4a57fadeb3e (diff) | |
| download | living-village-8bc60b461c5ec90ccf2820b7a3d8b1fc74b526a1.tar.gz | |
test(sim): M6a 谣言裁剪回归——容量内零拷贝与逐字节一致/按天淘汰/单调 id/DayIndex 存档往返
Diffstat (limited to 'src')
| -rw-r--r-- | src/LivingVillage.Kernel.Tests/RumorTests.fs | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/LivingVillage.Kernel.Tests/RumorTests.fs b/src/LivingVillage.Kernel.Tests/RumorTests.fs index f52fca9..2cbb189 100644 --- a/src/LivingVillage.Kernel.Tests/RumorTests.fs +++ b/src/LivingVillage.Kernel.Tests/RumorTests.fs @@ -91,6 +91,29 @@ module private RumorHarness = rumor.Depth) |> String.concat "\n" + let rumor id tick dayIndex = + { Id = RumorId id + Tick = tick + DayIndex = dayIndex + OriginTick = tick + Source = NpcId 0 + Narrator = NpcId 0 + Receiver = NpcId 1 + Parent = None + Depth = 0 + Strength = 1.0f } + + let withRumorBudget capacity retentionDays action = + let previousCapacity = rumorCapacity + let previousRetention = rumorRetentionDays + rumorCapacity <- capacity + rumorRetentionDays <- retentionDays + try + action () + finally + rumorCapacity <- previousCapacity + rumorRetentionDays <- previousRetention + [<TestClass>] type RumorTests () = @@ -188,3 +211,67 @@ type RumorTests () = sprintf "rumor id=2 tick=%d origin_tick=0 source=0 narrator=2 receiver=3 parent=1 depth=2 strength=0.250000 path=0,1,2" (2L * ticksPerDay) ] Assert.AreEqual<string>(expected, Sim.rumorTraceText third) + + [<TestMethod>] + member _.TrimWithinCapacityAndFreshWindowIsZeroCopy () = + RumorHarness.withRumorBudget 4 3L (fun () -> + let rumors = + [ RumorHarness.rumor 3L (3L * ticksPerDay) 3L + RumorHarness.rumor 2L (2L * ticksPerDay) 2L + RumorHarness.rumor 1L (1L * ticksPerDay) 1L ] + let trimmed = Sim.trimRumors (3L * ticksPerDay) rumors + Assert.IsTrue(System.Object.ReferenceEquals(rumors, trimmed), "within budget the list must be returned as-is") + Assert.AreEqual<RumorEvent list>(rumors, trimmed)) + + [<TestMethod>] + member _.TrimCapsToNewestRumors () = + RumorHarness.withRumorBudget 4 30L (fun () -> + let rumors = List.init 6 (fun index -> RumorHarness.rumor (int64 (5 - index)) (10L * ticksPerDay) 10L) + let trimmed = Sim.trimRumors (10L * ticksPerDay) rumors + Assert.AreEqual<int>(4, trimmed.Length) + let keptIds = trimmed |> List.map (fun rumor -> RumorHarness.rumorId rumor.Id) + CollectionAssert.AreEqual([| 5L; 4L; 3L; 2L |], List.toArray keptIds)) + + [<TestMethod>] + member _.TrimDropsRumorsOlderThanRetentionWindow () = + RumorHarness.withRumorBudget 100 3L (fun () -> + let rumors = + [ RumorHarness.rumor 3L (10L * ticksPerDay) 10L + RumorHarness.rumor 2L (9L * ticksPerDay) 9L + RumorHarness.rumor 1L (6L * ticksPerDay) 6L + RumorHarness.rumor 0L (5L * ticksPerDay) 5L ] + let trimmed = Sim.trimRumors (10L * ticksPerDay) rumors + let keptIds = trimmed |> List.map (fun rumor -> RumorHarness.rumorId rumor.Id) + CollectionAssert.AreEqual([| 3L; 2L |], List.toArray keptIds) + Assert.AreEqual<RumorEvent list>(trimmed, Sim.trimRumors (10L * ticksPerDay) trimmed)) + + [<TestMethod>] + member _.ChatKeepsWorkingSetWithinCapacityAndIdsStayMonotonic () = + RumorHarness.withRumorBudget 1 30L (fun () -> + let first = RumorHarness.chat 0 1 (RumorHarness.world 0L 4) + let second = RumorHarness.chat 1 2 (RumorHarness.readyForChat first) + Assert.AreEqual<int>(1, first.Rumors.Length) + Assert.AreEqual<int>(1, second.Rumors.Length) + let head = second.Rumors |> List.head + Assert.AreEqual<int64>(1L, RumorHarness.rumorId head.Id) + Assert.AreEqual<RumorId option>(Some(RumorId 0L), head.Parent)) + + [<TestMethod>] + member _.CappedRumorsWithinCapacitySaveByteIdenticallyToUncapped () = + let run capacity = + RumorHarness.withRumorBudget capacity 30L (fun () -> + let first = RumorHarness.chat 0 1 (RumorHarness.world 0L 4) + let second = RumorHarness.chat 1 2 (RumorHarness.readyForChat { first with Tick = ticksPerDay }) + WorldSave.save second) + Assert.AreEqual<string>(run 8, run System.Int32.MaxValue) + + [<TestMethod>] + member _.RumorDayIndexMirrorsTickAndSurvivesSaveRoundTrip () = + let world = RumorHarness.chat 0 1 (RumorHarness.world 0L 3) + let rumor = world.Rumors |> List.head + Assert.AreEqual<int64>(rumor.Tick / ticksPerDay, rumor.DayIndex) + match WorldSave.load (WorldSave.save world) with + | Error failure -> Assert.Fail($"round trip should load: {failure}") + | Ok restored -> + Assert.AreEqual<RumorEvent list>(world.Rumors, restored.Rumors) + Assert.AreEqual<int64>(rumor.DayIndex, (restored.Rumors |> List.head).DayIndex) |
