using System.Threading.Channels;
using RioJoy.Core.Serial;
namespace RioJoy.Core.Tests.Serial;
///
/// whose first write blocks until
/// — lets a test park one writer mid-sequence while another tries to cut in
/// (write-lock and latest-wins assertions).
///
internal sealed class GatedTransport : IRioTransport
{
private readonly Channel _writes = Channel.CreateUnbounded();
private readonly SemaphoreSlim _gate = new(0, 1);
private readonly object _armLock = new();
private bool _gateArmed = true;
public string Description => "gated";
/// Release the parked first write.
public void Open() => _gate.Release();
public Task ReadAsync(byte[] buffer, CancellationToken cancellationToken) =>
Task.FromResult(0);
public async Task WriteAsync(byte[] data, CancellationToken cancellationToken)
{
bool wait;
lock (_armLock)
{
wait = _gateArmed;
_gateArmed = false;
}
if (wait)
await _gate.WaitAsync(cancellationToken);
_writes.Writer.TryWrite((byte[])data.Clone());
}
/// Read the next write, failing if none arrives in time.
public async Task NextWriteAsync(TimeSpan? timeout = null)
{
using var cts = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(5));
return await _writes.Reader.ReadAsync(cts.Token);
}
/// True when no further write has arrived.
public bool NoMoreWrites => !_writes.Reader.TryPeek(out _);
public void Dispose() { }
}