using System; using MultiWheelC.Fleet; using MyParking.Shared; namespace MultiWheelC.Tests { internal static class InMemoryFleetTransportTests { public static void Run() { VerifyTargetedCommandRoutingAndFifoOrder(); VerifyBroadcastReachesAllFollowersOnly(); VerifyMemberReportReturnsToLeader(); VerifyEndpointRolesAndVehicleIdentity(); Console.WriteLine( "InMemoryFleetTransport测试通过,共4个场景。"); } private static void VerifyTargetedCommandRoutingAndFifoOrder() { var network = CreateNetwork(); var leader = network.CreateEndpoint(1); var member2 = network.CreateEndpoint(2); var member3 = network.CreateEndpoint(3); leader.SendCommand(CreateCommand(2, sequenceNumber: 1)); leader.SendCommand(CreateCommand(2, sequenceNumber: 2)); AssertTrue( member2.TryReceiveCommand(out var first) && first.SequenceNumber == 1, "定向命令第一条应到达目标车辆"); AssertTrue( member2.TryReceiveCommand(out var second) && second.SequenceNumber == 2, "定向命令应保持FIFO顺序"); AssertFalse( member2.TryReceiveCommand(out _), "目标车辆不应收到额外命令"); AssertFalse( member3.TryReceiveCommand(out _), "其他成员不应收到定向命令"); } private static void VerifyBroadcastReachesAllFollowersOnly() { var network = CreateNetwork(); var leader = network.CreateEndpoint(1); var member2 = network.CreateEndpoint(2); var member3 = network.CreateEndpoint(3); leader.SendCommand( CreateCommand( FleetProtocol.BroadcastVehicleId, sequenceNumber: 3)); AssertTrue( member2.TryReceiveCommand(out var command2) && command2.SequenceNumber == 3, "广播命令应到达成员车2"); AssertTrue( member3.TryReceiveCommand(out var command3) && command3.SequenceNumber == 3, "广播命令应到达成员车3"); AssertFalse( leader.TryReceiveCommand(out _), "主车本地命令不应通过通信层回环"); } private static void VerifyMemberReportReturnsToLeader() { var network = CreateNetwork(); var leader = network.CreateEndpoint(1); var member2 = network.CreateEndpoint(2); member2.SendReport( CreateReport( vehicleId: 2, sequenceNumber: 8)); AssertTrue( leader.TryReceiveReport(out var report), "主车应收到成员报告"); AssertTrue( report.VehicleId == 2 && report.SequenceNumber == 8, "主车收到的成员报告内容不正确"); AssertFalse( leader.TryReceiveReport(out _), "报告队列取空后应返回false"); } private static void VerifyEndpointRolesAndVehicleIdentity() { var network = CreateNetwork(); var leader = network.CreateEndpoint(1); var member2 = network.CreateEndpoint(2); AssertThrows( () => member2.SendCommand( CreateCommand(1, sequenceNumber: 1)), "从车不能发送车队命令"); AssertThrows( () => member2.TryReceiveReport(out _), "从车不能消费全队成员报告"); AssertThrows( () => member2.SendReport( CreateReport( vehicleId: 1, sequenceNumber: 1)), "端点不能冒用其他车辆身份"); leader.SendReport( CreateReport( vehicleId: 1, sequenceNumber: 2)); AssertTrue( leader.TryReceiveReport(out var leaderReport) && leaderReport.VehicleId == 1, "主车作为成员时也应能上报本车状态"); } private static InMemoryFleetTransportNetwork CreateNetwork() { return new InMemoryFleetTransportNetwork( new[] { 1, 2, 3 }, leaderVehicleId: 1); } private static FleetCommand CreateCommand( int targetVehicleId, long sequenceNumber) { return new FleetCommand( planId: 5, sequenceNumber, targetVehicleId, FleetCommandKind.Stop, motionDirectionInBodyRadians: 0.0, twistInVehicleBody: Twist2D.Zero, validForSeconds: 0.5); } private static FleetMemberReport CreateReport( int vehicleId, long sequenceNumber) { return new FleetMemberReport( vehicleId, planId: 5, sequenceNumber, sampleTimestampSeconds: 1.0, poseInCommonWorld: Pose2D.Identity, twistAtVehicleOriginInCommonWorld: Twist2D.Zero, isStateAvailable: true, hasValidVelocityEstimate: true, FleetMemberState.Active, lastAppliedCommandSequence: 1); } private static void AssertThrows( Action action, string scenario) where TException : Exception { try { action(); } catch (TException) { return; } throw new InvalidOperationException( $"{scenario}时应抛出{typeof(TException).Name}。"); } private static void AssertTrue( bool condition, string message) { if (!condition) { throw new InvalidOperationException(message); } } private static void AssertFalse( bool condition, string message) { AssertTrue(!condition, message); } } }