186 lines
7.1 KiB
Java
186 lines
7.1 KiB
Java
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<Integer> 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<Integer> 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<Integer> nodeIpSet = new HashSet<>();
|
||
for (int i = 0; i < numNodeIps; i++) {
|
||
nodeIpSet.add(i);
|
||
}
|
||
|
||
ArrayList<Integer> 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");
|
||
}
|
||
}
|