import java.util.*; /** * CWE-407 unit tests for Vert.x core defects. * * vertx-0001: HAManager.nodeLeft() uses List.contains() inside a loop * over clusterMap.entrySet() → O(N×M) where N = clusterMap size, M = node count. * Fix: convert List to HashSet before the loop → O(N+M). * * Additionally: HAManager.addHaInfoIfLost() calls * clusterManager.getNodes().contains(nodeID) on every nodeAdded/nodeLeft event * → O(M) per event with List, O(1) with Set. */ public class VertxTest { // ----------------------------------------------------------------------- // vertx-0001 helpers — simulate the HAManager.nodeLeft inner loop // ----------------------------------------------------------------------- /** * Defective: List.contains() called inside a for-each over * clusterMapKeys → O(N × M) total. * * Mirrors HAManager lines 307-314: * List nodes = clusterManager.getNodes(); * for (Map.Entry entry : clusterMap.entrySet()) { * if (!leftNodeID.equals(entry.getKey()) && !nodes.contains(entry.getKey())) { * checkFailover(...) * } * } */ static long nodeLeftDefect(List clusterMapKeys, List nodes, String leftNodeID) { long ops = 0; for (String key : clusterMapKeys) { ops++; if (!leftNodeID.equals(key)) { // List.contains = linear scan, O(M) worst case boolean found = nodes.contains(key); ops += nodes.size(); // account for full scan (worst case) } } return ops; } /** * Fixed: wrap nodes in HashSet once, then O(1) membership test per entry. */ static long nodeLeftFixed(List clusterMapKeys, List nodes, String leftNodeID) { Set nodeSet = new HashSet<>(nodes); // O(M) once long ops = nodes.size(); // cost of building the set for (String key : clusterMapKeys) { ops++; if (!leftNodeID.equals(key)) { boolean found = nodeSet.contains(key); // O(1) ops++; } } return ops; } // ----------------------------------------------------------------------- // vertx-0001 test A — nodeLeft loop op-count ratio // ----------------------------------------------------------------------- static void testVertx0001_opCount() { int N = 1000; // clusterMap size int M = 1000; // node count List clusterMapKeys = new ArrayList<>(N); List nodes = new ArrayList<>(M); for (int i = 0; i < N; i++) clusterMapKeys.add("node-" + i); for (int i = 0; i < M; i++) nodes.add("node-" + i); String leftNode = "node-0"; long defectOps = nodeLeftDefect(clusterMapKeys, nodes, leftNode); long fixedOps = nodeLeftFixed(clusterMapKeys, nodes, leftNode); double ratio = (double) defectOps / fixedOps; System.out.printf( "vertx-0001 nodeLeft op-count: defect=%d fixed=%d ratio=%.1fx%n", defectOps, fixedOps, ratio ); if (ratio < 50.0) { throw new AssertionError( "vertx-0001: expected op-count ratio >= 50x at N=M=1000, got " + ratio); } System.out.println("vertx-0001 op-count: PASS"); } // ----------------------------------------------------------------------- // vertx-0001 test B — wall-clock timing // ----------------------------------------------------------------------- static void testVertx0001_timing() { int N = 500; int M = 500; int REPS = 200; List clusterMapKeys = new ArrayList<>(N); List nodes = new ArrayList<>(M); for (int i = 0; i < N; i++) clusterMapKeys.add("node-" + i); for (int i = 0; i < M; i++) nodes.add("node-" + i); String leftNode = "node-0"; // Warm up for (int r = 0; r < 10; r++) { simulateNodeLeftDefect(clusterMapKeys, nodes, leftNode); simulateNodeLeftFixed(clusterMapKeys, nodes, leftNode); } long t0 = System.nanoTime(); for (int r = 0; r < REPS; r++) simulateNodeLeftDefect(clusterMapKeys, nodes, leftNode); long defectNs = System.nanoTime() - t0; long t1 = System.nanoTime(); for (int r = 0; r < REPS; r++) simulateNodeLeftFixed(clusterMapKeys, nodes, leftNode); long fixedNs = System.nanoTime() - t1; double ratio = (double) defectNs / fixedNs; System.out.printf( "vertx-0001 nodeLeft timing: defect=%.2fms fixed=%.2fms ratio=%.1fx%n", defectNs / 1e6, fixedNs / 1e6, ratio ); if (ratio < 5.0) { throw new AssertionError( "vertx-0001: expected wall-clock ratio >= 5x at N=M=500, got " + ratio); } System.out.println("vertx-0001 timing: PASS"); } // Real simulation (no op counting) for timing test static int simulateNodeLeftDefect(List clusterMapKeys, List nodes, String leftNodeID) { int failovers = 0; for (String key : clusterMapKeys) { if (!leftNodeID.equals(key) && !nodes.contains(key)) { failovers++; } } return failovers; } static int simulateNodeLeftFixed(List clusterMapKeys, List nodes, String leftNodeID) { Set nodeSet = new HashSet<>(nodes); int failovers = 0; for (String key : clusterMapKeys) { if (!leftNodeID.equals(key) && !nodeSet.contains(key)) { failovers++; } } return failovers; } // ----------------------------------------------------------------------- // vertx-0001 test C — correctness // ----------------------------------------------------------------------- static void testVertx0001_correctness() { List nodes = Arrays.asList("alpha", "beta", "gamma", "delta"); // HashSet must agree with List for all membership queries Set nodeSet = new HashSet<>(nodes); for (String n : nodes) { if (nodes.contains(n) != nodeSet.contains(n)) { throw new AssertionError("Membership mismatch for: " + n); } } if (nodes.contains("omega") != nodeSet.contains("omega")) { throw new AssertionError("Membership mismatch for omega"); } // nodeLeft logic must produce identical failover sets List clusterMapKeys = Arrays.asList("alpha", "beta", "gamma", "delta", "epsilon"); String leftNode = "alpha"; // nodes = {alpha, beta, gamma, delta} clusterMap has "epsilon" extra List defectiveFailovers = new ArrayList<>(); for (String key : clusterMapKeys) { if (!leftNode.equals(key) && !nodes.contains(key)) { defectiveFailovers.add(key); } } Set fixedNodeSet = new HashSet<>(nodes); List fixedFailovers = new ArrayList<>(); for (String key : clusterMapKeys) { if (!leftNode.equals(key) && !fixedNodeSet.contains(key)) { fixedFailovers.add(key); } } if (!defectiveFailovers.equals(fixedFailovers)) { throw new AssertionError( "Failover lists differ: defect=" + defectiveFailovers + " fixed=" + fixedFailovers); } if (fixedFailovers.size() != 1 || !fixedFailovers.get(0).equals("epsilon")) { throw new AssertionError("Expected [epsilon], got: " + fixedFailovers); } // addHaInfoIfLost pattern: single .contains() call on list vs set String targetNodeId = "delta"; boolean listResult = nodes.contains(targetNodeId); boolean setResult = nodeSet.contains(targetNodeId); if (listResult != setResult) { throw new AssertionError("addHaInfoIfLost: list/set mismatch for " + targetNodeId); } System.out.println("vertx-0001 correctness: PASS"); } // ----------------------------------------------------------------------- // Main // ----------------------------------------------------------------------- public static void main(String[] args) { testVertx0001_correctness(); testVertx0001_opCount(); testVertx0001_timing(); System.out.println("ALL PASS"); } }