java-topology/defects/consul/unit/ConsulTest.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

201 lines
7.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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");
}
}