using System; using System.Linq; using TrajectoryPlanningVisualization; namespace TrajectoryPlanningVisualizationVerificationHost; internal static class RuntimeChecks { public static void Run() { KeepsOnlyTheLatestFrame(); KeepsBoundedDeduplicatedCycleHistory(); SerializesStableCamelCaseJson(); RejectsInvalidOptions(); } private static void KeepsOnlyTheLatestFrame() { var store = new LatestVisualizationFrameStore(); store.Publish(Snap(1)); store.Publish(Snap(2)); Verification.True(store.TryReadAfter(0, out VisualizationFrame frame), "latest frame exists"); Verification.Equal(2L, frame.Snapshot.Sequence, "latest frame replaces old frame"); Verification.True(!store.TryReadAfter(frame.Version, out _), "same frame is not replayed"); } private static void KeepsBoundedDeduplicatedCycleHistory() { var history = new BoundedCycleHistory(2); history.Add(Cycle(1)); history.Add(Cycle(2)); history.Add(Cycle(3)); history.Add(Cycle(3)); Verification.Equal("2|3", string.Join("|", history.Snapshot().Select(x => x.CycleVersion)), "history is bounded and deduplicated"); } private static void SerializesStableCamelCaseJson() { string json = VisualizationJson.Serialize(Snap(2)); Verification.True(json.Contains("\"sessionStateChinese\"") && json.Contains("\"observedAtUtc\""), "JSON uses stable camel case names"); } private static void RejectsInvalidOptions() { Verification.Throws( () => new PlanningVisualizationOptions { Port = -1 }.CreateValidatedSnapshot(), "options reject a negative port"); Verification.Throws( () => new PlanningVisualizationOptions { RefreshRateHz = 0 }.CreateValidatedSnapshot(), "options reject a zero refresh rate"); Verification.Throws( () => new PlanningVisualizationOptions { HistoryCycleLimit = 0 }.CreateValidatedSnapshot(), "options reject a zero history limit"); Verification.Throws( () => new PlanningVisualizationOptions { Port = 65536 }.CreateValidatedSnapshot(), "options reject a port above the TCP range"); var snapshot = new PlanningVisualizationOptions().CreateValidatedSnapshot(); Verification.Equal(0, snapshot.Port, "default port is automatic"); Verification.Equal(10, snapshot.RefreshRateHz, "default refresh rate is 10 Hz"); Verification.Equal(60, snapshot.HistoryCycleLimit, "default history is bounded to 60 cycles"); Verification.Equal(2, snapshot.MaximumClients, "maximum clients is fixed at two"); } private static PlanningVisualizationDynamicSnapshot Snap(long sequence) { return new PlanningVisualizationDynamicSnapshot( sequence, DateTimeOffset.Parse("2026-08-06T00:00:00+00:00").AddSeconds(sequence), "运行中", 0, "forward", new VisualizationPose(1d, 2d, 0d), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Cycle(sequence)); } private static VisualizationCycleSummary Cycle(long cycleVersion) { return new VisualizationCycleSummary( cycleVersion, DateTimeOffset.Parse("2026-08-06T00:00:00+00:00").AddSeconds(cycleVersion), "成功", true, 2.5d, 0, "forward", "rolling", "none", null, null, ""); } }