import java.util.*; /** * Unit test for thunderbird-0003: nsAutoSyncManager IndexOf() in loops O(N^2) * * Simulates the IMAP auto-sync folder queue management where IndexOf() is * called per folder to find queue positions during sync scheduling. * * Defective: O(N^2) - IndexOf per folder in update loop * Fixed: O(N) - HashMap for O(1) index lookups */ public class ThunderbirdAutoSyncManagerTest { // --- Defective: O(N^2) index lookup per folder --- static int autoUpdateFoldersDefective(List folders, List updateQ) { int updates = 0; for (String folder : folders) { int idx = updateQ.indexOf(folder); // O(N) linear scan if (idx >= 0) { updates++; // Simulate state check using index position } } return updates; } // --- Fixed: O(N) with pre-built index map --- static int autoUpdateFoldersFixed(List folders, Map updateQIndex) { int updates = 0; for (String folder : folders) { Integer idx = updateQIndex.get(folder); // O(1) hash lookup if (idx != null) { updates++; } } return updates; } // --- Defective: O(N^2) sibling chaining --- static List chainFoldersDefective(List queue, List priorityQ) { List chained = new ArrayList<>(); chained.add(priorityQ.get(0)); for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) { String candidate = priorityQ.get(pqIdx); for (int idx = 0; idx < chained.size(); idx++) { // Simulate IsSibling check (same server prefix) if (candidate.startsWith(chained.get(idx).split("/")[0])) { chained.add(candidate); break; } } } return chained; } // --- Fixed: O(N) sibling chaining with server set --- static List chainFoldersFixed(List queue, List priorityQ) { List chained = new ArrayList<>(); Set serverSet = new HashSet<>(); chained.add(priorityQ.get(0)); serverSet.add(priorityQ.get(0).split("/")[0]); for (int pqIdx = 1; pqIdx < priorityQ.size(); pqIdx++) { String candidate = priorityQ.get(pqIdx); String server = candidate.split("/")[0]; if (serverSet.contains(server)) { chained.add(candidate); } } return chained; } public static void main(String[] args) { System.out.println("=== thunderbird-0003: nsAutoSyncManager IndexOf O(N^2) ===\n"); // Correctness for autoUpdateFolders List folders = Arrays.asList("f1", "f2", "f3", "f4"); List updateQ = Arrays.asList("f2", "f4", "f6"); Map updateQIndex = new HashMap<>(); for (int i = 0; i < updateQ.size(); i++) updateQIndex.put(updateQ.get(i), i); int defCount = autoUpdateFoldersDefective(folders, updateQ); int fixCount = autoUpdateFoldersFixed(folders, updateQIndex); assert defCount == fixCount : "Counts must match"; assert defCount == 2; System.out.println("PASS correctness: autoUpdate matches = " + defCount); // Benchmark autoUpdateFolders int[] sizes = {100, 500, 1000, 5000}; for (int N : sizes) { List allFolders = new ArrayList<>(); List uQ = new ArrayList<>(); Map uQIndex = new HashMap<>(); for (int i = 0; i < N; i++) { allFolders.add("folder" + i); if (i % 3 == 0) { // ~33% in update queue uQ.add("folder" + i); uQIndex.put("folder" + i, uQ.size() - 1); } } // Warmup for (int w = 0; w < 3; w++) { autoUpdateFoldersDefective(allFolders, uQ); autoUpdateFoldersFixed(allFolders, uQIndex); } int iters = Math.max(1, 50000 / N); long t0 = System.nanoTime(); for (int r = 0; r < iters; r++) autoUpdateFoldersDefective(allFolders, uQ); long defTime = System.nanoTime() - t0; t0 = System.nanoTime(); for (int r = 0; r < iters; r++) autoUpdateFoldersFixed(allFolders, uQIndex); long fixTime = System.nanoTime() - t0; double ratio = (double) defTime / Math.max(1, fixTime); String status = ratio >= 2.0 ? "PASS" : "FAIL"; System.out.printf("%s N=%5d defective=%8dns fixed=%8dns ratio=%.1fx%n", status, N, defTime / iters, fixTime / iters, ratio); } System.out.println("\nAll tests passed."); } }