From 559890c482f5973f06aafdfec9550fee5ba3c7f2 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 09 十一月 2026 17:02:15 +0800
Subject: [PATCH] feat: CAN 模拟器 TCP 化 + 多设备并发调试 + 后台生命周期管理
---
tests/Aging.MvpTests/SimulatorFramingTests.cs | 130 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 130 insertions(+), 0 deletions(-)
diff --git a/tests/Aging.MvpTests/SimulatorFramingTests.cs b/tests/Aging.MvpTests/SimulatorFramingTests.cs
new file mode 100644
index 0000000..ea68216
--- /dev/null
+++ b/tests/Aging.MvpTests/SimulatorFramingTests.cs
@@ -0,0 +1,130 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using CanSimulator;
+using Xunit;
+
+namespace Aging.MvpTests
+{
+ /// <summary>
+ /// CAN 妯℃嫙鍣� TCP 绾垮崗璁垎甯х紪瑙g爜鍣ㄦ祴璇曘��
+ /// 绾挎牸寮忥細4 瀛楄妭澶х闀垮害鍓嶇紑 + UTF8 鏂囨湰锛堜笌 BrokerIpc 鍚屾儻鐢ㄦ硶锛夈��
+ /// </summary>
+ public class SimulatorFramingTests
+ {
+ [Fact]
+ public void Encode_RoundTrip_DecodesOriginalText()
+ {
+ string msg = "HELLO:CAN_BUS_1:0:HOST/PID";
+ byte[] frame = SimulatorFraming.Encode(msg);
+
+ Assert.Equal(4 + Encoding.UTF8.GetByteCount(msg), frame.Length);
+
+ var ms = new MemoryStream(frame);
+ byte[] all = ms.ToArray();
+ var got = SimulatorFraming.Decode(all, SimulatorFraming.HeaderSize, all.Length - SimulatorFraming.HeaderSize);
+ Assert.Equal(msg, got);
+ }
+
+ [Fact]
+ public void ReadFrameAsync_TwoFramesInOneBuffer_DecodesBothInOrder()
+ {
+ // 绮樺寘锛氫袱涓畬鏁村抚杩炵画鍐欏叆鍚屼竴娴�
+ var ms = new MemoryStream();
+ byte[] f1 = SimulatorFraming.Encode("OK:TX:0x100:8");
+ ms.Write(f1, 0, f1.Length);
+ byte[] f2 = SimulatorFraming.Encode("EV:RECV:0x123:AA BB:12:00:00.111:2");
+ ms.Write(f2, 0, f2.Length);
+ ms.Position = 0;
+
+ string? r1 = SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult();
+ string? r2 = SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult();
+
+ Assert.Equal("OK:TX:0x100:8", r1);
+ Assert.Equal("EV:RECV:0x123:AA BB:12:00:00.111:2", r2);
+ }
+
+ [Fact]
+ public void ReadFrameAsync_FrameSplitAcrossTwoReads_ReassemblesFullFrame()
+ {
+ // 鍗婂寘锛氬抚浣撹鎷嗘垚涓ゆ鍒拌揪锛堢敤鍒嗘娴佹ā鎷燂級
+ byte[] frame = SimulatorFraming.Encode("SEND:0x777:DE AD BE EF");
+ var chunked = new ChunkedStream(frame, chunk: 6); // 澶撮儴 6 瀛楄妭鍚庡垏鏂�
+
+ string? got = SimulatorFraming.ReadFrameAsync(chunked, CancellationToken.None).GetAwaiter().GetResult();
+ Assert.Equal("SEND:0x777:DE AD BE EF", got);
+ }
+
+ [Fact]
+ public void ReadFrameAsync_LengthExceedsMax_Throws()
+ {
+ // 闈炴硶闀垮害鍓嶇紑锛氳秴杩� MaxFrameBytes
+ byte[] bad = new byte[4];
+ bad[0] = 0x7F; bad[1] = 0xFF; bad[2] = 0xFF; bad[3] = 0xFF; // ~2GB
+ var ms = new MemoryStream(bad);
+
+ Assert.Throws<InvalidDataException>(() =>
+ SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult());
+ }
+
+ [Fact]
+ public void ReadFrameAsync_ZeroLengthPrefix_Throws()
+ {
+ byte[] bad = new byte[4]; // 闀垮害 0
+ var ms = new MemoryStream(bad);
+
+ Assert.Throws<InvalidDataException>(() =>
+ SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult());
+ }
+
+ [Fact]
+ public void ReadFrameAsync_EmptyStream_ReturnsNull()
+ {
+ // 瀵圭姝e父鍏抽棴锛氳涓嶅埌浠讳綍瀛楄妭鏃惰繑鍥� null锛堟柇绾挎劅鐭ュ绾︼級
+ var ms = new MemoryStream(new byte[0]);
+ string? got = SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult();
+ Assert.Null(got);
+ }
+
+ [Fact]
+ public void ReadFrameAsync_TruncatedBody_ReturnsNull()
+ {
+ // 澶村畬鏁翠絾浣撲腑閫� EOF锛氳繑鍥� null 瑙﹀彂閲嶈繛
+ byte[] frame = SimulatorFraming.Encode("QUERY:RECV_ALL");
+ var truncated = new byte[frame.Length - 3];
+ Array.Copy(frame, truncated, truncated.Length);
+ var ms = new MemoryStream(truncated);
+
+ string? got = SimulatorFraming.ReadFrameAsync(ms, CancellationToken.None).GetAwaiter().GetResult();
+ Assert.Null(got);
+ }
+
+ /// <summary>姣忔 Read 鍙粠鍐呴儴缂撳啿鍖哄彇鍥哄畾鐗囨锛屾ā鎷熺綉缁滃垎鐗囧埌杈�</summary>
+ private sealed class ChunkedStream : Stream
+ {
+ private readonly byte[] _data;
+ private readonly int _chunk;
+ private int _pos;
+ public ChunkedStream(byte[] data, int chunk) { _data = data; _chunk = chunk; }
+ public override bool CanRead => true;
+ public override bool CanSeek => false;
+ public override bool CanWrite => false;
+ public override long Length => _data.Length;
+ public override long Position { get => _pos; set => throw new NotSupportedException(); }
+ public override void Flush() { }
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (_pos >= _data.Length) return 0;
+ int n = Math.Min(Math.Min(count, _chunk), _data.Length - _pos);
+ Array.Copy(_data, _pos, buffer, offset, n);
+ _pos += n;
+ return n;
+ }
+ public override long Seek(long o, SeekOrigin s) => throw new NotSupportedException();
+ public override void SetLength(long v) => throw new NotSupportedException();
+ public override void Write(byte[] b, int o, int c) => throw new NotSupportedException();
+ }
+ }
+}
--
Gitblit v1.9.1