189 lines
7.1 KiB
Java
189 lines
7.1 KiB
Java
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");
|
||
}
|
||
}
|