Files
CydandClaude Fable 5 3b2af7b79a Phase 8A (1/2): de-Span the serial layer, swap JSON to Newtonsoft
Prepares RioJoy.Core for the net40 (Windows XP) target, which has no
System.Memory, ValueTask, or System.Text.Json:
- IRioTransport and the whole protocol/framing layer now use byte[] +
  Task (RioPacket.Payload, PacketParser/Builder, RioChecksum, replies,
  AnalogReport, RioHidReport). At 9600 baud Span bought nothing; the
  SerialPortTransport bridge copies disappear entirely.
- ConfigStore/OverlayTemplateStore switch to Newtonsoft 13 with the
  same conventions (indented, PascalCase, string enums, null-skipping);
  verified against the real STJ-written config.json and regions.json
  (load + round-trip). System.Memory and System.Text.Json packages
  dropped.

275 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:36:19 -05:00

121 lines
4.0 KiB
C#

using RioJoy.Core.Protocol;
using Xunit;
namespace RioJoy.Core.Tests.Protocol;
public class PacketParserTests
{
private static List<RioRxEvent> FeedAll(PacketParser parser, byte[] data)
{
var events = new List<RioRxEvent>();
foreach (byte b in data)
{
if (parser.Feed(b, out RioRxEvent ev))
events.Add(ev);
}
return events;
}
[Fact]
public void FramesValidPacket_WithChecksumValid()
{
var parser = new PacketParser();
byte[] frame = PacketBuilder.Build(RioCommand.ButtonPressed, new byte[] { 0x05 });
List<RioRxEvent> events = FeedAll(parser, frame);
RioRxEvent ev = Assert.Single(events);
Assert.Equal(RioRxEventKind.Packet, ev.Kind);
Assert.True(ev.ChecksumValid);
Assert.Equal(RioCommand.ButtonPressed, ev.Packet.Command);
Assert.Equal(new byte[] { 0x05 }, ev.Packet.Payload.ToArray());
Assert.False(parser.InPacket);
}
[Fact]
public void ReportsChecksumInvalid_ButStillFramesPacket()
{
var parser = new PacketParser();
byte[] frame = PacketBuilder.Build(RioCommand.ButtonPressed, new byte[] { 0x05 });
frame[^1] ^= 0x01; // corrupt checksum (stays high-bit clear)
RioRxEvent ev = Assert.Single(FeedAll(parser, frame));
Assert.Equal(RioRxEventKind.Packet, ev.Kind);
Assert.False(ev.ChecksumValid);
}
[Fact]
public void ControlByte_OutsideFraming_IsReported()
{
var parser = new PacketParser();
RioRxEvent ev = Assert.Single(FeedAll(parser, new[] { (byte)RioControl.Ack }));
Assert.Equal(RioRxEventKind.ControlByte, ev.Kind);
Assert.True(ev.IsControl(RioControl.Ack));
}
[Fact]
public void HighBitMidPacket_AbortsAndResyncs()
{
var parser = new PacketParser();
// Start a ButtonPressed (expects 1 payload + 1 checksum), feed one payload
// byte, then a high-bit byte mid-packet → framing error + resync.
var events = new List<RioRxEvent>();
foreach (byte b in new byte[] { 0x88, 0x05, 0x82 })
{
if (parser.Feed(b, out RioRxEvent ev))
events.Add(ev);
}
RioRxEvent framing = Assert.Single(events);
Assert.Equal(RioRxEventKind.FramingError, framing.Kind);
Assert.False(parser.InPacket);
// A subsequent clean frame parses normally (the parser resynced).
byte[] next = PacketBuilder.Build(RioCommand.ButtonReleased, new byte[] { 0x07 });
RioRxEvent ev2 = Assert.Single(FeedAll(parser, next));
Assert.Equal(RioCommand.ButtonReleased, ev2.Packet.Command);
}
[Fact]
public void ParsesMultiplePackets_InOneStream()
{
var parser = new PacketParser();
byte[] a = PacketBuilder.Build(RioCommand.ButtonPressed, new byte[] { 0x01 });
byte[] b = PacketBuilder.Build(RioCommand.ButtonReleased, new byte[] { 0x02 });
byte[] stream = new byte[a.Length + b.Length];
a.CopyTo(stream, 0);
b.CopyTo(stream, a.Length);
List<RioRxEvent> events = FeedAll(parser, stream);
Assert.Equal(2, events.Count);
Assert.Equal(RioCommand.ButtonPressed, events[0].Packet.Command);
Assert.Equal(RioCommand.ButtonReleased, events[1].Packet.Command);
}
[Fact]
public void HandlesPacket_SplitAcrossFeeds()
{
var parser = new PacketParser();
byte[] frame = PacketBuilder.Build(RioCommand.AnalogReply, new byte[] { 1, 0, 2, 0, 3, 0, 4, 0, 5, 0 });
// Feed byte-by-byte: only the final byte should yield the packet event.
var events = new List<RioRxEvent>();
for (int i = 0; i < frame.Length; i++)
{
bool produced = parser.Feed(frame[i], out RioRxEvent ev);
if (produced)
events.Add(ev);
Assert.Equal(i == frame.Length - 1, produced);
}
RioRxEvent done = Assert.Single(events);
Assert.Equal(RioCommand.AnalogReply, done.Packet.Command);
Assert.True(done.ChecksumValid);
}
}