mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 20:43:32 +00:00
Merge pull request #25 from davidalves04/11-convert-intersection-to-standalone-process
11 convert intersection to standalone process
This commit is contained in:
@@ -83,33 +83,23 @@ public class IntersectionProcess {
|
|||||||
private void createTrafficLights() {
|
private void createTrafficLights() {
|
||||||
System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
|
System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
|
||||||
|
|
||||||
// Define directions based on the actual network topology
|
String[] directions = new String[0];
|
||||||
String[] directions;
|
|
||||||
switch (intersectionId) {
|
switch (intersectionId) {
|
||||||
case "Cr1":
|
case "Cr1":
|
||||||
// Cr1: East (to Cr2), South (to Cr4), West (from Cr2)
|
directions = new String[]{"East", "South"};
|
||||||
directions = new String[]{"East", "South", "West"};
|
|
||||||
break;
|
break;
|
||||||
case "Cr2":
|
case "Cr2":
|
||||||
// Cr2: West (to Cr1), East (to Cr3), South (to Cr5)
|
|
||||||
// Plus receiving from Cr1 and Cr3
|
|
||||||
directions = new String[]{"West", "East", "South"};
|
directions = new String[]{"West", "East", "South"};
|
||||||
break;
|
break;
|
||||||
case "Cr3":
|
case "Cr3":
|
||||||
// Cr3: West (to Cr2), East (to S)
|
directions = new String[]{"West", "South"};
|
||||||
directions = new String[]{"West", "East"};
|
|
||||||
break;
|
break;
|
||||||
case "Cr4":
|
case "Cr4":
|
||||||
// Cr4: East (to Cr5), plus pedestrian crossing
|
|
||||||
directions = new String[]{"East"};
|
directions = new String[]{"East"};
|
||||||
break;
|
break;
|
||||||
case "Cr5":
|
case "Cr5":
|
||||||
// Cr5: East (to S), receives from Cr2 and Cr4
|
|
||||||
directions = new String[]{"East"};
|
directions = new String[]{"East"};
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
// Fallback to all directions
|
|
||||||
directions = new String[]{"North", "South", "East", "West"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String direction : directions) {
|
for (String direction : directions) {
|
||||||
@@ -134,41 +124,31 @@ public class IntersectionProcess {
|
|||||||
|
|
||||||
switch (intersectionId) {
|
switch (intersectionId) {
|
||||||
case "Cr1":
|
case "Cr1":
|
||||||
// Cr1 connections: → Cr2 (East), → Cr4 (South), ← Cr2 (West)
|
intersection.configureRoute("Cr2", "East");
|
||||||
intersection.configureRoute("Cr2", "East"); // Go to Cr2
|
intersection.configureRoute("Cr4", "South");
|
||||||
intersection.configureRoute("Cr4", "South"); // Go to Cr4
|
|
||||||
// Routes through other intersections to reach S
|
|
||||||
intersection.configureRoute("S", "East"); // S via Cr2
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Cr2":
|
case "Cr2":
|
||||||
// Cr2 connections: ↔ Cr1 (West/East), ↔ Cr3 (East/West), → Cr5 (South)
|
intersection.configureRoute("Cr1", "West");
|
||||||
intersection.configureRoute("Cr1", "West"); // Go to Cr1
|
intersection.configureRoute("Cr3", "East");
|
||||||
intersection.configureRoute("Cr3", "East"); // Go to Cr3
|
intersection.configureRoute("Cr5", "South");
|
||||||
intersection.configureRoute("Cr5", "South"); // Go to Cr5
|
|
||||||
intersection.configureRoute("S", "South"); // S via Cr5 or direct
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Cr3":
|
case "Cr3":
|
||||||
// Cr3 connections: ← Cr2 (West), → S (South/East)
|
intersection.configureRoute("Cr2", "West");
|
||||||
intersection.configureRoute("Cr2", "West"); // Go back to Cr2
|
intersection.configureRoute("S", "South");
|
||||||
intersection.configureRoute("S", "East"); // Go to exit S
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Cr4":
|
case "Cr4":
|
||||||
// Cr4 connections: → Cr5 (East)
|
intersection.configureRoute("Cr5", "East");
|
||||||
intersection.configureRoute("Cr5", "East"); // Go to Cr5
|
|
||||||
intersection.configureRoute("S", "East"); // S via Cr5
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Cr5":
|
case "Cr5":
|
||||||
// Cr5 connections: → S (East/South)
|
intersection.configureRoute("S", "East");
|
||||||
intersection.configureRoute("S", "East"); // Go to exit S
|
|
||||||
// Cr5 might also receive from Cr2 and Cr4 but doesn't route back
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
System.err.println(" Warning: Unknown intersection ID: " + intersectionId);
|
System.err.println(" Error: unknown intersection ID: " + intersectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(" Routing configured.");
|
System.out.println(" Routing configured.");
|
||||||
@@ -197,7 +177,7 @@ public class IntersectionProcess {
|
|||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
try {
|
||||||
// GREEN phase
|
// Green state
|
||||||
light.changeState(TrafficLightState.GREEN);
|
light.changeState(TrafficLightState.GREEN);
|
||||||
System.out.println("[" + light.getId() + "] State: GREEN");
|
System.out.println("[" + light.getId() + "] State: GREEN");
|
||||||
|
|
||||||
@@ -207,7 +187,7 @@ public class IntersectionProcess {
|
|||||||
// Wait for green duration
|
// Wait for green duration
|
||||||
Thread.sleep((long) (light.getGreenTime() * 1000));
|
Thread.sleep((long) (light.getGreenTime() * 1000));
|
||||||
|
|
||||||
// RED phase
|
// RED state
|
||||||
light.changeState(TrafficLightState.RED);
|
light.changeState(TrafficLightState.RED);
|
||||||
System.out.println("[" + light.getId() + "] State: RED");
|
System.out.println("[" + light.getId() + "] State: RED");
|
||||||
|
|
||||||
@@ -300,8 +280,7 @@ public class IntersectionProcess {
|
|||||||
System.out.println("[" + intersectionId + "] Sent vehicle " + vehicle.getId() +
|
System.out.println("[" + intersectionId + "] Sent vehicle " + vehicle.getId() +
|
||||||
" to " + nextDestination);
|
" to " + nextDestination);
|
||||||
|
|
||||||
// Update vehicle's path - advance to next destination in route
|
// Note: vehicle route is advanced when it arrives at the next intersection
|
||||||
vehicle.advanceRoute();
|
|
||||||
|
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
System.err.println("[" + intersectionId + "] Failed to send vehicle " +
|
System.err.println("[" + intersectionId + "] Failed to send vehicle " +
|
||||||
@@ -343,10 +322,8 @@ public class IntersectionProcess {
|
|||||||
private String getHostForDestination(String destinationId) {
|
private String getHostForDestination(String destinationId) {
|
||||||
if (destinationId.equals("S")) {
|
if (destinationId.equals("S")) {
|
||||||
return config.getExitHost();
|
return config.getExitHost();
|
||||||
} else if (destinationId.startsWith("Cr")) {
|
|
||||||
return config.getIntersectionHost(destinationId);
|
|
||||||
} else {
|
} else {
|
||||||
return config.getDashboardHost();
|
return config.getIntersectionHost(destinationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,10 +336,8 @@ public class IntersectionProcess {
|
|||||||
private int getPortForDestination(String destinationId) {
|
private int getPortForDestination(String destinationId) {
|
||||||
if (destinationId.equals("S")) {
|
if (destinationId.equals("S")) {
|
||||||
return config.getExitPort();
|
return config.getExitPort();
|
||||||
} else if (destinationId.startsWith("Cr")) {
|
|
||||||
return config.getIntersectionPort(destinationId);
|
|
||||||
} else {
|
} else {
|
||||||
return config.getDashboardPort();
|
return config.getIntersectionPort(destinationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,45 +460,6 @@ public class IntersectionProcess {
|
|||||||
System.out.println("=".repeat(60));
|
System.out.println("=".repeat(60));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Main method to start an intersection process.
|
|
||||||
*
|
|
||||||
* @param args Command-line arguments:
|
|
||||||
* args[0] - Intersection ID (required, e.g., "Cr1")
|
|
||||||
* args[1] - Config file path (optional, defaults to "simulation.properties")
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
if (args.length < 1) {
|
|
||||||
System.err.println("Usage: java IntersectionProcess <intersectionId> [configFile]");
|
|
||||||
System.err.println("Example: java IntersectionProcess Cr1");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
String intersectionId = args[0];
|
|
||||||
String configFile = args.length > 1 ? args[1] : "simulation.properties";
|
|
||||||
|
|
||||||
IntersectionProcess process = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
process = new IntersectionProcess(intersectionId, configFile);
|
|
||||||
process.initialize();
|
|
||||||
|
|
||||||
// Add shutdown hook for graceful termination
|
|
||||||
final IntersectionProcess finalProcess = process;
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
finalProcess.shutdown();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Start the process
|
|
||||||
process.start();
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Error starting intersection process: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Inner class for Vehicle Transfer Messages ---
|
// --- Inner class for Vehicle Transfer Messages ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -104,16 +104,28 @@ public class Intersection {
|
|||||||
* Accepts an incoming vehicle and places it in the correct queue.
|
* Accepts an incoming vehicle and places it in the correct queue.
|
||||||
* * This method:
|
* * This method:
|
||||||
* 1. Increments the {@link #totalVehiclesReceived} counter.
|
* 1. Increments the {@link #totalVehiclesReceived} counter.
|
||||||
* 2. Gets the vehicle's *next* destination (from {@link Vehicle#getCurrentDestination()}).
|
* 2. Advances the vehicle's route (since it just arrived here)
|
||||||
* 3. Uses the {@link #routing} map to find the correct *direction* for that destination.
|
* 3. Gets the vehicle's *next* destination (from {@link Vehicle#getCurrentDestination()}).
|
||||||
* 4. Adds the vehicle to the queue of the {@link TrafficLight} for that direction.
|
* 4. Uses the {@link #routing} map to find the correct *direction* for that destination.
|
||||||
|
* 5. Adds the vehicle to the queue of the {@link TrafficLight} for that direction.
|
||||||
*
|
*
|
||||||
* @param vehicle The {@link Vehicle} arriving at the intersection.
|
* @param vehicle The {@link Vehicle} arriving at the intersection.
|
||||||
*/
|
*/
|
||||||
public void receiveVehicle(Vehicle vehicle) {
|
public void receiveVehicle(Vehicle vehicle) {
|
||||||
totalVehiclesReceived++;
|
totalVehiclesReceived++;
|
||||||
|
|
||||||
|
// Advance route since vehicle just arrived at this intersection
|
||||||
|
vehicle.advanceRoute();
|
||||||
|
|
||||||
String nextDestination = vehicle.getCurrentDestination();
|
String nextDestination = vehicle.getCurrentDestination();
|
||||||
|
|
||||||
|
// Check if vehicle reached final destination
|
||||||
|
if (nextDestination == null) {
|
||||||
|
System.out.printf("[%s] Vehicle %s reached final destination%n",
|
||||||
|
this.id, vehicle.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String direction = routing.get(nextDestination);
|
String direction = routing.get(nextDestination);
|
||||||
|
|
||||||
if (direction != null && trafficLights.containsKey(direction)) {
|
if (direction != null && trafficLights.containsKey(direction)) {
|
||||||
|
|||||||
473
main/src/test/java/IntersectionProcessTest.java
Normal file
473
main/src/test/java/IntersectionProcessTest.java
Normal file
@@ -0,0 +1,473 @@
|
|||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup after tests
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown() {
|
||||||
|
if (intersectionProcess != null) {
|
||||||
|
intersectionProcess.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 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 seperate thread
|
||||||
|
Thread serverThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
intersectionProcess.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// expected on shutdown
|
||||||
|
}
|
||||||
|
});
|
||||||
|
serverThread.start();
|
||||||
|
|
||||||
|
Thread.sleep(500); // wait for server to start
|
||||||
|
|
||||||
|
// try connecting to check if its running
|
||||||
|
try (Socket clientSocket = new Socket("localhost", 18001)) {
|
||||||
|
assertTrue(clientSocket.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// create test vehicle
|
||||||
|
java.util.List<String> route = Arrays.asList("Cr2", "Cr3", "S");
|
||||||
|
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route);
|
||||||
|
|
||||||
|
// send vehicle from Cr1 to Cr2
|
||||||
|
try (Socket socket = new Socket("localhost", 18002)) {
|
||||||
|
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||||
|
|
||||||
|
TestVehicleMessage message = new TestVehicleMessage("Cr1", "Cr2", vehicle);
|
||||||
|
out.writeObject(message);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
Thread.sleep(1000); // wait for procesing
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
Thread serverThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
intersectionProcess.start();
|
||||||
|
} catch (IOException e) { }
|
||||||
|
});
|
||||||
|
serverThread.start();
|
||||||
|
|
||||||
|
Thread.sleep(500);
|
||||||
|
|
||||||
|
// verify server running
|
||||||
|
try (Socket socket = new Socket("localhost", 18001)) {
|
||||||
|
assertTrue(socket.isConnected());
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectionProcess.shutdown();
|
||||||
|
serverThread.join(2000);
|
||||||
|
|
||||||
|
// after shutdown conection should fail
|
||||||
|
Thread.sleep(500);
|
||||||
|
Exception exception = assertThrows(IOException.class, () -> {
|
||||||
|
Socket socket = new Socket("localhost", 18001);
|
||||||
|
socket.close();
|
||||||
|
});
|
||||||
|
assertNotNull(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
// setup 2 intersections
|
||||||
|
IntersectionProcess cr1 = new IntersectionProcess("Cr1", configFile.toString());
|
||||||
|
IntersectionProcess cr2 = new IntersectionProcess("Cr2", configFile.toString());
|
||||||
|
|
||||||
|
cr1.initialize();
|
||||||
|
cr2.initialize();
|
||||||
|
|
||||||
|
// start both
|
||||||
|
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(1000); // wait for servers
|
||||||
|
|
||||||
|
// send vehicle to Cr1 that goes to Cr2
|
||||||
|
java.util.List<String> route = Arrays.asList("Cr1", "Cr2", "S");
|
||||||
|
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route);
|
||||||
|
|
||||||
|
try (Socket socket = new Socket("localhost", 18001)) {
|
||||||
|
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||||
|
|
||||||
|
TestVehicleMessage message = new TestVehicleMessage("Entry", "Cr1", vehicle);
|
||||||
|
out.writeObject(message);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
Thread.sleep(2000); // time for processing
|
||||||
|
}
|
||||||
|
|
||||||
|
cr1.shutdown();
|
||||||
|
cr2.shutdown();
|
||||||
|
thread1.join(2000);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user