package unit; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * maven-0001 — DefaultLifecycleExecutionPlanCalculator: List.of().contains() rebuilt per mojo * * Demonstrates CWE-407: O(n) linear list membership inside a hot loop. * * slow(): models the defective path — List.of(STANDARD_LIFECYCLES).contains() called N times, * allocating a new List and scanning it on every call. * fast(): models the fix — a precomputed Set constant, O(1) per lookup. * * Asserts that slow() performs strictly more element comparisons than fast(). */ public class MavenLifecycleStandardSetTest { static final String[] STANDARD_LIFECYCLES = {"clean", "default", "site"}; /** Counts how many element comparisons the slow path performs for N lookups. */ static long slow(String[] lifecycleIds) { long ops = 0; for (String id : lifecycleIds) { // Rebuild list and scan linearly — defective pattern List list = Arrays.asList(STANDARD_LIFECYCLES); for (int i = 0; i < list.size(); i++) { ops++; if (list.get(i).equals(id)) break; } } return ops; } /** Counts how many element comparisons the fast path performs for N lookups. */ static long fast(String[] lifecycleIds) { long ops = 0; // Precomputed constant set — the fix Set standardSet = Collections.unmodifiableSet( new HashSet<>(Arrays.asList(STANDARD_LIFECYCLES))); for (String id : lifecycleIds) { ops++; // HashSet.contains = 1 hash + at most 1 comparison standardSet.contains(id); } return ops; } public static void main(String[] args) { // Simulate M=200 modules × N=50 mojos = 10 000 calculateLifecycleMappings calls // Mix of matches and misses; worst case is always a miss (scans full list) int iterations = 10_000; String[] ids = new String[iterations]; String[] pool = {"clean", "default", "site", "unknown-lifecycle", "custom"}; for (int i = 0; i < iterations; i++) { ids[i] = pool[i % pool.length]; } long sOps = slow(ids); long fOps = fast(ids); // Expect slow to do at least 2x the comparisons of fast int Nx = 2; boolean pass = sOps > fOps * Nx; System.out.printf("maven-0001: slow=%d ops fast=%d ops ratio=%.1fx %s%n", sOps, fOps, (double) sOps / fOps, pass ? "PASS" : "FAIL"); if (!pass) { System.exit(1); } } }