test: expand service coverage and modernize test infrastructure
- replace legacy mockito-all with mockito-junit-jupiter - add tests for AppState, MQTT service, process manager, and animation file service - rewrite service tests for stronger edge-case coverage - refactor services for testability (MQTT client/process launcher/output path injection) - fix duplicate payload ID handling in BinaryEventService - update README and docs testing/source-map sections
This commit is contained in:
@@ -6,15 +6,29 @@ import vassistent.util.Logger;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import jakarta.websocket.*;
|
||||
|
||||
/**
|
||||
* Writes the current problem level as animation state JSON for Unreal integration.
|
||||
*/
|
||||
public class AnimationFileService {
|
||||
private static final String PATH = "C:\\Users\\Student\\Documents\\Dannick\\Prototyp1\\Saved\\animation.json";
|
||||
private final String outputPath;
|
||||
|
||||
/**
|
||||
* Creates the animation file service with default output path.
|
||||
*/
|
||||
public AnimationFileService() {
|
||||
this(PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the animation file service with an explicit output path.
|
||||
*
|
||||
* @param outputPath target JSON file path
|
||||
*/
|
||||
AnimationFileService(String outputPath) {
|
||||
this.outputPath = outputPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the mapped animation state for the given level to the configured JSON file.
|
||||
@@ -25,10 +39,15 @@ public class AnimationFileService {
|
||||
|
||||
String animation = mapLevelToAnimation(level);
|
||||
|
||||
File file = new File(PATH);
|
||||
File file = new File(outputPath);
|
||||
|
||||
try {
|
||||
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
String json = """
|
||||
{
|
||||
"animation": "%s"
|
||||
|
||||
@@ -52,7 +52,7 @@ public class BinaryEventService {
|
||||
|
||||
if (lastId != null && lastId == id) {
|
||||
Logger.warn("EVENT", "Payload ID bereits verarbeitet: " + id);
|
||||
//return;
|
||||
return;
|
||||
}
|
||||
|
||||
lastId = id;
|
||||
|
||||
@@ -30,29 +30,55 @@ public class MqttClientService implements MqttCallback {
|
||||
* @param appState shared state updated with connection status
|
||||
*/
|
||||
public MqttClientService(AppState appState) {
|
||||
this(appState, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an MQTT service with an optionally injected client (primarily for tests).
|
||||
*
|
||||
* @param appState shared state updated with connection status
|
||||
* @param injectedClient externally provided MQTT client, or {@code null} to create a default client
|
||||
* @param connectOnStart whether the service should connect immediately
|
||||
*/
|
||||
MqttClientService(AppState appState, MqttClient injectedClient, boolean connectOnStart) {
|
||||
this.appState = appState;
|
||||
try {
|
||||
client = new MqttClient(
|
||||
client = injectedClient != null
|
||||
? injectedClient
|
||||
: new MqttClient(
|
||||
BROKER_URL,
|
||||
CLIENT_ID,
|
||||
new MemoryPersistence()
|
||||
);
|
||||
|
||||
client.setCallback(this);
|
||||
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true);
|
||||
options.setAutomaticReconnect(true);
|
||||
|
||||
Logger.info("MQTT", "Verbinde mit Broker " + BROKER_URL);
|
||||
client.connect(options);
|
||||
appState.setMqttConnected(true);
|
||||
Logger.info("MQTT", "Verbindung hergestellt");
|
||||
if (connectOnStart) {
|
||||
connectClient();
|
||||
}
|
||||
|
||||
} catch (MqttException e) {
|
||||
Logger.error("MQTT", "Fehler beim Verbinden", e);
|
||||
appState.setMqttConnected(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the configured MQTT client with default options.
|
||||
*
|
||||
* @throws MqttException if connection fails
|
||||
*/
|
||||
private void connectClient() throws MqttException {
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setCleanSession(true);
|
||||
options.setAutomaticReconnect(true);
|
||||
|
||||
Logger.info("MQTT", "Verbinde mit Broker " + BROKER_URL);
|
||||
client.connect(options);
|
||||
appState.setMqttConnected(true);
|
||||
Logger.info("MQTT", "Verbindung hergestellt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the MQTT client when connected.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
@@ -13,7 +14,21 @@ import java.util.Properties;
|
||||
*/
|
||||
public class ProcessManagerService {
|
||||
|
||||
@FunctionalInterface
|
||||
interface ProcessLauncher {
|
||||
Process launch(ProcessBuilder processBuilder) throws IOException;
|
||||
}
|
||||
|
||||
private static final String DEFAULT_UNREAL_PID_FILE =
|
||||
"C:\\Users\\Student\\Documents\\Dannick\\avatar\\unreal.pid";
|
||||
|
||||
private static final String DEFAULT_SIGNALLING_PID_FILE =
|
||||
"C:\\Users\\Student\\Documents\\Dannick\\avatar\\signalling.pid";
|
||||
|
||||
private final Properties config;
|
||||
private final ProcessLauncher processLauncher;
|
||||
private final String unrealPidFile;
|
||||
private final String signallingPidFile;
|
||||
|
||||
private Process pythonProcess;
|
||||
private Process unrealProcess;
|
||||
@@ -25,7 +40,32 @@ public class ProcessManagerService {
|
||||
* @param config loaded application properties
|
||||
*/
|
||||
public ProcessManagerService(Properties config) {
|
||||
this.config = config;
|
||||
this(
|
||||
config,
|
||||
ProcessBuilder::start,
|
||||
DEFAULT_UNREAL_PID_FILE,
|
||||
DEFAULT_SIGNALLING_PID_FILE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a process manager with injectable process launcher and PID file paths.
|
||||
*
|
||||
* @param config loaded application properties
|
||||
* @param processLauncher strategy used to launch process builders
|
||||
* @param unrealPidFile PID file path for Unreal process cleanup
|
||||
* @param signallingPidFile PID file path for signalling process cleanup
|
||||
*/
|
||||
ProcessManagerService(
|
||||
Properties config,
|
||||
ProcessLauncher processLauncher,
|
||||
String unrealPidFile,
|
||||
String signallingPidFile
|
||||
) {
|
||||
this.config = Objects.requireNonNull(config);
|
||||
this.processLauncher = Objects.requireNonNull(processLauncher);
|
||||
this.unrealPidFile = Objects.requireNonNull(unrealPidFile);
|
||||
this.signallingPidFile = Objects.requireNonNull(signallingPidFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +95,7 @@ public class ProcessManagerService {
|
||||
ProcessBuilder pb = new ProcessBuilder(python, script);
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
pythonProcess = pb.start();
|
||||
pythonProcess = processLauncher.launch(pb);
|
||||
|
||||
Logger.info("PROCESS", "Mqtt Simulator gestartet");
|
||||
} catch (IOException e) {
|
||||
@@ -123,7 +163,7 @@ public class ProcessManagerService {
|
||||
unrealPsScript
|
||||
);
|
||||
|
||||
unrealProcess = pb.start();
|
||||
unrealProcess = processLauncher.launch(pb);
|
||||
|
||||
//pb.directory(new File(exe).getParentFile());
|
||||
|
||||
@@ -144,13 +184,8 @@ public class ProcessManagerService {
|
||||
terminateProcess(unrealProcess);
|
||||
terminateProcess(unrealSignallingProcess);
|
||||
|
||||
killProcessFromPidFile(
|
||||
"C:\\Users\\Student\\Documents\\Dannick\\avatar\\unreal.pid"
|
||||
);
|
||||
|
||||
killProcessFromPidFile(
|
||||
"C:\\Users\\Student\\Documents\\Dannick\\avatar\\signalling.pid"
|
||||
);
|
||||
killProcessFromPidFile(unrealPidFile);
|
||||
killProcessFromPidFile(signallingPidFile);
|
||||
|
||||
Logger.info("PROCESS", "Externe Prozesse beendet");
|
||||
}
|
||||
@@ -177,7 +212,7 @@ public class ProcessManagerService {
|
||||
"/F"
|
||||
);
|
||||
|
||||
pb.start().waitFor();
|
||||
processLauncher.launch(pb).waitFor();
|
||||
|
||||
Logger.info("PROCESS",
|
||||
"Process Tree beendet → PID " + pid);
|
||||
@@ -209,10 +244,37 @@ public class ProcessManagerService {
|
||||
"/F"
|
||||
);
|
||||
|
||||
pb.start().waitFor();
|
||||
processLauncher.launch(pb).waitFor();
|
||||
|
||||
} catch (Exception e) {
|
||||
Logger.error("PROCESS", "PID Kill fehlgeschlagen", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently tracked Python simulator process.
|
||||
*
|
||||
* @return Python process or {@code null}
|
||||
*/
|
||||
Process getPythonProcess() {
|
||||
return pythonProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently tracked Unreal process.
|
||||
*
|
||||
* @return Unreal process or {@code null}
|
||||
*/
|
||||
Process getUnrealProcess() {
|
||||
return unrealProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently tracked Unreal signalling process.
|
||||
*
|
||||
* @return signalling process or {@code null}
|
||||
*/
|
||||
Process getUnrealSignallingProcess() {
|
||||
return unrealSignallingProcess;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user