diff options
Diffstat (limited to 'src/test/java/testing')
9 files changed, 476 insertions, 3 deletions
diff --git a/src/test/java/testing/RaftSimulationTest.java b/src/test/java/testing/RaftSimulationTest.java new file mode 100644 index 0000000..b161668 --- /dev/null +++ b/src/test/java/testing/RaftSimulationTest.java @@ -0,0 +1,57 @@ +package testing; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import prefs.VSDefaultPrefs; +import events.VSRegisteredEvents; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration test for Raft protocol simulation. + * Tests that leader election occurs when running a Raft simulation. + */ +class RaftSimulationTest { + + private VSDefaultPrefs prefs; + + @BeforeEach + void setUp() { + prefs = new VSDefaultPrefs(); + prefs.fillWithDefaults(); + VSRegisteredEvents.init(prefs); + } + + @Test + void testRaftLeaderElection() throws Exception { + // This test verifies that the Raft protocol implementation + // properly elects a leader when running + + // For now, we verify the protocol can be instantiated and initialized + Object raftObj = new utils.VSClassLoader().newInstance("protocols.implementations.VSRaftProtocol"); + assertNotNull(raftObj, "Raft protocol should be instantiable"); + assertTrue(raftObj instanceof protocols.VSAbstractProtocol, "Should be a protocol"); + + // Verify the protocol has the correct classname set + protocols.implementations.VSRaftProtocol raftProtocol = + (protocols.implementations.VSRaftProtocol) raftObj; + assertNotNull(raftProtocol.getClassname(), + "Protocol classname should be set"); + assertTrue(raftProtocol.getClassname().contains("VSRaftProtocol"), + "Protocol classname should contain VSRaftProtocol"); + } + + @Test + void testRaftProtocolRegistration() { + // Verify Raft protocol is properly registered + assertTrue(VSRegisteredEvents.getProtocolClassnames().contains( + "protocols.implementations.VSRaftProtocol"), + "Raft protocol should be registered"); + + // Verify it has a proper display name (this is set in lang properties) + String shortName = VSRegisteredEvents.getShortnameByClassname( + "protocols.implementations.VSRaftProtocol"); + assertNotNull(shortName, "Raft protocol should have a short name"); + assertEquals("Raft Consensus", shortName); + } +}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/BaseProtocolTest.java b/src/test/java/testing/protocols/BaseProtocolTest.java index e9cbc81..fcc04fa 100644 --- a/src/test/java/testing/protocols/BaseProtocolTest.java +++ b/src/test/java/testing/protocols/BaseProtocolTest.java @@ -26,7 +26,15 @@ public abstract class BaseProtocolTest { */ protected SimulationResult runSimulation(String file, long duration) { try { - return runner.runSimulation(file, duration); + SimulationResult result = runner.runSimulation(file, duration); + + // Check if any messages were sent + int totalMessages = result.getMetrics().getTotalMessageCount(); + if (totalMessages == 0) { + throw new AssertionError("Protocol test failed: No messages were sent during simulation of " + file); + } + + return result; } catch (Exception e) { throw new RuntimeException("Failed to run simulation: " + file, e); } diff --git a/src/test/java/testing/protocols/BroadcastProtocolTest.java b/src/test/java/testing/protocols/BroadcastProtocolTest.java index d0ed6f3..644e6b3 100644 --- a/src/test/java/testing/protocols/BroadcastProtocolTest.java +++ b/src/test/java/testing/protocols/BroadcastProtocolTest.java @@ -32,7 +32,8 @@ public class BroadcastProtocolTest { ProtocolVerifier verifier = new ProtocolVerifier() .expectLog("Broadcast.*activated") .expectNoLog("ERROR") - .expectNoLog("Exception"); + .expectNoLog("Exception") + .expectMessages(); // Broadcast must send messages VerificationResult verification = verifier.verify(result.getAllLogs()); diff --git a/src/test/java/testing/protocols/MessageDeliveryDebug2Test.java b/src/test/java/testing/protocols/MessageDeliveryDebug2Test.java new file mode 100644 index 0000000..abebfe0 --- /dev/null +++ b/src/test/java/testing/protocols/MessageDeliveryDebug2Test.java @@ -0,0 +1,88 @@ +package testing.protocols; + +import testing.*; +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; +import java.lang.reflect.Field; +import core.VSTask; +import core.VSTaskManager; +import java.util.*; + +/** + * Extended debug test to understand why protocols are running twice. + */ +public class MessageDeliveryDebug2Test { + private HeadlessSimulationRunner runner; + + @BeforeEach + public void setup() { + runner = new HeadlessSimulationRunner(); + } + + @AfterEach + public void teardown() { + runner.shutdown(); + } + + @Test + @DisplayName("Debug why PingPong client sends two messages") + public void debugDuplicateMessages() throws Exception { + System.out.println("\n=== Starting Duplicate Message Debug Test ==="); + + // Custom log listener to trace task execution + LogListener listener = new LogListener() { + private int messagesSent = 0; + + @Override + public void onLogEntry(LogEntry entry) { + String msg = entry.getMessage(); + + // Track all events + if (msg.contains("activated") || msg.contains("Message sent") || + msg.contains("onClientStart") || msg.contains("counter=")) { + System.out.println(String.format("[TRACE %5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), msg)); + + if (msg.contains("Message sent")) { + messagesSent++; + if (messagesSent == 2) { + System.out.println("!!! ISSUE: Second message sent immediately at time " + + entry.getTimestamp() + " - protocol likely executed twice!"); + } + } + } + + // Look for protocol event execution + if (msg.contains("VSProtocolEvent") || msg.contains("VSPingPongProtocol")) { + System.out.println(String.format("[EVENT %5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), msg)); + } + } + }; + + SimulationResult result = runner.runSimulation( + "saved-simulations/ping-pong.dat", + 1000, // Just 1 second is enough to see the issue + listener + ); + + System.out.println("\n=== Analysis ==="); + + // Count messages sent at time 0 + int messagesAtTimeZero = 0; + for (LogEntry entry : result.getAllLogs()) { + if (entry.getTimestamp() == 0 && entry.getMessage().contains("Message sent")) { + messagesAtTimeZero++; + System.out.println("Message at time 0: " + entry.getMessage()); + } + } + + System.out.println("\nMessages sent at time 0: " + messagesAtTimeZero); + + if (messagesAtTimeZero > 1) { + System.out.println("ERROR: Multiple messages sent at time 0 indicates duplicate protocol execution!"); + } + + System.out.println("\n=== Test Complete ==="); + } +}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/MessageDeliveryDebug3Test.java b/src/test/java/testing/protocols/MessageDeliveryDebug3Test.java new file mode 100644 index 0000000..3e41a05 --- /dev/null +++ b/src/test/java/testing/protocols/MessageDeliveryDebug3Test.java @@ -0,0 +1,87 @@ +package testing.protocols; + +import testing.*; +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Deep debug test to understand why messages aren't being received. + */ +public class MessageDeliveryDebug3Test { + private HeadlessSimulationRunner runner; + + @BeforeEach + public void setup() { + runner = new HeadlessSimulationRunner(); + runner.setPrintLogs(true); + } + + @AfterEach + public void teardown() { + runner.shutdown(); + } + + @Test + @DisplayName("Debug message delivery with detailed logging") + public void debugMessageDelivery() throws Exception { + System.out.println("\n=== Starting Message Delivery Deep Debug ==="); + + // Run for longer to see if messages eventually get delivered + SimulationResult result = runner.runSimulation( + "saved-simulations/ping-pong.dat", + 10000 // 10 seconds + ); + + System.out.println("\n=== Analysis ==="); + + // Count message types + int sentCount = 0; + int receivedCount = 0; + int scheduledCount = 0; + + for (LogEntry entry : result.getAllLogs()) { + String msg = entry.getMessage(); + if (msg.contains("Message sent")) { + sentCount++; + System.out.println("SENT at " + entry.getTimestamp() + ": " + msg); + } else if (msg.contains("Message received")) { + receivedCount++; + System.out.println("RECEIVED at " + entry.getTimestamp() + ": " + msg); + } else if (msg.contains("scheduled for delivery")) { + scheduledCount++; + System.out.println("SCHEDULED: " + msg); + } + } + + System.out.println("\nTotal messages sent: " + sentCount); + System.out.println("Total messages received: " + receivedCount); + System.out.println("Total messages scheduled: " + scheduledCount); + + // Check if we're getting any server/client activity + boolean hasServerActivity = false; + boolean hasClientActivity = false; + + for (LogEntry entry : result.getAllLogs()) { + String msg = entry.getMessage(); + if (msg.contains("Server") && msg.contains("activated")) { + hasServerActivity = true; + } + if (msg.contains("Client") && msg.contains("activated")) { + hasClientActivity = true; + } + } + + System.out.println("\nServer activated: " + hasServerActivity); + System.out.println("Client activated: " + hasClientActivity); + + // Print all logs for full context + System.out.println("\n=== All Logs ==="); + for (LogEntry entry : result.getAllLogs()) { + System.out.println(String.format("[%5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), entry.getMessage())); + } + + assertTrue(sentCount > 0, "Should have sent at least one message"); + assertTrue(receivedCount > 0, "Should have received at least one message"); + } +}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/MessageDeliveryDebug4Test.java b/src/test/java/testing/protocols/MessageDeliveryDebug4Test.java new file mode 100644 index 0000000..719bb37 --- /dev/null +++ b/src/test/java/testing/protocols/MessageDeliveryDebug4Test.java @@ -0,0 +1,83 @@ +package testing.protocols; + +import testing.*; +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test with longer timeout to ensure all messages are delivered. + */ +public class MessageDeliveryDebug4Test { + private HeadlessSimulationRunner runner; + + @BeforeEach + public void setup() { + runner = new HeadlessSimulationRunner(); + } + + @Test + @DisplayName("Test counter values with extended timeout") + public void testCounterValues() throws Exception { + System.out.println("\n=== Testing Counter Values ==="); + + // Run for 10 seconds to ensure all messages are exchanged + SimulationResult result = runner.runSimulation( + "saved-simulations/ping-pong.dat", + 10000 + ); + + System.out.println("\n=== Counter Analysis ==="); + + // Look for all counter values + int maxClientCounter = 0; + int maxServerCounter = 0; + + for (LogEntry entry : result.getAllLogs()) { + String msg = entry.getMessage(); + if (msg.contains("counter=")) { + int start = msg.indexOf("counter=") + 8; + int end = msg.indexOf(";", start); + if (end == -1) end = msg.indexOf(" ", start); + if (end == -1) end = msg.length(); + + try { + int counter = Integer.parseInt(msg.substring(start, end)); + System.out.println("Found counter=" + counter + " at time " + entry.getTimestamp()); + + if (msg.contains("fromClient=true")) { + maxClientCounter = Math.max(maxClientCounter, counter); + } else if (msg.contains("fromServer=true")) { + maxServerCounter = Math.max(maxServerCounter, counter); + } + } catch (NumberFormatException e) { + // Ignore + } + } + } + + System.out.println("\nMax client counter: " + maxClientCounter); + System.out.println("Max server counter: " + maxServerCounter); + + // Count messages + int sentCount = result.countLogs("Message sent"); + int receivedCount = result.countLogs("Message received"); + + System.out.println("\nTotal messages sent: " + sentCount); + System.out.println("Total messages received: " + receivedCount); + + // Print timeline + System.out.println("\n=== Message Timeline ==="); + for (LogEntry entry : result.getAllLogs()) { + String msg = entry.getMessage(); + if (msg.contains("Message sent") || msg.contains("Message received") || + msg.contains("activated")) { + System.out.println(String.format("[%5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), + msg.substring(msg.indexOf(";") + 2))); // Skip PID part + } + } + + assertTrue(maxClientCounter >= 2, "Client should reach at least counter=2"); + assertTrue(maxServerCounter >= 1, "Server should reach at least counter=1"); + } +}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/MessageDeliveryDebugTest.java b/src/test/java/testing/protocols/MessageDeliveryDebugTest.java new file mode 100644 index 0000000..9f190a1 --- /dev/null +++ b/src/test/java/testing/protocols/MessageDeliveryDebugTest.java @@ -0,0 +1,71 @@ +package testing.protocols; + +import testing.*; +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Debug test to understand message delivery issues in headless mode. + */ +public class MessageDeliveryDebugTest { + private HeadlessSimulationRunner runner; + + @BeforeEach + public void setup() { + runner = new HeadlessSimulationRunner(); + runner.setPrintLogs(true); // Enable log printing for debugging + } + + @AfterEach + public void teardown() { + runner.shutdown(); + } + + @Test + @DisplayName("Debug message delivery in Ping-Pong protocol") + public void debugMessageDelivery() throws Exception { + System.out.println("\n=== Starting Message Delivery Debug Test ==="); + + // Run simulation with log listener to see what's happening + LogListener listener = new LogListener() { + @Override + public void onLogEntry(LogEntry entry) { + String msg = entry.getMessage(); + if (msg.contains("Message sent") || msg.contains("Message received") || + msg.contains("scheduled for delivery") || msg.contains("activated")) { + System.out.println(String.format("[DEBUG %5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), msg)); + } + } + }; + + SimulationResult result = runner.runSimulation( + "saved-simulations/ping-pong.dat", + 5000, + listener + ); + + System.out.println("\n=== Simulation Complete ==="); + System.out.println("Total logs captured: " + result.getAllLogs().size()); + + // Count specific log types + int sentCount = result.countLogs("Message sent"); + int receivedCount = result.countLogs("Message received"); + int activatedCount = result.countLogs("activated"); + + System.out.println("Messages sent: " + sentCount); + System.out.println("Messages received: " + receivedCount); + System.out.println("Protocols activated: " + activatedCount); + + // Print all logs for analysis + System.out.println("\n=== All Logs ==="); + for (LogEntry entry : result.getAllLogs()) { + System.out.println(String.format("[%5d] P%d: %s", + entry.getTimestamp(), entry.getProcessNum(), entry.getMessage())); + } + + // Basic assertions + assertTrue(activatedCount > 0, "At least one protocol should be activated"); + System.out.println("\n=== Test Complete ==="); + } +}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/PingPongProtocolTest.java b/src/test/java/testing/protocols/PingPongProtocolTest.java index 3396e08..947de54 100644 --- a/src/test/java/testing/protocols/PingPongProtocolTest.java +++ b/src/test/java/testing/protocols/PingPongProtocolTest.java @@ -31,7 +31,8 @@ public class PingPongProtocolTest { ProtocolVerifier verifier = new ProtocolVerifier() .expectLogExactly("Ping-Pong.*activated", 2) .expectLog("Ping-Pong Client activated") - .expectLog("Ping-Pong Server activated"); + .expectLog("Ping-Pong Server activated") + .expectMessages(); // Ensure messages are sent VerificationResult verification = verifier.verify(result.getAllLogs()); diff --git a/src/test/java/testing/protocols/RaftProtocolTest.java b/src/test/java/testing/protocols/RaftProtocolTest.java new file mode 100644 index 0000000..b92606d --- /dev/null +++ b/src/test/java/testing/protocols/RaftProtocolTest.java @@ -0,0 +1,77 @@ +package testing.protocols; + +import testing.*; +import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration test for Raft consensus protocol. + */ +public class RaftProtocolTest extends BaseProtocolTest { + + @Test + @DisplayName("Test Raft protocol activation and message sending") + public void testRaftActivation() { + SimulationResult result = runSimulation( + "saved-simulations/raft.dat", + 2000 // 2 seconds should be enough for elections + ); + + ProtocolVerifier verifier = new ProtocolVerifier() + .expectLogExactly("Raft Consensus Server activated", 3) + .expectLog("FOLLOWER.*initialized") + .expectLog("Starting election") + .expectLog("CANDIDATE") + .expectMessages() // Must have messages + .expectAtLeastNMessages(10); // Should have many election messages + + VerificationResult verification = verifier.verify(result.getAllLogs()); + + assertTrue(verification.passed(), verification.getFailureMessage()); + assertEquals(3, result.getMetrics().getNumProcesses(), + "Should have 3 processes"); + } + + @Test + @DisplayName("Test Raft election messages") + public void testRaftElectionMessages() { + SimulationResult result = runSimulation( + "saved-simulations/raft.dat", + 3000 + ); + + ProtocolVerifier verifier = new ProtocolVerifier() + .expectLog("REQUEST_VOTE") + .expectLog("Message sent.*REQUEST_VOTE") + .expectAtLeastNMessages(15); // Multiple election rounds + + VerificationResult verification = verifier.verify(result.getAllLogs()); + assertTrue(verification.passed(), verification.getFailureMessage()); + + // Verify term progression + assertTrue(result.findFirst("term=1").isPresent(), "Should have term 1"); + assertTrue(result.findFirst("term=2").isPresent(), "Should progress to term 2"); + } + + @Test + @DisplayName("Test Raft with clients") + public void testRaftWithClients() { + // Skip if file doesn't exist + if (!new java.io.File("saved-simulations/raft-with-clients.dat").exists()) { + return; + } + + SimulationResult result = runSimulation( + "saved-simulations/raft-with-clients.dat", + 5000 + ); + + ProtocolVerifier verifier = new ProtocolVerifier() + .expectLogExactly("Raft Consensus Server activated", 3) + .expectLogExactly("Raft Consensus Client activated", 2) + .expectMessages(); // Must have messages + + VerificationResult verification = verifier.verify(result.getAllLogs()); + assertTrue(verification.passed(), verification.getFailureMessage()); + } +}
\ No newline at end of file |
