diff options
Diffstat (limited to 'src/main/java/testing/ProtocolVerifier.java')
| -rw-r--r-- | src/main/java/testing/ProtocolVerifier.java | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/main/java/testing/ProtocolVerifier.java b/src/main/java/testing/ProtocolVerifier.java new file mode 100644 index 0000000..19ed1f2 --- /dev/null +++ b/src/main/java/testing/ProtocolVerifier.java @@ -0,0 +1,243 @@ +package testing; + +import java.util.*; +import java.util.regex.*; +import java.util.function.Predicate; + +/** + * Flexible verification system for checking protocol behavior through log analysis. + * Supports pattern matching, sequence verification, and count-based assertions. + */ +public class ProtocolVerifier { + private final List<VerificationRule> rules; + + public ProtocolVerifier() { + this.rules = new ArrayList<>(); + } + + /** + * Add a custom verification rule. + */ + public ProtocolVerifier withRule(VerificationRule rule) { + rules.add(rule); + return this; + } + + /** + * Expect a log message containing the pattern at least once. + */ + public ProtocolVerifier expectLog(String pattern) { + rules.add(new PatternRule(pattern, 1, Integer.MAX_VALUE)); + return this; + } + + /** + * Expect a log message containing the pattern exactly n times. + */ + public ProtocolVerifier expectLogExactly(String pattern, int count) { + rules.add(new PatternRule(pattern, count, count)); + return this; + } + + /** + * Expect a log message containing the pattern at least n times. + */ + public ProtocolVerifier expectLogAtLeast(String pattern, int minCount) { + rules.add(new PatternRule(pattern, minCount, Integer.MAX_VALUE)); + return this; + } + + /** + * Expect a log message containing the pattern at most n times. + */ + public ProtocolVerifier expectLogAtMost(String pattern, int maxCount) { + rules.add(new PatternRule(pattern, 0, maxCount)); + return this; + } + + /** + * Expect a sequence of patterns in order. + */ + public ProtocolVerifier expectSequence(String... patterns) { + rules.add(new SequenceRule(Arrays.asList(patterns))); + return this; + } + + /** + * Expect no log messages containing the pattern. + */ + public ProtocolVerifier expectNoLog(String pattern) { + rules.add(new PatternRule(pattern, 0, 0)); + return this; + } + + /** + * Expect a log from a specific process. + */ + public ProtocolVerifier expectLogFromProcess(int processNum, String pattern) { + rules.add(new ProcessPatternRule(processNum, pattern, 1, Integer.MAX_VALUE)); + return this; + } + + /** + * Verify all rules against the provided logs. + */ + public VerificationResult verify(List<LogEntry> logs) { + List<RuleResult> results = new ArrayList<>(); + + for (VerificationRule rule : rules) { + results.add(rule.verify(logs)); + } + + return new VerificationResult(results); + } + + // Rule implementations + + /** + * Rule that matches log messages against a pattern. + */ + private static class PatternRule implements VerificationRule { + private final Pattern pattern; + private final int minCount; + private final int maxCount; + private final String description; + + public PatternRule(String pattern, int minCount, int maxCount) { + // Try to compile as regex first, if it fails, use literal matching + Pattern compiledPattern; + try { + compiledPattern = Pattern.compile(pattern); + } catch (PatternSyntaxException e) { + // If not a valid regex, escape it for literal matching + compiledPattern = Pattern.compile(Pattern.quote(pattern)); + } + this.pattern = compiledPattern; + this.minCount = minCount; + this.maxCount = maxCount; + this.description = String.format( + "Pattern '%s' should appear %s times", + pattern, + minCount == maxCount ? + String.valueOf(minCount) : + minCount + "-" + (maxCount == Integer.MAX_VALUE ? "∞" : maxCount) + ); + } + + @Override + public RuleResult verify(List<LogEntry> logs) { + int count = 0; + List<LogEntry> matches = new ArrayList<>(); + + for (LogEntry log : logs) { + if (pattern.matcher(log.getMessage()).find()) { + count++; + matches.add(log); + } + } + + boolean passed = count >= minCount && count <= maxCount; + String message = String.format( + "%s (found %d occurrences)", + description, count + ); + + return new RuleResult(passed, message, matches); + } + } + + /** + * Rule that verifies a sequence of patterns appear in order. + */ + private static class SequenceRule implements VerificationRule { + private final List<Pattern> patterns; + private final String description; + + public SequenceRule(List<String> patterns) { + this.patterns = new ArrayList<>(); + for (String p : patterns) { + try { + this.patterns.add(Pattern.compile(p)); + } catch (PatternSyntaxException e) { + this.patterns.add(Pattern.compile(Pattern.quote(p))); + } + } + this.description = "Sequence: " + String.join(" → ", patterns); + } + + @Override + public RuleResult verify(List<LogEntry> logs) { + int patternIndex = 0; + List<LogEntry> matches = new ArrayList<>(); + + for (LogEntry log : logs) { + if (patternIndex < patterns.size() && + patterns.get(patternIndex).matcher(log.getMessage()).find()) { + matches.add(log); + patternIndex++; + } + } + + boolean passed = patternIndex == patterns.size(); + String message = String.format( + "%s (%d/%d patterns matched)", + description, patternIndex, patterns.size() + ); + + return new RuleResult(passed, message, matches); + } + } + + /** + * Rule that matches patterns from a specific process. + */ + private static class ProcessPatternRule implements VerificationRule { + private final int processNum; + private final Pattern pattern; + private final int minCount; + private final int maxCount; + private final String description; + + public ProcessPatternRule(int processNum, String pattern, int minCount, int maxCount) { + this.processNum = processNum; + Pattern compiledPattern; + try { + compiledPattern = Pattern.compile(pattern); + } catch (PatternSyntaxException e) { + compiledPattern = Pattern.compile(Pattern.quote(pattern)); + } + this.pattern = compiledPattern; + this.minCount = minCount; + this.maxCount = maxCount; + this.description = String.format( + "Process %d: Pattern '%s' should appear %s times", + processNum, pattern, + minCount == maxCount ? + String.valueOf(minCount) : + minCount + "-" + (maxCount == Integer.MAX_VALUE ? "∞" : maxCount) + ); + } + + @Override + public RuleResult verify(List<LogEntry> logs) { + int count = 0; + List<LogEntry> matches = new ArrayList<>(); + + for (LogEntry log : logs) { + if (log.isFromProcess(processNum) && + pattern.matcher(log.getMessage()).find()) { + count++; + matches.add(log); + } + } + + boolean passed = count >= minCount && count <= maxCount; + String message = String.format( + "%s (found %d occurrences)", + description, count + ); + + return new RuleResult(passed, message, matches); + } + } +}
\ No newline at end of file |
