package unit; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * CWE-407 unit test: cpython-0001 * * Models pkgutil.extend_path() portion deduplication. * * DEFECT: for each portion, scan the accumulator list with O(n) contains(). * Total cost: O(n²) for n unique portions. * * FIX: maintain a parallel HashSet for O(1) membership; list preserves order. * Total cost: O(n). * * Asserts: slowOps > fastOps * 10 at n=500 (actual ratio ≈ 250×). */ public class CpythonPkgutilTest { /** Mirrors the defective pkgutil.extend_path loop. Returns comparison count. */ static long slow(int n) { List path = new ArrayList<>(); long ops = 0; for (int i = 0; i < n; i++) { String portion = "/opt/pkg" + i + "/ns"; // O(path.size()) scan — mirrors `if portion not in path` boolean found = false; for (String existing : path) { ops++; if (existing.equals(portion)) { found = true; break; } } if (!found) { path.add(portion); } } return ops; } /** Mirrors the patched version: set for O(1) membership, list for order. */ static long fast(int n) { List path = new ArrayList<>(); Set pathSet = new HashSet<>(); long ops = 0; for (int i = 0; i < n; i++) { String portion = "/opt/pkg" + i + "/ns"; ops++; // one hash probe — mirrors `if portion not in path_set` if (!pathSet.contains(portion)) { path.add(portion); pathSet.add(portion); } } return ops; } public static void main(String[] args) { int passed = 0; int total = 0; // Test 1: slow is strictly more expensive than fast at n=100 { total++; int n = 100; long sOps = slow(n); long fOps = fast(n); // Expected: sOps ≈ n*(n-1)/2 ≈ 4950; fOps = n = 100 boolean ok = sOps > fOps * 10L; System.out.printf("Test 1 [n=100 slow=%d fast=%d ratio=%.1fx]: %s%n", sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 2: slow is at least 50× more expensive than fast at n=500 { total++; int n = 500; long sOps = slow(n); long fOps = fast(n); // Expected: sOps ≈ 125000; fOps = 500 boolean ok = sOps > fOps * 50L; System.out.printf("Test 2 [n=500 slow=%d fast=%d ratio=%.1fx]: %s%n", sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 3: slow is at least 200× more expensive than fast at n=1000 { total++; int n = 1000; long sOps = slow(n); long fOps = fast(n); // Expected: sOps ≈ 500000; fOps = 1000 boolean ok = sOps > fOps * 200L; System.out.printf("Test 3 [n=1000 slow=%d fast=%d ratio=%.1fx]: %s%n", sOps, fOps, (double) sOps / fOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 4: both produce identical result sets (correctness) { total++; int n = 200; List slowPath = new ArrayList<>(); List fastPath = new ArrayList<>(); Set fastSet = new HashSet<>(); for (int i = 0; i < n; i++) { String p = "/opt/pkg" + i + "/ns"; if (!slowPath.contains(p)) slowPath.add(p); if (!fastSet.contains(p)) { fastPath.add(p); fastSet.add(p); } } boolean ok = slowPath.equals(fastPath); System.out.printf("Test 4 [correctness n=200 equal=%b]: %s%n", ok, ok ? "PASS" : "FAIL"); if (ok) passed++; } System.out.printf("%d/%d PASS%n", passed, total); if (passed != total) System.exit(1); } }