Alt+W only put the boxes back, so wiping needed Ctrl+W or the button. It now goes both ways, the way N1MM's Wipe button does: with something in the boxes it clears them, and pressed again on empty boxes it puts back what it cleared. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015NZkFxHde1iifAQemFTHxf
731 lines
23 KiB
C#
731 lines
23 KiB
C#
using Nonemm.Contests;
|
|
using Nonemm.Contests.Rules;
|
|
using Nonemm.Core;
|
|
using Nonemm.Core.Calls;
|
|
using Nonemm.Core.Country;
|
|
using Nonemm.Storage;
|
|
|
|
namespace Nonemm.Session.Tests;
|
|
|
|
public class OperatingPositionTests
|
|
{
|
|
private const string Countries = """
|
|
Germany: 14: 28: EU: 51.00: -10.00: -1.0: DL:
|
|
DL,DK,DJ;
|
|
Japan: 25: 45: AS: 36.40: -138.38: -9.0: JA:
|
|
JA,JH,JR;
|
|
""";
|
|
|
|
private static readonly StationInfo Me = new()
|
|
{
|
|
Callsign = "DL1ABC",
|
|
CqZone = 14,
|
|
ItuZone = 28,
|
|
Continent = "EU",
|
|
CountryPrefix = "DL",
|
|
};
|
|
|
|
private static OperatingPosition Session(Contest? contest = null) =>
|
|
new(Shared(new FakeLogStore(), contest));
|
|
|
|
private static ContestSession Shared(FakeLogStore store, Contest? contest = null)
|
|
{
|
|
ContestInstance instance = store.AddContest(new ContestInstance
|
|
{
|
|
ContestNumber = 0,
|
|
ContestName = "CQWW",
|
|
});
|
|
return new ContestSession(
|
|
store,
|
|
contest ?? new CqWorldWide(ModeCategory.Cw),
|
|
instance,
|
|
Me,
|
|
CountryFile.Parse(Countries));
|
|
}
|
|
|
|
private static void Type(OperatingPosition session, string call, string zone)
|
|
{
|
|
session.Entry.Call = call;
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
|
|
session.Entry.Set(ExchangeSlot.Zone, zone);
|
|
}
|
|
|
|
/// N1MM shows what this station sends beside what it copies, keeps both
|
|
/// filled in and steps over them.
|
|
[Fact]
|
|
public void TheSentBoxesAreFilledInAndSteppedOver()
|
|
{
|
|
OperatingPosition session = Session(new CqWpx(ModeCategory.Cw));
|
|
|
|
Assert.Equal("599", session.Entry.ValueOf(ExchangeSlot.SentReport));
|
|
Assert.Equal("1", session.Entry.ValueOf(ExchangeSlot.SentNumber));
|
|
|
|
// the boxes are the call, the two reports, the sent serial and the
|
|
// serial being copied
|
|
session.Entry.Advance();
|
|
Assert.Equal(4, session.Entry.Focus);
|
|
}
|
|
|
|
[Fact]
|
|
public void TheSentSerialGoesUpWhenAContactIsLogged()
|
|
{
|
|
OperatingPosition session = Session(new CqWpx(ModeCategory.Cw));
|
|
session.Entry.Call = "JA1XYZ";
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
|
|
session.Entry.Set(ExchangeSlot.SerialNumber, "12");
|
|
|
|
Assert.Equal(1, session.LogContact().SentNumber);
|
|
Assert.Equal("2", session.Entry.ValueOf(ExchangeSlot.SentNumber));
|
|
}
|
|
|
|
/// The BARTG contest sends the time of the exchange, and logs the time it
|
|
/// sent rather than the time the contact was logged.
|
|
[Fact]
|
|
public void TheTimeTheExchangeWentOutIsLoggedForBartg()
|
|
{
|
|
OperatingPosition session = Session(new BartgSprint());
|
|
session.Entry.Call = "JA1XYZ";
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
|
|
session.Entry.Set(ExchangeSlot.SerialNumber, "12");
|
|
session.Entry.Set(ExchangeSlot.Exchange1, "1646");
|
|
session.StampSentTime();
|
|
string stamped = session.SentTime;
|
|
|
|
Qso logged = session.LogContact();
|
|
|
|
Assert.Equal(4, stamped.Length);
|
|
Assert.Equal(stamped, logged.MiscText);
|
|
Assert.Equal("", session.SentTime);
|
|
}
|
|
|
|
/// A contest that does not exchange a time keeps the column empty.
|
|
[Fact]
|
|
public void NothingIsStampedForAContestThatSendsNoTime()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.StampSentTime();
|
|
|
|
Assert.Equal("", session.SentTime);
|
|
Assert.Equal("", session.LogContact().MiscText);
|
|
}
|
|
|
|
[Fact]
|
|
public void LoggingAContactPutsItInTheLogAndClearsTheEntry()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
|
|
Assert.Equal("JA1XYZ", logged.Call.Text);
|
|
Assert.Equal(25, logged.Zone);
|
|
Assert.Equal(3, logged.Points);
|
|
Assert.Equal("", session.Entry.Call);
|
|
Assert.Single(session.Log.Qsos);
|
|
}
|
|
|
|
[Fact]
|
|
public void CountryAndPrefixAreFilledInFromTheCountryFile()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
|
|
Assert.Equal("JA", logged.CountryPrefix);
|
|
Assert.Equal("AS", logged.Continent);
|
|
Assert.Equal("JA1", logged.WpxPrefix);
|
|
}
|
|
|
|
[Fact]
|
|
public void AnIncompleteExchangeIsNotLogged()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.Call = "JA1XYZ";
|
|
Assert.Throws<InvalidOperationException>(() => session.LogContact());
|
|
}
|
|
|
|
[Fact]
|
|
public void TheSameStationOnTheSameBandReadsAsADupe()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.LogContact();
|
|
Type(session, "JA1XYZ", "25");
|
|
Assert.True(session.Verdict()?.IsDupe);
|
|
}
|
|
|
|
[Fact]
|
|
public void MovingBandClearsTheDupe()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.LogContact();
|
|
session.Tune(Frequency.FromKilohertz(7_025));
|
|
Type(session, "JA1XYZ", "25");
|
|
Assert.False(session.Verdict()?.IsDupe);
|
|
}
|
|
|
|
[Fact]
|
|
public void SerialNumbersCountUp()
|
|
{
|
|
OperatingPosition session = Session(new CqWpx(ModeCategory.Cw));
|
|
Assert.Equal(1, session.SentNumber);
|
|
session.Entry.Call = "JA1XYZ";
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
|
|
session.Entry.Set(ExchangeSlot.SerialNumber, "12");
|
|
session.LogContact();
|
|
Assert.Equal(2, session.SentNumber);
|
|
}
|
|
|
|
[Fact]
|
|
public void SpaceStepsOverTheReportAndComesBackToTheCall()
|
|
{
|
|
// CQ WW's boxes are the call, the two reports and the zone
|
|
OperatingPosition session = Session();
|
|
|
|
Assert.Equal(0, session.Entry.Focus);
|
|
session.Entry.Advance();
|
|
Assert.Equal(3, session.Entry.Focus);
|
|
session.Entry.Advance();
|
|
Assert.Equal(0, session.Entry.Focus);
|
|
}
|
|
|
|
[Fact]
|
|
public void GoingBackStepsOverTheReportToo()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.FocusOn(3);
|
|
|
|
session.Entry.Retreat();
|
|
|
|
Assert.Equal(0, session.Entry.Focus);
|
|
}
|
|
|
|
[Fact]
|
|
public void TheReportBoxesAreFilledInWithWhatWouldBeSentAnyway()
|
|
{
|
|
OperatingPosition session = Session();
|
|
|
|
Assert.True(session.FillReports());
|
|
|
|
Assert.Equal("599", session.Entry.ValueOf(ExchangeSlot.ReceivedReport));
|
|
Assert.False(session.FillReports());
|
|
}
|
|
|
|
[Fact]
|
|
public void AReportTheOperatorTypedIsLeftAlone()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "559");
|
|
|
|
Assert.False(session.FillReports());
|
|
Assert.Equal("559", session.Entry.ValueOf(ExchangeSlot.ReceivedReport));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("14025", 14_025_000)]
|
|
[InlineData("14.025", 14_025_000)]
|
|
[InlineData("7025.5", 7_025_500)]
|
|
public void AFrequencyTypedIntoTheCallBoxIsRecognised(string typed, long hertz)
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.Call = typed;
|
|
Assert.Equal(hertz, session.PendingQsy()?.Hertz);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("JA1XYZ")]
|
|
[InlineData("12000")]
|
|
public void ACallOrAnOutOfBandNumberIsNotAQsy(string typed)
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.Call = typed;
|
|
Assert.Null(session.PendingQsy());
|
|
}
|
|
|
|
[Fact]
|
|
public void AContactDeletedFromTheLogGivesItsMultiplierBack()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso first = session.LogContact();
|
|
Type(session, "JA2XYZ", "25");
|
|
session.LogContact();
|
|
Assert.Equal(2, session.Log.Tally.TotalMultipliers);
|
|
|
|
session.Session.Delete(first.Id);
|
|
Assert.Single(session.Log.Qsos);
|
|
Assert.Equal(2, session.Log.Tally.TotalMultipliers);
|
|
Assert.True(session.Log.Qsos.Single().IsMultiplier1);
|
|
}
|
|
|
|
[Fact]
|
|
public void AContactWorkedSplitRecordsWhereWeTransmitted()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Tune(
|
|
Frequency.FromKilohertz(14_025),
|
|
Modes.Cw,
|
|
Frequency.FromKilohertz(14_200));
|
|
Type(session, "JA1XYZ", "25");
|
|
|
|
Qso logged = session.LogContact();
|
|
|
|
Assert.Equal(14_025_000, logged.Frequency.Hertz);
|
|
Assert.Equal(14_200_000, logged.QsxFrequency.Hertz);
|
|
}
|
|
|
|
[Fact]
|
|
public void AContactRecordsWhichRadioMadeIt()
|
|
{
|
|
OperatingPosition session = new(Shared(new FakeLogStore()), 2);
|
|
Type(session, "JA1XYZ", "25");
|
|
|
|
Assert.Equal(2, session.LogContact().RadioNumber);
|
|
}
|
|
|
|
/// Tuning without saying anything about split leaves it where it was, so a
|
|
/// frequency typed by hand does not clear what the radio reported.
|
|
[Fact]
|
|
public void TuningWithoutASplitLeavesTheOneAlreadySet()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Tune(Frequency.FromKilohertz(14_025), null, Frequency.FromKilohertz(14_200));
|
|
|
|
session.Tune(Frequency.FromKilohertz(14_030));
|
|
|
|
Assert.Equal(14_200_000, session.TransmitFrequency.Hertz);
|
|
}
|
|
|
|
[Fact]
|
|
public void EditingAContactRescoresTheLog()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
Assert.Equal(3, session.Log.Tally.Points);
|
|
|
|
QsoEdit edit = session.Session.Edit(logged.Id, QsoField.Call, "DL2XYZ");
|
|
|
|
Assert.True(edit.IsAccepted);
|
|
Assert.Equal("DL2XYZ", session.Log.Qsos.Single().Call.Text);
|
|
Assert.Equal(0, session.Log.Tally.Points);
|
|
}
|
|
|
|
[Fact]
|
|
public void ARefusedEditLeavesTheLogAlone()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
|
|
QsoEdit edit = session.Session.Edit(logged.Id, QsoField.Zone, "41");
|
|
|
|
Assert.False(edit.IsAccepted);
|
|
Assert.Equal(25, session.Log.Qsos.Single().Zone);
|
|
}
|
|
|
|
/// The other stations need the call and time the contact had before, because
|
|
/// that is how they find their own copy of it.
|
|
[Fact]
|
|
public void AnEditReportsWhatTheContactUsedToBe()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
QsoChange? change = null;
|
|
session.Session.Edited += (_, c) => change = c;
|
|
|
|
session.Session.Edit(logged.Id, QsoField.Call, "JA2XYZ");
|
|
|
|
Assert.Equal("JA1XYZ", change?.OldCall);
|
|
Assert.Equal(logged.TimestampUtc, change?.OldTimestampUtc);
|
|
Assert.Equal("JA2XYZ", change?.Qso.Call.Text);
|
|
}
|
|
|
|
/// The country file gets a call wrong now and then. Correcting the country
|
|
/// by hand has to change the score, or the correction is decoration.
|
|
[Fact]
|
|
public void CorrectingTheCountryChangesTheScore()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
Assert.Equal(3, session.Log.Tally.Points);
|
|
|
|
session.Session.Edit(logged.Id, QsoField.CountryPrefix, "DL");
|
|
|
|
Assert.Equal(0, session.Log.Tally.Points);
|
|
}
|
|
|
|
[Fact]
|
|
public void ADeleteReportsTheContactThatWent()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
Qso? gone = null;
|
|
session.Session.Deleted += (_, q) => gone = q;
|
|
|
|
session.Session.Delete(logged.Id);
|
|
|
|
Assert.Equal("JA1XYZ", gone?.Call.Text);
|
|
}
|
|
|
|
[Fact]
|
|
public void StoredContactsAreReadBackWhenTheSessionStarts()
|
|
{
|
|
FakeLogStore store = new();
|
|
OperatingPosition first = new(Shared(store));
|
|
Type(first, "JA1XYZ", "25");
|
|
first.LogContact();
|
|
|
|
OperatingPosition second = new(new ContestSession(
|
|
store,
|
|
new CqWorldWide(ModeCategory.Cw),
|
|
first.Instance,
|
|
Me,
|
|
CountryFile.Parse(Countries)));
|
|
Assert.Single(second.Log.Qsos);
|
|
Assert.Equal(3, second.Log.Tally.Points);
|
|
}
|
|
|
|
private static OperatingPosition WithHistory(Contest contest, string file)
|
|
{
|
|
FakeLogStore store = new();
|
|
ContestInstance instance = store.AddContest(new ContestInstance
|
|
{
|
|
ContestNumber = 0,
|
|
ContestName = contest.Name,
|
|
});
|
|
return new OperatingPosition(new ContestSession(
|
|
store,
|
|
contest,
|
|
instance,
|
|
Me,
|
|
CountryFile.Parse(Countries),
|
|
CallHistory.Parse(file)));
|
|
}
|
|
|
|
[Fact]
|
|
public void TheCallHistoryFillsTheExchangeIn()
|
|
{
|
|
OperatingPosition session = WithHistory(
|
|
new Sweepstakes(ModeCategory.Cw),
|
|
"!!Order!!,Call,Sect,CK\nW3LPL,MDC,56");
|
|
session.Entry.Call = "W3LPL";
|
|
|
|
Assert.True(session.FillFromHistory());
|
|
Assert.Equal("56MDC", session.Entry.ValueOf(ExchangeSlot.Exchange1));
|
|
}
|
|
|
|
/// What the other station actually sends beats what was published, so a box
|
|
/// with something in it is left alone.
|
|
[Fact]
|
|
public void WhatIsAlreadyTypedIsNotOverwritten()
|
|
{
|
|
OperatingPosition session = WithHistory(
|
|
new Sweepstakes(ModeCategory.Cw),
|
|
"!!Order!!,Call,Sect\nW3LPL,MDC");
|
|
session.Entry.Call = "W3LPL";
|
|
session.Entry.Set(ExchangeSlot.Exchange1, "72A 74EPA");
|
|
|
|
session.FillFromHistory();
|
|
|
|
Assert.Equal("72A 74EPA", session.Entry.ValueOf(ExchangeSlot.Exchange1));
|
|
}
|
|
|
|
[Fact]
|
|
public void ACallTheHistoryDoesNotHoldFillsNothing()
|
|
{
|
|
OperatingPosition session = WithHistory(
|
|
new Sweepstakes(ModeCategory.Cw),
|
|
"!!Order!!,Call,Sect\nW3LPL,MDC");
|
|
session.Entry.Call = "JA1XYZ";
|
|
|
|
Assert.False(session.FillFromHistory());
|
|
Assert.Equal("", session.Entry.ValueOf(ExchangeSlot.Exchange1));
|
|
}
|
|
|
|
/// CQ WW exchanges the CQ zone, so the CQ zone column is the one to use.
|
|
[Fact]
|
|
public void TheZoneComesFromTheColumnTheContestAsksFor()
|
|
{
|
|
OperatingPosition session = WithHistory(
|
|
new CqWorldWide(ModeCategory.Cw),
|
|
"!!Order!!,Call,CqZone,ITUZone\nJA1XYZ,25,45");
|
|
session.Entry.Call = "JA1XYZ";
|
|
|
|
session.FillFromHistory();
|
|
|
|
Assert.Equal("25", session.Entry.ValueOf(ExchangeSlot.Zone));
|
|
}
|
|
|
|
/// A file for a section contest often carries the state instead.
|
|
[Fact]
|
|
public void TheStateFillsASectionWhenTheFileHasNoSection()
|
|
{
|
|
OperatingPosition session = WithHistory(
|
|
new Sweepstakes(ModeCategory.Cw),
|
|
"!!Order!!,Call,Sect,State\nK1TTT,,CT");
|
|
session.Entry.Call = "K1TTT";
|
|
|
|
session.FillFromHistory();
|
|
|
|
Assert.Equal("00CT", session.Entry.ValueOf(ExchangeSlot.Exchange1));
|
|
}
|
|
|
|
[Fact]
|
|
public void TheNameAndCommentBoxesReachTheLog()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.Entry.Call = "JA1XYZ";
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "599");
|
|
session.Entry.Set(ExchangeSlot.Zone, "25");
|
|
session.OtherName = "Ken";
|
|
session.Comment = "up 2";
|
|
|
|
Qso logged = session.LogContact();
|
|
|
|
Assert.Equal("Ken", logged.Name);
|
|
Assert.Equal("up 2", logged.Comment);
|
|
Assert.Equal("", session.OtherName);
|
|
Assert.Equal("", session.Comment);
|
|
}
|
|
|
|
[Fact]
|
|
public void WipingClearsTheNameAndTheComment()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.OtherName = "Ken";
|
|
session.Comment = "up 2";
|
|
|
|
session.Wipe();
|
|
|
|
Assert.Equal("", session.OtherName);
|
|
Assert.Equal("", session.Comment);
|
|
}
|
|
|
|
[Fact]
|
|
public void CqWwRttyStepsOverTheStateBoxForAStationOutsideTheUsAndCanada()
|
|
{
|
|
OperatingPosition session = Session(new CqWorldWide(ModeCategory.Digital));
|
|
session.Entry.Call = "JA1XYZ";
|
|
|
|
// the boxes are the call, the two reports, the zone and the state
|
|
session.MoveFocus(forward: true);
|
|
Assert.Equal(3, session.Entry.Focus);
|
|
session.MoveFocus(forward: true);
|
|
Assert.Equal(0, session.Entry.Focus);
|
|
}
|
|
|
|
[Fact]
|
|
public void CqWwRttyStopsOnTheStateBoxForAUsStation()
|
|
{
|
|
OperatingPosition session = Session(new CqWorldWide(ModeCategory.Digital));
|
|
session.Entry.Call = "K1ABC";
|
|
|
|
session.MoveFocus(forward: true);
|
|
Assert.Equal(3, session.Entry.Focus);
|
|
session.MoveFocus(forward: true);
|
|
Assert.Equal(4, session.Entry.Focus);
|
|
}
|
|
|
|
[Fact]
|
|
public void QuickEditLoadsTheLastContactAndSavesTheChangeBack()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
Qso logged = session.LogContact();
|
|
Type(session, "DL9ZZZ", "14");
|
|
|
|
Assert.True(session.QuickEdit(forward: false));
|
|
Assert.Equal(logged.Id, session.Editing?.Id);
|
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
|
Assert.Equal("25", session.Entry.ValueOf(ExchangeSlot.Zone));
|
|
|
|
session.Entry.Set(ExchangeSlot.Zone, "24");
|
|
Qso? saved = session.SaveQuickEdit();
|
|
|
|
Assert.Equal(24, saved?.Zone);
|
|
Assert.Equal(24, session.Log.Qsos[0].Zone);
|
|
Assert.Null(session.Editing);
|
|
}
|
|
|
|
[Fact]
|
|
public void LeavingQuickEditPutsBackWhatWasBeingTyped()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.LogContact();
|
|
Type(session, "DL9ZZZ", "14");
|
|
|
|
session.QuickEdit(forward: false);
|
|
session.LeaveQuickEdit();
|
|
|
|
Assert.Null(session.Editing);
|
|
Assert.Equal("DL9ZZZ", session.Entry.Call);
|
|
Assert.Equal("14", session.Entry.ValueOf(ExchangeSlot.Zone));
|
|
}
|
|
|
|
[Fact]
|
|
public void QuickEditForwardPastTheNewestContactLeavesQuickEdit()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.LogContact();
|
|
Type(session, "DL9ZZZ", "14");
|
|
|
|
session.QuickEdit(forward: false);
|
|
Assert.True(session.QuickEdit(forward: true));
|
|
|
|
Assert.Null(session.Editing);
|
|
Assert.Equal("DL9ZZZ", session.Entry.Call);
|
|
}
|
|
|
|
[Fact]
|
|
public void QuickEditStepsBackThroughTheLog()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.LogContact();
|
|
Type(session, "DL9ZZZ", "14");
|
|
session.LogContact();
|
|
|
|
session.QuickEdit(forward: false);
|
|
Assert.Equal("DL9ZZZ", session.Entry.Call);
|
|
session.QuickEdit(forward: false);
|
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
|
Assert.False(session.QuickEdit(forward: false));
|
|
}
|
|
|
|
/// The entry boxes and the log window take the same values. A zone that
|
|
/// does not exist is refused in both, and the box holding it is named so
|
|
/// the entry window can put the cursor back in it.
|
|
[Fact]
|
|
public void AZoneThatDoesNotExistCannotBeLogged()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "41");
|
|
|
|
EntryProblem? problem = session.EntryProblem();
|
|
|
|
Assert.Equal("CQ zones run from 1 to 40", problem!.Message);
|
|
Assert.Equal("41", session.Entry[problem.Field]);
|
|
Assert.Throws<InvalidOperationException>(session.LogContact);
|
|
}
|
|
|
|
[Fact]
|
|
public void ArbitraryTextInTheReportBoxCannotBeLogged()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.Entry.Set(ExchangeSlot.ReceivedReport, "good copy");
|
|
|
|
Assert.Contains("signal report", session.EntryProblem()!.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ACallThatIsNotShapedLikeOneCannotBeLogged()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "12345", "25");
|
|
|
|
Assert.Equal(0, session.EntryProblem()!.Field);
|
|
}
|
|
|
|
[Fact]
|
|
public void AnExchangeTheContestTakesGivesNoProblem()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "ja1xyz", "25");
|
|
|
|
Assert.Null(session.EntryProblem());
|
|
}
|
|
|
|
/// Nothing is typed yet when the operator is between contacts, and that is
|
|
/// not something to complain about.
|
|
[Fact]
|
|
public void EmptyBoxesGiveNoProblem() =>
|
|
Assert.Null(Session().EntryProblem());
|
|
|
|
/// N1MM's Alt+W goes both ways: it wipes the boxes, and pressed again on
|
|
/// empty boxes it puts back what it wiped.
|
|
[Fact]
|
|
public void AltWWipesAndThenPutsTheBoxesBack()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
|
|
session.WipeOrRestore();
|
|
Assert.Equal("", session.Entry.Call);
|
|
|
|
session.WipeOrRestore();
|
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
|
Assert.Equal("25", session.Entry.ValueOf(ExchangeSlot.Zone));
|
|
}
|
|
|
|
/// A wipe keeps what was in the boxes and Alt+W puts it back.
|
|
[Fact]
|
|
public void WhatAWipeClearedComesBack()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.Comment = "loud";
|
|
|
|
session.Wipe();
|
|
Assert.Equal("", session.Entry.Call);
|
|
|
|
Assert.True(session.RestoreWiped());
|
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
|
Assert.Equal("25", session.Entry.ValueOf(ExchangeSlot.Zone));
|
|
Assert.Equal("loud", session.Comment);
|
|
}
|
|
|
|
[Fact]
|
|
public void ThereIsNothingToPutBackBeforeAWipe() =>
|
|
Assert.False(Session().RestoreWiped());
|
|
|
|
/// Wiping empty boxes would otherwise throw away what the wipe before it
|
|
/// kept.
|
|
[Fact]
|
|
public void ASecondWipeKeepsTheFirstOne()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
|
|
session.Wipe();
|
|
session.Wipe();
|
|
|
|
Assert.True(session.RestoreWiped());
|
|
Assert.Equal("JA1XYZ", session.Entry.Call);
|
|
}
|
|
|
|
/// N1MM puts the boxes back only when they are empty, so typing is never
|
|
/// overwritten by a restore.
|
|
[Fact]
|
|
public void NothingIsPutBackOverSomethingTyped()
|
|
{
|
|
OperatingPosition session = Session();
|
|
Type(session, "JA1XYZ", "25");
|
|
session.Wipe();
|
|
session.Entry.Call = "DL2XYZ";
|
|
|
|
Assert.False(session.RestoreWiped());
|
|
Assert.Equal("DL2XYZ", session.Entry.Call);
|
|
}
|
|
|
|
/// A call put on the stack has not gone anywhere, so there is nothing to
|
|
/// put back and Alt+W would only make a second copy of it.
|
|
[Fact]
|
|
public void AStackedCallIsNotWaitingToBePutBack()
|
|
{
|
|
OperatingPosition session = Session();
|
|
session.IsRunning = true;
|
|
Type(session, "JA1XYZ", "25");
|
|
|
|
Assert.True(session.StackAnother());
|
|
Assert.False(session.RestoreWiped());
|
|
}
|
|
}
|