import java.util.*; /** * Unit tests for DOSBox-X CWE-407 defects in overlay drive caching. * * dosbox-x-0001: add_DOSname_to_cache linear vector dedup O(N^2) * dosbox-x-0002: is_deleted_file linear vector scan O(N) per call * * Models the C++ std::vector with strcasecmp dedup pattern * and demonstrates the fix using HashSet with case-insensitive semantics. */ public class DosboxXOverlayCacheTest { // --------------------------------------------------------------- // dosbox-x-0001: DOSnames_cache vector dedup // --------------------------------------------------------------- /** DEFECTIVE: O(N) linear scan per insertion → O(N^2) total */ static List addDOSname_defective(List cache, String name) { for (String entry : cache) { if (entry.equalsIgnoreCase(name)) return cache; } cache.add(name); return cache; } /** FIXED: O(1) amortized insertion with case-insensitive HashSet */ static Set addDOSname_fixed(Set cache, String name) { cache.add(name.toUpperCase()); return cache; } // --------------------------------------------------------------- // dosbox-x-0002: is_deleted_file linear vector scan // --------------------------------------------------------------- /** DEFECTIVE: O(N) linear scan through deleted_files_in_base */ static boolean isDeletedFile_defective(List deletedFiles, String name) { for (String f : deletedFiles) { if (f.equalsIgnoreCase(name)) return true; } return false; } /** FIXED: O(1) hash lookup */ static boolean isDeletedFile_fixed(Set deletedFiles, String name) { return deletedFiles.contains(name.toUpperCase()); } public static void main(String[] args) { int N = 5000; int passed = 0; int failed = 0; // --- Test 1: dosbox-x-0001 correctness --- { List defectCache = new ArrayList<>(); Set fixedCache = new HashSet<>(); for (int i = 0; i < 100; i++) { String name = "FILE" + i + ".TXT"; addDOSname_defective(defectCache, name); addDOSname_defective(defectCache, name.toLowerCase()); // case-insensitive dedup addDOSname_fixed(fixedCache, name); addDOSname_fixed(fixedCache, name.toLowerCase()); } boolean ok = defectCache.size() == 100 && fixedCache.size() == 100; System.out.println((ok ? "PASS" : "FAIL") + " dosbox-x-0001 correctness: dedup 100 names (case-insensitive)"); if (ok) passed++; else failed++; } // --- Test 2: dosbox-x-0001 performance --- { List defectCache = new ArrayList<>(); Set fixedCache = new HashSet<>(); long t0 = System.nanoTime(); for (int i = 0; i < N; i++) { addDOSname_defective(defectCache, "NAME" + i + ".DOS"); } long defectNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int i = 0; i < N; i++) { addDOSname_fixed(fixedCache, "NAME" + i + ".DOS"); } long fixedNs = System.nanoTime() - t0; double ratio = (double) defectNs / Math.max(fixedNs, 1); boolean ok = ratio > 5.0; System.out.printf("%s dosbox-x-0001 performance: N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", ok ? "PASS" : "FAIL", N, defectNs / 1e6, fixedNs / 1e6, ratio); if (ok) passed++; else failed++; } // --- Test 3: dosbox-x-0002 correctness --- { List defectList = new ArrayList<>(); Set fixedSet = new HashSet<>(); for (int i = 0; i < 100; i++) { String name = "DELETED" + i + ".TMP"; defectList.add(name); fixedSet.add(name.toUpperCase()); } boolean allMatch = true; for (int i = 0; i < 100; i++) { String query = "deleted" + i + ".tmp"; // lowercase query boolean d = isDeletedFile_defective(defectList, query); boolean f = isDeletedFile_fixed(fixedSet, query); if (d != f) { allMatch = false; break; } } // Also check non-existent boolean dNeg = isDeletedFile_defective(defectList, "NOTHERE.TXT"); boolean fNeg = isDeletedFile_fixed(fixedSet, "NOTHERE.TXT"); boolean ok = allMatch && !dNeg && !fNeg; System.out.println((ok ? "PASS" : "FAIL") + " dosbox-x-0002 correctness: deleted file lookup"); if (ok) passed++; else failed++; } // --- Test 4: dosbox-x-0002 performance --- { List defectList = new ArrayList<>(); Set fixedSet = new HashSet<>(); for (int i = 0; i < N; i++) { String name = "BASE\\OVERLAY\\FILE" + i + ".DAT"; defectList.add(name); fixedSet.add(name.toUpperCase()); } long t0 = System.nanoTime(); for (int i = 0; i < N; i++) { isDeletedFile_defective(defectList, "BASE\\OVERLAY\\FILE" + (N - 1 - i) + ".DAT"); } long defectNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int i = 0; i < N; i++) { isDeletedFile_fixed(fixedSet, "BASE\\OVERLAY\\FILE" + (N - 1 - i) + ".DAT"); } long fixedNs = System.nanoTime() - t0; double ratio = (double) defectNs / Math.max(fixedNs, 1); boolean ok = ratio > 5.0; System.out.printf("%s dosbox-x-0002 performance: N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", ok ? "PASS" : "FAIL", N, defectNs / 1e6, fixedNs / 1e6, ratio); if (ok) passed++; else failed++; } System.out.printf("%n%d/%d tests passed%n", passed, passed + failed); if (failed > 0) System.exit(1); } }