test: Add CommunicationSequence

This commit is contained in:
Michael Croes
2023-07-31 22:57:03 +02:00
parent 088cd0a4a8
commit 714ac62ab1
5 changed files with 173 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
using System.ComponentModel;
namespace System.Runtime.CompilerServices
{
[EditorBrowsable(EditorBrowsableState.Never)]
internal record IsExternalInit;
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace S7.Net.UnitTest;
internal class CommunicationSequence : IEnumerable<RequestResponsePair>
{
private readonly List<RequestResponsePair> _requestResponsePairs = new List<RequestResponsePair>();
public CommunicationSequence()
{
}
public void Add(string requestPattern, string responsePattern)
{
_requestResponsePairs.Add(new RequestResponsePair(requestPattern, responsePattern));
}
public IEnumerator<RequestResponsePair> GetEnumerator()
{
return _requestResponsePairs.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public Task Serve(out int port)
{
var socket = CreateBoundListenSocket(out port);
socket.Listen(0);
async Task Impl()
{
await Task.Yield();
var socketIn = socket.Accept();
var buffer = ArrayPool<byte>.Shared.Rent(1024);
try
{
foreach (var pair in _requestResponsePairs)
{
var bytesReceived = socketIn.Receive(buffer, SocketFlags.None);
var received = buffer.Take(bytesReceived).ToArray();
Console.WriteLine($"=> {BitConverter.ToString(received)}");
var response = Responder.Respond(pair, received);
Console.WriteLine($"<= {BitConverter.ToString(response)}");
socketIn.Send(response);
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
socketIn.Close();
}
return Impl();
}
private static Socket CreateBoundListenSocket(out int port)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endpoint = new IPEndPoint(IPAddress.Loopback, 0);
socket.Bind(endpoint);
var localEndpoint = (IPEndPoint)socket.LocalEndPoint!;
port = localEndpoint.Port;
return socket;
}
}

View File

@@ -0,0 +1,3 @@
namespace S7.Net.UnitTest;
internal record RequestResponsePair(string RequestPattern, string ResponsePattern);

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace S7.Net.UnitTest;
internal static class Responder
{
private const string Comment = "//";
private static char[] Space = " ".ToCharArray();
public static byte[] Respond(RequestResponsePair pair, byte[] request)
{
var offset = 0;
var matches = new Dictionary<string, byte>();
var res = new List<byte>();
using var requestReader = new StringReader(pair.RequestPattern);
string line;
while ((line = requestReader.ReadLine()) != null)
{
var tokens = line.Split(Space, StringSplitOptions.RemoveEmptyEntries);
foreach (var token in tokens)
{
if (token.StartsWith(Comment)) break;
if (offset >= request.Length)
{
throw new Exception("Request pattern has more data than request.");
}
var received = request[offset];
if (token.Length == 2 && byte.TryParse(token, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var value))
{
// Number, exact match
if (value != received)
{
throw new Exception($"Incorrect data at offset {offset}. Expected {value:X2}, received {received:X2}.");
}
}
else
{
matches[token] = received;
}
offset++;
}
}
if (offset != request.Length) throw new Exception("Request contained more data than request pattern.");
using var responseReader = new StringReader(pair.ResponsePattern);
while ((line = responseReader.ReadLine()) != null)
{
var tokens = line.Split(Space, StringSplitOptions.RemoveEmptyEntries);
foreach (var token in tokens)
{
if (token.StartsWith(Comment)) break;
if (token.Length == 2 && byte.TryParse(token, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var value))
{
res.Add(value);
}
else
{
if (!matches.TryGetValue(token, out var match))
{
throw new Exception($"Unmatched token '{token}' in response.");
}
res.Add(match);
}
}
}
return res.ToArray();
}
}

View File

@@ -8,6 +8,7 @@
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
<IsPackable>false</IsPackable>