`CabrilloString` names a field and a column width in turn. The line it makes follows N1MM: the operator's callsign in the first 13 columns, then every column padded to its width with one space after it, and a value longer than its column cut rather than pushed sideways. The cut is new for every contest, not just the user-defined ones — the columns are the sponsor's, not ours. `CabrilloVersion` is read too, so a contest whose sponsor still asks for 2.0 gets it in the header. A contest that lays out its own line can put the transmitter column where it wants it, or leave it out, so the writer no longer adds one at the end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
138 lines
4.4 KiB
C#
138 lines
4.4 KiB
C#
using Nonemm.Contests;
|
|
using Nonemm.Contests.Rules;
|
|
using Nonemm.Contests.Udc;
|
|
using Nonemm.Core;
|
|
using Nonemm.Formats.Cabrillo;
|
|
|
|
namespace Nonemm.Formats.Tests;
|
|
|
|
public class CabrilloWriterTests
|
|
{
|
|
private static readonly StationInfo Me = new()
|
|
{
|
|
Callsign = "DL1ABC",
|
|
CqZone = 14,
|
|
Continent = "EU",
|
|
CountryPrefix = "DL",
|
|
};
|
|
|
|
private static Qso Contact(double kilohertz = 14_025, Mode? mode = null) => new()
|
|
{
|
|
Id = Qso.NewId(),
|
|
TimestampUtc = new DateTime(2026, 5, 30, 12, 34, 56, DateTimeKind.Utc),
|
|
Call = Callsign.Parse("JA1XYZ"),
|
|
Frequency = Frequency.FromKilohertz(kilohertz),
|
|
Mode = mode ?? Modes.Cw,
|
|
ContestName = "CQWW",
|
|
SentReport = "599",
|
|
ReceivedReport = "599",
|
|
Zone = 25,
|
|
SentNumber = 12,
|
|
ReceivedNumber = 34,
|
|
};
|
|
|
|
[Fact]
|
|
public void QsoLineCarriesBothExchanges()
|
|
{
|
|
CabrilloWriter writer = new(new CqWorldWide(ModeCategory.Cw), Me);
|
|
string line = writer.QsoLine(Contact());
|
|
Assert.StartsWith("QSO: 14025 CW 2026-05-30 1234 DL1ABC", line);
|
|
Assert.Contains("JA1XYZ", line);
|
|
Assert.EndsWith("0", line);
|
|
Assert.Contains(" 14 ", line);
|
|
Assert.Contains(" 25 ", line);
|
|
}
|
|
|
|
[Fact]
|
|
public void WpxNumbersArePaddedToFour()
|
|
{
|
|
CabrilloWriter writer = new(new CqWpx(ModeCategory.Cw), Me);
|
|
string line = writer.QsoLine(Contact());
|
|
Assert.Contains("0012", line);
|
|
Assert.Contains("0034", line);
|
|
}
|
|
|
|
[Fact]
|
|
public void VhfContactsUseTheBandDesignator()
|
|
{
|
|
CabrilloWriter writer = new(new CqWorldWide(ModeCategory.Cw), Me);
|
|
Assert.Contains(" 144 ", writer.QsoLine(Contact(144_200)));
|
|
Assert.Contains(" 1.2G ", writer.QsoLine(Contact(1_296_100)));
|
|
}
|
|
|
|
[Fact]
|
|
public void HeaderAndFooterWrapTheContacts()
|
|
{
|
|
CabrilloWriter writer = new(new CqWorldWide(ModeCategory.Cw), Me);
|
|
string log = writer.Write(
|
|
new CabrilloHeader { Contest = "CQ-WW-CW", Callsign = "DL1ABC", ClaimedScore = 1234 },
|
|
[Contact()]);
|
|
Assert.StartsWith("START-OF-LOG: 3.0\r\n", log);
|
|
Assert.Contains("CONTEST: CQ-WW-CW\r\n", log);
|
|
Assert.Contains("CLAIMED-SCORE: 1234\r\n", log);
|
|
Assert.EndsWith("END-OF-LOG:\r\n", log);
|
|
}
|
|
|
|
[Fact]
|
|
public void ContactsLeftOutOfTheClaimedScoreAreNotSent()
|
|
{
|
|
CabrilloWriter writer = new(new CqWorldWide(ModeCategory.Cw), Me);
|
|
string log = writer.Write(
|
|
new CabrilloHeader { Contest = "CQ-WW-CW", Callsign = "DL1ABC" },
|
|
[Contact() with { IsClaimed = false }]);
|
|
Assert.DoesNotContain("QSO:", log);
|
|
}
|
|
|
|
/// The SA 10 file, whose sponsor asks for its own columns: the operator's
|
|
/// call in the first 13, then every column padded to the width the file
|
|
/// gives, with one space after it.
|
|
[Fact]
|
|
public void AUserDefinedContestCanLayOutItsOwnLine()
|
|
{
|
|
UserDefinedContest contest = new(UdcFile.Parse("""
|
|
[Contest]
|
|
Name=SA10_DX
|
|
CabrilloName=SA10-DX
|
|
CabrilloFormat=99
|
|
CabrilloString=SNT, 4, SentExch, 4, CallSign, 13, RCV, 4, Exchange1, 4
|
|
"""));
|
|
CabrilloWriter writer = new(contest, Me, new ContestEntry { SentExchange = "14" });
|
|
|
|
string line = writer.QsoLine(Contact() with { Exchange1 = "25" });
|
|
|
|
Assert.Equal(
|
|
"QSO: 14025 CW 2026-05-30 1234 DL1ABC 599 14 JA1XYZ 599 25 ",
|
|
line);
|
|
}
|
|
|
|
/// A value longer than its column is cut rather than pushing the columns
|
|
/// after it out of line, which is what N1MM writes.
|
|
[Fact]
|
|
public void AValueTooLongForItsColumnIsCut()
|
|
{
|
|
UserDefinedContest contest = new(UdcFile.Parse("""
|
|
[Contest]
|
|
Name=TIGHT
|
|
CabrilloString=Exchange1, 3
|
|
"""));
|
|
CabrilloWriter writer = new(contest, Me);
|
|
|
|
Assert.EndsWith("ABC ", writer.QsoLine(Contact() with { Exchange1 = "ABCDEF" }));
|
|
}
|
|
|
|
[Fact]
|
|
public void TheCabrilloVersionComesFromTheContest()
|
|
{
|
|
UserDefinedContest contest = new(UdcFile.Parse("""
|
|
[Contest]
|
|
Name=OLD
|
|
CabrilloVersion=2.0
|
|
"""));
|
|
CabrilloWriter writer = new(contest, Me);
|
|
|
|
Assert.StartsWith(
|
|
"START-OF-LOG: 2.0",
|
|
writer.Write(new CabrilloHeader { Contest = "OLD", Callsign = "DL1ABC" }, []));
|
|
}
|
|
}
|