Skip to content

Commit

Permalink
Merge pull request #44 from mycroes/communication-captures
Browse files Browse the repository at this point in the history
Add communication captures to tests to respond with matching request ID's without knowing them upfront.
  • Loading branch information
mycroes authored Jan 27, 2025
2 parents ae88509 + 3927553 commit c12995d
Showing 1 changed file with 63 additions and 21 deletions.
84 changes: 63 additions & 21 deletions Sally7.Tests/Protocol/CommunicationSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,34 @@ namespace Sally7.Tests.Protocol;

internal class CommunicationSequence
{
private readonly List<(byte[], byte[])> _sequence = new();
private readonly List<(Fragment[], Fragment[])> _sequence = new();

private readonly ITestOutputHelper _output;

public static Fragment Pdu1 { get; } = nameof(Pdu1);
public static Fragment Pdu2 { get; } = nameof(Pdu2);

public CommunicationSequence(ITestOutputHelper output)
{
_output = output;
}

public CommunicationSequence AddCall(byte[] request, byte[] response)
public class Fragment
{
public byte? Value { get; private init; }
public string? Key { get; private init; }

private Fragment()
{
}

public static implicit operator Fragment(byte value) => new() { Value = value };
public static implicit operator Fragment(string value) => new() { Key = value };

public static IEnumerable<Fragment> FromBytes(IEnumerable<byte> bytes) => bytes.Select(x => (Fragment)x);
}

public CommunicationSequence AddCall(Fragment[] request, Fragment[] response)
{
_sequence.Add((request, response));

Expand All @@ -27,7 +45,7 @@ public CommunicationSequence AddCall(byte[] request, byte[] response)

public CommunicationSequence AddConnectRequest(PduSizeParameter.PduSize pduSize, Tsap sourceTsap, Tsap destinationTsap)
{
return AddCall(new byte[]
return AddCall(new Fragment[]
{
// TPKT
3, // Version
Expand Down Expand Up @@ -57,7 +75,7 @@ public CommunicationSequence AddConnectRequest(PduSizeParameter.PduSize pduSize,
2, // Parameter length
destinationTsap.Channel, // Channel
destinationTsap.Position, // Position
}, new byte[]
}, new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -75,7 +93,7 @@ public CommunicationSequence AddConnectRequest(PduSizeParameter.PduSize pduSize,

public CommunicationSequence AddCommunicationSetup()
{
return AddCall(new byte[]
return AddCall(new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -91,7 +109,7 @@ public CommunicationSequence AddCommunicationSetup()
0x32, // Protocol ID
0x01, // Message type job request
0, 0, // Reserved
1, 0, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 8, // Parameter length (Communication Setup)
0, 0, // Data length

Expand All @@ -101,7 +119,7 @@ public CommunicationSequence AddCommunicationSetup()
0, 10, // Max AMQ caller
0, 10, // Max AMQ callee
3, 192, // PDU size (960)
}, new byte[]
}, new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -117,7 +135,7 @@ public CommunicationSequence AddCommunicationSetup()
0x32, // Protocol ID
0x03, // Message type ack data
0, 0, // Reserved
1, 0, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 8, // Parameter length (Communication Setup)
0, 0, // Data length
0, // Error class
Expand All @@ -137,7 +155,7 @@ public CommunicationSequence AddRead(Area area, int dbNumber, int address, int l
{
var dataLength = 4 + data.Length;

return AddCall(new byte[]
return AddCall(new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -153,7 +171,7 @@ public CommunicationSequence AddRead(Area area, int dbNumber, int address, int l
0x32, // Protocol ID
0x01, // Message type job request
0, 0, // Reserved
1, 1, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 14, // Parameter length (Read request)
0, 0, // Data length

Expand All @@ -174,7 +192,7 @@ public CommunicationSequence AddRead(Area area, int dbNumber, int address, int l
(byte) (address >> 16 & 0xff), // Address, upper byte
(byte) (address >> 8 & 0xff), // Address, middle byte
(byte) (address & 0xff), // Address, lower byte
}, new byte[]
}, new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -190,7 +208,7 @@ public CommunicationSequence AddRead(Area area, int dbNumber, int address, int l
0x32, // Protocol ID
0x03, // Message type ack data
0, 0, // Reserved
1, 1, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 2, // Parameter length (Read request)
(byte) (dataLength >> 8 & 0xff), (byte) (dataLength & 0xff), // Data length
0, // Error class
Expand All @@ -205,15 +223,15 @@ public CommunicationSequence AddRead(Area area, int dbNumber, int address, int l
(byte) transportSize, // Transport size
(byte) (data.Length >> 5 & 0xff), // Data length, upper byte, in bits
(byte) (data.Length << 3 & 0xff), // Data length, lower byte, in bits
}.Concat(data).ToArray());
}.Concat(Fragment.FromBytes(data)).ToArray());
}

public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int length, TransportSize transportSize,
VariableType variableType, byte[] data)
{
var dataLength = 4 + data.Length;

return AddCall(new byte[]
return AddCall(new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -229,7 +247,7 @@ public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int
0x32, // Protocol ID
0x01, // Message type job request
0, 0, // Reserved
1, 1, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 14, // Parameter length (Write request)
(byte)(dataLength >> 8 & 0xff), (byte)(dataLength & 0xff), // Data length

Expand All @@ -256,7 +274,7 @@ public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int
(byte) transportSize, // Transport size
(byte) (data.Length >> 5 & 0xff),
(byte) (data.Length << 3 & 0xff),
}.Concat(data).ToArray(), new byte[]
}.Concat(Fragment.FromBytes(data)).ToArray(), new Fragment[]
{
// TPKT
3, // Version
Expand All @@ -272,7 +290,7 @@ public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int
0x32, // Protocol ID
0x03, // Message type ack data
0, 0, // Reserved
1, 1, // PDU reference
Pdu1, Pdu2, // PDU reference
0, 2, // Parameter length (Write response)
0, 1, // Data length
0, // Error class
Expand All @@ -284,7 +302,7 @@ public CommunicationSequence AddWrite(Area area, int dbNumber, int address, int

// Result code per item
0xff, // ErrorCode
}.Concat(data).ToArray());
}.Concat(Fragment.FromBytes(data)).ToArray());
}

public Task Serve(out int port)
Expand All @@ -305,10 +323,34 @@ async Task Impl()

var received = buffer.Take(bytesReceived).ToArray();
_output.WriteLine($"=> {BitConverter.ToString(received)}");
received.ShouldBe(request);

_output.WriteLine($"<= {BitConverter.ToString(response)}");
socketIn.Send(response);
var captures = new Dictionary<string, byte>();

for (var i = 0; i < Math.Min(request.Length, received.Length); i++)
{
if (request[i].Key is { } key)
{
captures.Add(key, received[i]);
}
else if (request[i].Value != received[i])
{
throw new Exception(
$"Value '{received[i]}' at index {i} of received data does not match expected value '{request[i].Value}'.");
}
}

var res = response.Select((f, i) =>
{
if (f.Key is not { } key) return f.Value!.Value;

if (captures.TryGetValue(key, out var capture)) return capture;

throw new Exception(
$"Placeholder '{key}' at index {i} of response was not captured from the request.");
}).ToArray();

_output.WriteLine($"<= {BitConverter.ToString(res)}");
socketIn.Send(res);
}
}
finally
Expand Down

0 comments on commit c12995d

Please sign in to comment.