using Nonemm.Core; namespace Nonemm.Rig.Tests; /// The client driven over a real socket against a stand-in for rigctld. public class RigctldRadioTests { private static readonly TimeSpan Patience = TimeSpan.FromSeconds(5); private static RigctldRadio Radio(FakeRigctld node) => new("127.0.0.1", node.Port, pollInterval: TimeSpan.FromMilliseconds(20)); private static async Task FirstStateAsync(RigctldRadio radio) { TaskCompletionSource moved = new(); radio.Moved += (_, state) => moved.TrySetResult(state); radio.Start(); return await moved.Task.WaitAsync(Patience); } [Fact] public async Task TheRadioReportsWhereItIs() { using FakeRigctld node = new() { FrequencyHertz = 21_005_000, Mode = "USB" }; using RigctldRadio radio = Radio(node); RadioState state = await FirstStateAsync(radio); Assert.Equal(21_005_000, state.Frequency.Hertz); Assert.Equal(Modes.Usb, state.Mode); Assert.False(state.IsSplit); } [Fact] public async Task ASplitRadioReportsWhereItTransmits() { using FakeRigctld node = new() { IsSplit = true, TransmitFrequencyHertz = 14_200_000 }; using RigctldRadio radio = Radio(node); RadioState state = await FirstStateAsync(radio); Assert.True(state.IsSplit); Assert.Equal(14_200_000, state.TransmitFrequency.Hertz); } /// A radio that cannot do split answers -11. That is not a broken /// connection, and the frequency it did report still counts. [Fact] public async Task ARadioThatCannotDoSplitIsStillRead() { using FakeRigctld node = new() { SupportsSplit = false }; using RigctldRadio radio = Radio(node); RadioState state = await FirstStateAsync(radio); Assert.Equal(14_025_000, state.Frequency.Hertz); Assert.False(state.IsSplit); } /// The transmit frequency goes out before split is turned on, or a radio /// last split somewhere else transmits there. [Fact] public async Task TurningSplitOnSetsTheFrequencyFirst() { using FakeRigctld node = new(); using RigctldRadio radio = Radio(node); await FirstStateAsync(radio); await radio.SetSplitAsync(Frequency.FromKilohertz(14_200)); IReadOnlyList sent = [.. node.Received.Where(c => c.StartsWith("+I") || c.StartsWith("+S"))]; Assert.Equal(["+I 14200000", "+S 1 VFOB"], sent); } [Fact] public async Task TurningSplitOffSaysSo() { using FakeRigctld node = new(); using RigctldRadio radio = Radio(node); await FirstStateAsync(radio); await radio.SetSplitAsync(null); Assert.Contains("+S 0 VFOA", node.Received); } [Fact] public async Task TuningSendsTheFrequencyInHertz() { using FakeRigctld node = new(); using RigctldRadio radio = Radio(node); await FirstStateAsync(radio); await radio.TuneAsync(Frequency.FromKilohertz(7_025.5)); Assert.Contains("+F 7025500", node.Received); } /// Every command carries the + that asks for the answer with an RPRT /// terminator, so the reader can never fall a line behind. [Fact] public async Task EveryCommandAsksForTheExtendedAnswer() { using FakeRigctld node = new(); using RigctldRadio radio = Radio(node); await FirstStateAsync(radio); Assert.All(node.Received, command => Assert.StartsWith("+", command)); } }