summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 22:08:57 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 22:08:57 +0800
commit868509ce1d7ef0cc2f132475d6dd1aa80aac57b6 (patch)
tree79f3a4ed73a6a9b94b64d1da415dfd829c102c5b /src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs
parent0ae0173c6b25e54c9a4b97b0cc231356466cd7f8 (diff)
downloadliving-village-868509ce1d7ef0cc2f132475d6dd1aa80aac57b6.tar.gz
test(desktop): M3 批次输出格式与流式驱动回归
- world 行前缀不变、行尾 elapsed_s;[done k/N] 与 summary 追加字段断言 - runStreaming workers=1 断言逐世界 emit(非收完再打印);并行下不重不漏 - costLine ticks/s 由实测 ticks/墙钟推导
Diffstat (limited to 'src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs')
-rw-r--r--src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs b/src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs
new file mode 100644
index 0000000..e962e57
--- /dev/null
+++ b/src/LivingVillage.Desktop.Tests/M3BatchOutputTests.fs
@@ -0,0 +1,84 @@
+namespace LivingVillage.Desktop.Tests
+
+open System.Threading
+open System.Threading.Tasks
+open Microsoft.VisualStudio.TestTools.UnitTesting
+open LivingVillage.Headless
+
+/// M3-1 可观测性回归:批次输出格式与逐世界流式驱动。
+[<TestClass>]
+type M3BatchOutputTests () =
+
+ let sampleBase =
+ "world=3 seed=45 chats_max=9 chats_avg=2.00 ratio=4.500 gini=0.100 relcnt_max=5 relcnt_min=1 relconn=6/6 nonfinite=0 check_a=PASS check_b=PASS check_c=PASS old_crit=PASS OK"
+
+ [<TestMethod>]
+ member _.WorldLineKeepsPrefixAndAppendsElapsed () =
+ let line = BatchOutput.worldLine sampleBase 123.4
+ Assert.IsTrue(line.StartsWith("world=3 seed=45 "), "world 行前缀必须保持不变")
+ Assert.IsTrue(line.StartsWith(sampleBase), "判据字段必须原样保留")
+ Assert.IsTrue(line.EndsWith("elapsed_s=123.4"), "耗时字段追加在行尾")
+ // 旧判据 token 仍在(监督器据此判定 OK)。
+ Assert.AreEqual<string>(sampleBase + " elapsed_s=123.4", line)
+
+ [<TestMethod>]
+ member _.DoneLineReportsProgressAndElapsed () =
+ Assert.AreEqual<string>("[done 2/4] elapsed=12.3s world=1", BatchOutput.doneLine 2 4 12.3 1)
+ Assert.AreEqual<string>("[done 4/4] elapsed=600.0s world=3", BatchOutput.doneLine 4 4 600.0 3)
+
+ [<TestMethod>]
+ member _.SummaryLineKeepsOldFieldsAndAppendsWallClock () =
+ let line = BatchOutput.summaryLine 4 3 1 4 1.100 9.900 0.010 0.400 4 610.5
+ // 监督器正则只读前三个字段,前缀必须逐字保持。
+ Assert.IsTrue(line.StartsWith("batch_summary worlds=4 passed=3 failed=1 old_passed=4/4 "), line)
+ Assert.IsTrue(line.Contains("ratio_min=1.100 ratio_max=9.900 gini_min=0.010 gini_max=0.400"), line)
+ Assert.IsTrue(line.Contains("elapsed_s=610.5"), line)
+ Assert.IsTrue(line.EndsWith("workers=4 wall_s=610.5"), line)
+
+ [<TestMethod>]
+ member _.CostLineDerivesThroughputFromMeasuredTicks () =
+ let line = BatchOutput.costLine 10L 51840000L 254.0 12345678L 3L 1L 0L
+ Assert.IsTrue(line.StartsWith("cost days=10 ticks=51840000 elapsed_s=254.000 "), line)
+ Assert.IsTrue(line.Contains("ticks_per_s=204094.5"), line)
+ Assert.IsTrue(line.EndsWith("allocated_bytes=12345678 gen0=3 gen1=1 gen2=0"), line)
+
+ [<TestMethod>]
+ member _.DefaultCostLadderIsOneFiveTenTwentyFiftyHundred () =
+ CollectionAssert.AreEqual([| 1L; 5L; 10L; 20L; 50L; 100L |], List.toArray BatchOutput.defaultCostLadder)
+
+ [<TestMethod>]
+ member _.RunStreamingEmitsEachWorldBeforeTheNextStarts () =
+ // workers=1:每次 work 后必须立刻 emit 该世界(证明不是收完再打印)。
+ let events = ResizeArray<string> ()
+ let results =
+ BatchOutput.runStreaming 3 1
+ (fun k ->
+ lock events (fun () -> events.Add(sprintf "work%d" k))
+ k * k)
+ (fun k _ _ ->
+ lock events (fun () -> events.Add(sprintf "emit%d" k)))
+ CollectionAssert.AreEqual([| 0; 1; 4 |], results)
+ CollectionAssert.AreEqual(
+ [| "work0"; "emit0"; "work1"; "emit1"; "work2"; "emit2" |],
+ events.ToArray())
+
+ [<TestMethod>]
+ member _.RunStreamingIsCompleteUnderParallelism () =
+ let total = 12
+ let emitted = System.Collections.Concurrent.ConcurrentDictionary<int, int> ()
+ let completions = ResizeArray<int> ()
+ let results =
+ BatchOutput.runStreaming total 4
+ (fun k -> k + 100)
+ (fun k result doneCount ->
+ Assert.AreEqual<int>(k + 100, result)
+ emitted.[k] <- doneCount
+ lock completions (fun () -> completions.Add doneCount))
+ Assert.AreEqual<int>(total, results.Length)
+ Assert.AreEqual<int>(total, emitted.Count)
+ for k in 0 .. total - 1 do
+ Assert.IsTrue(emitted.ContainsKey k, sprintf "world=%d 未 emit" k)
+ // 完成计数恰为 1..total 的一个排列(并发下不重不漏)。
+ CollectionAssert.AreEqual(
+ [| 1 .. total |],
+ completions |> Seq.sort |> Seq.toArray)