Port the rebased diagnostics and session logging onto the modules

origin/main landed replayable session logging (MessageHistoryRecorder), file
logging (ApplicationFileLogging) and a user-list refresh split on the old flat
layout. The rebase carried the new files, but three things needed the new
architecture:

- ChatController.fireUserListUpdate() used javafx Platform; core is toolkit-free
  now, so it dispatches through uiDispatcher like every other fire method.
- Two new tests were left in the old src/test/java; moved into core.
- ScoreServiceRefreshTest mocked javafx Platform statically; it now injects the
  DirectUiDispatcher the no-arg constructor installs.

The Leaflet map performance work was dropped with the HTML map it optimised.

classes testClasses and test are green: 1069 tests, 0 failures, 3 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rsclub2_2andClaude Opus 5 committed 2026-10-05 22:42:08 +02:00
1 parent 2654a1178e
commit d1b910c992
4 files changed
+16 -12

No files matched your search

@@ -2501,10 +2501,10 @@ private SimpleRoster<String>
return;
}
if (Platform.isFxApplicationThread()) {
if (uiDispatcher.isUiThread()) {
statusListener.onUserListUpdated();
} else {
Platform.runLater(statusListener::onUserListUpdated);
uiDispatcher.runOnUi(statusListener::onUserListUpdated);
}
}
@@ -13,10 +13,8 @@ import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.invocation.Invocation;
import javafx.application.Platform;
import kst4contest.logic.PriorityCalculator;
import kst4contest.model.ChatCategory;
import kst4contest.model.ChatMember;
@@ -54,20 +52,26 @@ class ScoreServiceRefreshTest {
}
@Test
void userListRefreshKeepsDiagnosticAndReasonlessCallbacksSeparate() {
void userListRefreshKeepsDiagnosticAndReasonlessCallbacksSeparate() throws Exception {
ChatController controller = mock(
ChatController.class,
org.mockito.Answers.CALLS_REAL_METHODS);
/*
* CALLS_REAL_METHODS runs no constructor, so the dispatcher field stays null. Give
* the mock the direct dispatcher the no-arg constructor installs; it runs inline and
* reports the calling thread as the UI thread, which is what the fire methods branch
* on. This replaces the old static Platform mock -- core no longer touches JavaFX.
*/
java.lang.reflect.Field dispatcherField =
ChatController.class.getDeclaredField("uiDispatcher");
dispatcherField.setAccessible(true);
dispatcherField.set(controller, new kst4contest.observe.DirectUiDispatcher());
StatusUpdateListener listener = mock(StatusUpdateListener.class);
controller.setStatusListener(listener);
try (MockedStatic<Platform> platform =
org.mockito.Mockito.mockStatic(Platform.class)) {
platform.when(Platform::isFxApplicationThread).thenReturn(true);
controller.fireUserListUpdate();
controller.fireUserListUpdate("Reachability calculated");
}
controller.fireUserListUpdate();
controller.fireUserListUpdate("Reachability calculated");
verify(listener).onUserListUpdated();
verify(listener).onUserListUpdated("Reachability calculated");
@@ -0,0 +1,37 @@
package kst4contest.locatorUtils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
class DirectionUtilsConsoleOutputTest {
@Test
void outOfRangeCheckDoesNotWriteToStandardOutput() {
PrintStream originalOutput = System.out;
ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream();
try (PrintStream testOutput = new PrintStream(
capturedOutput,
true,
StandardCharsets.UTF_8)) {
System.setOut(testOutput);
assertFalse(DirectionUtils.isInAngleAndRange(
"JN49FL",
"JO43XM",
"JO30SA",
1.0,
50.0));
} finally {
System.setOut(originalOutput);
}
assertEquals("", capturedOutput.toString(StandardCharsets.UTF_8));
}
}
@@ -0,0 +1,50 @@
package kst4contest.utils;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
class ApplicationFileLoggingTest {
@TempDir
Path temporaryDirectory;
@Test
void warningsAreAlwaysLoggedAndDiagnosticsFollowDebugMode()
throws Exception {
Path logFile = temporaryDirectory.resolve("kst4contest-errors.log");
Logger logger = Logger.getLogger(
"kst4contest.test.filelogging." + UUID.randomUUID());
try (ApplicationFileLogging logging =
new ApplicationFileLogging(logFile, false)) {
logger.log(Level.INFO, "diagnostic while disabled");
logger.log(Level.WARNING, "warning while disabled");
logger.log(Level.INFO, "exception while disabled",
new IllegalStateException("simulated failure"));
logging.setDebugEnabled(true);
logger.log(Level.FINE, "fine diagnostic while enabled");
logger.log(Level.INFO, "info diagnostic while enabled");
logger.log(Level.SEVERE, "error while enabled");
}
String log = Files.readString(logFile);
assertFalse(log.contains("diagnostic while disabled"));
assertTrue(log.contains("warning while disabled"));
assertTrue(log.contains("exception while disabled"));
assertTrue(log.contains("simulated failure"));
assertTrue(log.contains("fine diagnostic while enabled"));
assertTrue(log.contains("info diagnostic while enabled"));
assertTrue(log.contains("error while enabled"));
assertTrue(log.contains("Z\tWARNING\t"));
}
}