using Nonemm.Contests; using Nonemm.Core; using Nonemm.Core.Country; using Nonemm.Storage; namespace Nonemm.Session.Tests; /// A box the walk between the entry boxes steps over cannot be typed in, so it /// must not be what holds a contact back either. CQ WW RTTY is the contest /// that skips a box today, and its state box is not required; this is the rule /// itself, so a contest that asks only some stations for a box cannot lock the /// operator out of his own log. public class SkippedFieldTests { private const string Countries = """ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: K,W,N,AA; Japan: 25: 45: AS: 36.40: -138.38: -9.0: JA: JA,JH,JR; Germany: 14: 28: EU: 51.00: -10.00: -1.0: DL: DL,DK; """; private static readonly StationInfo Me = new() { Callsign = "DL1ABC", CountryPrefix = "DL", Continent = "EU", CqZone = 14, }; private static OperatingPosition Position() { FakeLogStore store = new(); ContestInstance instance = store.AddContest(new ContestInstance { ContestNumber = 0, ContestName = "STATEONLY", }); return new OperatingPosition(new ContestSession( store, new StateFromAmericansOnly(), instance, Me, CountryFile.Parse(Countries))); } private static OperatingPosition Working(string call) { OperatingPosition position = Position(); position.Entry.Call = call; position.Entry.Set(ExchangeSlot.ReceivedReport, "599"); return position; } [Fact] public void ABoxTheWalkStepsOverDoesNotHoldTheContactBack() { OperatingPosition position = Working("JA1XYZ"); Assert.True(position.IsEntryComplete); Assert.Equal("JA1XYZ", position.LogContact().Call.Text); } [Fact] public void ABoxTheStationIsAskedForStillHoldsTheContactBack() { OperatingPosition position = Working("K1ABC"); Assert.False(position.IsEntryComplete); Assert.Throws(() => position.LogContact()); position.Entry.Set(ExchangeSlot.Section, "CT"); Assert.True(position.IsEntryComplete); } /// The cursor and the completeness check read the same rule. The report /// boxes are filled in by the program, so with the state box stepped over /// there is nothing left to type and the cursor stays on the callsign. [Fact] public void TheWalkStepsOverTheSameBox() { OperatingPosition japan = Working("JA1XYZ"); japan.Entry.FocusOn(0); japan.MoveFocus(forward: true); Assert.Equal(0, japan.Entry.Focus); OperatingPosition america = Working("K1ABC"); america.Entry.FocusOn(0); america.MoveFocus(forward: true); Assert.Equal( ExchangeSlot.Section, america.Entry.Exchange[america.Entry.Focus - 1].Slot); } /// A contest that asks only American stations for a state, and marks the /// box required — which is what the entry window used to lock up on. private sealed class StateFromAmericansOnly : Contest { public string Name => "STATEONLY"; public string DisplayName => "State from Americans only"; public string CabrilloName => "STATE-ONLY"; public IReadOnlyList ExchangeFieldsFor(StationInfo me) => [ new ExchangeField("Snt", ExchangeSlot.SentReport, ExchangeFieldKind.Report, IsRequired: false), new ExchangeField("Rcv", ExchangeSlot.ReceivedReport, ExchangeFieldKind.Report), new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.Text), ]; public bool SkipsField(ExchangeField field, CountryLookup? their) => field.Slot == ExchangeSlot.Section && their?.Entity.PrimaryPrefix != "K"; public IReadOnlyList MultiplierNames => ["States"]; public DupeScope DupeScope => DupeScope.PerBand; public bool HasSerialNumbers => false; public IReadOnlyList Modes => []; public string SentExchangeFor(StationInfo me) => me.State; public int PointsFor(QsoContext qso) => 1; public IReadOnlyList MultipliersFor(QsoContext qso) => []; public int TotalScore(ScoreTally tally, ContestEntry entry) => tally.Points; public CabrilloExchange CabrilloExchange(Qso qso, StationInfo me, ContestEntry entry) => new([new CabrilloField(me.Callsign, 13)], [new CabrilloField(qso.Call.Text, 13)]); } }