mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-11-10 23:22:27 +01:00
fix: complete Compose UI workflow corrections
This commit is contained in:
1 parent
971fb35109
commit
4be6c178f8
57 files changed
+3084
-465
No files matched your search
@@ -1648,6 +1648,7 @@ private SimpleRoster<String>
|
||||
* lst_chatMemberList is this very field.
|
||||
*/
|
||||
private final SimpleRoster<ChatMember> chatMemberList = new SimpleRoster<>(); // List of active stations
|
||||
private final SimpleRoster<RecentChatJoin> recentChatJoins = new SimpleRoster<>();
|
||||
|
||||
private final SimpleRoster<Predicate<ChatMember>> lst_chatMemberListFilterPredicates = new SimpleRoster<>();
|
||||
private final SimpleRoster<ClusterMessage> lst_clusterMemberList = new SimpleRoster<>();
|
||||
@@ -1803,6 +1804,20 @@ private SimpleRoster<String>
|
||||
* user-enter events.
|
||||
*/
|
||||
public void addOrUpdateActiveChatMember(ChatMember member) {
|
||||
addOrUpdateActiveChatMember(member, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or replaces an active member and records a join only for a previously absent
|
||||
* full callsign/category identity.
|
||||
*
|
||||
* @param member member received in a live user-enter frame
|
||||
* @param joinedAtEpochMillis wall-clock time of that frame
|
||||
*/
|
||||
public void addOrUpdateActiveChatMember(
|
||||
ChatMember member,
|
||||
long joinedAtEpochMillis
|
||||
) {
|
||||
String key = buildActiveChatMemberKey(member);
|
||||
if (key == null) {
|
||||
return;
|
||||
@@ -1810,7 +1825,7 @@ private SimpleRoster<String>
|
||||
|
||||
initializeFrequencyFromStationNameIfUnambiguous(member);
|
||||
|
||||
activeChatMembersByCallAndCategory.put(key, member);
|
||||
ChatMember previous = activeChatMembersByCallAndCategory.put(key, member);
|
||||
|
||||
runOnFxThread(() -> chatMemberList.mutate(list -> {
|
||||
int existingIndex = findChatMemberIndexInList(list, key);
|
||||
@@ -1820,6 +1835,18 @@ private SimpleRoster<String>
|
||||
list.add(member);
|
||||
}
|
||||
}));
|
||||
|
||||
if (previous == null) {
|
||||
int categoryNumber = member.getChatCategory().getCategoryNumber();
|
||||
recentChatJoins.mutate(joins -> {
|
||||
boolean alreadyRecorded = joins.stream().anyMatch(join ->
|
||||
join.matches(member.getCallSign(), categoryNumber));
|
||||
if (!alreadyRecorded) {
|
||||
joins.add(0, new RecentChatJoin(
|
||||
member.getCallSign(), categoryNumber, joinedAtEpochMillis));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1834,6 +1861,9 @@ private SimpleRoster<String>
|
||||
}
|
||||
|
||||
ChatMember removedMember = activeChatMembersByCallAndCategory.remove(key);
|
||||
int categoryNumber = member.getChatCategory().getCategoryNumber();
|
||||
recentChatJoins.mutate(joins -> joins.removeIf(join ->
|
||||
join.matches(member.getCallSign(), categoryNumber)));
|
||||
runOnFxThread(() -> chatMemberList.mutate(list ->
|
||||
list.removeIf(currentMember -> key.equals(buildActiveChatMemberKey(currentMember)))));
|
||||
return removedMember != null;
|
||||
@@ -1845,6 +1875,7 @@ private SimpleRoster<String>
|
||||
*/
|
||||
public void clearActiveChatMembers() {
|
||||
activeChatMembersByCallAndCategory.clear();
|
||||
recentChatJoins.clear();
|
||||
runOnFxThread(chatMemberList::clear);
|
||||
}
|
||||
|
||||
@@ -1867,6 +1898,8 @@ private SimpleRoster<String>
|
||||
}
|
||||
|
||||
int categoryNumber = category.getCategoryNumber();
|
||||
recentChatJoins.mutate(joins -> joins.removeIf(join ->
|
||||
join.getCategoryNumber() == categoryNumber));
|
||||
List<ChatMember> safeMembers = completeMembers == null
|
||||
? List.of() : new ArrayList<>(completeMembers);
|
||||
Map<String, ChatMember> workedDataFromDatabase =
|
||||
@@ -1992,8 +2025,8 @@ private SimpleRoster<String>
|
||||
|
||||
/**
|
||||
* Applies a QSO received from an external logger to all active callsign
|
||||
* variants. Active member objects back JavaFX views, so their state is changed
|
||||
* only on the JavaFX Application Thread. The band-upgrade hint is evaluated
|
||||
* variants. Active member objects back UI views, so their state is changed
|
||||
* only through the configured UI dispatcher. The band-upgrade hint is evaluated
|
||||
* afterwards and therefore sees the new Worked state.
|
||||
*
|
||||
* @param loggedQso validated external QSO
|
||||
@@ -2199,6 +2232,16 @@ private SimpleRoster<String>
|
||||
target.setQrv10G(sourceMember.isQrv10G());
|
||||
}
|
||||
|
||||
if (dbHandler != null) {
|
||||
try {
|
||||
dbHandler.updateNotQRVInfoOnChatMember(sourceMember);
|
||||
} catch (SQLException exception) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Could not persist NOT-QRV state for " + rawCall,
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (scoreService != null) {
|
||||
scoreService.requestRecompute("NOT-QRV state changed");
|
||||
}
|
||||
@@ -2743,6 +2786,96 @@ private SimpleRoster<String>
|
||||
return chatMemberList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies and persists a validated QSO through the same path for external
|
||||
* logger packets and explicit UI Worked actions.
|
||||
*
|
||||
* @param loggedQso validated QSO
|
||||
* @return true when the QSO was accepted and its station state was persisted
|
||||
*/
|
||||
public boolean recordLoggedQso(ExternalLoggedQso loggedQso) {
|
||||
return loggedQso != null
|
||||
&& recordLoggedQso(loggedQso, loggedQso.toWorkedChatMember());
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies and persists a QSO while retaining the supplied database/backup row.
|
||||
* Logger listeners use this overload so later backup serialization observes the
|
||||
* same normalization as database persistence.
|
||||
*
|
||||
* @param loggedQso validated QSO
|
||||
* @param workedCall mutable row derived from the QSO
|
||||
* @return true when the QSO was accepted and its station state was persisted
|
||||
*/
|
||||
public boolean recordLoggedQso(ExternalLoggedQso loggedQso, ChatMember workedCall) {
|
||||
if (loggedQso == null || workedCall == null || dbHandler == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoggedQsoBand loggedBand = loggedQso.getBand();
|
||||
if (loggedBand != null
|
||||
&& loggedBand.getProjectBand() != null
|
||||
&& loggedQso.getLocator() != null) {
|
||||
registerWorkedGrossField(
|
||||
loggedBand.getProjectBand(),
|
||||
loggedQso.getLocator(),
|
||||
workedCall,
|
||||
loggedQso.getSource());
|
||||
}
|
||||
|
||||
applyExternalLoggedQso(loggedQso);
|
||||
|
||||
try {
|
||||
boolean isInChat = dbHandler.updateWkdInfoOnChatMember(workedCall);
|
||||
if (!isInChat) {
|
||||
workedCall.setName("unknown");
|
||||
if (workedCall.getQra() == null || workedCall.getQra().isBlank()) {
|
||||
workedCall.setQra("unknown");
|
||||
}
|
||||
workedCall.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
|
||||
dbHandler.storeChatMember(workedCall);
|
||||
}
|
||||
return true;
|
||||
} catch (SQLException exception) {
|
||||
LOGGER.log(Level.WARNING,
|
||||
"Could not persist Worked state for " + loggedQso.getCallSign(),
|
||||
exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks one station and band as worked with contactinfo-equivalent semantics.
|
||||
* Worked contacts are additive; invoking this method for an already worked band
|
||||
* is intentionally idempotent.
|
||||
*
|
||||
* @param member station selected by the UI
|
||||
* @param band band to mark as worked
|
||||
* @return true when a valid contact was accepted and persisted
|
||||
*/
|
||||
public boolean markStationWorkedOnBand(ChatMember member, Band band) {
|
||||
if (member == null || band == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoggedQsoBand loggedBand = LoggedQsoBand.fromProjectBand(band);
|
||||
if (loggedBand == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String callSign = member.getCallSign() != null
|
||||
? member.getCallSign()
|
||||
: member.getCallSignRaw();
|
||||
return ExternalLoggedQso.create(callSign, loggedBand, member.getQra(), "MANUAL")
|
||||
.map(this::recordLoggedQso)
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/** Live joins only; initial and resynchronized user lists never enter this roster. */
|
||||
public ObservableRoster<RecentChatJoin> getRecentChatJoins() {
|
||||
return recentChatJoins;
|
||||
}
|
||||
|
||||
public ObservableRoster<ChatMember> getLst_DBBasedWkdCallSignList() {
|
||||
return lst_DBBasedWkdCallSignList;
|
||||
}
|
||||
|
||||
@@ -92,6 +92,27 @@ public enum LoggedQsoBand {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a project band for UI actions that must follow the same Worked
|
||||
* processing as an external logger contact.
|
||||
*
|
||||
* @param band project band
|
||||
* @return normalized logged-QSO band, or {@code null} for a missing band
|
||||
*/
|
||||
public static LoggedQsoBand fromProjectBand(Band band) {
|
||||
if (band == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (LoggedQsoBand loggedBand : values()) {
|
||||
if (loggedBand.projectBand == band) {
|
||||
return loggedBand;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common Band value used by worked-grid persistence.
|
||||
*
|
||||
|
||||
@@ -1145,7 +1145,7 @@ public class MessageBusManagementThread extends Thread {
|
||||
// System.out.println("MSGBUS: User detected");
|
||||
|
||||
|
||||
if (!client.getChatPreferences().getStn_loginCallSign().equals(splittedMessageLine[2])) { //own call ignore
|
||||
if (!client.getChatPreferences().getStn_loginCallSign().equalsIgnoreCase(splittedMessageLine[2])) { //own call ignore
|
||||
|
||||
ChatMember newMember = new ChatMember();
|
||||
|
||||
@@ -1164,7 +1164,8 @@ public class MessageBusManagementThread extends Thread {
|
||||
|
||||
newMember = this.client.getDbHandler().fetchChatMemberWkdDataForOnlyOneCallsignFromDB(newMember);
|
||||
|
||||
this.client.addOrUpdateActiveChatMember(newMember);
|
||||
this.client.addOrUpdateActiveChatMember(
|
||||
newMember, millisClock.getAsLong());
|
||||
// this.client.getReachabilityService().ensureAutoTropoMarginCalculated(newMember);
|
||||
|
||||
this.client.getDbHandler().storeChatMember(newMember);
|
||||
|
||||
@@ -774,30 +774,7 @@ public class ReadUDPByWintestThread extends Thread {
|
||||
}
|
||||
|
||||
ChatMember workedCall = loggedQso.toWorkedChatMember();
|
||||
if (loggedBand != null
|
||||
&& loggedBand.getProjectBand() != null
|
||||
&& locatorFromLogger != null) {
|
||||
this.client.registerWorkedGrossField(
|
||||
loggedBand.getProjectBand(), locatorFromLogger, workedCall, loggedQso.getSource());
|
||||
}
|
||||
|
||||
client.applyExternalLoggedQso(loggedQso);
|
||||
|
||||
boolean isInChat = this.client.getDbHandler().updateWkdInfoOnChatMember(workedCall);
|
||||
// This will update the worked info on a worked chatmember. DBHandler will
|
||||
// check, if an entry at the db had been modified. If not, then the worked
|
||||
// station had not been stored. DBHandler will store the information then.
|
||||
if (!isInChat) {
|
||||
|
||||
workedCall.setName("unknown");
|
||||
|
||||
if (workedCall.getQra() == null || workedCall.getQra().isBlank()) {
|
||||
workedCall.setQra("unknown");
|
||||
}
|
||||
|
||||
workedCall.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
|
||||
this.client.getDbHandler().storeChatMember(workedCall);
|
||||
}
|
||||
client.recordLoggedQso(loggedQso, workedCall);
|
||||
|
||||
File logUDPMessageToThisFile = new File(this.client.getChatPreferences()
|
||||
.getLogSynch_storeWorkedCallSignsFileNameUDPMessageBackup());
|
||||
|
||||
@@ -3,7 +3,6 @@ package kst4contest.controller;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
@@ -366,118 +365,11 @@ public class ReadUDPbyUCXMessageThread extends Thread {
|
||||
// client.getChatPreferences().setBcn_contestScoreSum(Long.parseLong(points));
|
||||
|
||||
ChatMember workedCall = loggedQso.toWorkedChatMember();
|
||||
Band workedBand = loggedBand == null ? null : loggedBand.getProjectBand();
|
||||
if (loggedBand == null && !rawBand.isEmpty()) {
|
||||
System.out.println("[ReadUDPFromUCX, warning]: unexpected band value: \"" + rawBand + "\"");
|
||||
}
|
||||
|
||||
{
|
||||
/**
|
||||
* That means, the station is worked already but maybe at another band. So we
|
||||
* have to get the worked ChatMember out of the list and to modify the worked
|
||||
* options.
|
||||
*/
|
||||
|
||||
// modifyThat = (ChatMember) client.getMap_ucxLogInfoWorkedCalls().get(call);
|
||||
|
||||
// asd //TODO: Check if callsign and callsignraw is similar, then mark first and further via new checklistforchatmembermultiplemethod with array of indize
|
||||
|
||||
client.applyExternalLoggedQso(loggedQso);
|
||||
|
||||
/**
|
||||
* old mechanic to markup worked stations in the chatmember table
|
||||
*/
|
||||
// int indexOfChatMemberInTable = -1; //chatmember not in table
|
||||
// indexOfChatMemberInTable = client.checkListForChatMemberIndexByCallSign(workedCall);
|
||||
//
|
||||
// if (indexOfChatMemberInTable == -1) {
|
||||
// // do nothing
|
||||
// } else {
|
||||
// modifyThat = client.getLst_chatMemberList().get(indexOfChatMemberInTable);
|
||||
//
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat)).setWorked(true);
|
||||
//
|
||||
// if (workedCall.isWorked144()) {
|
||||
// modifyThat.setWorked144(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked144(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked432()) {
|
||||
// modifyThat.setWorked432(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked432(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked1240()) {
|
||||
// modifyThat.setWorked1240(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked1240(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked2300()) {
|
||||
// modifyThat.setWorked2300(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked2300(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked3400()) {
|
||||
// modifyThat.setWorked3400(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked3400(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked5600()) {
|
||||
// modifyThat.setWorked5600(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked5600(true);
|
||||
//
|
||||
// } else if (workedCall.isWorked10G()) {
|
||||
// modifyThat.setWorked10G(true);
|
||||
// client.getLst_chatMemberList()
|
||||
// .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
|
||||
// .setWorked10G(true);
|
||||
// }
|
||||
/**
|
||||
* //TODO: following line is a quick fix to making disappear worked chatmembers of the list
|
||||
* Thats uncomfortable due to this also causes selection changes,
|
||||
* Better way is to change all worked and qrv values to observables and then trigger the underlying
|
||||
* list to fire an invalidationevent. Really Todo!
|
||||
*/
|
||||
// try{
|
||||
//
|
||||
// GuiUtils.triggerGUIFilteredChatMemberListChange(client); //not clean at all
|
||||
// } catch (Exception IllegalStateException) {
|
||||
// //do nothing, as it works...
|
||||
// }
|
||||
// }
|
||||
/**
|
||||
* end -> old mechanic to markup worked stations in the chatmember table
|
||||
*/
|
||||
}
|
||||
|
||||
if (workedBand != null && gridSquare != null) {
|
||||
this.client.registerWorkedGrossField(
|
||||
workedBand, gridSquare, workedCall, loggedQso.getSource());
|
||||
}
|
||||
|
||||
boolean isInChat = this.client.getDbHandler().updateWkdInfoOnChatMember(workedCall);
|
||||
// This will update the worked info on a worked chatmember. DBHandler will
|
||||
// check, if an entry at the db had been modified. If not, then the worked
|
||||
// station had not been stored. DBHandler will store the information then.
|
||||
if (!isInChat) {
|
||||
|
||||
workedCall.setName("unknown");
|
||||
|
||||
if (workedCall.getQra() == null || workedCall.getQra().isBlank()) {
|
||||
workedCall.setQra("unknown");
|
||||
}
|
||||
|
||||
workedCall.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
|
||||
this.client.getDbHandler().storeChatMember(workedCall);
|
||||
}
|
||||
client.recordLoggedQso(loggedQso, workedCall);
|
||||
|
||||
|
||||
appendToUdpBackupFile(this.client.getChatPreferences()
|
||||
@@ -594,18 +486,6 @@ public class ReadUDPbyUCXMessageThread extends Thread {
|
||||
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
|
||||
} catch (SQLException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
threadStateMessage = new ThreadStateMessage(this.ThreadNickName, true, "CRASHED" + udpMsg, true);
|
||||
threadStateMessage.setCriticalStateFurtherInfo(Arrays.toString(e.getStackTrace()));
|
||||
|
||||
callBackToController.onThreadStatus(ThreadNickName,threadStateMessage);
|
||||
|
||||
// threadStatusMessage = new String[2];
|
||||
// threadStatusMessage[0] = "STOPPED";
|
||||
// threadStatusMessage[1] = Arrays.toString(e.getStackTrace());
|
||||
// callBackToController.onThreadStatus(ThreadNickName,threadStatusMessage);
|
||||
}
|
||||
|
||||
// System.out.println("[ReadUDPbyUCXTh: ] worked size = " + this.client.getMap_ucxLogInfoWorkedCalls().size());
|
||||
|
||||
@@ -146,6 +146,22 @@ public final class WorkedGrossFieldCache {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable snapshot of all gross fields worked on at least one
|
||||
* band. Map rendering uses the snapshot without holding this cache's monitor.
|
||||
*
|
||||
* @return immutable any-band gross-field snapshot
|
||||
*/
|
||||
public synchronized Set<String> snapshotWorkedGrossFieldsAny() {
|
||||
Set<String> snapshot = new HashSet<>();
|
||||
for (Set<String> workedGrossFields : workedGrossFieldsByBand.values()) {
|
||||
if (workedGrossFields != null) {
|
||||
snapshot.addAll(workedGrossFields);
|
||||
}
|
||||
}
|
||||
return Set.copyOf(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to normalize a locator. Accepts a plain six-character locator or extracts
|
||||
* one from exchange strings such as "001JO41HK".
|
||||
@@ -199,4 +215,4 @@ public final class WorkedGrossFieldCache {
|
||||
String normalized = grossField.trim().toUpperCase(Locale.ROOT);
|
||||
return normalized.matches("[A-R]{2}[0-9]{2}") ? normalized : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package kst4contest.model;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* One live ON4KST join event.
|
||||
*
|
||||
* <p>The full callsign and category form the identity. The timestamp belongs to the
|
||||
* first event while that identity remains active; ordinary user updates must not renew it.</p>
|
||||
*/
|
||||
public final class RecentChatJoin {
|
||||
|
||||
private final String callSign;
|
||||
private final int categoryNumber;
|
||||
private final long joinedAtEpochMillis;
|
||||
|
||||
public RecentChatJoin(
|
||||
String callSign,
|
||||
int categoryNumber,
|
||||
long joinedAtEpochMillis
|
||||
) {
|
||||
this.callSign = Objects.requireNonNull(callSign, "callSign")
|
||||
.trim()
|
||||
.toUpperCase(Locale.ROOT);
|
||||
this.categoryNumber = categoryNumber;
|
||||
this.joinedAtEpochMillis = joinedAtEpochMillis;
|
||||
}
|
||||
|
||||
public String getCallSign() {
|
||||
return callSign;
|
||||
}
|
||||
|
||||
public int getCategoryNumber() {
|
||||
return categoryNumber;
|
||||
}
|
||||
|
||||
public long getJoinedAtEpochMillis() {
|
||||
return joinedAtEpochMillis;
|
||||
}
|
||||
|
||||
/** Returns whether the event belongs to the same complete chat identity. */
|
||||
public boolean matches(String otherCallSign, int otherCategoryNumber) {
|
||||
return otherCallSign != null
|
||||
&& categoryNumber == otherCategoryNumber
|
||||
&& callSign.equalsIgnoreCase(otherCallSign.trim());
|
||||
}
|
||||
}
|
||||
@@ -60,10 +60,30 @@ public record MapCallsignRawSnapshot(
|
||||
}
|
||||
|
||||
public String markerLabel() {
|
||||
String baseLabel = bandSummary.isBlank()
|
||||
? displayCallSign
|
||||
: displayCallSign + " (" + bandSummary + ")";
|
||||
return offersSelectedBand ? baseLabel + " B+" : baseLabel;
|
||||
return displayCallSign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the details removed from the visible marker label for its hover text.
|
||||
*
|
||||
* @return multiline callsign, locator, band and frequency summary
|
||||
*/
|
||||
public String tooltipText() {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
joiner.add(displayCallSign);
|
||||
if (!locator6.isBlank()) {
|
||||
joiner.add("Locator: " + locator6);
|
||||
}
|
||||
if (!bandSummary.isBlank()) {
|
||||
joiner.add("Bands: " + bandSummary);
|
||||
}
|
||||
if (offersSelectedBand) {
|
||||
joiner.add("B+");
|
||||
}
|
||||
if (!lastKnownFrequenciesByBand.isEmpty()) {
|
||||
joiner.add(detailFrequencyText());
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
public String detailFrequencyText() {
|
||||
@@ -77,4 +97,4 @@ public record MapCallsignRawSnapshot(
|
||||
}
|
||||
return joiner.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public final class MapMarkerClusterTooltip {
|
||||
public static String build(MapMarkerCluster cluster) {
|
||||
List<String> callsigns = new ArrayList<>();
|
||||
for (MapCallsignRawSnapshot marker : cluster.getMarkers()) {
|
||||
String label = marker.markerLabel();
|
||||
String label = marker.tooltipText().replace('\n', ' ');
|
||||
if (label != null && !label.isBlank()) {
|
||||
callsigns.add(label.trim());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import kst4contest.model.ChatCategory;
|
||||
import kst4contest.model.ChatMember;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class ChatControllerNotQrvPersistenceTest {
|
||||
|
||||
@Test
|
||||
void propagatedNotQrvStateIsPersistedForTheSharedRawCall() throws SQLException {
|
||||
DBController database = mock(DBController.class);
|
||||
ChatController controller = new ChatController();
|
||||
controller.setDbHandler(database);
|
||||
ChatMember selected = member("9A0BB-2", 2);
|
||||
ChatMember second = member("9A0BB-70", 3);
|
||||
controller.addOrUpdateActiveChatMember(selected);
|
||||
controller.addOrUpdateActiveChatMember(second);
|
||||
selected.setQrv432(false);
|
||||
|
||||
controller.propagateNotQrvStateToActiveMembers(selected);
|
||||
|
||||
assertFalse(second.isQrv432());
|
||||
verify(database).updateNotQRVInfoOnChatMember(argThat(member ->
|
||||
"9A0BB".equals(member.getCallSignRaw()) && !member.isQrv432()));
|
||||
}
|
||||
|
||||
private static ChatMember member(String callSign, int category) {
|
||||
ChatMember member = new ChatMember();
|
||||
member.setCallSign(callSign);
|
||||
member.setChatCategory(new ChatCategory(category));
|
||||
return member;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import kst4contest.model.ChatCategory;
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.RecentChatJoin;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ChatControllerRecentJoinTest {
|
||||
|
||||
@Test
|
||||
void liveJoinIsRecordedOnceWithoutRenewingTheTimestampOnDuplicateUpdates() {
|
||||
ChatController controller = new ChatController();
|
||||
|
||||
controller.addOrUpdateActiveChatMember(member("HB9SJV", 2), 1_000L);
|
||||
controller.addOrUpdateActiveChatMember(member("HB9SJV", 2), 9_000L);
|
||||
|
||||
List<RecentChatJoin> joins = controller.getRecentChatJoins().snapshot();
|
||||
assertEquals(1, joins.size());
|
||||
assertEquals("HB9SJV", joins.get(0).getCallSign());
|
||||
assertEquals(2, joins.get(0).getCategoryNumber());
|
||||
assertEquals(1_000L, joins.get(0).getJoinedAtEpochMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void theSameCallsignInTwoCategoriesRemainsTwoJoinIdentities() {
|
||||
ChatController controller = new ChatController();
|
||||
|
||||
controller.addOrUpdateActiveChatMember(member("DL1KDA", 2), 1_000L);
|
||||
controller.addOrUpdateActiveChatMember(member("DL1KDA", 3), 2_000L);
|
||||
|
||||
List<RecentChatJoin> joins = controller.getRecentChatJoins().snapshot();
|
||||
assertEquals(2, joins.size());
|
||||
assertEquals(List.of(3, 2), joins.stream().map(RecentChatJoin::getCategoryNumber).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void leavingRemovesTheJoinAndARealRejoinStartsAFreshTimestamp() {
|
||||
ChatController controller = new ChatController();
|
||||
ChatMember member = member("OE5SOS", 2);
|
||||
|
||||
controller.addOrUpdateActiveChatMember(member, 1_000L);
|
||||
controller.removeActiveChatMember(member);
|
||||
assertTrue(controller.getRecentChatJoins().isEmpty());
|
||||
|
||||
controller.addOrUpdateActiveChatMember(member("OE5SOS", 2), 7_000L);
|
||||
|
||||
assertEquals(7_000L,
|
||||
controller.getRecentChatJoins().snapshot().get(0).getJoinedAtEpochMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearingTheActiveRosterAlsoClearsTransientJoinEvents() {
|
||||
ChatController controller = new ChatController();
|
||||
controller.addOrUpdateActiveChatMember(member("PA2M", 2), 1_000L);
|
||||
|
||||
controller.clearActiveChatMembers();
|
||||
|
||||
assertTrue(controller.getRecentChatJoins().isEmpty());
|
||||
}
|
||||
|
||||
private static ChatMember member(String callSign, int categoryNumber) {
|
||||
ChatMember member = new ChatMember();
|
||||
member.setCallSign(callSign);
|
||||
member.setChatCategory(new ChatCategory(categoryNumber));
|
||||
return member;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import kst4contest.model.Band;
|
||||
import kst4contest.model.ChatCategory;
|
||||
import kst4contest.model.ChatMember;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ChatControllerWorkedActionTest {
|
||||
|
||||
@Test
|
||||
void manualWorkedActionUsesContactInfoPersistenceAndUpdatesEveryActiveVariant()
|
||||
throws SQLException {
|
||||
DBController database = mock(DBController.class);
|
||||
when(database.updateWkdInfoOnChatMember(argThat(member -> member != null)))
|
||||
.thenReturn(true);
|
||||
ChatController controller = new ChatController();
|
||||
controller.setDbHandler(database);
|
||||
ChatMember selected = member("9A0BB-2", 2, "JN75AA");
|
||||
ChatMember secondCategory = member("9A0BB-70", 3, "JN75AA");
|
||||
controller.addOrUpdateActiveChatMember(selected);
|
||||
controller.addOrUpdateActiveChatMember(secondCategory);
|
||||
|
||||
assertTrue(controller.markStationWorkedOnBand(selected, Band.B_432));
|
||||
|
||||
assertTrue(selected.isWorked());
|
||||
assertTrue(selected.isWorked432());
|
||||
assertTrue(secondCategory.isWorked());
|
||||
assertTrue(secondCategory.isWorked432());
|
||||
assertTrue(controller.getWorkedGrossFieldCache()
|
||||
.isGrossFieldWorked(Band.B_432, "JN75"));
|
||||
verify(database).updateWkdInfoOnChatMember(argThat(member ->
|
||||
"9A0BB".equals(member.getCallSignRaw())
|
||||
&& member.isWorked()
|
||||
&& member.isWorked432()
|
||||
&& "JN75AA".equals(member.getQra())));
|
||||
verify(database).upsertWorkedGrossField(
|
||||
Band.B_432, "JN75AA", "9A0BB", "MANUAL");
|
||||
}
|
||||
|
||||
@Test
|
||||
void manualWorkedActionRejectsMissingStationOrBandWithoutChangingState() {
|
||||
ChatController controller = new ChatController();
|
||||
ChatMember member = member("DL1ABC", 2, "JO50AA");
|
||||
|
||||
assertFalse(controller.markStationWorkedOnBand(null, Band.B_144));
|
||||
assertFalse(controller.markStationWorkedOnBand(member, null));
|
||||
assertFalse(member.isWorked());
|
||||
}
|
||||
|
||||
private static ChatMember member(String callSign, int category, String locator) {
|
||||
ChatMember member = new ChatMember();
|
||||
member.setCallSign(callSign);
|
||||
member.setChatCategory(new ChatCategory(category));
|
||||
member.setQra(locator);
|
||||
return member;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import kst4contest.model.Band;
|
||||
import kst4contest.model.ChatMember;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@@ -37,6 +38,15 @@ class ExternalLoggedQsoTest {
|
||||
assertNull(LoggedQsoBand.fromWinTestBandId("99"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesEveryProjectBandForManualWorkedActions() {
|
||||
for (Band band : Band.values()) {
|
||||
LoggedQsoBand loggedBand = LoggedQsoBand.fromProjectBand(band);
|
||||
|
||||
assertEquals(band, loggedBand.getProjectBand());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingCallsignIsRejectedBeforeAnyStateIsBuilt() {
|
||||
assertTrue(ExternalLoggedQso.create(null, LoggedQsoBand.BAND_144, "JO50AA", "TEST").isEmpty());
|
||||
|
||||
@@ -41,7 +41,7 @@ class ExternalLoggerListenerValidationTest {
|
||||
|
||||
assertDoesNotThrow(() -> listener.processUCXUDPMessage(packet));
|
||||
|
||||
verify(controller, never()).applyExternalLoggedQso(any());
|
||||
verify(controller, never()).recordLoggedQso(any(), any());
|
||||
verify(controller, never()).getDbHandler();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class ExternalLoggerListenerValidationTest {
|
||||
|
||||
assertDoesNotThrow(() -> listener.processWinTestMessage(packet));
|
||||
|
||||
verify(controller, never()).applyExternalLoggedQso(any());
|
||||
verify(controller, never()).recordLoggedQso(any(), any());
|
||||
verify(controller, never()).getDbHandler();
|
||||
}
|
||||
}
|
||||
+61
-1
@@ -63,6 +63,7 @@ class MessageBusManagementThreadChatFrameTest {
|
||||
private static final String RECEIVER_LOCATOR = "JO70LL";
|
||||
private static final double MAX_QRB_KM = 900.0;
|
||||
private static final double BEAM_WIDTH_DEG = 50.0;
|
||||
private static final long FIXED_NOW_MILLIS = 1_700_000_123_456L;
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
@@ -102,6 +103,10 @@ class MessageBusManagementThreadChatFrameTest {
|
||||
.thenReturn(mock(StationMetricsService.class));
|
||||
when(controller.getScoreService()).thenReturn(mock(ScoreService.class));
|
||||
when(controller.getDxClusterServer()).thenReturn(dxClusterServer);
|
||||
DBController database = mock(DBController.class);
|
||||
when(controller.getDbHandler()).thenReturn(database);
|
||||
when(database.fetchChatMemberWkdDataForOnlyOneCallsignFromDB(any(ChatMember.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(controller.findActiveChatMember(any(ChatMember.class)))
|
||||
.thenAnswer(invocation -> activeMemberFor(invocation.getArgument(0)));
|
||||
}
|
||||
@@ -181,6 +186,60 @@ class MessageBusManagementThreadChatFrameTest {
|
||||
assertFalse(published.isMessageDirectedToMe());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
void liveUserEntryCarriesTheInjectedJoinTimestampToTheController()
|
||||
throws InterruptedException {
|
||||
runThrough(message("UA5|2|HB9SJV|Contest team|JN47AA|3|"));
|
||||
|
||||
ArgumentCaptor<ChatMember> member = ArgumentCaptor.forClass(ChatMember.class);
|
||||
verify(controller).addOrUpdateActiveChatMember(member.capture(),
|
||||
org.mockito.ArgumentMatchers.eq(FIXED_NOW_MILLIS));
|
||||
assertEquals("HB9SJV", member.getValue().getCallSign());
|
||||
assertEquals(ChatCategory.VUHF,
|
||||
member.getValue().getChatCategory().getCategoryNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
void alternateLiveUserEntryCarriesTheInjectedJoinTimestampToTheController()
|
||||
throws InterruptedException {
|
||||
runThrough(message("UA2|3|OE5SOS|Microwave team|JN78AA|3|"));
|
||||
|
||||
ArgumentCaptor<ChatMember> member = ArgumentCaptor.forClass(ChatMember.class);
|
||||
verify(controller).addOrUpdateActiveChatMember(member.capture(),
|
||||
org.mockito.ArgumentMatchers.eq(FIXED_NOW_MILLIS));
|
||||
assertEquals("OE5SOS", member.getValue().getCallSign());
|
||||
assertEquals(ChatCategory.MICROWAVE,
|
||||
member.getValue().getChatCategory().getCategoryNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
void initialUserListAndItsTerminatorDoNotCreateLiveJoinEvents()
|
||||
throws InterruptedException {
|
||||
runThrough(
|
||||
message("UA0|2|HB9SJV|Contest team|JN47AA|3|"),
|
||||
message("UE|2|1|"));
|
||||
|
||||
verify(controller, never()).addOrUpdateActiveChatMember(
|
||||
any(ChatMember.class), org.mockito.ArgumentMatchers.anyLong());
|
||||
verify(controller).stageInitialOn4KstChatMember(
|
||||
org.mockito.ArgumentMatchers.eq(7L), any(ChatMember.class));
|
||||
verify(controller).onOn4KstInitialUserListCompleted(
|
||||
org.mockito.ArgumentMatchers.eq(7L), any(ChatCategory.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(10)
|
||||
void ownCallIsExcludedFromLiveJoinEventsIgnoringCase()
|
||||
throws InterruptedException {
|
||||
runThrough(message("UA5|2|dn9apw|Own station|JO50LL|3|"));
|
||||
|
||||
verify(controller, never()).addOrUpdateActiveChatMember(
|
||||
any(ChatMember.class), org.mockito.ArgumentMatchers.anyLong());
|
||||
}
|
||||
|
||||
// --- sent by me ---------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@@ -417,7 +476,8 @@ class MessageBusManagementThreadChatFrameTest {
|
||||
mock(ThreadStatusCallback.class),
|
||||
7L,
|
||||
rxQueue,
|
||||
ignored -> true);
|
||||
ignored -> true,
|
||||
() -> FIXED_NOW_MILLIS);
|
||||
for (ChatMessage frame : frames) {
|
||||
rxQueue.add(frame);
|
||||
}
|
||||
|
||||
+13
-26
@@ -25,7 +25,6 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import kst4contest.model.Band;
|
||||
import kst4contest.model.ChatMember;
|
||||
import kst4contest.model.ChatPreferences;
|
||||
import kst4contest.model.ThreadStateMessage;
|
||||
@@ -145,12 +144,12 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* A UCXLog QSO packet marks the station worked, registers the gross field
|
||||
* and stores an unknown station in the database and the UDP backup file.
|
||||
* A UCXLog QSO packet delegates the complete contact to the shared controller
|
||||
* action and writes the derived row to the UDP backup file.
|
||||
* Bytes before the XML declaration are transport framing and are dropped.
|
||||
*/
|
||||
@Test
|
||||
void contactInfoPacketMarksTheStationWorkedAndStoresIt(@TempDir Path tempDir) throws Exception {
|
||||
void contactInfoPacketUsesSharedWorkedActionAndWritesBackup(@TempDir Path tempDir) throws Exception {
|
||||
Path backupFile = tempDir.resolve("udp-backup.txt");
|
||||
DBController database = givenQsoProcessingEnabled(backupFile);
|
||||
when(database.updateWkdInfoOnChatMember(any())).thenReturn(false);
|
||||
@@ -164,24 +163,13 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
+ "</contactinfo>");
|
||||
|
||||
ArgumentCaptor<ExternalLoggedQso> qso = ArgumentCaptor.forClass(ExternalLoggedQso.class);
|
||||
verify(controller).applyExternalLoggedQso(qso.capture());
|
||||
verify(controller).recordLoggedQso(qso.capture(), any(ChatMember.class));
|
||||
assertEquals("OK1ABC", qso.getValue().getCallSign());
|
||||
assertEquals(LoggedQsoBand.BAND_144, qso.getValue().getBand());
|
||||
assertEquals("JO70FD", qso.getValue().getLocator());
|
||||
assertEquals("UCXLOG", qso.getValue().getSource());
|
||||
verify(controller).registerWorkedGrossField(
|
||||
eq(Band.B_144), eq("JO70FD"), any(ChatMember.class), eq("UCXLOG"));
|
||||
|
||||
ArgumentCaptor<ChatMember> stored = ArgumentCaptor.forClass(ChatMember.class);
|
||||
verify(database).storeChatMember(stored.capture());
|
||||
assertEquals("OK1ABC", stored.getValue().getCallSign());
|
||||
assertEquals("unknown", stored.getValue().getName());
|
||||
assertEquals("JO70FD", stored.getValue().getQra());
|
||||
assertTrue(stored.getValue().isWorked());
|
||||
assertTrue(stored.getValue().isWorked144());
|
||||
|
||||
String backup = Files.readString(backupFile, StandardCharsets.UTF_8);
|
||||
assertTrue(backup.startsWith("\nOK1ABC;unknown;JO70FD;"), backup);
|
||||
assertTrue(backup.startsWith("\nOK1ABC;null;JO70FD;"), backup);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,10 +186,10 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
+ "<contactreplace><app>DXLog</app><band>432</band><call>SP2XYZ</call>"
|
||||
+ "<rcvnr>042JO93AB</rcvnr></contactreplace>");
|
||||
|
||||
verify(controller).registerWorkedGrossField(
|
||||
eq(Band.B_432), eq("JO93AB"), any(ChatMember.class), eq("UCXLOG"));
|
||||
verify(database).updateWkdInfoOnChatMember(any());
|
||||
verify(database, never()).storeChatMember(any());
|
||||
ArgumentCaptor<ExternalLoggedQso> qso = ArgumentCaptor.forClass(ExternalLoggedQso.class);
|
||||
verify(controller).recordLoggedQso(qso.capture(), any(ChatMember.class));
|
||||
assertEquals(LoggedQsoBand.BAND_432, qso.getValue().getBand());
|
||||
assertEquals("JO93AB", qso.getValue().getLocator());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,7 +199,7 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
|
||||
sendThenAwaitMarker("<contactinfo><band>144</band><call>OK1ABC</call></contactinfo>");
|
||||
|
||||
verify(controller, never()).applyExternalLoggedQso(any());
|
||||
verify(controller, never()).recordLoggedQso(any(), any());
|
||||
verify(database, never()).updateWkdInfoOnChatMember(any());
|
||||
}
|
||||
|
||||
@@ -229,8 +217,7 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
sendThenAwaitMarker("<contactinfo><band>144</band><call>OK1ABC</call>"
|
||||
+ "<gridsquare>JO70FD</gridsquare></contactinfo>");
|
||||
|
||||
verify(controller).applyExternalLoggedQso(any());
|
||||
verify(database).storeChatMember(any());
|
||||
verify(controller).recordLoggedQso(any(), any());
|
||||
}
|
||||
|
||||
/** A RadioInfo without Freq used to end the listener with a NullPointerException. */
|
||||
@@ -250,7 +237,7 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
@Test
|
||||
void runtimeExceptionWhileProcessingDropsOnlyThatPacket(@TempDir Path tempDir) throws Exception {
|
||||
givenQsoProcessingEnabled(tempDir.resolve("udp-backup.txt"));
|
||||
doThrow(new IllegalStateException("boom")).when(controller).applyExternalLoggedQso(any());
|
||||
doThrow(new IllegalStateException("boom")).when(controller).recordLoggedQso(any(), any());
|
||||
|
||||
sendThenAwaitMarker("<contactinfo><band>144</band><call>OK1ABC</call></contactinfo>");
|
||||
|
||||
@@ -297,7 +284,7 @@ class ReadUDPbyUCXMessageThreadLoopbackTest {
|
||||
@Test
|
||||
void failingStatusCallbackDoesNotEndTheListener(@TempDir Path tempDir) throws Exception {
|
||||
givenQsoProcessingEnabled(tempDir.resolve("udp-backup.txt"));
|
||||
doThrow(new IllegalStateException("boom")).when(controller).applyExternalLoggedQso(any());
|
||||
doThrow(new IllegalStateException("boom")).when(controller).recordLoggedQso(any(), any());
|
||||
statusCallbackThrows = true;
|
||||
|
||||
sendThenAwaitMarker("<contactinfo><band>144</band><call>OK1ABC</call></contactinfo>");
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package kst4contest.controller;
|
||||
|
||||
import kst4contest.model.Band;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class WorkedGrossFieldCacheTest {
|
||||
|
||||
@Test
|
||||
void anyBandSnapshotContainsUniqueNormalizedGrossFieldsAndIsImmutable() {
|
||||
WorkedGrossFieldCache cache = new WorkedGrossFieldCache();
|
||||
cache.addWorked(Band.B_144, "jo51aa");
|
||||
cache.addWorked(Band.B_432, "JO51BB");
|
||||
cache.addWorked(Band.B_1296, "JN49AA");
|
||||
|
||||
Set<String> snapshot = cache.snapshotWorkedGrossFieldsAny();
|
||||
|
||||
assertEquals(Set.of("JO51", "JN49"), snapshot);
|
||||
assertThrows(UnsupportedOperationException.class, () -> snapshot.add("JO50"));
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,8 @@ class ChatPreferencesLayoutPersistenceTest {
|
||||
preferences.getGUIscn_ChatwindowMainSceneSizeHW()[0] = 812;
|
||||
preferences.getGUIscn_ChatwindowMainSceneSizeHW()[1] = 1340;
|
||||
preferences.setGUIstationMapClusteringEnabled(false);
|
||||
preferences.setGUIselectedCallSignSplitPane_dividerposition(
|
||||
new double[] {0.68});
|
||||
preferences.setTableColumnWidth("qso-other-monitor", "call-tx", 123.75);
|
||||
|
||||
assertTrue(preferences.writeLayoutPreferencesToXmlFile());
|
||||
@@ -124,6 +126,8 @@ class ChatPreferencesLayoutPersistenceTest {
|
||||
assertTrue(restored.readPreferencesFromXmlFile());
|
||||
assertEquals("SAVED-CALL", restored.getStn_loginCallSign());
|
||||
assertFalse(restored.isGUIstationMapClusteringEnabled());
|
||||
assertArrayEquals(new double[] {0.68},
|
||||
restored.getGUIselectedCallSignSplitPane_dividerposition(), 0.0001);
|
||||
assertEquals(123.75,
|
||||
restored.getTableColumnWidth("qso-other-monitor", "call-tx").orElseThrow());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package kst4contest.view.map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MapCallsignRawSnapshotLabelTest {
|
||||
|
||||
@Test
|
||||
void visibleMarkerLabelContainsOnlyTheCallsign() {
|
||||
MapCallsignRawSnapshot snapshot = snapshot();
|
||||
|
||||
assertEquals("DM5M", snapshot.markerLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tooltipRetainsLocatorBandsUpgradeAndFrequencyDetails() {
|
||||
String tooltip = snapshot().tooltipText();
|
||||
|
||||
assertTrue(tooltip.contains("DM5M"));
|
||||
assertTrue(tooltip.contains("JO51AA"));
|
||||
assertTrue(tooltip.contains("144, 432"));
|
||||
assertTrue(tooltip.contains("B+"));
|
||||
assertTrue(tooltip.contains("144: 144.310"));
|
||||
}
|
||||
|
||||
private MapCallsignRawSnapshot snapshot() {
|
||||
return new MapCallsignRawSnapshot(
|
||||
"DM5M", "DM5M", "JO51AA", 51.0, 10.0,
|
||||
"144, 432", Map.of("144", "144.310"), true,
|
||||
false, false, false, 100.0, 180.0, 0, 0L
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,10 @@ class MapMarkerClusterTooltipTest {
|
||||
List<String> lines = List.of(MapMarkerClusterTooltip.build(clusterOf(3)).split("\\n"));
|
||||
|
||||
assertEquals("3 stations", lines.get(0));
|
||||
assertEquals("DL000AA", lines.get(1));
|
||||
assertEquals("DL001AA", lines.get(2));
|
||||
assertEquals("DL002AA", lines.get(3));
|
||||
assertTrue(lines.get(1).startsWith("DL000AA"));
|
||||
assertTrue(lines.get(2).startsWith("DL001AA"));
|
||||
assertTrue(lines.get(3).startsWith("DL002AA"));
|
||||
assertTrue(lines.get(1).contains("JO50JP"));
|
||||
}
|
||||
|
||||
/** A hundred callsigns is a box taller than the window. */
|
||||
|
||||
Reference in new issue
Block a user