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)
201 lines
7.9 KiB
Java
201 lines
7.9 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 unit test — Consul consul-0001
|
||
*
|
||
* agent/ui_endpoint.go summarizeServices()
|
||
*
|
||
* For each service instance in the catalog dump the function deduplicates:
|
||
* (a) Node names → sum.Nodes []string — linear scan O(N) per instance
|
||
* (b) Service tags → sum.Tags []string — linear scan O(T) per tag
|
||
*
|
||
* When iterating over I instances of S services (outer dump loop), the tag
|
||
* dedup becomes O(S×I×T²): for each instance, for each tag, scan the already-
|
||
* accumulated sum.Tags slice. In large Consul deployments (S=500 services,
|
||
* I=20 instances each, T=30 tags) this is millions of comparisons per API call.
|
||
*
|
||
* Fix: add nodesSet map[string]struct{} and tagsSet map[string]struct{} fields
|
||
* to ServiceSummary. O(1) set-membership replaces O(N)/O(T) slice scan.
|
||
* Total complexity drops from O(S×I×T²) → O(S×I×T).
|
||
*/
|
||
public class ConsulTest {
|
||
|
||
// ---- defect simulation --------------------------------------------------
|
||
|
||
/** Simulate the defect: sum.Tags is a List with linear-scan dedup. */
|
||
static List<String> summarizeTags_quadratic(List<List<String>> instanceTagLists) {
|
||
List<String> tags = new ArrayList<>();
|
||
for (List<String> instanceTags : instanceTagLists) {
|
||
for (String tag : instanceTags) {
|
||
boolean found = false;
|
||
for (String existing : tags) { // O(T) linear scan — the defect
|
||
if (existing.equals(tag)) { found = true; break; }
|
||
}
|
||
if (!found) tags.add(tag);
|
||
}
|
||
}
|
||
return tags;
|
||
}
|
||
|
||
/** Simulate the fix: use a HashSet for O(1) membership. */
|
||
static List<String> summarizeTags_linear(List<List<String>> instanceTagLists) {
|
||
Set<String> tagsSet = new LinkedHashSet<>(); // preserves insertion order
|
||
for (List<String> instanceTags : instanceTagLists) {
|
||
tagsSet.addAll(instanceTags);
|
||
}
|
||
return new ArrayList<>(tagsSet);
|
||
}
|
||
|
||
// Node dedup — same pattern
|
||
static List<String> deduplicateNodes_quadratic(List<String> nodeNames) {
|
||
List<String> nodes = new ArrayList<>();
|
||
for (String node : nodeNames) {
|
||
boolean found = false;
|
||
for (String existing : nodes) {
|
||
if (existing.equals(node)) { found = true; break; }
|
||
}
|
||
if (!found) nodes.add(node);
|
||
}
|
||
return nodes;
|
||
}
|
||
|
||
static List<String> deduplicateNodes_linear(List<String> nodeNames) {
|
||
Set<String> seen = new LinkedHashSet<>(nodeNames);
|
||
return new ArrayList<>(seen);
|
||
}
|
||
|
||
// ---- helpers ------------------------------------------------------------
|
||
|
||
/** Build instanceTagLists: I instances each sharing the same T tags. */
|
||
static List<List<String>> makeInstanceTags(int instances, int tagsPerInstance) {
|
||
List<String> tags = new ArrayList<>();
|
||
for (int t = 0; t < tagsPerInstance; t++) {
|
||
tags.add("tag-" + t);
|
||
}
|
||
List<List<String>> result = new ArrayList<>();
|
||
for (int i = 0; i < instances; i++) {
|
||
result.add(new ArrayList<>(tags)); // each instance has all tags
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// ---- tests --------------------------------------------------------------
|
||
|
||
static void testTagCorrectnessSmall() {
|
||
List<List<String>> instanceTags = Arrays.asList(
|
||
Arrays.asList("web", "v1", "prod"),
|
||
Arrays.asList("v1", "prod", "dc1"),
|
||
Arrays.asList("web", "dc1", "v2")
|
||
);
|
||
List<String> q = summarizeTags_quadratic(instanceTags);
|
||
List<String> l = summarizeTags_linear(instanceTags);
|
||
Collections.sort(q); Collections.sort(l);
|
||
assert q.equals(l) : "tag mismatch: " + q + " vs " + l;
|
||
System.out.println("PASS testTagCorrectnessSmall");
|
||
}
|
||
|
||
static void testNodeDeduplicateCorrectness() {
|
||
List<String> nodeNames = Arrays.asList(
|
||
"node-1", "node-2", "node-1", "node-3", "node-2", "node-4"
|
||
);
|
||
List<String> q = deduplicateNodes_quadratic(nodeNames);
|
||
List<String> l = deduplicateNodes_linear(nodeNames);
|
||
Collections.sort(q); Collections.sort(l);
|
||
assert q.equals(l) : "node dedup mismatch: " + q + " vs " + l;
|
||
assert q.size() == 4 : "expected 4 unique nodes, got " + q.size();
|
||
System.out.println("PASS testNodeDeduplicateCorrectness");
|
||
}
|
||
|
||
static void testEmptyInstanceList() {
|
||
List<List<String>> instanceTags = Collections.emptyList();
|
||
List<String> q = summarizeTags_quadratic(instanceTags);
|
||
List<String> l = summarizeTags_linear(instanceTags);
|
||
assert q.equals(l) && q.isEmpty() : "empty instance list should yield empty tags";
|
||
System.out.println("PASS testEmptyInstanceList");
|
||
}
|
||
|
||
static void testSingleInstanceSingleTag() {
|
||
List<List<String>> instanceTags = Collections.singletonList(
|
||
Collections.singletonList("only-tag")
|
||
);
|
||
List<String> q = summarizeTags_quadratic(instanceTags);
|
||
List<String> l = summarizeTags_linear(instanceTags);
|
||
assert q.equals(l) && q.equals(Collections.singletonList("only-tag")) :
|
||
"single tag mismatch";
|
||
System.out.println("PASS testSingleInstanceSingleTag");
|
||
}
|
||
|
||
static void testBenchmarkTagDedup() {
|
||
// Simulate: 20 instances each carrying 40 tags (all the same → max dedup pressure)
|
||
int INSTANCES = 20;
|
||
int TAGS = 40;
|
||
int ITERS = 300;
|
||
List<List<String>> instanceTags = makeInstanceTags(INSTANCES, TAGS);
|
||
|
||
long t0 = System.nanoTime();
|
||
for (int i = 0; i < ITERS; i++) {
|
||
summarizeTags_quadratic(instanceTags);
|
||
}
|
||
long quadraticNs = System.nanoTime() - t0;
|
||
|
||
long t1 = System.nanoTime();
|
||
for (int i = 0; i < ITERS; i++) {
|
||
summarizeTags_linear(instanceTags);
|
||
}
|
||
long linearNs = System.nanoTime() - t1;
|
||
|
||
double ratio = (double) quadraticNs / linearNs;
|
||
System.out.printf(
|
||
"BENCH instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n",
|
||
INSTANCES, TAGS, ITERS,
|
||
quadraticNs / 1e6, linearNs / 1e6, ratio);
|
||
assert ratio >= 2.0 :
|
||
"Expected >=2x speedup for I=" + INSTANCES + " T=" + TAGS + ", got " + ratio;
|
||
System.out.println("PASS testBenchmarkTagDedup");
|
||
}
|
||
|
||
static void testBenchmarkLargeDeployment() {
|
||
// Simulate a large Consul deployment: 50 instances × 30 tags
|
||
int INSTANCES = 50;
|
||
int TAGS = 30;
|
||
int ITERS = 500;
|
||
List<List<String>> instanceTags = makeInstanceTags(INSTANCES, TAGS);
|
||
|
||
// JIT warmup
|
||
for (int i = 0; i < 200; i++) {
|
||
summarizeTags_quadratic(instanceTags);
|
||
summarizeTags_linear(instanceTags);
|
||
}
|
||
|
||
long t0 = System.nanoTime();
|
||
for (int i = 0; i < ITERS; i++) {
|
||
summarizeTags_quadratic(instanceTags);
|
||
}
|
||
long quadraticNs = System.nanoTime() - t0;
|
||
|
||
long t1 = System.nanoTime();
|
||
for (int i = 0; i < ITERS; i++) {
|
||
summarizeTags_linear(instanceTags);
|
||
}
|
||
long linearNs = System.nanoTime() - t1;
|
||
|
||
double ratio = (double) quadraticNs / linearNs;
|
||
System.out.printf(
|
||
"BENCH large instances=%d tags=%d iters=%d quadratic=%.2fms linear=%.2fms ratio=%.1fx%n",
|
||
INSTANCES, TAGS, ITERS,
|
||
quadraticNs / 1e6, linearNs / 1e6, ratio);
|
||
assert ratio >= 2.0 :
|
||
"Expected >=2x speedup for large deployment, got " + ratio;
|
||
System.out.println("PASS testBenchmarkLargeDeployment");
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
testTagCorrectnessSmall();
|
||
testNodeDeduplicateCorrectness();
|
||
testEmptyInstanceList();
|
||
testSingleInstanceSingleTag();
|
||
testBenchmarkTagDedup();
|
||
testBenchmarkLargeDeployment();
|
||
System.out.println("ALL PASS");
|
||
}
|
||
}
|