java-topology/defects/ofbiz-0002/test/test_ofbiz_0002.java
russell@unturf.com 896a29f83b erp scan: erpnext-0001/0002, ofbiz-0001/0002; odoo CLEAN; 4/4 PASS
erpnext-0001: BOM.get_children list dedup O(B^2) MEDIUM 4x
erpnext-0002: serial_batch_bundle serial/batch dedup O(N^2) HIGH 45x
ofbiz-0001: PaymentGatewayServices processList LinkedList O(N^2) HIGH 45x
ofbiz-0002: OrderReturnServices/OrderReadHelper payment dedup O(N^2) MEDIUM 36x
odoo: CLEAN (consistent set/frozenset/OrderedSet usage throughout)
2026-03-30 15:24:39 -04:00

90 lines
3.4 KiB
Java

import java.util.*;
/**
* Unit test for ofbiz-0002: OrderReturnServices/OrderReadHelper LinkedList.contains O(N^2)
*
* Simulates payment ID and return header ID dedup where membership is checked
* via LinkedList.contains (defect) vs HashSet.contains (fix).
*
* Defect: OrderReturnServices.java line 2364 — paymentList (LinkedList) .contains()
* OrderReadHelper.java line 2247 — returnHeaderList (LinkedList) .contains()
*
* Fix: use HashSet/LinkedHashSet for O(1) contains.
*/
public class test_ofbiz_0002 {
// --- Defect: LinkedList dedup ---
static List<String> dedupPaymentsDefect(List<String> paymentIds) {
List<String> paymentList = new LinkedList<>();
for (String pid : paymentIds) {
if (!paymentList.contains(pid)) { // O(N)
paymentList.add(pid);
}
}
return paymentList;
}
// --- Fix: HashSet dedup ---
static Set<String> dedupPaymentsFixed(List<String> paymentIds) {
Set<String> paymentSet = new LinkedHashSet<>();
for (String pid : paymentIds) {
paymentSet.add(pid); // O(1) contains + add
}
return paymentSet;
}
static List<String> generatePaymentIds(int n, int uniquePayments) {
List<String> ids = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
ids.add("PMT-" + String.format("%06d", i % uniquePayments));
}
return ids;
}
public static void main(String[] args) {
System.out.println("=== ofbiz-0002: OrderReturnServices/OrderReadHelper dedup O(N^2) ===\n");
// Correctness test
List<String> input = Arrays.asList("PMT-001", "PMT-002", "PMT-001", "PMT-003");
List<String> defectResult = dedupPaymentsDefect(input);
Set<String> fixedResult = dedupPaymentsFixed(input);
assert defectResult.size() == 3 : "Defect: wrong count";
assert fixedResult.size() == 3 : "Fixed: wrong count";
assert new LinkedHashSet<>(defectResult).equals(fixedResult) : "Results differ";
System.out.println("Correctness: PASS");
// Performance test: 5000 return items, 1000 unique payments
int N = 5000, uniquePayments = 1000;
List<String> paymentIds = generatePaymentIds(N, uniquePayments);
System.out.println("Return items: " + N + ", unique payments: " + uniquePayments);
// Warmup
for (int i = 0; i < 5; i++) {
dedupPaymentsDefect(paymentIds);
dedupPaymentsFixed(paymentIds);
}
int iterations = 100;
long t0 = System.nanoTime();
for (int i = 0; i < iterations; i++) {
dedupPaymentsDefect(paymentIds);
}
long defectNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int i = 0; i < iterations; i++) {
dedupPaymentsFixed(paymentIds);
}
long fixedNs = System.nanoTime() - t0;
double ratio = (double) defectNs / fixedNs;
System.out.printf("Defect: %,d ns / %d iters%n", defectNs, iterations);
System.out.printf("Fixed: %,d ns / %d iters%n", fixedNs, iterations);
System.out.printf("Ratio: %.1fx%n", ratio);
boolean pass = ratio > 5.0;
System.out.println("\nResult: " + (pass ? "PASS" : "FAIL") + " (ratio=" + String.format("%.1f", ratio) + "x)");
assert pass : "Expected speedup > 5x, got " + ratio;
}
}