java-topology/defects/celery/unit/CeleryTest.java

189 lines
7.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
* CeleryTest
*
* Models CWE-407 defects in celery/celery:
*
* CEL-001 (MEDIUM) — canvas.py append_to_list_option(): `if value not in items`
* where items is a plain list; called inside chain-build loops over T tasks
* and E errbacks. O(L) scan per call × O(T×E) calls = O(T×E×L) total,
* approaching O(N³) for long chains with many callbacks.
* Fix: parallel set for O(1) membership test.
*
* All measurements are instrumented operation counts, not wall-clock timing.
*/
public class CeleryTest {
// -----------------------------------------------------------------------
// CEL-001 modelling helpers
//
// Defective: ArrayList membership for value deduplication — O(L) per call
// Fixed: HashSet mirror for O(1) deduplication
//
// Models append_to_list_option called T times (once per task) for E errbacks.
// L = growing list length as callbacks accumulate.
// Returns total scan cost across all calls.
// -----------------------------------------------------------------------
/**
* Defective: `if value not in items` where items is a list.
* Called T*E times (T tasks × E errbacks each).
* items grows as new values are added.
*/
static long cel001Defective(int numTasks, int numErrbacks) {
// Each task has its own options list (link_error key)
// We model the worst case: all errbacks are unique, list grows per task
long totalScans = 0;
for (int task = 0; task < numTasks; task++) {
ArrayList<Integer> items = new ArrayList<>(); // per-task link_error list
for (int errback = 0; errback < numErrbacks; errback++) {
// model `if value not in items` — O(current list size)
totalScans += items.size(); // linear scan cost
// actual check (correctness)
if (!items.contains(errback)) {
items.add(errback);
}
}
}
return totalScans;
}
/**
* Fixed: parallel HashSet for O(1) membership test.
*/
static long cel001Fixed(int numTasks, int numErrbacks) {
long totalLookups = 0;
for (int task = 0; task < numTasks; task++) {
ArrayList<Integer> items = new ArrayList<>();
HashSet<Integer> itemsSet = new HashSet<>();
for (int errback = 0; errback < numErrbacks; errback++) {
// model `if value not in items_set` — O(1)
totalLookups++; // one hash lookup per errback per task
if (!itemsSet.contains(errback)) {
items.add(errback);
itemsSet.add(errback);
}
}
}
return totalLookups;
}
// -----------------------------------------------------------------------
// Test 1 — CEL-001: defective O(T×E²) vs fixed O(T×E) at T=100, E=50
//
// For each task, the inner errback loop scans a growing list:
// 0+1+2+...+(E-1) = E*(E-1)/2 scans per task.
// Total defect cost = T * E*(E-1)/2.
// Fixed cost = T * E.
// -----------------------------------------------------------------------
static void test1_cel001_quadraticPerTask() {
int T = 100; // tasks in chain
int E = 50; // errbacks per task
long defectCost = cel001Defective(T, E);
long fixedCost = cel001Fixed(T, E);
System.out.printf("test1 CEL-001: T=%d tasks E=%d errbacks defect=%d fixed=%d%n",
T, E, defectCost, fixedCost);
assert defectCost > fixedCost
: "defect must be more expensive than fix";
// defective: T * E*(E-1)/2
long expectedDefect = (long) T * E * (E - 1) / 2;
assert defectCost == expectedDefect
: "expected defect cost=" + expectedDefect + " got=" + defectCost;
// fixed: T * E
long expectedFixed = (long) T * E;
assert fixedCost == expectedFixed
: "expected fixed cost=" + expectedFixed + " got=" + fixedCost;
double ratio = (double) defectCost / Math.max(1, fixedCost);
assert ratio > 10.0
: "expected ratio>10x, got " + ratio;
}
// -----------------------------------------------------------------------
// Test 2 — CEL-001: scaling — doubling E grows defect super-linearly
// -----------------------------------------------------------------------
static void test2_cel001_errbackScaling() {
int T = 50;
int E1 = 40;
int E2 = 80; // double E
long d1 = cel001Defective(T, E1);
long d2 = cel001Defective(T, E2);
long f1 = cel001Fixed(T, E1);
long f2 = cel001Fixed(T, E2);
double defectGrowth = (double) d2 / Math.max(1, d1);
double fixedGrowth = (double) f2 / Math.max(1, f1);
System.out.printf("test2 CEL-001: T=%d E1=%d E2=%d defect_growth=%.2fx fixed_growth=%.2fx%n",
T, E1, E2, defectGrowth, fixedGrowth);
// defect grows ~4x when E doubles (O(E²) per task)
assert defectGrowth > 3.5
: "defect should grow ~4x when E doubles, got " + defectGrowth;
// fixed grows ~2x when E doubles (O(E) per task)
assert fixedGrowth >= 1.8 && fixedGrowth <= 2.2
: "fixed should grow ~2x when E doubles, got " + fixedGrowth;
assert defectGrowth > fixedGrowth
: "defect growth must exceed fixed growth";
}
// -----------------------------------------------------------------------
// Test 3 — CEL-001: large chain — T=500, E=20 — high-throughput scenario
// -----------------------------------------------------------------------
static void test3_cel001_largeChain() {
int T = 500;
int E = 20;
long defectCost = cel001Defective(T, E);
long fixedCost = cel001Fixed(T, E);
double ratio = (double) defectCost / Math.max(1, fixedCost);
System.out.printf("test3 CEL-001: T=%d E=%d defect=%d fixed=%d ratio=%.1fx%n",
T, E, defectCost, fixedCost, ratio);
assert defectCost > fixedCost
: "defect must be more expensive at T=" + T + " E=" + E;
assert ratio > 5.0
: "expected ratio>5x at T=500 E=20, got " + ratio;
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("=== CeleryTest ===");
System.out.println("Modelling CWE-407: CEL-001 canvas.py append_to_list_option list O(T×E²) scan");
System.out.println();
test1_cel001_quadraticPerTask();
System.out.println(" PASS test1_cel001_quadraticPerTask");
test2_cel001_errbackScaling();
System.out.println(" PASS test2_cel001_errbackScaling");
test3_cel001_largeChain();
System.out.println(" PASS test3_cel001_largeChain");
System.out.println();
System.out.println("3/3 PASS");
}
}