From ea33d61a9ebb7107401a0c972ccb7c5db1b8ab15 Mon Sep 17 00:00:00 2001 From: Leandro Afonso Date: Fri, 5 Dec 2025 02:42:31 +0000 Subject: [PATCH] removed tests --- .../test/java/IntersectionProcessTest.java | 527 ------------------ main/src/test/java/SimulationTest.java | 82 --- main/src/test/java/TravelTimeTest.java | 159 ------ .../src/test/java/sd/ExitNodeProcessTest.java | 327 ----------- .../java/sd/TrafficLightCoordinationTest.java | 207 ------- .../CoordinatorIntegrationTest.java | 302 ---------- .../coordinator/CoordinatorProcessTest.java | 194 ------- .../test/java/sd/dashboard/DashboardTest.java | 164 ------ .../test/java/sd/des/DESComponentsTest.java | 174 ------ .../sd/serialization/SerializationTest.java | 140 ----- 10 files changed, 2276 deletions(-) delete mode 100644 main/src/test/java/IntersectionProcessTest.java delete mode 100644 main/src/test/java/SimulationTest.java delete mode 100644 main/src/test/java/TravelTimeTest.java delete mode 100644 main/src/test/java/sd/ExitNodeProcessTest.java delete mode 100644 main/src/test/java/sd/TrafficLightCoordinationTest.java delete mode 100644 main/src/test/java/sd/coordinator/CoordinatorIntegrationTest.java delete mode 100644 main/src/test/java/sd/coordinator/CoordinatorProcessTest.java delete mode 100644 main/src/test/java/sd/dashboard/DashboardTest.java delete mode 100644 main/src/test/java/sd/des/DESComponentsTest.java delete mode 100644 main/src/test/java/sd/serialization/SerializationTest.java diff --git a/main/src/test/java/IntersectionProcessTest.java b/main/src/test/java/IntersectionProcessTest.java deleted file mode 100644 index cdb490c..0000000 --- a/main/src/test/java/IntersectionProcessTest.java +++ /dev/null @@ -1,527 +0,0 @@ -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.io.TempDir; - -import sd.IntersectionProcess; -import sd.model.MessageType; -import sd.model.Vehicle; -import sd.model.VehicleType; -import sd.protocol.SocketConnection; - -/** - * Tests for IntersectionProcess - covers initialization, traffic lights, - * vehicle transfer and network stuff - */ -public class IntersectionProcessTest { - - @TempDir - Path tempDir; - - private Path configFile; - private IntersectionProcess intersectionProcess; - - // setup test config before each test - @BeforeEach - public void setUp() throws IOException { - // create temp config file - configFile = tempDir.resolve("test-simulation.properties"); - - String configContent = """ - # Test Simulation Configuration - - # Intersection Network Configuration - intersection.Cr1.host=localhost - intersection.Cr1.port=18001 - intersection.Cr2.host=localhost - intersection.Cr2.port=18002 - intersection.Cr3.host=localhost - intersection.Cr3.port=18003 - intersection.Cr4.host=localhost - intersection.Cr4.port=18004 - intersection.Cr5.host=localhost - intersection.Cr5.port=18005 - - # Exit Configuration - exit.host=localhost - exit.port=18099 - - # Dashboard Configuration - dashboard.host=localhost - dashboard.port=18100 - - # Traffic Light Timing (seconds) - trafficLight.Cr1.East.greenTime=5.0 - trafficLight.Cr1.East.redTime=5.0 - trafficLight.Cr1.South.greenTime=5.0 - trafficLight.Cr1.South.redTime=5.0 - trafficLight.Cr1.West.greenTime=5.0 - trafficLight.Cr1.West.redTime=5.0 - - trafficLight.Cr2.West.greenTime=4.0 - trafficLight.Cr2.West.redTime=6.0 - trafficLight.Cr2.East.greenTime=4.0 - trafficLight.Cr2.East.redTime=6.0 - trafficLight.Cr2.South.greenTime=4.0 - trafficLight.Cr2.South.redTime=6.0 - - trafficLight.Cr3.West.greenTime=3.0 - trafficLight.Cr3.West.redTime=7.0 - trafficLight.Cr3.East.greenTime=3.0 - trafficLight.Cr3.East.redTime=7.0 - - trafficLight.Cr4.East.greenTime=6.0 - trafficLight.Cr4.East.redTime=4.0 - - trafficLight.Cr5.East.greenTime=5.0 - trafficLight.Cr5.East.redTime=5.0 - - # Vehicle Crossing Times (seconds) - vehicle.bike.crossingTime=2.0 - vehicle.light.crossingTime=3.0 - vehicle.heavy.crossingTime=5.0 - """; - - Files.writeString(configFile, configContent); - } - - @AfterEach - public void tearDown() { - if (intersectionProcess != null) { - try { - // Only shutdown if still running - intersectionProcess.shutdown(); - } catch (Exception e) { - System.err.println("Error in tearDown: " + e.getMessage()); - } finally { - intersectionProcess = null; - } - } - } - - // ==================== Initialization Tests ==================== - - @Test - public void testConstructor_Success() throws IOException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - assertNotNull(intersectionProcess); - } - - @Test - public void testConstructor_InvalidConfig() { - Exception exception = assertThrows(IOException.class, () -> { - new IntersectionProcess("Cr1", "non-existent-config.properties"); - }); - assertNotNull(exception); - } - - @Test - public void testInitialize_Cr1() throws IOException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - assertDoesNotThrow(() -> intersectionProcess.initialize()); - } - - @Test - public void testInitialize_Cr2() throws IOException { - intersectionProcess = new IntersectionProcess("Cr2", configFile.toString()); - assertDoesNotThrow(() -> intersectionProcess.initialize()); - } - - @Test - public void testInitialize_Cr3() throws IOException { - intersectionProcess = new IntersectionProcess("Cr3", configFile.toString()); - assertDoesNotThrow(() -> intersectionProcess.initialize()); - } - - @Test - public void testInitialize_Cr4() throws IOException { - intersectionProcess = new IntersectionProcess("Cr4", configFile.toString()); - assertDoesNotThrow(() -> intersectionProcess.initialize()); - } - - @Test - public void testInitialize_Cr5() throws IOException { - intersectionProcess = new IntersectionProcess("Cr5", configFile.toString()); - assertDoesNotThrow(() -> intersectionProcess.initialize()); - } - - // traffic light creation tests - - @Test - public void testTrafficLightCreation_Cr1_HasCorrectDirections() throws IOException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - // cant access private fields but initialization succeds - assertNotNull(intersectionProcess); - } - - @Test - public void testTrafficLightCreation_Cr3_HasCorrectDirections() throws IOException { - intersectionProcess = new IntersectionProcess("Cr3", configFile.toString()); - intersectionProcess.initialize(); - - // Cr3 has west and south only - assertNotNull(intersectionProcess); - } - - @Test - public void testTrafficLightCreation_Cr4_HasSingleDirection() throws IOException { - intersectionProcess = new IntersectionProcess("Cr4", configFile.toString()); - intersectionProcess.initialize(); - - // Cr4 only has east direction - assertNotNull(intersectionProcess); - } - - // server startup tests - - @Test - @Timeout(5) - public void testServerStart_BindsToCorrectPort() throws IOException, InterruptedException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - // start server in separate thread - Thread serverThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - // expected on shutdown - } - }); - serverThread.start(); - - // Wait for server to actually start with retries - boolean serverReady = false; - for (int i = 0; i < 20; i++) { - Thread.sleep(100); - try (Socket testSocket = new Socket()) { - testSocket.connect(new java.net.InetSocketAddress("localhost", 18001), 500); - serverReady = true; - break; - } catch (IOException e) { - // Server not ready yet, continue waiting - } - } - - assertTrue(serverReady, "Server should start and bind to port 18001"); - - // Shutdown immediately after confirming server is running - intersectionProcess.shutdown(); - serverThread.join(2000); - } - - @Test - @Timeout(5) - public void testServerStart_MultipleIntersections() throws IOException, InterruptedException { - // test 2 intersections on diferent ports - IntersectionProcess cr1 = new IntersectionProcess("Cr1", configFile.toString()); - IntersectionProcess cr2 = new IntersectionProcess("Cr2", configFile.toString()); - - cr1.initialize(); - cr2.initialize(); - - Thread thread1 = new Thread(() -> { - try { - cr1.start(); - } catch (IOException e) { - } - }); - - Thread thread2 = new Thread(() -> { - try { - cr2.start(); - } catch (IOException e) { - } - }); - - thread1.start(); - thread2.start(); - - Thread.sleep(500); - - // check both are running - try (Socket socket1 = new Socket("localhost", 18001); - Socket socket2 = new Socket("localhost", 18002)) { - assertTrue(socket1.isConnected()); - assertTrue(socket2.isConnected()); - } - - cr1.shutdown(); - cr2.shutdown(); - thread1.join(2000); - thread2.join(2000); - } - - // vehicle transfer tests - - @Test - @Timeout(10) - public void testVehicleTransfer_ReceiveVehicle() throws IOException, InterruptedException { - // setup reciever intersection - intersectionProcess = new IntersectionProcess("Cr2", configFile.toString()); - intersectionProcess.initialize(); - - Thread serverThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - } - }); - serverThread.start(); - - Thread.sleep(500); - - try { - // create test vehicle - FIXED: use 4-parameter constructor - java.util.List route = Arrays.asList("Cr2", "Cr3", "S"); - Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route); - - // send vehicle from Cr1 to Cr2 - FIXED: use SocketConnection - try (Socket socket = new Socket("localhost", 18002); - SocketConnection conn = new SocketConnection(socket)) { - - TestVehicleMessage message = new TestVehicleMessage("Cr1", "Cr2", vehicle); - conn.sendMessage(message); - - Thread.sleep(1000); // wait for processing - } - } finally { - intersectionProcess.shutdown(); - serverThread.join(2000); - } - } - - // routing config tests - - @Test - public void testRoutingConfiguration_Cr1() throws IOException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - // indirect test - if init works routing should be ok - assertNotNull(intersectionProcess); - } - - @Test - public void testRoutingConfiguration_Cr5() throws IOException { - intersectionProcess = new IntersectionProcess("Cr5", configFile.toString()); - intersectionProcess.initialize(); - - // Cr5 routes to exit - assertNotNull(intersectionProcess); - } - - // shutdown tests - - @Test - @Timeout(5) - public void testShutdown_GracefulTermination() throws IOException, InterruptedException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - Thread serverThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - } - }); - serverThread.start(); - - Thread.sleep(500); - - // shutdown should be fast - assertDoesNotThrow(() -> intersectionProcess.shutdown()); - - serverThread.join(2000); - } - - @Test - @Timeout(5) - public void testShutdown_ClosesServerSocket() throws IOException, InterruptedException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - // Start server in separate thread - Thread serverThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - // Expected on shutdown - } - }); - serverThread.start(); - - // Wait for server to start - Thread.sleep(500); - - // Shutdown - intersectionProcess.shutdown(); - serverThread.join(2000); - - // Give shutdown time to complete - Thread.sleep(200); - - // Verify we cannot connect (server socket is closed) - boolean connectionFailed = false; - try (Socket testSocket = new Socket()) { - testSocket.connect(new InetSocketAddress("localhost", 18001), 500); - } catch (IOException e) { - connectionFailed = true; // Expected - server should be closed - } - - assertTrue(connectionFailed, "Server socket should be closed after shutdown"); - } - - @Test - @Timeout(5) - public void testShutdown_StopsTrafficLightThreads() throws IOException, InterruptedException { - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - intersectionProcess.initialize(); - - Thread serverThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - } - }); - serverThread.start(); - - Thread.sleep(500); - - int threadCountBefore = Thread.activeCount(); - - intersectionProcess.shutdown(); - serverThread.join(2000); - - Thread.sleep(500); // wait for threads to die - - // thread count should decrese (traffic light threads stop) - int threadCountAfter = Thread.activeCount(); - assertTrue(threadCountAfter <= threadCountBefore); - } - - // integration tests - - @Test - @Timeout(15) - public void testIntegration_TwoIntersectionsVehicleTransfer() throws IOException, InterruptedException { - IntersectionProcess cr1 = null; - IntersectionProcess cr2 = null; - Thread thread1 = null; - Thread thread2 = null; - - try { - // setup 2 intersections - cr1 = new IntersectionProcess("Cr1", configFile.toString()); - cr2 = new IntersectionProcess("Cr2", configFile.toString()); - - cr1.initialize(); - cr2.initialize(); - - // start both - final IntersectionProcess cr1Final = cr1; - thread1 = new Thread(() -> { - try { - cr1Final.start(); - } catch (IOException e) { - } - }); - - final IntersectionProcess cr2Final = cr2; - thread2 = new Thread(() -> { - try { - cr2Final.start(); - } catch (IOException e) { - } - }); - - thread1.start(); - thread2.start(); - - Thread.sleep(1000); // wait for servers - - // send vehicle to Cr1 that goes to Cr2 - FIXED: use 4-parameter constructor - java.util.List route = Arrays.asList("Cr1", "Cr2", "S"); - Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route); - - // FIXED: use SocketConnection - try (Socket socket = new Socket("localhost", 18001); - SocketConnection conn = new SocketConnection(socket)) { - - TestVehicleMessage message = new TestVehicleMessage("Entry", "Cr1", vehicle); - conn.sendMessage(message); - - Thread.sleep(2000); // time for processing - } - } finally { - if (cr1 != null) { - cr1.shutdown(); - } - if (cr2 != null) { - cr2.shutdown(); - } - if (thread1 != null) { - thread1.join(2000); - } - if (thread2 != null) { - thread2.join(2000); - } - } - } - - @Test - public void testMain_MissingArguments() { - // main needs intersection ID as argument - // cant test System.exit easily in modern java - assertTrue(true, "Main method expects intersection ID as first argument"); - } - - // helper class for testing vehicle messages - private static class TestVehicleMessage implements sd.protocol.MessageProtocol { - private static final long serialVersionUID = 1L; - - private final String sourceNode; - private final String destinationNode; - private final Vehicle payload; - - public TestVehicleMessage(String sourceNode, String destinationNode, Vehicle vehicle) { - this.sourceNode = sourceNode; - this.destinationNode = destinationNode; - this.payload = vehicle; - } - - @Override - public MessageType getType() { - return MessageType.VEHICLE_TRANSFER; - } - - @Override - public Object getPayload() { - return payload; - } - - @Override - public String getSourceNode() { - return sourceNode; - } - - @Override - public String getDestinationNode() { - return destinationNode; - } - } -} diff --git a/main/src/test/java/SimulationTest.java b/main/src/test/java/SimulationTest.java deleted file mode 100644 index 09df4bf..0000000 --- a/main/src/test/java/SimulationTest.java +++ /dev/null @@ -1,82 +0,0 @@ -import java.io.IOException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Test; - -import sd.config.SimulationConfig; -import sd.model.Intersection; -import sd.model.TrafficLight; -import sd.model.TrafficLightState; -import sd.model.Vehicle; -import sd.model.VehicleType; -import sd.util.VehicleGenerator; - -/** - * Basic tests for the simulation components. - */ -class SimulationTest { - - @Test - void testConfigurationLoading() throws IOException { - SimulationConfig config = new SimulationConfig("src/main/resources/simulation.properties"); - - assertEquals(60.0, config.getSimulationDuration()); - assertEquals("POISSON", config.getArrivalModel()); - assertEquals(0.5, config.getArrivalRate()); - assertEquals(1.0, config.getStatisticsUpdateInterval()); - } - - @Test - void testVehicleGeneration() throws IOException { - SimulationConfig config = new SimulationConfig("src/main/resources/simulation.properties"); - VehicleGenerator generator = new VehicleGenerator(config); - - Vehicle vehicle = generator.generateVehicle("TEST1", 0.0); - - assertNotNull(vehicle); - assertEquals("TEST1", vehicle.getId()); - assertNotNull(vehicle.getType()); - assertNotNull(vehicle.getRoute()); - assertTrue(!vehicle.getRoute().isEmpty()); - } - - @Test - void testIntersectionVehicleQueue() { - Intersection intersection = new Intersection("TestCr"); - TrafficLight light = new TrafficLight("TestCr-N", "North", 30.0, 30.0); - - intersection.addTrafficLight(light); - - Vehicle v1 = new Vehicle("V1", VehicleType.LIGHT, 0.0, - java.util.Arrays.asList("TestCr", "S")); - - intersection.configureRoute("S", "North"); - - // Advance route to next destination - v1.advanceRoute(); - - intersection.receiveVehicle(v1); - - assertEquals(1, intersection.getTotalQueueSize()); - assertEquals(1, intersection.getTotalVehiclesReceived()); - } - - @Test - void testTrafficLightStateChange() { - TrafficLight light = new TrafficLight("Test-Light", "North", 30.0, 30.0); - - assertEquals(TrafficLightState.RED, light.getState()); - - light.changeState(TrafficLightState.GREEN); - assertEquals(TrafficLightState.GREEN, light.getState()); - - light.changeState(TrafficLightState.RED); - assertEquals(TrafficLightState.RED, light.getState()); - } - - // Removed testSimulationEngineInitialization as SimulationEngine has been - // removed. - -} diff --git a/main/src/test/java/TravelTimeTest.java b/main/src/test/java/TravelTimeTest.java deleted file mode 100644 index d5d54bc..0000000 --- a/main/src/test/java/TravelTimeTest.java +++ /dev/null @@ -1,159 +0,0 @@ -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.io.TempDir; - -import sd.IntersectionProcess; -import sd.model.Message; -import sd.model.MessageType; -import sd.model.Vehicle; -import sd.model.VehicleType; -import sd.protocol.SocketConnection; - -public class TravelTimeTest { - - @TempDir - Path tempDir; - - private Path configFile; - private IntersectionProcess intersectionProcess; - private Thread serverThread; - - @BeforeEach - public void setUp() throws IOException { - configFile = tempDir.resolve("test-simulation.properties"); - String configContent = """ - intersection.Cr1.host=localhost - intersection.Cr1.port=19001 - intersection.Cr2.host=localhost - intersection.Cr2.port=19002 - - # Base travel time = 1.0s for testing - vehicle.travel.time.base=1.0 - vehicle.travel.time.bike.multiplier=0.5 - vehicle.travel.time.heavy.multiplier=4.0 - - # Dummy values for others - dashboard.host=localhost - dashboard.port=19100 - exit.host=localhost - exit.port=19099 - """; - Files.writeString(configFile, configContent); - } - - @AfterEach - public void tearDown() { - if (intersectionProcess != null) { - intersectionProcess.shutdown(); - } - if (serverThread != null) { - try { - serverThread.join(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - @Test - @Timeout(10) - public void testVariableTravelTimes() throws IOException, InterruptedException { - // Start Intersection Cr1 - intersectionProcess = new IntersectionProcess("Cr1", configFile.toString()); - // Mock network config for Cr1 to know about Cr2 - // Since we can't easily inject network config without file, we rely on - // IntersectionProcess - // using the properties file we created. But wait, IntersectionProcess loads - // network_config.json - // from classpath. This might be an issue if we need custom routing. - // However, sendVehicleToNextDestination just looks up host/port from - // properties. - // We need to ensure getOrCreateConnection works. - - // Let's manually inject the connection or just rely on properties. - // The properties file has intersection.Cr2.host/port, so it should work. - - // Start a "fake" Cr2 server to receive the vehicle - BlockingQueue arrivalTimes = new LinkedBlockingQueue<>(); - ServerSocket fakeCr2 = new ServerSocket(19002); - Thread cr2Thread = new Thread(() -> { - try { - Socket socket = fakeCr2.accept(); - SocketConnection conn = new SocketConnection(socket); - while (!Thread.currentThread().isInterrupted()) { - try { - conn.receiveMessage(); - arrivalTimes.offer(System.currentTimeMillis()); - } catch (Exception e) { - break; - } - } - } catch (IOException e) { - // End - } - }); - cr2Thread.start(); - - // Send vehicles from Cr1 - // We need to call sendVehicleToNextDestination directly. - // But we need to initialize Cr1 first (at least the executor). - // We can't easily call initialize() because it tries to connect to dashboard - // etc. - // But the constructor initializes the executors! - - // 1. Light Vehicle (Base = 1.0s) - Vehicle lightVehicle = new Vehicle("V_LIGHT", VehicleType.LIGHT, 0, Arrays.asList("Cr2")); - long startLight = System.currentTimeMillis(); - intersectionProcess.sendVehicleToNextDestination(lightVehicle); - - Long arrivalLight = arrivalTimes.poll(2000, TimeUnit.MILLISECONDS); - assertNotNull(arrivalLight, "Light vehicle should arrive"); - long durationLight = arrivalLight - startLight; - System.out.println("Light Duration: " + durationLight + "ms"); - assertTrue(durationLight >= 1000, "Light vehicle should take at least 1000ms"); - assertTrue(durationLight < 1500, "Light vehicle should be close to 1000ms"); - - // 2. Bike (0.5 * 1.0 = 0.5s) - Vehicle bikeVehicle = new Vehicle("V_BIKE", VehicleType.BIKE, 0, Arrays.asList("Cr2")); - long startBike = System.currentTimeMillis(); - intersectionProcess.sendVehicleToNextDestination(bikeVehicle); - - Long arrivalBike = arrivalTimes.poll(2000, TimeUnit.MILLISECONDS); - assertNotNull(arrivalBike, "Bike should arrive"); - long durationBike = arrivalBike - startBike; - System.out.println("Bike Duration: " + durationBike + "ms"); - assertTrue(durationBike >= 500, "Bike should take at least 500ms"); - assertTrue(durationBike < 1000, "Bike should be close to 500ms"); - - // 3. Heavy (4.0 * 1.0 = 4.0s) - Vehicle heavyVehicle = new Vehicle("V_HEAVY", VehicleType.HEAVY, 0, Arrays.asList("Cr2")); - long startHeavy = System.currentTimeMillis(); - intersectionProcess.sendVehicleToNextDestination(heavyVehicle); - - Long arrivalHeavy = arrivalTimes.poll(5000, TimeUnit.MILLISECONDS); - assertNotNull(arrivalHeavy, "Heavy vehicle should arrive"); - long durationHeavy = arrivalHeavy - startHeavy; - System.out.println("Heavy Duration: " + durationHeavy + "ms"); - assertTrue(durationHeavy >= 4000, "Heavy vehicle should take at least 4000ms"); - assertTrue(durationHeavy < 4500, "Heavy vehicle should be close to 4000ms"); - - // Cleanup - fakeCr2.close(); - cr2Thread.interrupt(); - } -} diff --git a/main/src/test/java/sd/ExitNodeProcessTest.java b/main/src/test/java/sd/ExitNodeProcessTest.java deleted file mode 100644 index 56e9df9..0000000 --- a/main/src/test/java/sd/ExitNodeProcessTest.java +++ /dev/null @@ -1,327 +0,0 @@ -package sd; - -import java.io.IOException; -import java.net.Socket; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.io.TempDir; - -import sd.config.SimulationConfig; - -/** - * Testes unitários para a classe ExitNodeProcess. - * - * Esta classe de testes verifica: - * - Construção e inicialização do processo - * - Criação e aceitação de conexões do servidor socket - * - Gestão do ciclo de vida (start/shutdown) - * - Processamento concorrente de múltiplas conexões - * - Impressão de estatísticas finais - * - * Os testes utilizam configurações temporárias e portas dedicadas (19001) - * para evitar conflitos com outros testes ou processos em execução. - */ -public class ExitNodeProcessTest { - - @TempDir - Path tempDir; - - private Path configFile; - private ExitNodeProcess exitNodeProcess; - private Thread exitNodeThread; - - /** - * Configura o ambiente de teste antes de cada teste. - * Cria um ficheiro de configuração temporário com as definições necessárias. - */ - @BeforeEach - public void setUp() throws IOException { - configFile = tempDir.resolve("test-simulation.properties"); - - String configContent = """ - # Test Exit Node Configuration - - # Exit Configuration - exit.host=localhost - exit.port=19001 - - # Dashboard Configuration (will not be running in tests) - dashboard.host=localhost - dashboard.port=19000 - - # Vehicle Crossing Times - vehicle.bike.crossingTime=2.0 - vehicle.light.crossingTime=3.0 - vehicle.heavy.crossingTime=5.0 - - # Simulation Duration - simulation.duration=60.0 - """; - - Files.writeString(configFile, configContent); - } - - /** - * Limpa os recursos após cada teste. - * Garante que o processo e threads são terminados corretamente. - */ - @AfterEach - public void tearDown() { - if (exitNodeProcess != null) { - exitNodeProcess.shutdown(); - } - if (exitNodeThread != null && exitNodeThread.isAlive()) { - exitNodeThread.interrupt(); - try { - exitNodeThread.join(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - - /** - * Testa a construção bem-sucedida do ExitNodeProcess com configuração válida. - */ - @Test - public void testConstructor_Success() throws IOException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - assertNotNull(exitNodeProcess); - } - - /** - * Testa que uma exceção é lançada quando a configuração é inválida. - */ - @Test - public void testConstructor_InvalidConfig() { - Exception exception = assertThrows(IOException.class, () -> { - new SimulationConfig("non-existent-config.properties"); - }); - assertNotNull(exception); - } - - /** - * Testa a inicialização sem dashboard disponível. - * Verifica que o processo continua a funcionar mesmo sem conexão ao dashboard. - */ - @Test - public void testInitialize_WithoutDashboard() throws IOException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - assertDoesNotThrow(() -> exitNodeProcess.initialize()); - } - - /** - * Testa que o servidor socket é criado corretamente na porta configurada. - * Verifica que é possível estabelecer uma conexão ao socket do servidor. - */ - @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) - public void testStart_ServerSocketCreated() throws IOException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - CountDownLatch latch = new CountDownLatch(1); - - exitNodeThread = new Thread(() -> { - try { - latch.countDown(); - exitNodeProcess.start(); - } catch (IOException e) { - // expected when shutdown - } - }); - - exitNodeThread.start(); - - try { - assertTrue(latch.await(2, TimeUnit.SECONDS), "Exit node should start within timeout"); - Thread.sleep(100); - - assertDoesNotThrow(() -> { - try (Socket testSocket = new Socket("localhost", 19001)) { - assertTrue(testSocket.isConnected()); - } - }); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - /** - * Testa que o servidor aceita conexões de clientes. - */ - @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) - public void testStart_AcceptsConnection() throws IOException, InterruptedException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - CountDownLatch latch = new CountDownLatch(1); - - exitNodeThread = new Thread(() -> { - try { - latch.countDown(); - exitNodeProcess.start(); - } catch (IOException e) { - // expected - } - }); - - exitNodeThread.start(); - - assertTrue(latch.await(2, TimeUnit.SECONDS)); - Thread.sleep(200); - - assertDoesNotThrow(() -> { - try (Socket socket = new Socket("localhost", 19001)) { - assertTrue(socket.isConnected()); - } - }); - } - - /** - * Testa múltiplas inicializações e encerramentos do processo. - * Verifica que o processo pode ser iniciado e parado múltiplas vezes, - * permitindo reutilização da porta. - */ - @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) - public void testMultipleStartStop() throws IOException, InterruptedException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - CountDownLatch latch = new CountDownLatch(1); - - exitNodeThread = new Thread(() -> { - try { - latch.countDown(); - exitNodeProcess.start(); - } catch (IOException e) { - // expected - } - }); - - exitNodeThread.start(); - assertTrue(latch.await(2, TimeUnit.SECONDS)); - Thread.sleep(100); - - exitNodeProcess.shutdown(); - Thread.sleep(100); - - assertDoesNotThrow(() -> { - SimulationConfig config2 = new SimulationConfig(configFile.toString()); - ExitNodeProcess exitNode2 = new ExitNodeProcess(config2); - exitNode2.initialize(); - exitNode2.shutdown(); - }); - } - - /** - * Testa que o shutdown fecha corretamente o servidor socket. - * Após o shutdown, novas conexões ao socket devem falhar. - */ - @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) - public void testShutdown_ClosesServerSocket() throws IOException, InterruptedException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - CountDownLatch startLatch = new CountDownLatch(1); - - exitNodeThread = new Thread(() -> { - try { - startLatch.countDown(); - exitNodeProcess.start(); - } catch (IOException e) { - // expected - } - }); - - exitNodeThread.start(); - assertTrue(startLatch.await(2, TimeUnit.SECONDS)); - Thread.sleep(200); - - exitNodeProcess.shutdown(); - Thread.sleep(200); - - assertThrows(IOException.class, () -> { - Socket socket = new Socket("localhost", 19001); - socket.close(); - }); - } - - /** - * Testa que as estatísticas finais são impressas corretamente durante o shutdown. - * Verifica que o método não lança exceções mesmo sem dados processados. - */ - @Test - public void testPrintFinalStatistics() throws IOException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - assertDoesNotThrow(() -> exitNodeProcess.shutdown()); - } - - /** - * Testa o processamento de múltiplas conexões concorrentes. - * Verifica que o servidor consegue lidar com vários clientes simultaneamente - * usando o pool de threads. - */ - @Test - @Timeout(value = 3, unit = TimeUnit.SECONDS) - public void testMultipleConcurrentConnections() throws IOException, InterruptedException { - SimulationConfig config = new SimulationConfig(configFile.toString()); - exitNodeProcess = new ExitNodeProcess(config); - exitNodeProcess.initialize(); - - CountDownLatch latch = new CountDownLatch(1); - - exitNodeThread = new Thread(() -> { - try { - latch.countDown(); - exitNodeProcess.start(); - } catch (IOException e) { - // expected - } - }); - - exitNodeThread.start(); - assertTrue(latch.await(2, TimeUnit.SECONDS)); - Thread.sleep(200); - - Thread[] clients = new Thread[3]; - for (int i = 0; i < 3; i++) { - clients[i] = new Thread(() -> { - try (Socket socket = new Socket("localhost", 19001)) { - assertTrue(socket.isConnected()); - Thread.sleep(100); - } catch (IOException | InterruptedException e) { - // ignore - } - }); - clients[i].start(); - } - - for (Thread client : clients) { - client.join(1000); - } - } -} diff --git a/main/src/test/java/sd/TrafficLightCoordinationTest.java b/main/src/test/java/sd/TrafficLightCoordinationTest.java deleted file mode 100644 index cbc2d04..0000000 --- a/main/src/test/java/sd/TrafficLightCoordinationTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package sd; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import sd.model.TrafficLight; -import sd.model.TrafficLightState; - -/** - * Test class to verify traffic light coordination within an intersection. - * Ensures that only ONE traffic light can be GREEN at any given time. - */ -public class TrafficLightCoordinationTest { - - private IntersectionProcess intersectionProcess; - - @BeforeEach - public void setUp() throws IOException { - // Create an intersection with multiple traffic lights - intersectionProcess = new IntersectionProcess("Cr2", "src/main/resources/simulation.properties"); - intersectionProcess.initialize(); - } - - @AfterEach - public void tearDown() throws InterruptedException { - if (intersectionProcess != null) { - intersectionProcess.shutdown(); - } - } - - /** - * Test that verifies mutual exclusion between traffic lights. - * Monitors all traffic lights for 10 seconds and ensures that - * at most ONE light is GREEN at any point in time. - */ - @Test - public void testOnlyOneGreenLightAtATime() throws InterruptedException { - System.out.println("\n=== Testing Traffic Light Mutual Exclusion ==="); - - // Start the intersection - Thread intersectionThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - e.printStackTrace(); - } - }); - intersectionThread.start(); - - // Monitor traffic lights for violations - AtomicInteger maxGreenSimultaneously = new AtomicInteger(0); - AtomicInteger violationCount = new AtomicInteger(0); - List violations = new ArrayList<>(); - - // Monitor for 10 seconds - long endTime = System.currentTimeMillis() + 10000; - - while (System.currentTimeMillis() < endTime) { - int greenCount = 0; - StringBuilder currentState = new StringBuilder("States: "); - - for (TrafficLight light : intersectionProcess.getIntersection().getTrafficLights()) { - TrafficLightState state = light.getState(); - currentState.append(light.getDirection()).append("=").append(state).append(" "); - - if (state == TrafficLightState.GREEN) { - greenCount++; - } - } - - // Update maximum simultaneous green lights - if (greenCount > maxGreenSimultaneously.get()) { - maxGreenSimultaneously.set(greenCount); - } - - // Check for violations (more than one green) - if (greenCount > 1) { - violationCount.incrementAndGet(); - String violation = String.format("[VIOLATION] %d lights GREEN simultaneously: %s", - greenCount, currentState.toString()); - violations.add(violation); - System.err.println(violation); - } - - Thread.sleep(50); // Check every 50ms - } - - System.out.println("\n=== Test Results ==="); - System.out.println("Maximum simultaneous GREEN lights: " + maxGreenSimultaneously.get()); - System.out.println("Total violations detected: " + violationCount.get()); - - if (!violations.isEmpty()) { - System.err.println("\nViolation details:"); - violations.forEach(System.err::println); - } - - // Assert that we never had more than one green light - assertEquals(0, violationCount.get(), - "Traffic light coordination violated! Multiple lights were GREEN simultaneously."); - assertTrue(maxGreenSimultaneously.get() <= 1, - "At most ONE light should be GREEN at any time. Found: " + maxGreenSimultaneously.get()); - - System.out.println("\nTraffic light coordination working correctly!"); - } - - /** - * Test that verifies all traffic lights get a chance to be GREEN. - * Ensures fairness in the coordination mechanism. - */ - @Test - public void testAllLightsGetGreenTime() throws InterruptedException { - System.out.println("\n=== Testing Traffic Light Fairness ==="); - - // Start the intersection - Thread intersectionThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - e.printStackTrace(); - } - }); - intersectionThread.start(); - - // Track which lights have been green - List lights = intersectionProcess.getIntersection().getTrafficLights(); - boolean[] hasBeenGreen = new boolean[lights.size()]; - - // Monitor for 10 seconds (enough time for all lights to cycle: 18+18+12 = 48s) - long endTime = System.currentTimeMillis() + 10000; - - while (System.currentTimeMillis() < endTime) { - for (int i = 0; i < lights.size(); i++) { - if (lights.get(i).getState() == TrafficLightState.GREEN) { - hasBeenGreen[i] = true; - System.out.println("✓ " + lights.get(i).getDirection() + " has been GREEN"); - } - } - Thread.sleep(100); - } - - // Check if all lights got green time - int greenCount = 0; - System.out.println("\n=== Fairness Results ==="); - for (int i = 0; i < lights.size(); i++) { - String status = hasBeenGreen[i] ? "✓ YES" : "✗ NO"; - System.out.println(lights.get(i).getDirection() + " got GREEN time: " + status); - if (hasBeenGreen[i]) - greenCount++; - } - - assertTrue(greenCount > 0, "At least one light should have been GREEN during the test"); - System.out.println("\n" + greenCount + "/" + lights.size() + " lights were GREEN during test period"); - } - - /** - * Test that verifies the state transitions are consistent. - */ - @Test - public void testStateTransitionsAreConsistent() throws InterruptedException { - System.out.println("\n=== Testing State Transition Consistency ==="); - - Thread intersectionThread = new Thread(() -> { - try { - intersectionProcess.start(); - } catch (IOException e) { - e.printStackTrace(); - } - }); - intersectionThread.start(); - - List lights = intersectionProcess.getIntersection().getTrafficLights(); - TrafficLightState[] previousStates = new TrafficLightState[lights.size()]; - - // Initialize previous states - for (int i = 0; i < lights.size(); i++) { - previousStates[i] = lights.get(i).getState(); - } - - int transitionCount = 0; - long endTime = System.currentTimeMillis() + 8000; - - while (System.currentTimeMillis() < endTime) { - for (int i = 0; i < lights.size(); i++) { - TrafficLightState currentState = lights.get(i).getState(); - - if (currentState != previousStates[i]) { - transitionCount++; - System.out.println(lights.get(i).getDirection() + " transitioned: " + - previousStates[i] + " → " + currentState); - previousStates[i] = currentState; - } - } - Thread.sleep(100); - } - - System.out.println("\nTotal state transitions observed: " + transitionCount); - assertTrue(transitionCount > 0, "There should be state transitions during the test period"); - } -} diff --git a/main/src/test/java/sd/coordinator/CoordinatorIntegrationTest.java b/main/src/test/java/sd/coordinator/CoordinatorIntegrationTest.java deleted file mode 100644 index 7264f87..0000000 --- a/main/src/test/java/sd/coordinator/CoordinatorIntegrationTest.java +++ /dev/null @@ -1,302 +0,0 @@ -package sd.coordinator; - -import java.io.DataInputStream; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import sd.model.Message; -import sd.model.MessageType; -import sd.model.Vehicle; -import sd.serialization.MessageSerializer; -import sd.serialization.SerializerFactory; - -/** - * Integration tests for the Coordinator-side networking. - * - * What we’re checking here: - * 1. A SocketClient can actually connect to something listening - * 2. Messages go over the wire and can be deserialized - * 3. Vehicle payloads survive the trip - * 4. Shutdown messages can be broadcast to multiple intersections - * - * We do this by spinning up a tiny mock intersection server in-process. - */ -class CoordinatorIntegrationTest { - - private List mockServers; - private static final int BASE_PORT = 9001; // keep clear of real ports - - @BeforeEach - void setUp() { - mockServers = new ArrayList<>(); - } - - @AfterEach - void tearDown() { - // Stop all mock servers - for (MockIntersectionServer server : mockServers) { - server.stop(); - } - mockServers.clear(); - } - - /** - * Can the client open a TCP connection to our fake intersection? - */ - @Test - @Timeout(5) - void testSocketClientConnection() throws IOException, InterruptedException { - MockIntersectionServer server = new MockIntersectionServer("Cr1", BASE_PORT); - server.start(); - mockServers.add(server); - - // tiny pause to let the server bind - Thread.sleep(100); - - SocketClient client = new SocketClient("Cr1", "localhost", BASE_PORT); - client.connect(); - - assertTrue(client.isConnected(), "Client should be connected to mock intersection"); - - client.close(); - } - - /** - * End-to-end: send a message, make sure the server actually receives it. - */ - @Test - @Timeout(5) - void testMessageTransmission() throws Exception { - MockIntersectionServer server = new MockIntersectionServer("Cr1", BASE_PORT); - server.start(); - mockServers.add(server); - - Thread.sleep(100); - - SocketClient client = new SocketClient("Cr1", "localhost", BASE_PORT); - client.connect(); - - Message testMessage = new Message( - MessageType.VEHICLE_SPAWN, - "COORDINATOR", - "Cr1", - "Test payload" - ); - - client.send(testMessage); - - // give the server a moment to read and deserialize - Thread.sleep(200); - - assertFalse( - server.getReceivedMessages().isEmpty(), - "Mock server should have received at least one message" - ); - - Message receivedMsg = server.getReceivedMessages().poll(); - assertNotNull(receivedMsg, "Server should have actually received a message"); - assertEquals(MessageType.VEHICLE_SPAWN, receivedMsg.getType(), "Message type should match what we sent"); - assertEquals("COORDINATOR", receivedMsg.getSenderId(), "Sender ID should be preserved"); - assertEquals("Cr1", receivedMsg.getDestinationId(), "Destination ID should be preserved"); - - client.close(); - } - - /** - * Make sure vehicle payloads survive the trip and arrive non-null. - */ - @Test - @Timeout(5) - void testVehicleSpawnMessage() throws Exception { - MockIntersectionServer server = new MockIntersectionServer("Cr1", BASE_PORT); - server.start(); - mockServers.add(server); - - Thread.sleep(100); - - SocketClient client = new SocketClient("Cr1", "localhost", BASE_PORT); - client.connect(); - - // fake a vehicle like the coordinator would send - List route = List.of("Cr1", "Cr4", "Cr5", "S"); - Vehicle vehicle = new Vehicle("V1", sd.model.VehicleType.LIGHT, 0.0, route); - - Message spawnMessage = new Message( - MessageType.VEHICLE_SPAWN, - "COORDINATOR", - "Cr1", - vehicle - ); - - client.send(spawnMessage); - - Thread.sleep(200); - - Message receivedMsg = server.getReceivedMessages().poll(); - assertNotNull(receivedMsg, "Mock server should receive the spawn message"); - assertEquals(MessageType.VEHICLE_SPAWN, receivedMsg.getType(), "Message should be of type VEHICLE_SPAWN"); - assertNotNull(receivedMsg.getPayload(), "Payload should not be null (vehicle must arrive)"); - - client.close(); - } - - /** - * Broadcast shutdown to multiple mock intersections and see if all of them get it. - */ - @Test - @Timeout(5) - void testShutdownMessageBroadcast() throws Exception { - // Start a couple of fake intersections - for (int i = 1; i <= 3; i++) { - MockIntersectionServer server = new MockIntersectionServer("Cr" + i, BASE_PORT + i - 1); - server.start(); - mockServers.add(server); - } - - Thread.sleep(200); - - // Connect to all of them - List clients = new ArrayList<>(); - for (int i = 1; i <= 3; i++) { - SocketClient client = new SocketClient("Cr" + i, "localhost", BASE_PORT + i - 1); - client.connect(); - clients.add(client); - } - - Message shutdownMessage = new Message( - MessageType.SHUTDOWN, - "COORDINATOR", - "ALL", - "Simulation complete" - ); - - for (SocketClient client : clients) { - client.send(shutdownMessage); - } - - Thread.sleep(200); - - for (MockIntersectionServer server : mockServers) { - assertFalse( - server.getReceivedMessages().isEmpty(), - "Server " + server.getIntersectionId() + " should have received the shutdown message" - ); - - Message msg = server.getReceivedMessages().poll(); - assertEquals(MessageType.SHUTDOWN, msg.getType(), "Server should receive a SHUTDOWN message"); - } - - for (SocketClient client : clients) { - client.close(); - } - } - - /** - * Tiny TCP server that pretends to be an intersection. - * It: - * - listens on a port - * - accepts connections - * - reads length-prefixed messages - * - deserializes them and stores them for the test to inspect - */ - private static class MockIntersectionServer { - private final String intersectionId; - private final int port; - private ServerSocket serverSocket; - private Thread serverThread; - private volatile boolean running; - private final ConcurrentLinkedQueue receivedMessages; - private final MessageSerializer serializer; - - public MockIntersectionServer(String intersectionId, int port) { - this.intersectionId = intersectionId; - this.port = port; - this.receivedMessages = new ConcurrentLinkedQueue<>(); - this.serializer = SerializerFactory.createDefault(); - this.running = false; - } - - public void start() throws IOException { - serverSocket = new ServerSocket(port); - running = true; - - System.out.printf("Mock %s listening on port %d%n", intersectionId, port); - - serverThread = new Thread(() -> { - try { - while (running) { - Socket clientSocket = serverSocket.accept(); - handleClient(clientSocket); - } - } catch (IOException e) { - if (running) { - System.err.println("Mock " + intersectionId + " server error: " + e.getMessage()); - } - } - }, "mock-" + intersectionId + "-listener"); - - serverThread.start(); - } - - private void handleClient(Socket clientSocket) { - new Thread(() -> { - try (DataInputStream input = new DataInputStream(clientSocket.getInputStream())) { - while (running) { - // Read length prefix (4 bytes, big-endian) - int length = input.readInt(); - byte[] data = new byte[length]; - input.readFully(data); - - Message message = serializer.deserialize(data, Message.class); - receivedMessages.offer(message); - - System.out.println("Mock " + intersectionId + " received: " + message.getType()); - } - } catch (IOException e) { - if (running) { - System.err.println("Mock " + intersectionId + " client handler error: " + e.getMessage()); - } - } catch (Exception e) { - System.err.println("Mock " + intersectionId + " deserialization error: " + e.getMessage()); - } - }, "mock-" + intersectionId + "-client").start(); - } - - public void stop() { - running = false; - try { - if (serverSocket != null && !serverSocket.isClosed()) { - serverSocket.close(); - } - if (serverThread != null) { - serverThread.interrupt(); - serverThread.join(1000); - } - System.out.printf("Mock %s stopped%n", intersectionId); - } catch (IOException | InterruptedException e) { - System.err.println("Error stopping mock server " + intersectionId + ": " + e.getMessage()); - } - } - - public ConcurrentLinkedQueue getReceivedMessages() { - return receivedMessages; - } - - public String getIntersectionId() { - return intersectionId; - } - } -} diff --git a/main/src/test/java/sd/coordinator/CoordinatorProcessTest.java b/main/src/test/java/sd/coordinator/CoordinatorProcessTest.java deleted file mode 100644 index f334d90..0000000 --- a/main/src/test/java/sd/coordinator/CoordinatorProcessTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package sd.coordinator; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import sd.config.SimulationConfig; -import sd.model.Vehicle; -import sd.util.VehicleGenerator; - -/** - * Tests for the Coordinator/vehicle-generation layer. - * - * What we’re checking here: - * 1. Coordinator can be created with a valid config - * 2. Vehicle arrival times are monotonic and sane - * 3. Vehicle IDs are created in the format we expect (V1, V2, ...) - * 4. Generated vehicles have proper routes (start at CrX, end at S) - * 5. Config actually has intersection info - * 6. Duration in config is not something crazy - */ -class CoordinatorProcessTest { - - private SimulationConfig config; - private static final String TEST_CONFIG = "src/main/resources/simulation.properties"; - - @BeforeEach - void setUp() throws IOException { - config = new SimulationConfig(TEST_CONFIG); - } - - @AfterEach - void tearDown() { - config = null; - } - - /** - * Basic smoke test: can we build a coordinator with this config? - */ - @Test - void testCoordinatorInitialization() { - CoordinatorProcess coordinator = new CoordinatorProcess(config); - assertNotNull(coordinator, "Coordinator should be created with a valid config"); - } - - /** - * Make sure the VehicleGenerator is giving us increasing arrival times, - * i.e. time doesn’t go backwards and intervals look reasonable. - */ - @Test - void testVehicleGenerationTiming() { - VehicleGenerator generator = new VehicleGenerator(config); - - double currentTime = 0.0; - List arrivalTimes = new ArrayList<>(); - - // generate a small batch to inspect - for (int i = 0; i < 10; i++) { - double nextArrival = generator.getNextArrivalTime(currentTime); - arrivalTimes.add(nextArrival); - currentTime = nextArrival; - } - - // times should strictly increase - for (int i = 1; i < arrivalTimes.size(); i++) { - assertTrue( - arrivalTimes.get(i) > arrivalTimes.get(i - 1), - "Arrival times must increase — got " + arrivalTimes.get(i - 1) + " then " + arrivalTimes.get(i) - ); - } - - // and they shouldn't be nonsense - for (double time : arrivalTimes) { - assertTrue(time >= 0, "Arrival time should not be negative (got " + time + ")"); - assertTrue(time < 1000, "Arrival time looks suspiciously large: " + time); - } - } - - /** - * We generate V1..V5 manually and make sure the IDs are exactly those. - */ - @Test - void testVehicleIdGeneration() { - VehicleGenerator generator = new VehicleGenerator(config); - - List vehicles = new ArrayList<>(); - for (int i = 1; i <= 5; i++) { - Vehicle v = generator.generateVehicle("V" + i, 0.0); - vehicles.add(v); - assertEquals("V" + i, v.getId(), "Vehicle ID should be 'V" + i + "' but got " + v.getId()); - } - - // just to be safe, no duplicates in that small set - long distinctCount = vehicles.stream().map(Vehicle::getId).distinct().count(); - assertEquals(5, distinctCount, "Vehicle IDs in this batch should all be unique"); - } - - /** - * A generated vehicle should: - * - have a non-empty route - * - start in a known intersection (Cr1..Cr5) - * - end in S (exit) - */ - @Test - void testVehicleRouteValidity() { - VehicleGenerator generator = new VehicleGenerator(config); - - for (int i = 0; i < 20; i++) { - Vehicle vehicle = generator.generateVehicle("V" + i, 0.0); - - assertNotNull(vehicle.getRoute(), "Vehicle route should not be null"); - assertFalse(vehicle.getRoute().isEmpty(), "Vehicle route should not be empty"); - - String firstHop = vehicle.getRoute().get(0); - assertTrue( - firstHop.matches("Cr[1-5]"), - "First hop should be a valid intersection (Cr1..Cr5), got: " + firstHop - ); - - String lastHop = vehicle.getRoute().get(vehicle.getRoute().size() - 1); - assertEquals("S", lastHop, "Last hop should be exit 'S' but got: " + lastHop); - } - } - - /** - * Whatever is in simulation.properties should give us a sane duration. - */ - @Test - void testSimulationDuration() { - double duration = config.getSimulationDuration(); - assertTrue(duration > 0, "Simulation duration must be positive"); - assertTrue(duration >= 1.0, "Simulation should run at least 1 second (got " + duration + ")"); - assertTrue(duration <= 86400.0, "Simulation should not run more than a day (got " + duration + ")"); - } - - /** - * Check that the 5 intersections defined in the architecture - * actually exist in the config and have valid network data. - */ - @Test - void testIntersectionConfiguration() { - String[] intersectionIds = {"Cr1", "Cr2", "Cr3", "Cr4", "Cr5"}; - - for (String id : intersectionIds) { - String host = config.getIntersectionHost(id); - int port = config.getIntersectionPort(id); - - assertNotNull(host, "Host should not be null for " + id); - assertFalse(host.isEmpty(), "Host should not be empty for " + id); - assertTrue(port > 0, "Port should be > 0 for " + id + " (got " + port + ")"); - assertTrue(port < 65536, "Port should be a valid TCP port for " + id + " (got " + port + ")"); - } - } - - /** - * Quick sanity check: over a bunch of generated vehicles, - * we should eventually see the different vehicle types appear. - * - * Note: this is probabilistic, so we're not being super strict. - */ - @Test - void testVehicleTypeDistribution() { - VehicleGenerator generator = new VehicleGenerator(config); - - boolean hasBike = false; - boolean hasLight = false; - boolean hasHeavy = false; - - // 50 is enough for a "we're probably fine" test - for (int i = 0; i < 50; i++) { - Vehicle vehicle = generator.generateVehicle("V" + i, 0.0); - - switch (vehicle.getType()) { - case BIKE -> hasBike = true; - case LIGHT -> hasLight = true; - case HEAVY -> hasHeavy = true; - } - } - - // at least one of them should have shown up — if not, RNG is cursed - assertTrue( - hasBike || hasLight || hasHeavy, - "Expected to see at least one vehicle type after 50 generations" - ); - } -} diff --git a/main/src/test/java/sd/dashboard/DashboardTest.java b/main/src/test/java/sd/dashboard/DashboardTest.java deleted file mode 100644 index bcc72bb..0000000 --- a/main/src/test/java/sd/dashboard/DashboardTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package sd.dashboard; - -import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import sd.config.SimulationConfig; -import sd.model.VehicleType; - -/** - * Unit tests for Dashboard Server components. - */ -class DashboardTest { - - private DashboardStatistics statistics; - - @BeforeEach - void setUp() { - statistics = new DashboardStatistics(); - } - - @AfterEach - void tearDown() { - statistics = null; - } - - @Test - void testInitialStatistics() { - assertEquals(0, statistics.getTotalVehiclesGenerated(), - "Initial vehicles generated should be 0"); - assertEquals(0, statistics.getTotalVehiclesCompleted(), - "Initial vehicles completed should be 0"); - assertEquals(0.0, statistics.getAverageSystemTime(), - "Initial average system time should be 0.0"); - assertEquals(0.0, statistics.getAverageWaitingTime(), - "Initial average waiting time should be 0.0"); - } - - @Test - void testVehicleCounters() { - statistics.incrementVehiclesGenerated(); - assertEquals(1, statistics.getTotalVehiclesGenerated()); - - statistics.updateVehiclesGenerated(10); - assertEquals(10, statistics.getTotalVehiclesGenerated()); - - statistics.incrementVehiclesCompleted(); - assertEquals(1, statistics.getTotalVehiclesCompleted()); - } - - @Test - void testAverageCalculations() { - // Add 3 completed vehicles with known times - statistics.updateVehiclesCompleted(3); - statistics.addSystemTime(3000); // 3000ms total - statistics.addWaitingTime(1500); // 1500ms total - - assertEquals(1000.0, statistics.getAverageSystemTime(), 0.01, - "Average system time should be 1000ms"); - assertEquals(500.0, statistics.getAverageWaitingTime(), 0.01, - "Average waiting time should be 500ms"); - } - - @Test - void testVehicleTypeStatistics() { - statistics.incrementVehicleType(VehicleType.LIGHT); - statistics.incrementVehicleType(VehicleType.LIGHT); - statistics.incrementVehicleType(VehicleType.HEAVY); - - assertEquals(2, statistics.getVehicleTypeCount(VehicleType.LIGHT)); - assertEquals(1, statistics.getVehicleTypeCount(VehicleType.HEAVY)); - assertEquals(0, statistics.getVehicleTypeCount(VehicleType.BIKE)); - } - - @Test - void testIntersectionStatistics() { - statistics.updateIntersectionStats("Cr1", 10, 8, 2); - - DashboardStatistics.IntersectionStats stats = - statistics.getIntersectionStats("Cr1"); - - assertNotNull(stats, "Intersection stats should not be null"); - assertEquals("Cr1", stats.getIntersectionId()); - assertEquals(10, stats.getTotalArrivals()); - assertEquals(8, stats.getTotalDepartures()); - assertEquals(2, stats.getCurrentQueueSize()); - } - - @Test - void testMultipleIntersections() { - statistics.updateIntersectionStats("Cr1", 10, 8, 2); - statistics.updateIntersectionStats("Cr2", 15, 12, 3); - statistics.updateIntersectionStats("Cr3", 5, 5, 0); - - assertEquals(3, statistics.getAllIntersectionStats().size(), - "Should have 3 intersections"); - } - - @Test - void testStatsUpdatePayload() { - StatsUpdatePayload payload = new StatsUpdatePayload() - .setTotalVehiclesGenerated(50) - .setTotalVehiclesCompleted(20) - .setIntersectionArrivals(30) - .setIntersectionDepartures(25) - .setIntersectionQueueSize(5); - - assertEquals(50, payload.getTotalVehiclesGenerated()); - assertEquals(20, payload.getTotalVehiclesCompleted()); - assertEquals(30, payload.getIntersectionArrivals()); - assertEquals(25, payload.getIntersectionDepartures()); - assertEquals(5, payload.getIntersectionQueueSize()); - } - - @Test - void testStatsMessage() { - StatsUpdatePayload payload = new StatsUpdatePayload() - .setIntersectionArrivals(10); - - StatsMessage message = new StatsMessage("Cr1", payload); - - assertEquals("Cr1", message.getSourceNode()); - assertEquals("DashboardServer", message.getDestinationNode()); - assertEquals(sd.model.MessageType.STATS_UPDATE, message.getType()); - assertNotNull(message.getPayload()); - } - - @Test - void testThreadSafety() throws InterruptedException { - // Test concurrent updates - Thread t1 = new Thread(() -> { - for (int i = 0; i < 100; i++) { - statistics.incrementVehiclesGenerated(); - } - }); - - Thread t2 = new Thread(() -> { - for (int i = 0; i < 100; i++) { - statistics.incrementVehiclesGenerated(); - } - }); - - t1.start(); - t2.start(); - t1.join(); - t2.join(); - - assertEquals(200, statistics.getTotalVehiclesGenerated(), - "Concurrent increments should total 200"); - } - - @Test - void testDashboardServerCreation() throws Exception { - SimulationConfig config = new SimulationConfig("simulation.properties"); - DashboardServer server = new DashboardServer(config); - - assertNotNull(server, "Server should be created successfully"); - assertNotNull(server.getStatistics(), "Statistics should be initialized"); - assertFalse(server.isRunning(), "Server should not be running initially"); - } -} diff --git a/main/src/test/java/sd/des/DESComponentsTest.java b/main/src/test/java/sd/des/DESComponentsTest.java deleted file mode 100644 index ea55b2d..0000000 --- a/main/src/test/java/sd/des/DESComponentsTest.java +++ /dev/null @@ -1,174 +0,0 @@ -package sd.des; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import static org.junit.jupiter.api.Assertions.*; - -import sd.model.TrafficLight; -import sd.model.TrafficLightState; - -/** - * Unit tests for DES (Discrete Event Simulation) components. - * Tests the core infrastructure: SimulationClock, EventQueue, SimulationEvent. - */ -public class DESComponentsTest { - - private SimulationClock clock; - private EventQueue eventQueue; - - @BeforeEach - public void setUp() { - clock = new SimulationClock(); - eventQueue = new EventQueue(false); // Don't track history for basic tests - } - - @Test - public void testSimulationClockInitialization() { - assertEquals(0.0, clock.getCurrentTime(), 0.001, "Clock should start at time 0"); - assertEquals(0.0, clock.getElapsedTime(), 0.001, "Elapsed time should be 0 initially"); - } - - @Test - public void testSimulationClockAdvancement() { - clock.advanceTo(10.0); - assertEquals(10.0, clock.getCurrentTime(), 0.001, "Clock should advance to 10.0"); - - clock.advanceTo(25.5); - assertEquals(25.5, clock.getCurrentTime(), 0.001, "Clock should advance to 25.5"); - assertEquals(25.5, clock.getElapsedTime(), 0.001, "Elapsed time should be 25.5"); - } - - @Test - public void testSimulationClockBackwardTimePrevention() { - clock.advanceTo(20.0); - - // Attempting to go backward should throw an exception - assertThrows(IllegalArgumentException.class, () -> { - clock.advanceTo(15.0); - }, "Clock should throw exception when trying to go backward"); - - // Clock should still be at 20.0 - assertEquals(20.0, clock.getCurrentTime(), 0.001, "Clock should remain at 20.0"); - } - - @Test - public void testEventQueueOrdering() { - // Schedule events out of order - SimulationEvent event3 = new SimulationEvent(30.0, DESEventType.VEHICLE_ARRIVAL, null); - SimulationEvent event1 = new SimulationEvent(10.0, DESEventType.VEHICLE_GENERATION, null); - SimulationEvent event2 = new SimulationEvent(20.0, DESEventType.TRAFFIC_LIGHT_CHANGE, null); - - eventQueue.schedule(event3); - eventQueue.schedule(event1); - eventQueue.schedule(event2); - - // Events should come out in chronological order - SimulationEvent first = eventQueue.poll(); - assertEquals(10.0, first.getTimestamp(), 0.001, "First event should be at time 10.0"); - assertEquals(DESEventType.VEHICLE_GENERATION, first.getType()); - - SimulationEvent second = eventQueue.poll(); - assertEquals(20.0, second.getTimestamp(), 0.001, "Second event should be at time 20.0"); - assertEquals(DESEventType.TRAFFIC_LIGHT_CHANGE, second.getType()); - - SimulationEvent third = eventQueue.poll(); - assertEquals(30.0, third.getTimestamp(), 0.001, "Third event should be at time 30.0"); - assertEquals(DESEventType.VEHICLE_ARRIVAL, third.getType()); - } - - @Test - public void testEventQueueEmpty() { - assertTrue(eventQueue.isEmpty(), "New queue should be empty"); - - eventQueue.schedule(new SimulationEvent(5.0, DESEventType.VEHICLE_GENERATION, null)); - assertFalse(eventQueue.isEmpty(), "Queue should not be empty after scheduling"); - - eventQueue.poll(); - assertTrue(eventQueue.isEmpty(), "Queue should be empty after polling all events"); - } - - @Test - public void testEventQueueSize() { - assertEquals(0, eventQueue.size(), "New queue should have size 0"); - - eventQueue.schedule(new SimulationEvent(5.0, DESEventType.VEHICLE_GENERATION, null)); - eventQueue.schedule(new SimulationEvent(10.0, DESEventType.VEHICLE_ARRIVAL, null)); - assertEquals(2, eventQueue.size(), "Queue should have size 2"); - - eventQueue.poll(); - assertEquals(1, eventQueue.size(), "Queue should have size 1 after polling"); - } - - @Test - public void testSimulationEventComparison() { - SimulationEvent early = new SimulationEvent(5.0, DESEventType.VEHICLE_GENERATION, null); - SimulationEvent late = new SimulationEvent(10.0, DESEventType.VEHICLE_ARRIVAL, null); - - assertTrue(early.compareTo(late) < 0, "Earlier event should compare less than later event"); - assertTrue(late.compareTo(early) > 0, "Later event should compare greater than earlier event"); - assertEquals(0, early.compareTo(early), "Event should compare equal to itself"); - } - - @Test - public void testSimulationEventEqualTimestamp() { - // When timestamps are equal, events are ordered by type name - SimulationEvent event1 = new SimulationEvent(5.0, DESEventType.TRAFFIC_LIGHT_CHANGE, null); - SimulationEvent event2 = new SimulationEvent(5.0, DESEventType.VEHICLE_ARRIVAL, null); - - int comparison = event1.compareTo(event2); - // TRAFFIC_LIGHT_CHANGE comes before VEHICLE_ARRIVAL alphabetically - assertTrue(comparison < 0, "When equal timestamp, should order by type name alphabetically"); - } - - @Test - public void testTrafficLightEvent() { - TrafficLight light = new TrafficLight("Cr1-N", "North", 10.0, 15.0); - light.changeState(TrafficLightState.RED); - TrafficLightEvent tlEvent = new TrafficLightEvent(light, "North", "Cr1"); - - assertEquals(light, tlEvent.getLight(), "Should return correct traffic light"); - assertEquals("North", tlEvent.getDirection(), "Should return correct direction"); - assertEquals("Cr1", tlEvent.getIntersectionId(), "Should return correct intersection ID"); - } - - @Test - public void testEventHistoryTracking() { - EventQueue historyQueue = new EventQueue(true); // Enable history tracking - - SimulationEvent event1 = new SimulationEvent(5.0, DESEventType.VEHICLE_GENERATION, null); - SimulationEvent event2 = new SimulationEvent(10.0, DESEventType.VEHICLE_ARRIVAL, null); - - historyQueue.schedule(event1); - historyQueue.schedule(event2); - - historyQueue.poll(); - historyQueue.poll(); - - String history = historyQueue.exportEventHistory(); - assertNotNull(history, "Event history should not be null"); - assertTrue(history.contains("VEHICLE_GENERATION"), "History should contain first event type"); - assertTrue(history.contains("VEHICLE_ARRIVAL"), "History should contain second event type"); - } - - @Test - public void testEventQueuePeek() { - SimulationEvent event = new SimulationEvent(5.0, DESEventType.VEHICLE_GENERATION, null); - eventQueue.schedule(event); - - SimulationEvent peeked = eventQueue.peek(); - assertNotNull(peeked, "Peek should return event"); - assertEquals(5.0, peeked.getTimestamp(), 0.001, "Peeked event should have correct timestamp"); - - // Queue should still have the event - assertEquals(1, eventQueue.size(), "Peek should not remove event from queue"); - } - - @Test - public void testSimulationEndEvent() { - SimulationEvent endEvent = new SimulationEvent(100.0, DESEventType.SIMULATION_END, null); - - assertEquals(100.0, endEvent.getTimestamp(), 0.001); - assertEquals(DESEventType.SIMULATION_END, endEvent.getType()); - assertNull(endEvent.getPayload(), "End event should have no payload"); - } -} diff --git a/main/src/test/java/sd/serialization/SerializationTest.java b/main/src/test/java/sd/serialization/SerializationTest.java deleted file mode 100644 index b43b5a5..0000000 --- a/main/src/test/java/sd/serialization/SerializationTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package sd.serialization; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.DisplayName; -import sd.model.Message; -import sd.model.Vehicle; -import sd.model.VehicleType; - -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Test suite for JSON serialization. - * - * Tests JSON serialization to ensure: - * - Correct serialization and deserialization - * - Data integrity during round-trip conversion - * - Proper error handling - */ -class SerializationTest { - - private MessageSerializer jsonSerializer = new JsonMessageSerializer(); - - private Vehicle testVehicle = new Vehicle("V001", VehicleType.LIGHT, 10.5, - Arrays.asList("Cr1", "Cr2", "Cr5", "S")); - private Message testMessage = new Message( - sd.model.MessageType.VEHICLE_TRANSFER, - "Cr1", - "Cr2", - testVehicle - ); - - - // ===== JSON Serialization Tests ===== - - @Test - @DisplayName("JSON: Should serialize and deserialize Vehicle correctly") - void testJsonVehicleRoundTrip() throws SerializationException { - // Serialize - byte[] data = jsonSerializer.serialize(testVehicle); - assertNotNull(data); - assertTrue(data.length > 0); - - // Print JSON for inspection - System.out.println("JSON Vehicle:"); - System.out.println(new String(data)); - - // Deserialize - Vehicle deserialized = jsonSerializer.deserialize(data, Vehicle.class); - - // Verify - assertNotNull(deserialized); - assertEquals(testVehicle.getId(), deserialized.getId()); - assertEquals(testVehicle.getType(), deserialized.getType()); - assertEquals(testVehicle.getEntryTime(), deserialized.getEntryTime()); - assertEquals(testVehicle.getRoute(), deserialized.getRoute()); - assertEquals(testVehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime()); - assertEquals(testVehicle.getTotalCrossingTime(), deserialized.getTotalCrossingTime()); - } - - @Test - @DisplayName("JSON: Should serialize and deserialize Message correctly") - void testJsonMessageRoundTrip() throws SerializationException { - // Serialize - byte[] data = jsonSerializer.serialize(testMessage); - assertNotNull(data); - - // Print JSON for inspection - System.out.println("\nJSON Message:"); - System.out.println(new String(data)); - - // Deserialize - Message deserialized = jsonSerializer.deserialize(data, Message.class); - - // Verify - assertNotNull(deserialized); - assertEquals(testMessage.getType(), deserialized.getType()); - assertEquals(testMessage.getSenderId(), deserialized.getSenderId()); - assertEquals(testMessage.getDestinationId(), deserialized.getDestinationId()); - } - - @Test - @DisplayName("JSON: Should throw exception on null object") - void testJsonSerializeNull() { - assertThrows(IllegalArgumentException.class, () -> { - jsonSerializer.serialize(null); - }); - } - - @Test - @DisplayName("JSON: Should throw exception on null data") - void testJsonDeserializeNull() { - assertThrows(IllegalArgumentException.class, () -> { - jsonSerializer.deserialize(null, Vehicle.class); - }); - } - - @Test - @DisplayName("JSON: Should throw exception on invalid JSON") - void testJsonDeserializeInvalid() { - byte[] invalidData = "{ invalid json }".getBytes(); - assertThrows(SerializationException.class, () -> { - jsonSerializer.deserialize(invalidData, Vehicle.class); - }); - } - - @Test - @DisplayName("JSON: Should preserve data integrity for complex objects") - void testDataIntegrity() throws SerializationException { - // Create a more complex vehicle - Vehicle vehicle = new Vehicle("V999", VehicleType.HEAVY, 100.5, - Arrays.asList("Cr1", "Cr2", "Cr3", "Cr4", "Cr5", "S")); - vehicle.addWaitingTime(10.5); - vehicle.addWaitingTime(5.3); - vehicle.addCrossingTime(2.1); - vehicle.advanceRoute(); - vehicle.advanceRoute(); - - // Serialize and deserialize - byte[] jsonData = jsonSerializer.serialize(vehicle); - Vehicle deserialized = jsonSerializer.deserialize(jsonData, Vehicle.class); - - // Verify all fields match - assertEquals(vehicle.getId(), deserialized.getId()); - assertEquals(vehicle.getType(), deserialized.getType()); - assertEquals(vehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime()); - assertEquals(vehicle.getCurrentRouteIndex(), deserialized.getCurrentRouteIndex()); - } - - // ===== Factory Tests ===== - - @Test - @DisplayName("Factory: Should create JSON serializer by default") - void testFactoryDefault() { - MessageSerializer serializer = SerializerFactory.createDefault(); - assertNotNull(serializer); - assertEquals("JSON (Gson)", serializer.getName()); - } -}