From d3b697218773eaa5a3dd368705184726dbc0fa38 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 21 Jun 2025 15:54:07 +0300 Subject: Implement headless testing framework for DS-Sim protocol simulations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created HeadlessSimulationRunner that loads and runs simulations without GUI - Implemented LogCapture to intercept and store all simulation logs - Added ProtocolVerifier for flexible pattern-based log verification - Created test runners: standard, with logs, and clean (filters GUI errors) - Implemented tests for all non-Raft protocols - Added DummySimulatorFrame to satisfy GUI dependencies during loading - Created CleanHeadlessRunner that filters GUI-related errors from output - Updated run-tests.sh script with quiet mode option - Documented the framework architecture and usage The framework successfully runs protocol tests and verifies behavior through log analysis. GUI errors occur internally due to tight coupling in DS-Sim but are filtered in quiet mode for clean output. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/main/java/testing/SimulationResult.java | 94 +++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/main/java/testing/SimulationResult.java (limited to 'src/main/java/testing/SimulationResult.java') diff --git a/src/main/java/testing/SimulationResult.java b/src/main/java/testing/SimulationResult.java new file mode 100644 index 0000000..4cab8ee --- /dev/null +++ b/src/main/java/testing/SimulationResult.java @@ -0,0 +1,94 @@ +package testing; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Contains the results of a headless simulation run, including + * captured logs and execution metrics. + */ +public class SimulationResult { + private final List allLogs; + private final Map> processLogs; + private final SimulationMetrics metrics; + + public SimulationResult(List allLogs, + Map> processLogs, + SimulationMetrics metrics) { + this.allLogs = Collections.unmodifiableList(allLogs); + this.processLogs = Collections.unmodifiableMap(processLogs); + this.metrics = metrics; + } + + public List getAllLogs() { + return allLogs; + } + + public List getLogsForProcess(int processNum) { + return processLogs.getOrDefault(processNum, Collections.emptyList()); + } + + public Map> getProcessLogs() { + return processLogs; + } + + public SimulationMetrics getMetrics() { + return metrics; + } + + /** + * Count logs matching a pattern + */ + public int countLogs(String pattern) { + return (int) allLogs.stream() + .filter(log -> log.getMessage().contains(pattern)) + .count(); + } + + /** + * Find first log matching pattern + */ + public Optional findFirst(String pattern) { + return allLogs.stream() + .filter(log -> log.getMessage().contains(pattern)) + .findFirst(); + } + + /** + * Find all logs matching pattern + */ + public List findAll(String pattern) { + return allLogs.stream() + .filter(log -> log.getMessage().contains(pattern)) + .collect(Collectors.toList()); + } + + /** + * Get logs in time range + */ + public List getLogsInTimeRange(long startTime, long endTime) { + return allLogs.stream() + .filter(log -> log.getTimestamp() >= startTime && + log.getTimestamp() <= endTime) + .collect(Collectors.toList()); + } + + /** + * Generate summary report + */ + public String generateSummary() { + StringBuilder sb = new StringBuilder(); + sb.append("=== Simulation Result Summary ===\n"); + sb.append("Total logs: ").append(allLogs.size()).append("\n"); + sb.append("Processes: ").append(metrics.getNumProcesses()).append("\n"); + sb.append("\nLogs per process:\n"); + + for (Map.Entry entry : + metrics.getProcessMessageCounts().entrySet()) { + sb.append(" Process ").append(entry.getKey()) + .append(": ").append(entry.getValue()).append(" logs\n"); + } + + return sb.toString(); + } +} \ No newline at end of file -- cgit v1.2.3