java-topology/defects/nats-server/unit/NatsServerTest.java
russell@unturf.com 783e406633 pygame/libgdx: CWE-407 findings
pygame: CLEAN — runtime uses dict/set throughout (O(1) membership)
libgdx-0002: ModelBuilder.rebuildReferences Array.contains O(P*M) — patch
libgdx-0005: Stage.touchDragged touchFocuses.contains O(F^2) — patch
libgdx-0006: AfterAction.delegate currentActions.indexOf O(W*A) per frame — patch
unit: extend LibGDXTest to 7 tests, all PASS (50x-1971x speedup measured)
2026-03-30 09:13:34 -04:00

165 lines
6.7 KiB
Java

import java.util.*;
/**
* CWE-407 unit test — NATS Server nats-server-0001
*
* 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<String> old, List<String> newList,
List<String> add, List<String> 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<String> old, List<String> newList,
List<String> add, List<String> remove) {
Set<String> newSet = new HashSet<>(newList);
Set<String> 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<String> makeRoutes(int base, int count) {
List<String> 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<String> old = Arrays.asList(
"nats://a:6222", "nats://b:6222", "nats://c:6222"
);
List<String> newList = Arrays.asList(
"nats://b:6222", "nats://c:6222", "nats://d:6222"
);
List<String> addQ = new ArrayList<>(), removeQ = new ArrayList<>();
List<String> 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<String> old = Collections.emptyList();
List<String> newList = Arrays.asList("nats://x:6222", "nats://y:6222");
List<String> addQ = new ArrayList<>(), removeQ = new ArrayList<>();
List<String> 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<String> old = Arrays.asList("nats://x:6222", "nats://y:6222");
List<String> newList = Collections.emptyList();
List<String> addQ = new ArrayList<>(), removeQ = new ArrayList<>();
List<String> 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<String> old = Arrays.asList("nats://a:6222", "nats://b:6222");
List<String> newList = new ArrayList<>(old);
List<String> addQ = new ArrayList<>(), removeQ = new ArrayList<>();
List<String> 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<String> old = makeRoutes(0, R);
List<String> 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<String> 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<String> 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");
}
}