feat: add planning visualization contracts

This commit is contained in:
梁薄云
2026-08-06 09:26:06 +08:00
parent fa4938b3fc
commit 55d8e1eebf
10 changed files with 581 additions and 0 deletions
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using TrajectoryPlanningVisualization;
namespace TrajectoryPlanningVisualizationVerificationHost;
internal static class ContractChecks
{
public static void Run()
{
CopiesCollectionsAndRejectsNonFiniteNumbers();
StoresOccupancyAsDefensiveCompactBits();
ExposesImmutableSnapshotContracts();
}
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");
}
}
}
@@ -0,0 +1,21 @@
using System;
namespace TrajectoryPlanningVisualizationVerificationHost;
internal static class Program
{
private static int Main()
{
try
{
ContractChecks.Run();
Console.WriteLine("PASS trajectory-planning-visualization");
return 0;
}
catch (Exception exception)
{
Console.Error.WriteLine(exception);
return 1;
}
}
}
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TrajectoryPlanningVisualization\TrajectoryPlanningVisualization.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,45 @@
using System;
namespace TrajectoryPlanningVisualizationVerificationHost;
internal static class Verification
{
public static void Equal<T>(T expected, T actual, string message)
{
if (!Equals(expected, actual))
{
throw new InvalidOperationException(message + ": expected " + expected + ", actual " + actual);
}
}
public static void NearlyEqual(double expected, double actual, string message)
{
if (Math.Abs(expected - actual) > 0.0000001d)
{
throw new InvalidOperationException(message + ": expected " + expected + ", actual " + actual);
}
}
public static void True(bool value, string message)
{
if (!value)
{
throw new InvalidOperationException(message);
}
}
public static void Throws<TException>(Action action, string message)
where TException : Exception
{
try
{
action();
}
catch (TException)
{
return;
}
throw new InvalidOperationException(message);
}
}