summaryrefslogtreecommitdiff
path: root/src/main/java/testing
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/testing')
-rw-r--r--src/main/java/testing/HeadlessProtocolRunner.java15
-rw-r--r--src/main/java/testing/HeadlessSimulationRunner.java55
-rw-r--r--src/main/java/testing/LogCapture.java29
-rw-r--r--src/main/java/testing/ProtocolVerifier.java65
-rw-r--r--src/main/java/testing/SimulationMetrics.java6
5 files changed, 156 insertions, 14 deletions
diff --git a/src/main/java/testing/HeadlessProtocolRunner.java b/src/main/java/testing/HeadlessProtocolRunner.java
index daf96aa..a6098de 100644
--- a/src/main/java/testing/HeadlessProtocolRunner.java
+++ b/src/main/java/testing/HeadlessProtocolRunner.java
@@ -50,6 +50,21 @@ public class HeadlessProtocolRunner {
System.out.println(" Log entries: " + result.getMetrics().getTotalLogCount());
System.out.println(" Messages per process: " + result.getMetrics().getProcessMessageCounts());
+ // Count total messages sent
+ int totalMessages = result.getMetrics().getTotalMessageCount();
+ System.out.println(" Total messages sent: " + totalMessages);
+
+ // Check if any messages were sent
+ if (totalMessages == 0) {
+ System.err.println("\n⚠️ WARNING: No messages were sent during simulation!");
+ System.err.println(" This indicates the protocol may not be functioning correctly.");
+ if (!verbose) {
+ System.err.println(" Re-run with -Dds.sim.verbose=true for detailed output.");
+ }
+ // Mark as failure
+ throw new RuntimeException("Protocol test failed: No messages sent");
+ }
+
System.out.println();
} catch (Exception e) {
System.err.println("✗ FAILED: " + e.getMessage());
diff --git a/src/main/java/testing/HeadlessSimulationRunner.java b/src/main/java/testing/HeadlessSimulationRunner.java
index 9d2274c..6279fa9 100644
--- a/src/main/java/testing/HeadlessSimulationRunner.java
+++ b/src/main/java/testing/HeadlessSimulationRunner.java
@@ -26,6 +26,11 @@ public class HeadlessSimulationRunner {
public HeadlessSimulationRunner() {
this.prefs = new VSDefaultPrefs();
this.prefs.fillWithDefaults();
+
+ // Set reasonable message delays for testing (10-50ms instead of 500-2000ms)
+ this.prefs.initLong("message.sendingtime.min", 10);
+ this.prefs.initLong("message.sendingtime.max", 50);
+
VSRegisteredEvents.init(prefs);
this.executor = Executors.newSingleThreadExecutor();
}
@@ -55,14 +60,20 @@ public class HeadlessSimulationRunner {
simulator = loaded.getSimulator();
viz = loaded.getVisualization();
+ // Update message delays on all processes after loading
+ for (int i = 0; i < viz.getNumProcesses(); i++) {
+ VSInternalProcess process = viz.getProcess(i);
+ if (process != null) {
+ process.initLong("message.sendingtime.min", 10);
+ process.initLong("message.sendingtime.max", 50);
+ }
+ }
+
if (simulator == null || viz == null) {
throw new IllegalStateException("Failed to load simulation");
}
- // Set up headless message handlers for all processes
- setupHeadlessMessageHandlers(viz);
-
- // Install log capture
+ // Install log capture first
logCapture = new LogCapture();
logCapture.setPrintLogs(printLogs);
if (listener != null) {
@@ -70,6 +81,9 @@ public class HeadlessSimulationRunner {
}
installLogCapture();
+ // Set up headless message handlers for all processes (after log capture is ready)
+ setupHeadlessMessageHandlers(viz);
+
// Get the simulation's configured end time
long untilTime = viz.getUntilTime();
long actualMaxTime = Math.min(maxTime, untilTime);
@@ -187,10 +201,11 @@ public class HeadlessSimulationRunner {
lastActiveTime = currentTime;
} else {
noActivityCount++;
- // If no activity for 3000ms (3 seconds) of simulation time, stop
- // This accounts for message delivery times of 500-2000ms plus some buffer
- if (noActivityCount > 3000 && (currentTime - lastActiveTime) > 3000) {
- System.out.println("No activity detected for 3 seconds - simulation complete at time " + simulatorTime);
+ // If no activity for 5000ms (5 seconds) of simulation time, stop
+ // This accounts for message delivery times of 500-2000ms plus extra buffer
+ // to ensure all messages are delivered
+ if (noActivityCount > 5000 && (currentTime - lastActiveTime) > 5000) {
+ System.out.println("No activity detected for 5 seconds - simulation complete at time " + simulatorTime);
break;
}
}
@@ -208,10 +223,19 @@ public class HeadlessSimulationRunner {
private boolean hasPendingActivity(VSTaskManager taskManager, Field globalTasksField, long currentTime) {
try {
- // Check global tasks
+ // Check global tasks - but also check if any are scheduled for future times
Queue<?> globalTasks = (Queue<?>) globalTasksField.get(taskManager);
if (globalTasks != null && !globalTasks.isEmpty()) {
- return true; // If any global tasks exist, keep running
+ // Check if any tasks are scheduled for the future
+ for (Object obj : globalTasks) {
+ VSTask task = (VSTask) obj;
+ if (task.getTaskTime() > currentTime) {
+ // There's a future task scheduled, keep running
+ return true;
+ }
+ }
+ // If all tasks are in the past or present, they should execute now
+ return true;
}
// Check process-specific tasks
@@ -220,12 +244,19 @@ public class HeadlessSimulationRunner {
if (process != null) {
Queue<VSTask> tasks = process.getTasks();
if (tasks != null && !tasks.isEmpty()) {
- return true; // If any process tasks exist, keep running
+ // Check if any tasks are scheduled for the future
+ for (VSTask task : tasks) {
+ if (task.getTaskTime() > process.getTime()) {
+ return true;
+ }
+ }
+ // If all tasks are ready to run, keep going
+ return true;
}
}
}
- // Check for messages in transit
+ // Check for messages in transit (visualization lines)
Field messageLinesField = VSSimulatorVisualization.class.getDeclaredField("messageLines");
messageLinesField.setAccessible(true);
LinkedList<?> messageLines = (LinkedList<?>) messageLinesField.get(viz);
diff --git a/src/main/java/testing/LogCapture.java b/src/main/java/testing/LogCapture.java
index 97bb127..ddd0ad0 100644
--- a/src/main/java/testing/LogCapture.java
+++ b/src/main/java/testing/LogCapture.java
@@ -30,6 +30,17 @@ public class LogCapture extends VSLogging {
this.printLogs = printLogs;
}
+ public void setSimulatorCanvas(VSSimulatorVisualization viz) {
+ // Store reference for process count
+ try {
+ Field field = VSLogging.class.getDeclaredField("simulatorVisualization");
+ field.setAccessible(true);
+ field.set(this, viz);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
public void setLogPrefix(String prefix) {
this.logPrefix = prefix;
}
@@ -136,9 +147,23 @@ public class LogCapture extends VSLogging {
public Map<Integer, Integer> getProcessMessageCounts() {
Map<Integer, Integer> counts = new HashMap<>();
- for (Map.Entry<Integer, List<LogEntry>> entry : processLogs.entrySet()) {
- counts.put(entry.getKey(), entry.getValue().size());
+
+ // Initialize counts for all processes
+ VSSimulatorVisualization viz = getSimulatorVisualization();
+ if (viz != null) {
+ for (int i = 0; i < viz.getNumProcesses(); i++) {
+ counts.put(i, 0);
+ }
}
+
+ // Count messages from all captured logs
+ for (LogEntry log : capturedLogs) {
+ if (log.getMessage().contains("Message sent")) {
+ int processNum = log.getProcessNum();
+ counts.put(processNum, counts.getOrDefault(processNum, 0) + 1);
+ }
+ }
+
return counts;
}
diff --git a/src/main/java/testing/ProtocolVerifier.java b/src/main/java/testing/ProtocolVerifier.java
index 19ed1f2..e5338d4 100644
--- a/src/main/java/testing/ProtocolVerifier.java
+++ b/src/main/java/testing/ProtocolVerifier.java
@@ -80,6 +80,29 @@ public class ProtocolVerifier {
}
/**
+ * Expect at least n messages to be sent during the simulation.
+ */
+ public ProtocolVerifier expectAtLeastNMessages(int minMessages) {
+ rules.add(new MessageCountRule(minMessages, Integer.MAX_VALUE));
+ return this;
+ }
+
+ /**
+ * Expect exactly n messages to be sent during the simulation.
+ */
+ public ProtocolVerifier expectExactlyNMessages(int count) {
+ rules.add(new MessageCountRule(count, count));
+ return this;
+ }
+
+ /**
+ * Expect messages to be sent (at least 1).
+ */
+ public ProtocolVerifier expectMessages() {
+ return expectAtLeastNMessages(1);
+ }
+
+ /**
* Verify all rules against the provided logs.
*/
public VerificationResult verify(List<LogEntry> logs) {
@@ -240,4 +263,46 @@ public class ProtocolVerifier {
return new RuleResult(passed, message, matches);
}
}
+
+ /**
+ * Rule that verifies message count.
+ */
+ private static class MessageCountRule implements VerificationRule {
+ private final int minCount;
+ private final int maxCount;
+ private final String description;
+
+ public MessageCountRule(int minCount, int maxCount) {
+ this.minCount = minCount;
+ this.maxCount = maxCount;
+ this.description = String.format(
+ "Message count should be %s",
+ minCount == maxCount ?
+ String.valueOf(minCount) :
+ minCount + "-" + (maxCount == Integer.MAX_VALUE ? "∞" : maxCount)
+ );
+ }
+
+ @Override
+ public RuleResult verify(List<LogEntry> logs) {
+ int messageCount = 0;
+ List<LogEntry> messageLogs = new ArrayList<>();
+
+ // Count all "Message sent" logs
+ for (LogEntry log : logs) {
+ if (log.getMessage().contains("Message sent")) {
+ messageCount++;
+ messageLogs.add(log);
+ }
+ }
+
+ boolean passed = messageCount >= minCount && messageCount <= maxCount;
+ String message = String.format(
+ "%s (found %d messages)",
+ description, messageCount
+ );
+
+ return new RuleResult(passed, message, messageLogs);
+ }
+ }
} \ No newline at end of file
diff --git a/src/main/java/testing/SimulationMetrics.java b/src/main/java/testing/SimulationMetrics.java
index 2b80631..dc8fc39 100644
--- a/src/main/java/testing/SimulationMetrics.java
+++ b/src/main/java/testing/SimulationMetrics.java
@@ -44,4 +44,10 @@ public class SimulationMetrics {
return (double) totalProcessMessages / numProcesses;
}
+
+ public int getTotalMessageCount() {
+ return processMessageCounts.values().stream()
+ .mapToInt(Integer::intValue)
+ .sum();
+ }
} \ No newline at end of file