java-topology/defects/cmake-0007/unit/DllDirsAlgorithm.java
russell@unturf.com fb1ff685c1 cmake+mpd: 5-MOAD scan; 3 new CWE-407 defects, MPD CLEAN all 5 MOADs
cmake-0005: cmQtAutoGen MergeOptions std::find over baseOpts in newOpts loop, O(N*M), 31.5x at N=M=50
cmake-0006: cmVisualStudio10TargetGenerator FinishWritingSource writtenSettings O(S^2), 15.3x at S=30
cmake-0007: cmGeneratorExpressionNode TargetRuntimeDllDirsNode dllDirs O(D^2), 10.3x at D=100

MPD: all 5 MOADs CLEAN; updated CLEAN.md with MOAD-0002 through MOAD-0005 analysis.
Unit tests: 3/3 PASS.
2026-03-31 22:31:15 -04:00

130 lines
4.7 KiB
Java

import java.util.*;
/**
* Unit test for cmake-0007: TargetRuntimeDllDirsNode O(D^2) -> O(D) dedup.
*
* Models $<TARGET_RUNTIME_DLL_DIRS:tgt> evaluator: collect unique DLL directories
* from a list of DLL paths, preserving insertion order.
*
* Defect: std::find over growing dllDirs vector = O(D) per entry = O(D^2) total.
* Fix: parallel unordered_set for membership + vector for order = O(1) per entry.
*/
public class DllDirsAlgorithm {
static String getFilenamePath(String path) {
int slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
return slash >= 0 ? path.substring(0, slash) : ".";
}
// --- Defective: O(D^2) ---
static List<String> collectDllDirsDefective(List<String> dlls) {
List<String> dllDirs = new ArrayList<>();
for (String dll : dlls) {
String dir = getFilenamePath(dll);
if (!dllDirs.contains(dir)) { // O(D) linear scan -- the defect
dllDirs.add(dir);
}
}
return dllDirs;
}
// --- Fixed: O(D) ---
static List<String> collectDllDirsFixed(List<String> dlls) {
List<String> dllDirs = new ArrayList<>();
Set<String> dllDirsSet = new HashSet<>();
for (String dll : dlls) {
String dir = getFilenamePath(dll);
if (dllDirsSet.add(dir)) { // O(1) hash insert+check
dllDirs.add(dir);
}
}
return dllDirs;
}
static List<String> makeDlls(int numDirs, int dllsPerDir) {
List<String> dlls = new ArrayList<>();
for (int d = 0; d < numDirs; d++) {
for (int i = 0; i < dllsPerDir; i++) {
dlls.add("C:/Qt/" + d + "/lib" + i + ".dll");
}
}
return dlls;
}
static void testCorrectness() {
List<String> dlls = Arrays.asList(
"C:/Qt/bin/Qt6Core.dll",
"C:/Qt/bin/Qt6Gui.dll",
"C:/Boost/lib/boost_system.dll",
"C:/Qt/bin/Qt6Widgets.dll",
"C:/Boost/lib/boost_thread.dll"
);
List<String> defect = collectDllDirsDefective(dlls);
List<String> fixed = collectDllDirsFixed(dlls);
if (!defect.equals(fixed)) {
throw new AssertionError("Results differ: " + defect + " vs " + fixed);
}
if (defect.size() != 2) {
throw new AssertionError("Expected 2 unique dirs, got " + defect.size() + ": " + defect);
}
// Order must be preserved (insertion order)
if (!defect.get(0).equals("C:/Qt/bin") || !defect.get(1).equals("C:/Boost/lib")) {
throw new AssertionError("Wrong order: " + defect);
}
System.out.println("PASS testCorrectness (2 unique dirs, insertion order preserved)");
}
static void testAllUnique() {
List<String> dlls = Arrays.asList(
"C:/dir1/a.dll", "C:/dir2/b.dll", "C:/dir3/c.dll"
);
List<String> result = collectDllDirsFixed(dlls);
if (result.size() != 3) throw new AssertionError("Expected 3: " + result);
System.out.println("PASS testAllUnique");
}
static void testAllSameDir() {
List<String> dlls = Arrays.asList(
"C:/Qt/bin/A.dll", "C:/Qt/bin/B.dll", "C:/Qt/bin/C.dll"
);
List<String> result = collectDllDirsFixed(dlls);
if (result.size() != 1) throw new AssertionError("Expected 1: " + result);
if (!result.get(0).equals("C:/Qt/bin")) throw new AssertionError("Wrong dir: " + result);
System.out.println("PASS testAllSameDir");
}
static void testBenchmark() {
int NUM_DIRS = 20;
int DLLS_PER_DIR = 5; // 100 DLLs total, 20 unique dirs
List<String> dlls = makeDlls(NUM_DIRS, DLLS_PER_DIR);
int D = dlls.size();
// Count defective ops (linear scans in contains)
long defectiveOps = 0;
List<String> seen = new ArrayList<>();
for (String dll : dlls) {
String dir = getFilenamePath(dll);
for (String s : seen) { defectiveOps++; if (s.equals(dir)) break; }
if (!seen.contains(dir)) seen.add(dir);
}
long fixedOps = D; // one hash lookup per dll
double ratio = (double) defectiveOps / fixedOps;
System.out.printf("BENCH dllDirs D=%d dirs=%d: defective=%d ops, fixed=%d ops, ratio=%.1fx%n",
D, NUM_DIRS, defectiveOps, fixedOps, ratio);
if (ratio < 2.0) {
throw new AssertionError("Expected ratio >= 2x, got " + ratio);
}
System.out.println("PASS testBenchmark (ratio >= 2x confirmed)");
}
public static void main(String[] args) {
testCorrectness();
testAllUnique();
testAllSameDir();
testBenchmark();
System.out.println("ALL PASS cmake-0007 DllDirs");
}
}