Files
Nonemm/tests/Nonemm.Spotting.Tests/BandmapTests.cs
T
erikbrockoandClaude Sonnet 5.5 0a23815f38 Lay out Available Mults and Qs like N1MM's window
The window is now a summary table of mults, Qs and totals per band over a
sortable spot grid with Call, Freq, Mode, Dir, Mlt, SN, TS, Spotter, Grid,
Sun, Pts and a preferred-spotter column. Columns can be hidden, headers
sorted, and the list stops re-sorting for six seconds under the mouse.

Adds the Mults / Mults & Qs button, a Bands & Modes filter, delete and
blacklist from the right-click menu, Alt+A stepping, and QSY to the top spot
after logging. Spot gains Snr and IsSkimmer, Bandmap gains Remaining, and
AvailableStation carries the path to the station.

The menu item no longer has apostrophes. SO2R, SO2V and the other missing
pieces are listed in docs/unfinished.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 11:56:32 +00:00

140 lines
4.1 KiB
C#

using Nonemm.Core;
namespace Nonemm.Spotting.Tests;
public class BandmapTests
{
private static readonly DateTime Now = new(2026, 5, 30, 12, 0, 0, DateTimeKind.Utc);
private static Spot At(string call, double kilohertz, DateTime? at = null) =>
new(Callsign.Parse(call), Frequency.FromKilohertz(kilohertz), at ?? Now, SpotSource.Cluster);
[Fact]
public void SpotsComeBackInFrequencyOrder()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_100));
map.Add(At("JA1XYZ", 14_010));
Assert.Equal(["JA1XYZ", "DL1ABC"], map.On(Bands.Band20M).Select(s => s.Call.Text));
}
[Fact]
public void EachSpotLivesAsLongAsItsOwnLifetime()
{
Bandmap map = new(clock: () => Now)
{
LifetimeFor = spot => TimeSpan.FromMinutes(spot.Frequency.Kilohertz < 14_100 ? 10 : 30),
};
map.Add(At("DL1ABC", 14_025));
map.Add(At("JA1XYZ", 14_200));
map.DropOlderThan(Now.AddMinutes(15));
Assert.Equal(["JA1XYZ"], map.All().Select(s => s.Call.Text));
}
[Fact]
public void AStationThatMovesKeepsOneEntryOnItsBand()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("DL1ABC", 14_020));
Spot only = Assert.Single(map.On(Bands.Band20M));
Assert.Equal(14_020_000, only.Frequency.Hertz);
}
[Fact]
public void TheSameStationOnAnotherBandIsASeparateEntry()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("DL1ABC", 7_010));
Assert.Equal(2, map.All().Count);
}
[Fact]
public void SpotsOlderThanAnHourAreDropped()
{
DateTime arriving = Now.AddHours(-2);
Bandmap map = new(clock: () => arriving);
map.Add(At("DL1ABC", 14_010));
arriving = Now.AddMinutes(-5);
map.Add(At("JA1XYZ", 14_020));
map.DropOlderThan(Now);
Assert.Equal("JA1XYZ", Assert.Single(map.All()).Call.Text);
}
/// Nodes get their clocks wrong and replay their backlog on connect. Age is
/// counted from when the spot arrived, so a spot stamped hours ago still
/// shows for the hour after it turned up.
[Fact]
public void ASpotStampedLongAgoIsKeptIfItOnlyJustArrived()
{
Bandmap map = new(clock: () => Now);
map.Add(At("DL1ABC", 14_010, Now.AddHours(-4)));
map.DropOlderThan(Now);
Assert.Single(map.All());
}
[Fact]
public void ARespottedStationStartsItsHourAgain()
{
DateTime arriving = Now.AddMinutes(-50);
Bandmap map = new(clock: () => arriving);
map.Add(At("DL1ABC", 14_010));
arriving = Now;
map.Add(At("DL1ABC", 14_012));
map.DropOlderThan(Now.AddMinutes(30));
Assert.Single(map.All());
}
/// N1MM's Mark button leaves this where the radio is, to say the frequency
/// is busy.
[Fact]
public void AMarkIsLabelledWithTheTimeAndIsNotAStation()
{
Spot mark = Spot.Mark(
Frequency.FromKilohertz(14_030),
new DateTime(2026, 1, 1, 22, 31, 5, DateTimeKind.Utc));
Assert.Equal("BUSY@22:31:05", mark.Call.Text);
Assert.False(mark.IsStation);
}
[Fact]
public void TheNearestSpotInsideTheWindowIsFound()
{
Bandmap map = new();
map.Add(At("DL1ABC", 14_010));
map.Add(At("JA1XYZ", 14_050));
Assert.Equal(
"DL1ABC",
map.Near(Frequency.FromKilohertz(14_010.2), Frequency.FromKilohertz(0.5))?.Call.Text);
Assert.Null(map.Near(Frequency.FromKilohertz(14_030), Frequency.FromKilohertz(0.5)));
}
[Fact]
public void ASpotHasItsLifetimeLessTheTimeItHasBeenThere()
{
DateTime clock = Now;
Bandmap map = new(TimeSpan.FromMinutes(30), () => clock);
Spot spot = At("DL1ABC", 14_025);
map.Add(spot);
clock = Now.AddMinutes(12);
Assert.Equal(TimeSpan.FromMinutes(18), map.Remaining(spot));
}
[Fact]
public void ASpotThatIsNotOnTheMapHasNoTimeLeft()
{
Assert.Null(new Bandmap().Remaining(At("DL1ABC", 14_025)));
}
}