mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 14:28:25 +08:00
test: Add CommunicationSequence
This commit is contained in:
7
S7.Net.UnitTest/Framework/IsExternalInit.cs
Normal file
7
S7.Net.UnitTest/Framework/IsExternalInit.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal record IsExternalInit;
|
||||
}
|
||||
82
S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs
Normal file
82
S7.Net.UnitTest/Infrastructure/CommunicationSequence.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
3
S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs
Normal file
3
S7.Net.UnitTest/Infrastructure/RequestResponsePair.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace S7.Net.UnitTest;
|
||||
|
||||
internal record RequestResponsePair(string RequestPattern, string ResponsePattern);
|
||||
80
S7.Net.UnitTest/Infrastructure/Responder.cs
Normal file
80
S7.Net.UnitTest/Infrastructure/Responder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
Reference in New Issue
Block a user