java-topology/defects/calligra-0001/test/CalligraShapeManagerAddTest.java
russell@unturf.com ad90dddbcc libreoffice+calligra: 2 CWE-407 defects, MOADs 0002-0005 CLEAN
libreoffice-0001: SavePivotTableXml member-to-cache O(M*C) std::find
  sc/source/filter/excel/xepivotxml.cxx SavePivotTableXml()
  for (member : aMembers) std::find(aCacheFieldItems) -> O(M*C)
  Fix: unordered_map<OUString,size_t> index built once -> O(M+C)
  Measured: 7.1x speedup at M=C=2000

calligra-0001: KoShapeManager addShape QList::contains O(N^2) dedup
  libs/flake/KoShapeManager.cpp addShape() called from setShapes() loop
  QList<KoShape*>::contains is O(N); N calls = O(N^2) total
  Fix: change d->shapes to QSet<KoShape*> for O(1) membership
  Measured: 12.6x speedup at N=5000

MOADs 0002-0005: CLEAN for both targets (see README.md per defect)
Both unit tests: PASS
2026-03-31 20:29:29 -04:00

106 lines
3.8 KiB
Java

import java.util.*;
/**
* Unit test for calligra-0001: KoShapeManager addShape O(N^2) membership check.
*
* Defect: libs/flake/KoShapeManager.cpp addShape()
* if (d->shapes.contains(shape)) // QList::contains is O(N)
* d->shapes.append(shape);
*
* Called from setShapes() in a loop over N shapes, producing O(N^2) total.
*
* Fix: change d->shapes from QList to QSet, making contains() O(1).
*
* Complexity: O(N^2) -> O(N).
*/
public class CalligraShapeManagerAddTest {
// --- Defective implementation: QList equivalent ---
static List<Integer> buildShapeListLinear(int[] shapes) {
List<Integer> result = new ArrayList<>();
for (int shape : shapes) {
if (!result.contains(shape)) { // O(N) each
result.add(shape);
}
}
return result;
}
// --- Fixed implementation: QSet equivalent ---
static Set<Integer> buildShapeSetFixed(int[] shapes) {
Set<Integer> result = new LinkedHashSet<>();
for (int shape : shapes) {
result.add(shape); // O(1) each, set deduplicates automatically
}
return result;
}
// --- Benchmark ---
static long benchmarkLinear(int N) {
int[] shapes = new int[N];
for (int i = 0; i < N; i++) shapes[i] = i; // all unique -> worst case
long start = System.nanoTime();
buildShapeListLinear(shapes);
return System.nanoTime() - start;
}
static long benchmarkFixed(int N) {
int[] shapes = new int[N];
for (int i = 0; i < N; i++) shapes[i] = i;
long start = System.nanoTime();
buildShapeSetFixed(shapes);
return System.nanoTime() - start;
}
public static void main(String[] args) {
System.out.println("=== calligra-0001: KoShapeManager addShape dedup O(N^2) ===");
// Correctness: unique shapes
{
int[] shapes = {10, 20, 30, 40, 50};
List<Integer> linear = buildShapeListLinear(shapes);
Set<Integer> fixed = buildShapeSetFixed(shapes);
if (!new HashSet<>(linear).equals(fixed)) {
System.err.println("FAIL: unique-shape mismatch linear=" + linear + " fixed=" + fixed);
System.exit(1);
}
System.out.println("PASS: unique shapes, result size=" + fixed.size());
}
// Correctness: duplicate shapes (setShapes called with duplicates)
{
int[] shapes = {1, 2, 3, 2, 1, 4};
List<Integer> linear = buildShapeListLinear(shapes);
Set<Integer> fixed = buildShapeSetFixed(shapes);
if (!new HashSet<>(linear).equals(fixed)) {
System.err.println("FAIL: duplicate-shape mismatch linear=" + linear + " fixed=" + fixed);
System.exit(1);
}
if (linear.size() != 4 || fixed.size() != 4) {
System.err.println("FAIL: expected 4 unique shapes, linear=" + linear.size() + " fixed=" + fixed.size());
System.exit(1);
}
System.out.println("PASS: duplicate shapes deduped correctly, result size=" + fixed.size());
}
// Performance test
int N = 5000;
// Warmup
for (int w = 0; w < 3; w++) { benchmarkLinear(N/10); benchmarkFixed(N/10); }
long linearNs = benchmarkLinear(N);
long fixedNs = benchmarkFixed(N);
double ratio = (double) linearNs / fixedNs;
System.out.printf("Linear N=%d: %,d ns%n", N, linearNs);
System.out.printf("Fixed N=%d: %,d ns%n", N, fixedNs);
System.out.printf("Speedup: %.1fx%n", ratio);
if (ratio < 5.0) {
System.err.printf("FAIL: expected >= 5x speedup at N=%d, got %.1fx%n", N, ratio);
System.exit(1);
}
System.out.println("PASS: speedup >= 5x");
System.out.println("PASS: all tests passed");
}
}