import java.util.*; /** * CWE-407 simulation tests for Scribus defects. * * scribus-0001: getSortedStyleList retList.contains O(N²) dedup * scribus-0002: getUsedPatterns results.contains O(I×R) pattern accumulation * scribus-0003: Selection::addItems m_SelList.contains O(N×M) selection dedup */ public class ScribusTest { // --------------------------------------------------------------- // scribus-0001: getSortedStyleList — QList::contains inside loop // Simulates walking parent chains and deduplicating into retList // --------------------------------------------------------------- /** Defective: List.contains O(N) inside O(N) loop */ static int sortedStyleListDefective(int styleCount, int[] parentIdx) { int ops = 0; List retList = new ArrayList<>(); for (int i = 0; i < styleCount; i++) { // Check if already in retList for (int x : retList) { ops++; if (x == i) break; } if (!retList.contains(i)) { // Walk parent chain List chain = new ArrayList<>(); chain.add(i); int p = parentIdx[i]; while (p >= 0) { boolean found = false; for (int x : chain) { ops++; if (x == p) { found = true; break; } } if (!found) chain.add(0, p); p = parentIdx[p]; } // Merge chain into retList with contains check for (int idx : chain) { boolean found = false; for (int x : retList) { ops++; if (x == idx) { found = true; break; } } if (!found) retList.add(idx); } } } return ops; } /** Fixed: QSet companion */ static int sortedStyleListFixed(int styleCount, int[] parentIdx) { int ops = 0; List retList = new ArrayList<>(); Set retSet = new HashSet<>(); for (int i = 0; i < styleCount; i++) { ops++; if (!retSet.contains(i)) { List chain = new ArrayList<>(); Set chainSet = new HashSet<>(); chain.add(i); chainSet.add(i); ops++; int p = parentIdx[i]; while (p >= 0) { ops++; if (!chainSet.contains(p)) { chain.add(0, p); chainSet.add(p); } p = parentIdx[p]; } for (int idx : chain) { ops++; if (!retSet.contains(idx)) { retList.add(idx); retSet.add(idx); } } } } return ops; } static void testSortedStyleList() { int N = 500; int[] parentIdx = new int[N]; // Linear parent chain: 0←1←2←...←N-1 (worst case) parentIdx[0] = -1; for (int i = 1; i < N; i++) parentIdx[i] = i - 1; int defectOps = sortedStyleListDefective(N, parentIdx); int fixedOps = sortedStyleListFixed(N, parentIdx); double ratio = (double) defectOps / fixedOps; System.out.printf("scribus-0001 sorted style list: defect=%d fixed=%d ratio=%.1fx%n", defectOps, fixedOps, ratio); assert ratio > 10 : "Expected >10x ratio, got " + ratio; System.out.println(" PASS"); } // --------------------------------------------------------------- // scribus-0002: getUsedPatterns — QStringList::contains inside item loop // --------------------------------------------------------------- /** Defective: linear scan for each pattern check */ static int getUsedPatternsDefective(String[][] itemPatterns) { int ops = 0; List results = new ArrayList<>(); for (String[] pats : itemPatterns) { for (String pat : pats) { if (pat == null || pat.isEmpty()) continue; boolean found = false; for (String r : results) { ops++; if (r.equals(pat)) { found = true; break; } } if (!found) results.add(pat); } } return ops; } /** Fixed: HashSet companion */ static int getUsedPatternsFixed(String[][] itemPatterns) { int ops = 0; Set resultSet = new HashSet<>(); for (String[] pats : itemPatterns) { for (String pat : pats) { if (pat == null || pat.isEmpty()) continue; ops++; if (!resultSet.contains(pat)) resultSet.add(pat); } } return ops; } static void testGetUsedPatterns() { int items = 2000; int patsPerItem = 3; // fill, stroke, mask int uniquePatterns = 200; Random rng = new Random(42); String[][] itemPatterns = new String[items][patsPerItem]; for (int i = 0; i < items; i++) for (int j = 0; j < patsPerItem; j++) itemPatterns[i][j] = "pattern_" + rng.nextInt(uniquePatterns); int defectOps = getUsedPatternsDefective(itemPatterns); int fixedOps = getUsedPatternsFixed(itemPatterns); double ratio = (double) defectOps / fixedOps; System.out.printf("scribus-0002 used patterns: defect=%d fixed=%d ratio=%.1fx%n", defectOps, fixedOps, ratio); assert ratio > 10 : "Expected >10x ratio, got " + ratio; System.out.println(" PASS"); } // --------------------------------------------------------------- // scribus-0003: Selection::addItems — m_SelList.contains inside loop // --------------------------------------------------------------- /** Defective: QList::contains O(M) for each of N items */ static int selectionAddItemsDefective(int existing, int toAdd) { int ops = 0; List selList = new ArrayList<>(); for (int i = 0; i < existing; i++) selList.add(i); for (int i = existing; i < existing + toAdd; i++) { // linear scan for (int s : selList) { ops++; if (s == i) break; } selList.add(i); } return ops; } /** Fixed: QSet companion for O(1) check */ static int selectionAddItemsFixed(int existing, int toAdd) { int ops = 0; Set selSet = new HashSet<>(); for (int i = 0; i < existing; i++) { selSet.add(i); ops++; } for (int i = existing; i < existing + toAdd; i++) { ops++; if (!selSet.contains(i)) selSet.add(i); } return ops; } static void testSelectionAddItems() { int existing = 2000; int toAdd = 3000; int defectOps = selectionAddItemsDefective(existing, toAdd); int fixedOps = selectionAddItemsFixed(existing, toAdd); double ratio = (double) defectOps / fixedOps; System.out.printf("scribus-0003 selection addItems: defect=%d fixed=%d ratio=%.1fx%n", defectOps, fixedOps, ratio); assert ratio > 10 : "Expected >10x ratio, got " + ratio; System.out.println(" PASS"); } // --------------------------------------------------------------- public static void main(String[] args) { testSortedStyleList(); testGetUsedPatterns(); testSelectionAddItems(); System.out.println("\nAll 3 Scribus CWE-407 tests PASSED."); } }