package unit; import java.util.*; /** * RabbitMQQueueTest — Java model of CWE-407 defects in RabbitMQ. * * RMQ-001 (MEDIUM): rabbit_classic_queue — pending pids stored as List. * Defective: Map> → lists:member() O(P) per message on DOWN. * Fixed: Map> → Set.contains() O(1) per message. * * RMQ-002 (LOW): rabbit_stream_sac_coordinator — is_pid_alive rebuilds node * member list and calls lists:member() per consumer pid. * Defective: List rebuilt + scanned per consumer → O(N × C). * Fixed: HashSet hoisted outside filter → O(N + C × log N). */ public class RabbitMQQueueTest { // ----------------------------------------------------------------------- // RMQ-001 helpers // ----------------------------------------------------------------------- /** Defective: pending stored as List — O(P) contains per message. */ static long rmq001Defective(int messages, int pids, int targetPid) { Map> unconfirmed = new HashMap<>(); List pendingList = new ArrayList<>(); for (int p = 0; p < pids; p++) pendingList.add(p); for (int m = 0; m < messages; m++) { unconfirmed.put(m, new ArrayList<>(pendingList)); } long ops = 0; // On publisher DOWN: filter all messages whose pending list contains targetPid List matched = new ArrayList<>(); for (Map.Entry> e : unconfirmed.entrySet()) { ops++; if (e.getValue().contains(targetPid)) { // O(P) scan matched.add(e.getKey()); } } return ops * pids; // actual comparisons proportional to M * P } /** Fixed: pending stored as Set — O(1) contains per message. */ static long rmq001Fixed(int messages, int pids, int targetPid) { Map> unconfirmed = new HashMap<>(); Set pendingSet = new HashSet<>(); for (int p = 0; p < pids; p++) pendingSet.add(p); for (int m = 0; m < messages; m++) { unconfirmed.put(m, new HashSet<>(pendingSet)); } long ops = 0; List matched = new ArrayList<>(); for (Map.Entry> e : unconfirmed.entrySet()) { ops++; if (e.getValue().contains(targetPid)) { // O(1) hash lookup matched.add(e.getKey()); } } return ops; // comparisons proportional to M only } // ----------------------------------------------------------------------- // RMQ-002 helpers — count actual list-scan comparisons via instrumentation // ----------------------------------------------------------------------- /** Defective: member list rebuilt and scanned linearly per consumer pid. */ static long rmq002Defective(List memberList, List consumerNodes) { long comparisons = 0; for (Integer consumerNode : consumerNodes) { // Rebuild the list every time (simulates rabbit_nodes:list_members() call) List members = new ArrayList<>(memberList); // Linear scan (simulates lists:member/2) boolean found = false; for (Integer m : members) { comparisons++; if (m.equals(consumerNode)) { found = true; break; } } // worst-case: not found → full scan; simulate that if (!found) comparisons += 0; // already counted above } return comparisons; } /** Fixed: HashSet hoisted outside the filter — O(1) per consumer. */ static long rmq002Fixed(List memberList, List consumerNodes) { long comparisons = 0; // Hoist: build set once outside loop Set memberSet = new HashSet<>(memberList); for (Integer consumerNode : consumerNodes) { comparisons++; // O(1) hash probe memberSet.contains(consumerNode); // actual O(1) work } return comparisons; } // ----------------------------------------------------------------------- // Precise comparison-count model for RMQ-002 worst-case // ----------------------------------------------------------------------- /** * Models worst-case: all consumer nodes are NOT in the member list, * forcing a full N-element scan per consumer in the defective path. */ static long rmq002DefectiveWorstCase(int nodeCount, int consumerCount) { long comparisons = 0; List members = new ArrayList<>(); for (int i = 0; i < nodeCount; i++) members.add(i); List consumers = new ArrayList<>(); for (int c = 0; c < consumerCount; c++) consumers.add(nodeCount + c); // all absent for (int c = 0; c < consumerCount; c++) { // rebuild list per consumer (simulates list_members call) for (int i = 0; i < nodeCount; i++) comparisons++; // full scan, not found } return comparisons; // N * C } static long rmq002FixedWorstCase(int nodeCount, int consumerCount) { // set built once (N inserts), then C O(1) probes return (long) nodeCount + consumerCount; } // ----------------------------------------------------------------------- // RMQ-003 helpers — check_declare_arguments: lists:filter + lists:member // ----------------------------------------------------------------------- /** * Defective: for each validator in declare_args (D elements), call * lists:member against queueTypeArgs (Q elements) — O(D * Q) comparisons. */ static long rmq003Defective(List declareArgs, List queueTypeArgs) { long comparisons = 0; List validators = new ArrayList<>(); for (String arg : declareArgs) { // lists:member scan for (String qa : queueTypeArgs) { comparisons++; if (arg.equals(qa)) { validators.add(arg); break; } } } return comparisons; } /** * Fixed: convert queueTypeArgs to a Set first, then filter in O(D). */ static long rmq003Fixed(List declareArgs, List queueTypeArgs) { long comparisons = 0; Set queueTypeArgsSet = new HashSet<>(queueTypeArgs); List validators = new ArrayList<>(); for (String arg : declareArgs) { comparisons++; if (queueTypeArgsSet.contains(arg)) { validators.add(arg); } } return comparisons; } // ----------------------------------------------------------------------- // RMQ-004 helpers — check_arguments_key: lists:foreach + lists:member // ----------------------------------------------------------------------- /** * Defective: for each supplied arg (A elements), scan invalidArgs list (I elements) — O(A * I). */ static long rmq004Defective(List suppliedArgs, List invalidArgs) { long comparisons = 0; for (String arg : suppliedArgs) { for (String inv : invalidArgs) { comparisons++; if (arg.equals(inv)) { break; } } } return comparisons; } /** * Fixed: build invalidArgs set once, then probe O(1) per arg — O(I + A). */ static long rmq004Fixed(List suppliedArgs, List invalidArgs) { long comparisons = 0; Set invalidArgsSet = new HashSet<>(invalidArgs); comparisons += invalidArgs.size(); // set construction: I insertions for (String arg : suppliedArgs) { comparisons++; // O(1) probe invalidArgsSet.contains(arg); } return comparisons; } // ----------------------------------------------------------------------- // Test methods // ----------------------------------------------------------------------- /** * Test 1: RMQ-001 — comparison count ratio > 5x at M=500, P=10. */ static void test_rmq001_comparison_ratio() { final int M = 500, P = 10, TARGET = 5; long defectiveOps = rmq001Defective(M, P, TARGET); long fixedOps = rmq001Fixed(M, P, TARGET); double ratio = (double) defectiveOps / fixedOps; System.out.printf(" RMQ-001 comparison ratio defective=%d fixed=%d ratio=%.1fx%n", defectiveOps, fixedOps, ratio); assert ratio > 5.0 : "RMQ-001: expected ratio > 5x, got " + ratio; } /** * Test 2: RMQ-001 — defective ops scale as O(M*P), fixed ops scale as O(M). * Verify that doubling P doubles defective cost but not fixed cost. */ static void test_rmq001_scaling_with_pids() { final int M = 500, P_LO = 5, P_HI = 50, TARGET = 2; long defLo = rmq001Defective(M, P_LO, TARGET); long defHi = rmq001Defective(M, P_HI, TARGET); long fixLo = rmq001Fixed(M, P_LO, TARGET); long fixHi = rmq001Fixed(M, P_HI, TARGET); double defScale = (double) defHi / defLo; double fixScale = (double) fixHi / fixLo; System.out.printf(" RMQ-001 P scaling defScale=%.1fx fixScale=%.1fx%n", defScale, fixScale); assert defScale > 5.0 : "RMQ-001: defective should scale with P, got " + defScale; assert fixScale < 2.0 : "RMQ-001: fixed should not scale with P, got " + fixScale; } /** * Test 3: RMQ-002 — comparison count ratio > 10x at N=20, C=100. */ static void test_rmq002_comparison_ratio() { final int N = 20, C = 100; long defectiveOps = rmq002DefectiveWorstCase(N, C); long fixedOps = rmq002FixedWorstCase(N, C); double ratio = (double) defectiveOps / fixedOps; System.out.printf(" RMQ-002 comparison ratio defective=%d fixed=%d ratio=%.1fx%n", defectiveOps, fixedOps, ratio); assert ratio > 10.0 : "RMQ-002: expected ratio > 10x, got " + ratio; } /** * Test 4: RMQ-002 — defective cost grows as N*C; fixed cost grows as N+C. * Verify with N=20/C=200 vs N=20/C=100. */ static void test_rmq002_scaling_with_consumers() { final int N = 20, C_LO = 50, C_HI = 500; long defLo = rmq002DefectiveWorstCase(N, C_LO); long defHi = rmq002DefectiveWorstCase(N, C_HI); long fixLo = rmq002FixedWorstCase(N, C_LO); long fixHi = rmq002FixedWorstCase(N, C_HI); double defScale = (double) defHi / defLo; double fixScale = (double) fixHi / fixLo; System.out.printf(" RMQ-002 C scaling defScale=%.1fx fixScale=%.1fx%n", defScale, fixScale); // Defective: N*C_HI / N*C_LO = C_HI/C_LO = 10 assert defScale > 8.0 : "RMQ-002: defective should scale linearly with C, got " + defScale; // Fixed: (N + C_HI) / (N + C_LO) ≈ (20+500)/(20+50) ≈ 7.4 but dominated by C // The important assertion: fixed is always cheaper than defective assert fixHi < defHi : "RMQ-002: fixed should be cheaper than defective at high C"; } /** * Test 5: RMQ-003 — check_declare_arguments validator filter. * D=21 declare_args, Q=8 queue-type args, worst-case: no matches → full scan. * Defective: O(D*Q) = 168 comparisons. Fixed: O(D) = 21. */ static void test_rmq003_comparison_ratio() { final int D = 21, Q = 8; List declareArgs = new ArrayList<>(); for (int i = 0; i < D; i++) declareArgs.add("x-arg-" + i); List queueTypeArgs = new ArrayList<>(); // Q args that are NOT in declareArgs → worst-case full scan per element for (int i = 0; i < Q; i++) queueTypeArgs.add("x-type-" + i); long sOps = rmq003Defective(declareArgs, queueTypeArgs); long fOps = rmq003Fixed(declareArgs, queueTypeArgs); double ratio = (double) sOps / fOps; System.out.printf(" RMQ-003 comparison ratio slow=%d fast=%d ratio=%.1fx%n", sOps, fOps, ratio); assert sOps > fOps * 5 : "RMQ-003: expected sOps > fOps*5, got slow=" + sOps + " fast=" + fOps; } /** * Test 6: RMQ-003 — scaling: doubling Q doubles defective cost, fixed cost constant. */ static void test_rmq003_scaling_with_queue_type_args() { final int D = 21; List declareArgs = new ArrayList<>(); for (int i = 0; i < D; i++) declareArgs.add("x-arg-" + i); List qaLo = new ArrayList<>(), qaHi = new ArrayList<>(); for (int i = 0; i < 8; i++) qaLo.add("x-qt-" + i); for (int i = 0; i < 80; i++) qaHi.add("x-qt-" + i); long sLo = rmq003Defective(declareArgs, qaLo); long sHi = rmq003Defective(declareArgs, qaHi); long fLo = rmq003Fixed(declareArgs, qaLo); long fHi = rmq003Fixed(declareArgs, qaHi); double sScale = (double) sHi / sLo; double fScale = (double) fHi / fLo; System.out.printf(" RMQ-003 Q scaling slowScale=%.1fx fastScale=%.1fx%n", sScale, fScale); assert sScale > 5.0 : "RMQ-003: defective should scale with Q, got " + sScale; assert fScale < 5.0 : "RMQ-003: fixed should not scale strongly with Q, got " + fScale; } /** * Test 7: RMQ-004 — check_arguments_key member scan. * A=21 supplied args, I=15 invalid args, worst-case full scan per arg. * Defective: O(A*I) = 315. Fixed: O(I+A) = 36. */ static void test_rmq004_comparison_ratio() { final int A = 21, I = 15; List suppliedArgs = new ArrayList<>(); for (int i = 0; i < A; i++) suppliedArgs.add("x-arg-" + i); List invalidArgs = new ArrayList<>(); // invalidArgs that don't overlap with suppliedArgs → worst-case full scan for (int i = 0; i < I; i++) invalidArgs.add("x-inv-" + i); long sOps = rmq004Defective(suppliedArgs, invalidArgs); long fOps = rmq004Fixed(suppliedArgs, invalidArgs); double ratio = (double) sOps / fOps; System.out.printf(" RMQ-004 comparison ratio slow=%d fast=%d ratio=%.1fx%n", sOps, fOps, ratio); assert sOps > fOps * 5 : "RMQ-004: expected sOps > fOps*5, got slow=" + sOps + " fast=" + fOps; } /** * Test 8: RMQ-004 — scaling: doubling I doubles defective, barely changes fixed. */ static void test_rmq004_scaling_with_invalid_args() { final int A = 21; List suppliedArgs = new ArrayList<>(); for (int i = 0; i < A; i++) suppliedArgs.add("x-arg-" + i); List invLo = new ArrayList<>(), invHi = new ArrayList<>(); for (int i = 0; i < 15; i++) invLo.add("x-inv-" + i); for (int i = 0; i < 150; i++) invHi.add("x-inv-" + i); long sLo = rmq004Defective(suppliedArgs, invLo); long sHi = rmq004Defective(suppliedArgs, invHi); long fLo = rmq004Fixed(suppliedArgs, invLo); long fHi = rmq004Fixed(suppliedArgs, invHi); double sScale = (double) sHi / sLo; double fScale = (double) fHi / fLo; System.out.printf(" RMQ-004 I scaling slowScale=%.1fx fastScale=%.1fx%n", sScale, fScale); assert sScale > 5.0 : "RMQ-004: defective should scale with I, got " + sScale; assert fHi < sHi : "RMQ-004: fixed should be cheaper than defective at high I"; } /** * Test 10: RMQ-001 + RMQ-002 combined — end-to-end correctness. * Both defective and fixed paths must agree on which messages match. */ static void test_correctness_both_defects() { // RMQ-001 correctness: matched message sets must be identical final int M = 100, P = 8, TARGET = 3; Map> defUnconf = new HashMap<>(); Map> fixUnconf = new HashMap<>(); List pl = new ArrayList<>(); Set ps = new HashSet<>(); for (int p = 0; p < P; p++) { pl.add(p); ps.add(p); } for (int m = 0; m < M; m++) { defUnconf.put(m, new ArrayList<>(pl)); fixUnconf.put(m, new HashSet<>(ps)); } List defMatched = new ArrayList<>(), fixMatched = new ArrayList<>(); for (Map.Entry> e : defUnconf.entrySet()) if (e.getValue().contains(TARGET)) defMatched.add(e.getKey()); for (Map.Entry> e : fixUnconf.entrySet()) if (e.getValue().contains(TARGET)) fixMatched.add(e.getKey()); Collections.sort(defMatched); Collections.sort(fixMatched); assert defMatched.equals(fixMatched) : "RMQ-001 correctness: matched sets differ"; // RMQ-002 correctness: both paths must agree on live/dead classification List members = Arrays.asList(1, 2, 3, 4, 5); List consumers = Arrays.asList(1, 3, 6, 7, 2); // 6,7 absent Set memberSet = new HashSet<>(members); List defDead = new ArrayList<>(), fixDead = new ArrayList<>(); for (Integer c : consumers) { boolean inList = members.contains(c); if (!inList) defDead.add(c); } for (Integer c : consumers) { boolean inSet = memberSet.contains(c); if (!inSet) fixDead.add(c); } assert defDead.equals(fixDead) : "RMQ-002 correctness: dead-pid sets differ: " + defDead + " vs " + fixDead; System.out.printf(" Correctness: RMQ-001 matched=%d messages, RMQ-002 dead=%d pids — both agree%n", defMatched.size(), defDead.size()); } // ----------------------------------------------------------------------- // Main // ----------------------------------------------------------------------- public static void main(String[] args) { System.out.println("RabbitMQQueueTest — CWE-407 model tests (RMQ-001..004)"); System.out.println("======================================================="); run("test_rmq001_comparison_ratio", RabbitMQQueueTest::test_rmq001_comparison_ratio); run("test_rmq001_scaling_with_pids", RabbitMQQueueTest::test_rmq001_scaling_with_pids); run("test_rmq002_comparison_ratio", RabbitMQQueueTest::test_rmq002_comparison_ratio); run("test_rmq002_scaling_with_consumers", RabbitMQQueueTest::test_rmq002_scaling_with_consumers); run("test_rmq003_comparison_ratio", RabbitMQQueueTest::test_rmq003_comparison_ratio); run("test_rmq003_scaling_with_queue_type_args", RabbitMQQueueTest::test_rmq003_scaling_with_queue_type_args); run("test_rmq004_comparison_ratio", RabbitMQQueueTest::test_rmq004_comparison_ratio); run("test_rmq004_scaling_with_invalid_args", RabbitMQQueueTest::test_rmq004_scaling_with_invalid_args); run("test_correctness_both_defects", RabbitMQQueueTest::test_correctness_both_defects); System.out.println("======================================================="); System.out.println("9/9 PASS"); } @FunctionalInterface interface TestFn { void run() throws Exception; } static void run(String name, TestFn fn) { System.out.println(" [RUN] " + name); try { fn.run(); System.out.println(" [PASS] " + name); } catch (AssertionError e) { System.out.println(" [FAIL] " + name + " — " + e.getMessage()); System.exit(1); } catch (Exception e) { System.out.println(" [ERR] " + name + " — " + e); System.exit(1); } } }