import java.util.*; /** * CWE-407 unit test — NATS Server nats-server-0002 * * server/reload.go diffRoutes() * Two nested O(R) loops compare every old URL against every new URL. * Complexity: O(R²) where R = number of route URLs. * Large NATS clusters can carry hundreds of route URLs; config-reload * (triggered by SIGHUP or file watcher) executes diffRoutes on every reload. * * Fix: build map[string]struct{} keyed on url.String() for each list, * then two single-pass O(R) sweeps replace the double loop → O(R) total. */ public class NatsServerTest { // ---- defect simulation -------------------------------------------------- /** O(R²): for each old URL, scan all new URLs for a match. */ static void diffRoutes_quadratic(List old, List newList, List add, List remove) { outer: for (String oldRoute : old) { for (String newRoute : newList) { if (oldRoute.equals(newRoute)) { continue outer; } } remove.add(oldRoute); } outer: for (String newRoute : newList) { for (String oldRoute : old) { if (newRoute.equals(oldRoute)) { continue outer; } } add.add(newRoute); } } // ---- fix simulation ----------------------------------------------------- /** O(R): build HashSets, then single-pass each list. */ static void diffRoutes_linear(List old, List newList, List add, List remove) { Set newSet = new HashSet<>(newList); Set oldSet = new HashSet<>(old); for (String o : old) { if (!newSet.contains(o)) remove.add(o); } for (String n : newList) { if (!oldSet.contains(n)) add.add(n); } } // ---- helpers ------------------------------------------------------------ static List makeRoutes(int base, int count) { List urls = new ArrayList<>(count); for (int i = 0; i < count; i++) { urls.add("nats://node-" + (base + i) + ".cluster.local:6222"); } return urls; } // ---- tests -------------------------------------------------------------- static void testCorrectnessSmall() { List old = Arrays.asList( "nats://a:6222", "nats://b:6222", "nats://c:6222" ); List newList = Arrays.asList( "nats://b:6222", "nats://c:6222", "nats://d:6222" ); List addQ = new ArrayList<>(), removeQ = new ArrayList<>(); List addL = new ArrayList<>(), removeL = new ArrayList<>(); diffRoutes_quadratic(old, newList, addQ, removeQ); diffRoutes_linear(old, newList, addL, removeL); Collections.sort(addQ); Collections.sort(addL); Collections.sort(removeQ); Collections.sort(removeL); assert addQ.equals(addL) : "add mismatch: " + addQ + " vs " + addL; assert removeQ.equals(removeL) : "remove mismatch: " + removeQ + " vs " + removeL; assert addL.equals(Arrays.asList("nats://d:6222")) : "expected d to be added"; assert removeL.equals(Arrays.asList("nats://a:6222")) : "expected a to be removed"; System.out.println("PASS testCorrectnessSmall"); } static void testEmptyOld() { List old = Collections.emptyList(); List newList = Arrays.asList("nats://x:6222", "nats://y:6222"); List addQ = new ArrayList<>(), removeQ = new ArrayList<>(); List addL = new ArrayList<>(), removeL = new ArrayList<>(); diffRoutes_quadratic(old, newList, addQ, removeQ); diffRoutes_linear(old, newList, addL, removeL); assert addQ.equals(addL) && removeQ.equals(removeL) : "empty-old mismatch"; System.out.println("PASS testEmptyOld"); } static void testEmptyNew() { List old = Arrays.asList("nats://x:6222", "nats://y:6222"); List newList = Collections.emptyList(); List addQ = new ArrayList<>(), removeQ = new ArrayList<>(); List addL = new ArrayList<>(), removeL = new ArrayList<>(); diffRoutes_quadratic(old, newList, addQ, removeQ); diffRoutes_linear(old, newList, addL, removeL); assert addQ.equals(addL) && removeQ.equals(removeL) : "empty-new mismatch"; System.out.println("PASS testEmptyNew"); } static void testIdenticalLists() { List old = Arrays.asList("nats://a:6222", "nats://b:6222"); List newList = new ArrayList<>(old); List addQ = new ArrayList<>(), removeQ = new ArrayList<>(); List addL = new ArrayList<>(), removeL = new ArrayList<>(); diffRoutes_quadratic(old, newList, addQ, removeQ); diffRoutes_linear(old, newList, addL, removeL); assert addQ.isEmpty() && removeQ.isEmpty() : "identical: unexpected diff (quadratic)"; assert addL.isEmpty() && removeL.isEmpty() : "identical: unexpected diff (linear)"; System.out.println("PASS testIdenticalLists"); } static void testBenchmarkRatio() { // Simulate a large NATS cluster: R=300 route URLs, 1 change on reload int R = 300; List old = makeRoutes(0, R); List newList = makeRoutes(1, R); // shifted by 1 → 1 remove, 1 add int ITERS = 200; long t0 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { List add = new ArrayList<>(), remove = new ArrayList<>(); diffRoutes_quadratic(old, newList, add, remove); } long quadraticNs = System.nanoTime() - t0; long t1 = System.nanoTime(); for (int i = 0; i < ITERS; i++) { List add = new ArrayList<>(), remove = new ArrayList<>(); diffRoutes_linear(old, newList, add, remove); } long linearNs = System.nanoTime() - t1; double ratio = (double) quadraticNs / linearNs; System.out.printf("BENCH R=%d iters=%d quadratic=%.1fms linear=%.1fms ratio=%.1fx%n", R, ITERS, quadraticNs / 1e6, linearNs / 1e6, ratio); assert ratio >= 2.0 : "Expected >=2x speedup at R=" + R + ", got " + ratio; System.out.println("PASS testBenchmarkRatio"); } public static void main(String[] args) { testCorrectnessSmall(); testEmptyOld(); testEmptyNew(); testIdenticalLists(); testBenchmarkRatio(); System.out.println("ALL PASS"); } }