Act on the .udc session, off-time and band-change settings
Four settings were read by nothing. MultipleSessions = 0000/30 runs the contest in half-hour sessions from 00:00 UTC. The session is part of the dupe key, so a station worked in an earlier one may be worked again, and ResetMultsEverySession and ResetSNEverySession start the multipliers and the serial numbers over. DupeQSOMinutesAgo is the other way back to a station: after so many minutes rather than in the next session. The log holds the time of the last contact per dupe key instead of only the key, and the setting's IgnoreBand and ThisMode decide the scope, because a file that uses it turns DupeType off. MinimumOffTime drives the time on and time off the score summary now shows under the score, and beside them are the band changes made against the allowance for a contest that limits them. N1MM's countdown for the minutes you have to stay on a new band needs its info window and is not here. UdcFile.Flag now takes 1 and 0 as well as True and False, which is what these settings are written as and what N1MM reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
64
tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs
Normal file
64
tests/Nonemm.Contests.Tests/BandChangeRulesTests.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Contests.Tests;
|
||||
|
||||
/// The `…BandChange…` settings: what counts as a change, and over what stretch
|
||||
/// of time the changes are counted.
|
||||
public class BandChangeRulesTests
|
||||
{
|
||||
private static readonly DateTime Start = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private static Qso At(int minutes, double kilohertz, Mode? mode = null) =>
|
||||
TestLog.Contact("OM3XYZ", kilohertz, mode: mode) with
|
||||
{
|
||||
TimestampUtc = Start.AddMinutes(minutes),
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void EachMoveToAnotherBandIsOneChange() =>
|
||||
Assert.Equal(
|
||||
2,
|
||||
new BandChangeRules(8).CountedIn(
|
||||
[At(0, 14_025), At(5, 7_040), At(10, 7_045), At(15, 14_030)],
|
||||
Start.AddMinutes(20)));
|
||||
|
||||
/// The count is per clock hour unless the file says otherwise, so a change
|
||||
/// made in the hour before does not count against this one.
|
||||
[Fact]
|
||||
public void ChangesMadeInAnEarlierHourAreNotCounted() =>
|
||||
Assert.Equal(
|
||||
1,
|
||||
new BandChangeRules(8).CountedIn(
|
||||
[At(0, 14_025), At(5, 7_040), At(65, 14_025)],
|
||||
Start.AddMinutes(70)));
|
||||
|
||||
[Fact]
|
||||
public void CountingPerContestKeepsEveryChange() =>
|
||||
Assert.Equal(
|
||||
2,
|
||||
new BandChangeRules(8, PerContest: true).CountedIn(
|
||||
[At(0, 14_025), At(5, 7_040), At(65, 14_025)],
|
||||
Start.AddMinutes(70)));
|
||||
|
||||
/// `CountBandChangesPerPeriod` counts the hour up to now rather than the
|
||||
/// clock hour.
|
||||
[Fact]
|
||||
public void CountingPerPeriodLooksBackAnHour() =>
|
||||
Assert.Equal(
|
||||
1,
|
||||
new BandChangeRules(8, PerPeriod: true).CountedIn(
|
||||
[At(0, 14_025), At(5, 7_040), At(65, 14_025)],
|
||||
Start.AddMinutes(70)));
|
||||
|
||||
/// `CountBandOrModeChange` counts a move to another mode on the same band.
|
||||
[Fact]
|
||||
public void ModeChangesCountWhenTheContestSaysSo() =>
|
||||
Assert.Equal(
|
||||
1,
|
||||
new BandChangeRules(8, CountsModeChanges: true).CountedIn(
|
||||
[At(0, 14_025), At(5, 14_250, Modes.Usb)],
|
||||
Start.AddMinutes(10)));
|
||||
|
||||
[Fact]
|
||||
public void AContestWithNoLimitCountsNothing() => Assert.False(BandChangeRules.None.IsCounted);
|
||||
}
|
||||
35
tests/Nonemm.Contests.Tests/OperatingTimeTests.cs
Normal file
35
tests/Nonemm.Contests.Tests/OperatingTimeTests.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Nonemm.Core;
|
||||
|
||||
namespace Nonemm.Contests.Tests;
|
||||
|
||||
/// Time on and time off, as a sponsor with a rest rule asks for it.
|
||||
public class OperatingTimeTests
|
||||
{
|
||||
private static readonly DateTime Start = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private static Qso At(int minutes) =>
|
||||
TestLog.Contact("OM3XYZ") with { TimestampUtc = Start.AddMinutes(minutes) };
|
||||
|
||||
[Fact]
|
||||
public void AGapAsLongAsTheMinimumIsTimeOff() =>
|
||||
Assert.Equal(
|
||||
TimeSpan.FromMinutes(30),
|
||||
OperatingTime.Off([At(0), At(10), At(40), At(50)], TimeSpan.FromMinutes(30)));
|
||||
|
||||
[Fact]
|
||||
public void AShorterGapIsTimeOperating() =>
|
||||
Assert.Equal(
|
||||
TimeSpan.Zero,
|
||||
OperatingTime.Off([At(0), At(20), At(40)], TimeSpan.FromMinutes(30)));
|
||||
|
||||
/// The time from the first contact to the last, less the breaks.
|
||||
[Fact]
|
||||
public void TimeOnIsWhatIsLeftOfTheContest() =>
|
||||
Assert.Equal(
|
||||
TimeSpan.FromMinutes(20),
|
||||
OperatingTime.On([At(0), At(10), At(40), At(50)], TimeSpan.FromMinutes(30)));
|
||||
|
||||
[Fact]
|
||||
public void OneContactIsNoTimeAtAll() =>
|
||||
Assert.Equal(TimeSpan.Zero, OperatingTime.On([At(0)], TimeSpan.FromMinutes(30)));
|
||||
}
|
||||
@@ -292,6 +292,84 @@ public class UdcScoringTests
|
||||
.Judge(TestLog.Contact("OM3XYZ", section: section))
|
||||
.NewMultipliers.Count);
|
||||
|
||||
/// `DupeQSOMinutesAgo = 10` lets the same station be worked again ten
|
||||
/// minutes after the last contact, and not before.
|
||||
[Fact]
|
||||
public void ADupeWindowLetsAStationBeWorkedAgainLater()
|
||||
{
|
||||
UserDefinedContest contest = Contest("DupeType=4", "DupeQSOMinutesAgo=10");
|
||||
ContestLog log = LogFor(contest);
|
||||
DateTime first = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
|
||||
log.Add(TestLog.Contact("OM3XYZ") with { TimestampUtc = first });
|
||||
|
||||
Assert.True(log.Judge(TestLog.Contact("OM3XYZ") with
|
||||
{
|
||||
TimestampUtc = first.AddMinutes(9),
|
||||
}).IsDupe);
|
||||
Assert.False(log.Judge(TestLog.Contact("OM3XYZ") with
|
||||
{
|
||||
TimestampUtc = first.AddMinutes(11),
|
||||
}).IsDupe);
|
||||
}
|
||||
|
||||
/// `IgnoreBand` looks for the earlier contact on every band rather than
|
||||
/// this one, so the same station on another band is still a dupe.
|
||||
[Fact]
|
||||
public void ADupeWindowThatIgnoresTheBandCoversEveryBand()
|
||||
{
|
||||
UserDefinedContest contest = Contest("DupeType=4", "DupeQSOMinutesAgo=10, True, True");
|
||||
ContestLog log = LogFor(contest);
|
||||
DateTime first = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
|
||||
log.Add(TestLog.Contact("OM3XYZ") with { TimestampUtc = first });
|
||||
|
||||
Assert.True(log.Judge(TestLog.Contact("OM3XYZ", 7_040) with
|
||||
{
|
||||
TimestampUtc = first.AddMinutes(5),
|
||||
}).IsDupe);
|
||||
}
|
||||
|
||||
/// `MultipleSessions = 0000/30` runs the contest in half-hour sessions,
|
||||
/// and a station worked in one of them may be worked again in the next.
|
||||
[Fact]
|
||||
public void AStationWorkedInAnEarlierSessionIsNoDupe()
|
||||
{
|
||||
UserDefinedContest contest = Contest("MultipleSessions=0000/30");
|
||||
ContestLog log = LogFor(contest);
|
||||
DateTime first = new(2026, 5, 30, 12, 10, 0, DateTimeKind.Utc);
|
||||
log.Add(TestLog.Contact("OM3XYZ") with { TimestampUtc = first });
|
||||
|
||||
Assert.True(log.Judge(TestLog.Contact("OM3XYZ") with
|
||||
{
|
||||
TimestampUtc = first.AddMinutes(10),
|
||||
}).IsDupe);
|
||||
Assert.False(log.Judge(TestLog.Contact("OM3XYZ") with
|
||||
{
|
||||
TimestampUtc = first.AddMinutes(25),
|
||||
}).IsDupe);
|
||||
}
|
||||
|
||||
/// `ResetMultsEverySession` counts the same multiplier again in the next
|
||||
/// session. Without it the multiplier stands for the whole contest.
|
||||
[Theory]
|
||||
[InlineData("ResetMultsEverySession=1", 1)]
|
||||
[InlineData("ResetMultsEverySession=0", 0)]
|
||||
public void MultipliersStartOverEachSessionWhenTheFileSaysSo(string setting, int expected)
|
||||
{
|
||||
UserDefinedContest contest = Contest(
|
||||
"MultipleSessions=0000/30",
|
||||
"MultSqlString=CountryPrefix",
|
||||
"IsMultPer=4",
|
||||
setting);
|
||||
ContestLog log = LogFor(contest);
|
||||
DateTime first = new(2026, 5, 30, 12, 10, 0, DateTimeKind.Utc);
|
||||
log.Add(TestLog.Contact("OM3XYZ") with { TimestampUtc = first });
|
||||
|
||||
Assert.Equal(
|
||||
expected,
|
||||
log.Judge(TestLog.Contact("OM5ZZZ") with { TimestampUtc = first.AddMinutes(25) })
|
||||
.NewMultipliers.Count);
|
||||
}
|
||||
|
||||
/// `BonusPoints2` names how much a station on a list is worth and the file
|
||||
/// the list is in. `+50` adds to what the contact scored.
|
||||
[Theory]
|
||||
|
||||
Reference in New Issue
Block a user