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() { } }