Skip to content

Commit 5d200bc

Browse files
committed
Cover PCAP endian and timestamp variants
1 parent 6f5e06b commit 5d200bc

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using ProcessBus.Iec61850.Raw.Replay;
2+
using System.Buffers.Binary;
3+
using Xunit;
4+
5+
namespace ProcessBus.Tests;
6+
7+
public sealed class PcapFormatVariantTests
8+
{
9+
[Theory]
10+
[InlineData(true, false)]
11+
[InlineData(false, false)]
12+
[InlineData(true, true)]
13+
[InlineData(false, true)]
14+
[Trait("Category", "RuntimeArchitecture")]
15+
public void Reader_AcceptsAllClassicEndianAndResolutionVariants(bool littleEndian, bool nanosecond)
16+
{
17+
var fraction = nanosecond ? 123_456_700u : 123_456u;
18+
using var pcap = BuildSingleFramePcap(
19+
GoldenFrames.SvFrame(),
20+
littleEndian,
21+
nanosecond,
22+
seconds: 1_700_000_000u,
23+
fraction: fraction);
24+
25+
var frame = new PcapReplayReader().Read(pcap).Single();
26+
var expected = DateTimeOffset.FromUnixTimeSeconds(1_700_000_000).UtcDateTime.AddTicks(
27+
nanosecond ? fraction / 100u : fraction * 10u);
28+
29+
Assert.Equal(expected, frame.CaptureTimeUtc);
30+
Assert.Equal(GoldenFrames.SvFrame(), frame.FrameBytes.ToArray());
31+
}
32+
33+
[Theory]
34+
[InlineData(false, 1_000_000u)]
35+
[InlineData(true, 1_000_000_000u)]
36+
[Trait("Category", "RuntimeArchitecture")]
37+
public void Reader_RejectsInvalidTimestampFraction(bool nanosecond, uint fraction)
38+
{
39+
using var pcap = BuildSingleFramePcap(
40+
GoldenFrames.SvFrame(),
41+
littleEndian: true,
42+
nanosecond: nanosecond,
43+
seconds: 1_700_000_000u,
44+
fraction: fraction);
45+
46+
var error = Assert.Throws<InvalidDataException>(() => new PcapReplayReader().Read(pcap).ToArray());
47+
Assert.Contains("timestamp fraction", error.Message, StringComparison.OrdinalIgnoreCase);
48+
}
49+
50+
[Fact]
51+
[Trait("Category", "RuntimeArchitecture")]
52+
public void Reader_EnforcesConfiguredFrameBoundary()
53+
{
54+
var oversized = new byte[101];
55+
using var pcap = BuildSingleFramePcap(
56+
oversized,
57+
littleEndian: true,
58+
nanosecond: false,
59+
seconds: 1_700_000_000u,
60+
fraction: 0);
61+
62+
var reader = new PcapReplayReader(maximumFrameBytes: 100);
63+
var error = Assert.Throws<InvalidDataException>(() => reader.Read(pcap).ToArray());
64+
Assert.Contains("frame boundary", error.Message, StringComparison.OrdinalIgnoreCase);
65+
}
66+
67+
private static MemoryStream BuildSingleFramePcap(
68+
byte[] frame,
69+
bool littleEndian,
70+
bool nanosecond,
71+
uint seconds,
72+
uint fraction)
73+
{
74+
var stream = new MemoryStream();
75+
var global = new byte[24];
76+
77+
var magic = (littleEndian, nanosecond) switch
78+
{
79+
(true, false) => new byte[] { 0xD4, 0xC3, 0xB2, 0xA1 },
80+
(false, false) => new byte[] { 0xA1, 0xB2, 0xC3, 0xD4 },
81+
(true, true) => new byte[] { 0x4D, 0x3C, 0xB2, 0xA1 },
82+
_ => new byte[] { 0xA1, 0xB2, 0x3C, 0x4D }
83+
};
84+
magic.CopyTo(global, 0);
85+
WriteUInt16(global.AsSpan(4, 2), 2, littleEndian);
86+
WriteUInt16(global.AsSpan(6, 2), 4, littleEndian);
87+
WriteUInt32(global.AsSpan(16, 4), 65_535, littleEndian);
88+
WriteUInt32(global.AsSpan(20, 4), 1, littleEndian);
89+
stream.Write(global);
90+
91+
var record = new byte[16];
92+
WriteUInt32(record.AsSpan(0, 4), seconds, littleEndian);
93+
WriteUInt32(record.AsSpan(4, 4), fraction, littleEndian);
94+
WriteUInt32(record.AsSpan(8, 4), checked((uint)frame.Length), littleEndian);
95+
WriteUInt32(record.AsSpan(12, 4), checked((uint)frame.Length), littleEndian);
96+
stream.Write(record);
97+
stream.Write(frame);
98+
stream.Position = 0;
99+
return stream;
100+
}
101+
102+
private static void WriteUInt16(Span<byte> destination, ushort value, bool littleEndian)
103+
{
104+
if (littleEndian)
105+
BinaryPrimitives.WriteUInt16LittleEndian(destination, value);
106+
else
107+
BinaryPrimitives.WriteUInt16BigEndian(destination, value);
108+
}
109+
110+
private static void WriteUInt32(Span<byte> destination, uint value, bool littleEndian)
111+
{
112+
if (littleEndian)
113+
BinaryPrimitives.WriteUInt32LittleEndian(destination, value);
114+
else
115+
BinaryPrimitives.WriteUInt32BigEndian(destination, value);
116+
}
117+
}

0 commit comments

Comments
 (0)