package unit; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; /** * RayTest * * Models CWE-407 defects in ray-project/ray: * * RAY-001 (MEDIUM) — local/node_provider.py ClusterState.__init__ and * OnPremCoordinatorState.__init__: `list_of_node_ips = list(...)` followed * by `for worker_ip in workers: if worker_ip not in list_of_node_ips`. * O(N) linear scan per node × O(N) nodes = O(N²) during cluster reconciliation. * Fix: set(worker_ips) for O(1) average membership. * * All measurements are instrumented operation counts, not wall-clock timing. */ public class RayTest { // ----------------------------------------------------------------------- // RAY-001 modelling helpers // // Defective: ArrayList (list_of_node_ips) membership — O(N) scan per node // Fixed: HashSet (node_ip_set) membership — O(1) average per node // // Returns total membership-test cost across all nodes. // ----------------------------------------------------------------------- /** * Models ClusterState.__init__ reconciliation — defective path. * * list_of_node_ips is built as a List, then each of the N tracked workers * is tested against it with `not in`, costing O(N) per check → O(N²) total. */ static long ray001Defective(int numWorkers, int numNodeIps) { // Build list_of_node_ips (contains the valid IPs) ArrayList listOfNodeIps = new ArrayList<>(); for (int i = 0; i < numNodeIps; i++) { listOfNodeIps.add(i); } // workers dict: tracked workers include some not in list_of_node_ips // (half valid, half stale) ArrayList workers = new ArrayList<>(); for (int i = 0; i < numWorkers; i++) { workers.add(i); // some overlap with listOfNodeIps, some don't } long comparisons = 0; for (int workerIp : workers) { // model `if worker_ip not in list_of_node_ips` — O(N) scan comparisons += listOfNodeIps.size(); // worst-case linear scan cost // actual check (for correctness) if (!listOfNodeIps.contains(workerIp)) { // would del workers[worker_ip] } } return comparisons; } /** * Models ClusterState.__init__ reconciliation — fixed path. * * node_ip_set is a HashSet; each membership test is O(1). */ static long ray001Fixed(int numWorkers, int numNodeIps) { HashSet nodeIpSet = new HashSet<>(); for (int i = 0; i < numNodeIps; i++) { nodeIpSet.add(i); } ArrayList workers = new ArrayList<>(); for (int i = 0; i < numWorkers; i++) { workers.add(i); } long lookups = 0; for (int workerIp : workers) { // model `if worker_ip not in node_ip_set` — O(1) lookups++; // one hash lookup per worker if (!nodeIpSet.contains(workerIp)) { // would del workers[worker_ip] } } return lookups; } // ----------------------------------------------------------------------- // Test 1 — RAY-001: defective O(N²) vs fixed O(N) at N=200 nodes // ----------------------------------------------------------------------- static void test1_ray001_quadraticVsLinear() { int N = 200; long defectCost = ray001Defective(N, N); long fixedCost = ray001Fixed(N, N); System.out.printf("test1 RAY-001: N=%d nodes defect=%d fixed=%d%n", N, defectCost, fixedCost); assert defectCost > fixedCost : "defect must be more expensive than fix at N=" + N; // defective: each of N workers scans list of size N → N*N total long expectedDefect = (long) N * N; assert defectCost == expectedDefect : "expected defect cost=" + expectedDefect + " got=" + defectCost; double ratio = (double) defectCost / Math.max(1, fixedCost); assert ratio > 10.0 : "expected ratio>10x for N=" + N + ", got " + ratio; } // ----------------------------------------------------------------------- // Test 2 — RAY-001: scaling — doubling N grows defect quadratically // ----------------------------------------------------------------------- static void test2_ray001_scalingGrowth() { int N1 = 100; int N2 = 200; // double N long d1 = ray001Defective(N1, N1); long d2 = ray001Defective(N2, N2); long f1 = ray001Fixed(N1, N1); long f2 = ray001Fixed(N2, N2); double defectGrowth = (double) d2 / Math.max(1, d1); double fixedGrowth = (double) f2 / Math.max(1, f1); System.out.printf("test2 RAY-001: N1=%d N2=%d defect_growth=%.2fx fixed_growth=%.2fx%n", N1, N2, defectGrowth, fixedGrowth); // defect should grow ~4x when N doubles (O(N²)) assert defectGrowth > 3.5 : "defect should grow ~4x when N doubles (O(N²)), got " + defectGrowth; // fixed should grow ~2x when N doubles (O(N)) assert fixedGrowth >= 1.8 && fixedGrowth <= 2.2 : "fixed should grow ~2x when N doubles (O(N)), got " + fixedGrowth; assert defectGrowth > fixedGrowth : "defect growth must exceed fixed growth"; } // ----------------------------------------------------------------------- // Test 3 — RAY-001: OnPremCoordinatorState same pattern, N=300 nodes // ----------------------------------------------------------------------- static void test3_ray001_onPremCoordinator() { // Models OnPremCoordinatorState.__init__ — same list vs set pattern // for node_ip in list(nodes): if node_ip not in list_of_node_ips int N = 300; long defectCost = ray001Defective(N, N); long fixedCost = ray001Fixed(N, N); double ratio = (double) defectCost / Math.max(1, fixedCost); System.out.printf("test3 RAY-001 OnPrem: N=%d defect=%d fixed=%d ratio=%.0fx%n", N, defectCost, fixedCost, ratio); assert defectCost > fixedCost : "defect must be more expensive at N=" + N; assert ratio > 50.0 : "expected ratio>50x at N=300, got " + ratio; } // ----------------------------------------------------------------------- // Main // ----------------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== RayTest ==="); System.out.println("Modelling CWE-407: RAY-001 local node_provider list_of_node_ips O(N²) scan"); System.out.println(); test1_ray001_quadraticVsLinear(); System.out.println(" PASS test1_ray001_quadraticVsLinear"); test2_ray001_scalingGrowth(); System.out.println(" PASS test2_ray001_scalingGrowth"); test3_ray001_onPremCoordinator(); System.out.println(" PASS test3_ray001_onPremCoordinator"); System.out.println(); System.out.println("3/3 PASS"); } }