Files
Nonemm/tests/Nonemm.Session.Tests/MessageExpanderTests.cs
ericek111 9f635550bd Read a QTC series out on RTTY
N1MM's Send All: the whole series as one message — the heading, every
line with the operator's spacing between them, then the ending, with
`{TX}` and `{RX}` around it so it keys the transmitter and drops it
again. The shape is N1MM's, from `QTCWindow.cs:3609`. A line is the three
fields joined with hyphens, which is how N1MM writes them, and Snd n
sends one line again for a station that missed it.

One message rather than a button per line because a series is close to a
minute of transmission at 45 baud: the type-ahead buffer holds it and
paces the engine.

Three settings with N1MM's names and defaults — `WAESendAllHeadingText`,
`WAESendAllEndingText` and `WAESQTCSpacing` — on the QTC window's setup
dialog. `{ENTERLF}` is new to the expander and stands for a carriage
return and a line feed; `{QTC}` is filled in by the window, which is the
only place that knows which series is going out.

The messages are tested as text. Nothing has gone on the air.

Left out: N1MM's four SSB recordings, which need a voice keyer, and its
RTTY messages for the station taking traffic down, which are typed into
the digital window's transmit pane here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdAYHcdRktqKry7nk414TU
2026-09-03 15:02:08 +00:00

143 lines
4.6 KiB
C#

using Nonemm.Contests;
using Nonemm.Contests.Rules;
using Nonemm.Core;
using Nonemm.Core.Calls;
using Nonemm.Storage;
namespace Nonemm.Session.Tests;
public class MessageExpanderTests
{
private static OperatingPosition Session(CallHistory? history = null, int radioNumber = 1)
{
FakeLogStore store = new();
ContestInstance instance = store.AddContest(new ContestInstance
{
ContestNumber = 0,
ContestName = "CQWW",
SentExchange = "14",
});
return new OperatingPosition(
new ContestSession(
store,
new CqWorldWide(ModeCategory.Cw),
instance,
new StationInfo { Callsign = "DL1ABC", CqZone = 14, Name = "Erik", GridSquare = "JN88" },
null,
history),
radioNumber);
}
[Fact]
public void MyCallAndTheirCallAreFilledIn()
{
OperatingPosition session = Session();
session.Entry.Call = "JA1XYZ";
Assert.Equal("JA1XYZ DE DL1ABC", MessageExpander.Expand("{CALL} DE {MYCALL}", session));
}
[Fact]
public void TheSentExchangeComesFromTheContestSetup() =>
Assert.Equal("599 14", MessageExpander.Expand("{SENTRST} {EXCH}", Session()));
[Fact]
public void HashIsTheSerialNumber() =>
Assert.Equal("NR 1", MessageExpander.Expand("NR #", Session()));
[Fact]
public void CutNumbersReplaceZeroAndNine() =>
Assert.Equal("5NN", MessageExpander.Expand("{SENTRSTCUT}", Session()));
[Fact]
public void AMacroThatIsNotKnownDisappears() =>
Assert.Equal("TU ", MessageExpander.Expand("TU {NOTAMACRO}", Session()));
[Fact]
public void AnUnclosedMacroIsLeftAsTyped() =>
Assert.Equal("TU {MYCALL", MessageExpander.Expand("TU {MYCALL", Session()));
[Fact]
public void TheSingleCharacterMacrosAreTheirCallMyCallAndTheSerial()
{
OperatingPosition session = Session();
session.Entry.Call = "JA1XYZ";
Assert.Equal("JA1XYZ 599 1 DE DL1ABC", MessageExpander.Expand("! 599 # DE *", session));
}
[Fact]
public void TheirCallFallsBackToTheLastOneLogged()
{
OperatingPosition session = Session();
session.Entry.Call = "JA1XYZ";
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
session.Entry.Set(ExchangeSlot.Zone, "25");
session.LogContact();
Assert.Equal("JA1XYZ JA1XYZ", MessageExpander.Expand("{CALL} {LASTCALL}", session));
}
[Fact]
public void TheFrequencySendsRForTheDecimalPointOnCw()
{
OperatingPosition session = Session();
session.Tune(Frequency.FromKilohertz(14_025.5));
Assert.Equal("14025R5 14026", MessageExpander.Expand("{FREQ} {FREQROUND}", session));
}
[Fact]
public void TheOtherRadioFillsInTheOtherMacros()
{
OperatingPosition here = Session();
OperatingPosition there = Session(radioNumber: 2);
there.Tune(Frequency.FromKilohertz(3_525));
Assert.Equal("3525R0 3R5 80M", MessageExpander.Expand("{OTHERFREQ} {OTHERMHZ} {OTHERBAND}", here, there));
}
[Fact]
public void WithOneRadioTheOtherMacrosStandForNothing() =>
Assert.Equal("", MessageExpander.Expand("{OTHERFREQ}{OTHERMHZ}{OTHERBAND}", Session()));
/// The two carriage return macros a digital message is written with. N1MM's
/// own RTTY message defaults use `{ENTERLF}`.
[Fact]
public void TheCarriageReturnMacrosAreTheCharactersTheyStandFor()
{
Assert.Equal("A\rB\r\nC", MessageExpander.Expand("A{ENTER}B{ENTERLF}C", Session()));
}
[Fact]
public void TheNameComesFromTheCallHistoryWhenTheContestHasNoNameBox()
{
OperatingPosition session = Session(CallHistory.Parse("!!Order!!,Call,Name\nJA1XYZ,Ken"));
session.Entry.Call = "JA1XYZ";
Assert.Equal("TU Ken GL", MessageExpander.Expand("TU {NAMEANDSPACE}GL", session));
}
[Fact]
public void MyGridAndTheirsAreDifferentMacros()
{
OperatingPosition session = Session(CallHistory.Parse("!!Order!!,Call,LOC1\nJA1XYZ,PM95"));
session.Entry.Call = "JA1XYZ";
Assert.Equal("JN88 PM95", MessageExpander.Expand("{GRID} {GRIDSQUARE}", session));
}
/// N1MM's {OPERATOR}: who is at the radio, which is not always whose
/// callsign the station is on the air with.
[Fact]
public void TheOperatorIsWhoeverIsAtTheRadio()
{
OperatingPosition session = Session();
Assert.Equal("DL1ABC", MessageExpander.Expand("{OPERATOR}", session));
session.Session.Operator = "OM3KFF";
Assert.Equal("OM3KFF", MessageExpander.Expand("{OPERATOR}", session));
}
}