N1MM's log window has two panes. The lower one lists every contact already logged with the call in the callsign box, on any band and in any mode, so an apparent dupe can be read off the screen rather than looked for. EarlierContacts.Matching holds the rule, so it is unit-tested without a window: three characters before anything is listed, a call that holds what is typed, `*` for any run of characters and `?` for one, the grid square instead when nothing is typed, ordered by call, mode, band and time, and fifty at most. That is N1MM's DupeSQL, except that N1MM passes `?` to SQLite as a plain character, which finds nothing. The two grids share their columns, because a value in the pane has to sit under the column it belongs to. Each column is now as wide as the longest value it can hold, measured in the font it is drawn in, and the last column takes what is left over. The values and the header are in different fonts, so each is measured on its own. This also settles the column widths, which used to change as rows scrolled into view. A DataGrid does not pass its own font down to its cells, so the cell font is set in a style. Without that the cells draw larger than the width measured for them and every column is a character short. Looked at under Xvfb with a call typed: the pane lists the two contacts that hold it, under columns that line up with the log above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
119 lines
3.4 KiB
C#
119 lines
3.4 KiB
C#
using Nonemm.Core;
|
|
|
|
namespace Nonemm.Session.Tests;
|
|
|
|
public class EarlierContactsTests
|
|
{
|
|
private static Qso Contact(
|
|
string call,
|
|
double kilohertz = 14_030,
|
|
Mode? mode = null,
|
|
int minute = 0,
|
|
string grid = "")
|
|
{
|
|
return new Qso
|
|
{
|
|
Id = Qso.NewId(),
|
|
TimestampUtc = new DateTime(2026, 1, 1, 0, minute, 0, DateTimeKind.Utc),
|
|
Call = Callsign.Parse(call),
|
|
Frequency = Frequency.FromKilohertz(kilohertz),
|
|
Mode = mode ?? Modes.Cw,
|
|
ContestName = "CQWW",
|
|
GridSquare = grid,
|
|
};
|
|
}
|
|
|
|
[Fact]
|
|
public void FindsEveryContactWhoseCallHoldsWhatIsTyped()
|
|
{
|
|
List<Qso> log = [Contact("DL1ABC"), Contact("OM3KFF"), Contact("W1ABCD")];
|
|
|
|
IReadOnlyList<Qso> found = EarlierContacts.Matching(log, "ABC");
|
|
|
|
Assert.Equal(["DL1ABC", "W1ABCD"], found.Select(q => q.Call.Text));
|
|
}
|
|
|
|
[Fact]
|
|
public void SaysNothingUntilThreeCharactersAreTyped()
|
|
{
|
|
List<Qso> log = [Contact("DL1ABC")];
|
|
|
|
Assert.Empty(EarlierContacts.Matching(log, "DL"));
|
|
Assert.Single(EarlierContacts.Matching(log, "DL1"));
|
|
}
|
|
|
|
[Fact]
|
|
public void AStarStandsForAnyRunOfCharacters()
|
|
{
|
|
List<Qso> log = [Contact("DL1ABC"), Contact("DL2XYZ"), Contact("OM3DL1")];
|
|
|
|
IReadOnlyList<Qso> found = EarlierContacts.Matching(log, "DL*C");
|
|
|
|
Assert.Equal(["DL1ABC"], found.Select(q => q.Call.Text));
|
|
}
|
|
|
|
[Fact]
|
|
public void AQuestionMarkStandsForOneCharacter()
|
|
{
|
|
List<Qso> log = [Contact("DL1ABC"), Contact("DL11ABC")];
|
|
|
|
IReadOnlyList<Qso> found = EarlierContacts.Matching(log, "DL?ABC");
|
|
|
|
Assert.Equal(["DL1ABC"], found.Select(q => q.Call.Text));
|
|
}
|
|
|
|
[Fact]
|
|
public void AWildcardPatternCoversTheWholeCall()
|
|
{
|
|
List<Qso> log = [Contact("DL1ABC"), Contact("OM3DL1")];
|
|
|
|
Assert.Equal(["DL1ABC"], EarlierContacts.Matching(log, "DL1*").Select(q => q.Call.Text));
|
|
}
|
|
|
|
[Fact]
|
|
public void WithNothingTypedItFollowsTheGridSquare()
|
|
{
|
|
List<Qso> log =
|
|
[
|
|
Contact("DL1ABC", grid: "JN88MD"),
|
|
Contact("OM3KFF", grid: "JO70AA"),
|
|
];
|
|
|
|
IReadOnlyList<Qso> found = EarlierContacts.Matching(log, "", "JN88");
|
|
|
|
Assert.Equal(["DL1ABC"], found.Select(q => q.Call.Text));
|
|
}
|
|
|
|
[Fact]
|
|
public void WithNeitherACallNorAGridItSaysNothing()
|
|
{
|
|
Assert.Empty(EarlierContacts.Matching([Contact("DL1ABC", grid: "JN88MD")], "", ""));
|
|
}
|
|
|
|
[Fact]
|
|
public void OrdersByCallThenModeThenFrequencyThenTime()
|
|
{
|
|
List<Qso> log =
|
|
[
|
|
Contact("OM3KFF", kilohertz: 14_200, mode: Modes.Usb, minute: 4),
|
|
Contact("DL1ABC", kilohertz: 21_030, minute: 3),
|
|
Contact("DL1ABC", kilohertz: 14_030, mode: Modes.Usb, minute: 2),
|
|
Contact("DL1ABC", kilohertz: 14_030, minute: 1),
|
|
];
|
|
|
|
IReadOnlyList<Qso> found = EarlierContacts.Matching(log, "??????");
|
|
|
|
Assert.Equal(
|
|
[(1, "DL1ABC"), (3, "DL1ABC"), (2, "DL1ABC"), (4, "OM3KFF")],
|
|
found.Select(q => (q.TimestampUtc.Minute, q.Call.Text)));
|
|
}
|
|
|
|
[Fact]
|
|
public void KeepsAtMostTheLimit()
|
|
{
|
|
List<Qso> log = [.. Enumerable.Range(0, 60).Select(n => Contact($"DL1AB{n:00}"))];
|
|
|
|
Assert.Equal(EarlierContacts.Limit, EarlierContacts.Matching(log, "DL1").Count);
|
|
}
|
|
}
|