Files
ParkingRobot/ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/ContractChecks.cs
T

109 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using TrajectoryPlanningVisualization;
namespace TrajectoryPlanningVisualizationVerificationHost;
internal static class ContractChecks
{
public static void Run()
{
CopiesCollectionsAndRejectsNonFiniteNumbers();
StoresOccupancyAsDefensiveCompactBits();
ExposesImmutableSnapshotContracts();
DocumentsPublicSafetyAndLifecycleBoundary();
}
private static void CopiesCollectionsAndRejectsNonFiniteNumbers()
{
var points = new List<VisualizationPoint> { new VisualizationPoint(1d, 2d) };
var series = new VisualizationSeries("current", "当前轨迹", VisualizationLineStyle.Solid, points);
points[0] = new VisualizationPoint(9d, 9d);
Verification.NearlyEqual(1d, series.Points[0].X, "series copies points");
Verification.Throws<ArgumentOutOfRangeException>(
() => new VisualizationPoint(double.NaN, 0d), "point rejects NaN");
Verification.Throws<ArgumentOutOfRangeException>(
() => new VisualizationPose(0d, 0d, double.PositiveInfinity), "pose rejects infinity");
Verification.Throws<ArgumentOutOfRangeException>(
() => new VisualizationBounds(0d, 1d, 0d, double.NaN), "bounds rejects NaN");
Verification.Throws<ArgumentException>(
() => new VisualizationSeries("invalid", "无效", VisualizationLineStyle.Solid,
new VisualizationPoint[] { null! }), "series rejects null point");
}
private static void StoresOccupancyAsDefensiveCompactBits()
{
byte[] bits = { 0x01 };
var grid = new VisualizationOccupancyGrid(new VisualizationBounds(0d, 1d, 0d, 1d),
0.5d, rows: 2, columns: 2, bits);
bits[0] = 0x00;
Verification.Equal("AQ==", grid.OccupancyBitsBase64, "occupancy grid copies compact bits");
Verification.Throws<ArgumentException>(
() => new VisualizationOccupancyGrid(new VisualizationBounds(0d, 1d, 0d, 1d),
0.5d, rows: 2, columns: 2, new byte[2]), "occupancy grid validates bit length");
}
private static void ExposesImmutableSnapshotContracts()
{
var staticSnapshot = new PlanningVisualizationStaticSnapshot(
"会话",
new VisualizationBounds(0d, 1d, 0d, 1d),
null,
Array.Empty<VisualizationPolyline>(),
Array.Empty<VisualizationMarker>(),
Array.Empty<VisualizationDirectionSegment>(),
Array.Empty<VisualizationConfigurationGroup>());
var dynamicSnapshot = new PlanningVisualizationDynamicSnapshot(
1L,
DateTimeOffset.UtcNow,
"运行中",
0,
"forward",
new VisualizationPose(0d, 0d, 0d),
Array.Empty<VisualizationPolyline>(),
Array.Empty<VisualizationMarker>(),
Array.Empty<VisualizationChart>(),
Array.Empty<VisualizationValue>(),
new VisualizationCycleSummary(1L, DateTimeOffset.UtcNow, "成功", true, 1d, 0,
"forward", "rolling", "none", null, null, ""));
Verification.True(staticSnapshot.StaticPolylines.GetType().Name.Contains("ReadOnlyCollection"),
"static snapshot uses read-only collection");
Verification.True(dynamicSnapshot.Charts.GetType().Name.Contains("ReadOnlyCollection"),
"dynamic snapshot uses read-only collection");
AssertNoSetters(typeof(PlanningVisualizationStaticSnapshot));
AssertNoSetters(typeof(PlanningVisualizationDynamicSnapshot));
}
private static void AssertNoSetters(Type type)
{
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
Verification.True(property.SetMethod == null, type.Name + " exposes no property setters");
}
}
private static void DocumentsPublicSafetyAndLifecycleBoundary()
{
string readmePath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory,
"..", "..", "..", "..", "..", "TrajectoryPlanningVisualization", "README.md"));
string readme = File.ReadAllText(readmePath);
Verification.True(readme.Contains("OBSERVE_ONLY"), "README documents observe-only boundary");
Verification.True(readme.Contains("127.0.0.1"), "README documents loopback boundary");
Verification.True(readme.Contains("capacity 1"), "README documents latest-frame capacity");
Verification.True(readme.Contains("60"), "README documents default history limit");
Verification.True(readme.Contains("10 Hz"), "README documents default refresh rate");
Verification.True(readme.Contains("j[i]"), "README documents jerk interval semantics");
Verification.True(readme.Contains("末点后无时间区间"), "README documents jerk terminal explanation");
Verification.True(readme.Contains("PlanningVisualizationSessionInfo info = visualization.Start(staticSnapshot);"),
"README documents Start example");
Verification.True(readme.Contains("visualization.Publish(dynamicSnapshot);"), "README documents Publish example");
Verification.True(readme.Contains("visualization.Stop();"), "README documents Stop example");
}
}