Files
Nonemm/tests/Nonemm.Session.Tests/EarlierContactsTests.cs
ericek111 a31c40fb2d List the earlier contacts with the call being typed under the log
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
2026-08-30 21:00:04 +00:00

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);
}
}