feat: add unit, integration, and functional test coverage for all 9 MOADs
Each MOAD now has a synthetic defective specimen and fixed specimen proven from first principles across three test tiers: Unit (tests/unit/Moad000X*.java): - Correctness: defective and fixed produce identical functional output - Defect behavior: defective specimen exhibits the defect (measurable) - Fix behavior: fixed specimen eliminates the defect Integration (tests/integration/AllMoadsIntegrationTest.java): - All 9 MOADs proven at medium scale (N=500-2000) - MOAD-0001: O(N^2) vs O(N) list scan at N=1000 - MOAD-0002: 500 sessions trample each other (defective) vs coexist (fixed) - MOAD-0003: 250 anonymous requests leak auth identity (defective) vs zero (fixed) - MOAD-0004: 3000 credential exposures across 1000 requests (defective) vs zero (fixed) - MOAD-0005: 500 computes for 500 concurrent misses vs exactly 1 - MOAD-0006: all 500 passwords extractable from DB (defective) vs unextractable (fixed) - MOAD-0007: N=2000 spatial objects, defective visits all 2000 vs O(log N + k) - MOAD-0009: 990 wasted firings for 1000 ticks / 10 events vs zero waste - MOAD-0011: 10240 NFA steps vs 13 steps on N=12 adversarial input (788x) Functional (tests/functional/AllMoadsFunctionalTest.java): - MOAD-0005: real-thread contention proves herd (defective >1 compute, fixed exactly 1) - MOAD-0007: N=50000 spatial objects, 50M defective probes vs 516K fixed (97x speedup) - MOAD-0009: 10000 ticks / 10 events, 9990 wasted firings vs zero (1000x ratio) - MOAD-0011: N=16 adversarial, 163840 defective steps vs 17 fixed (9638x ratio) Support algorithms (tests/support/Moad000X*.java): - Moad0002Algorithm: shared mutable global state (DefectiveAudioSystem / FixedAudioSystem + Context) - Moad0003Algorithm: ThreadLocal not cleared (handleDefective / handleFixed with finally) - Moad0004Algorithm: HTTP headers logged verbatim (logDefective / logFixed with CREDENTIAL_HEADERS denylist) - Moad0005Algorithm: get+null+compute+put (DefectiveCache HashMap / FixedCache ConcurrentHashMap.computeIfAbsent) - Moad0006Algorithm: Base64 password storage (DefectiveCredentialStore / FixedCredentialStore SHA-256+salt) - Moad0007Algorithm: linear spatial scan (queryDefective list / queryFixed sorted array + binary search) - Moad0009Algorithm: timer-driven polling (runDefectiveScheduler / runFixedEventDriven) - Moad0011Algorithm: PCRE nested quantifiers (matchDefective backtracking NFA / matchFixed linear NFA) Makefile: added unit-moad-0002 through unit-moad-0011 targets, integration-all-moads, functional-all-moads. integration and functional targets now depend on all-MOADs variants.
This commit is contained in:
parent
1f88aa0374
commit
4888153e40
19 changed files with 3359 additions and 5 deletions
474
tests/integration/AllMoadsIntegrationTest.java
Normal file
474
tests/integration/AllMoadsIntegrationTest.java
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
package integration;
|
||||
|
||||
import support.Moad0002Algorithm;
|
||||
import support.Moad0002Algorithm.*;
|
||||
import support.Moad0003Algorithm;
|
||||
import support.Moad0004Algorithm;
|
||||
import support.Moad0004Algorithm.Result;
|
||||
import support.Moad0005Algorithm;
|
||||
import support.Moad0006Algorithm;
|
||||
import support.Moad0006Algorithm.*;
|
||||
import support.Moad0007Algorithm;
|
||||
import support.Moad0007Algorithm.SpatialObject;
|
||||
import support.Moad0009Algorithm;
|
||||
import support.Moad0009Algorithm.Event;
|
||||
import support.Moad0011Algorithm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Integration tests for all 9 active MOADs.
|
||||
*
|
||||
* Validates at medium scale (N=500 to N=2000) that:
|
||||
* - Each MOAD's defective specimen exhibits the defect at realistic input sizes.
|
||||
* - Each MOAD's fixed specimen eliminates the defect at the same input sizes.
|
||||
* - Defect/fix pairs produce identical functional results (same output).
|
||||
* - Complexity ratios at medium scale match first-principles predictions.
|
||||
*
|
||||
* All 9 MOADs run as a single suite. A failure in any section reports the
|
||||
* MOAD name so the specific defect is immediately identifiable.
|
||||
*
|
||||
* No build tool required. Compile and run:
|
||||
*
|
||||
* cd tests
|
||||
* java -m jdk.compiler/com.sun.tools.javac.Main -cp . \
|
||||
* support/Moad0002Algorithm.java support/Moad0003Algorithm.java \
|
||||
* support/Moad0004Algorithm.java support/Moad0005Algorithm.java \
|
||||
* support/Moad0006Algorithm.java support/Moad0007Algorithm.java \
|
||||
* support/Moad0009Algorithm.java support/Moad0011Algorithm.java \
|
||||
* integration/AllMoadsIntegrationTest.java
|
||||
* java -cp . integration.AllMoadsIntegrationTest
|
||||
*/
|
||||
public class AllMoadsIntegrationTest {
|
||||
|
||||
private static int passed = 0;
|
||||
private static int failed = 0;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== AllMoadsIntegrationTest — 9 MOADs at medium scale ===\n");
|
||||
|
||||
testMoad0001_SedimentaryDefect();
|
||||
testMoad0002_IntertangledDefect();
|
||||
testMoad0003_LeakedContext();
|
||||
testMoad0004_LoggedSecret();
|
||||
testMoad0005_ThunderingHerd();
|
||||
testMoad0006_GlassSafe();
|
||||
testMoad0007_FlatlandDefect();
|
||||
testMoad0009_MeteredHeart();
|
||||
testMoad0011_CatastrophicInheritance();
|
||||
|
||||
System.out.printf("\n=== %d passed, %d failed ===%n", passed, failed);
|
||||
if (failed > 0) System.exit(1);
|
||||
}
|
||||
|
||||
// ── MOAD-0001: A Sedimentary Defect (CWE-407) ────────────────────────────
|
||||
// Linear scan inside a loop: O(N^2) vs HashSet O(N).
|
||||
// Proven in detail by TarjanComplexityTest and friends.
|
||||
// Integration check: verify the structural claim holds at N=1000.
|
||||
|
||||
static void testMoad0001_SedimentaryDefect() {
|
||||
System.out.println("── MOAD-0001: A Sedimentary Defect ──");
|
||||
|
||||
// Simulate the sedimentary pattern: list.contains() inside an O(N) loop.
|
||||
// Defective: list scan O(N) per lookup in O(N) loop → O(N²) total.
|
||||
int N = 1000;
|
||||
List<Integer> haystack = new ArrayList<>();
|
||||
for (int i = 0; i < N; i++) haystack.add(i);
|
||||
|
||||
long defComparisons = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
int target = i;
|
||||
for (int j = 0; j < haystack.size(); j++) { // O(N) scan
|
||||
defComparisons++;
|
||||
if (haystack.get(j).equals(target)) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed: set.contains() O(1) per lookup in O(N) loop → O(N) total.
|
||||
java.util.HashSet<Integer> fastSet = new java.util.HashSet<>(haystack);
|
||||
long fixComparisons = 0;
|
||||
for (int i = 0; i < N; i++) {
|
||||
fastSet.contains(i); // O(1) — counted as 1 operation
|
||||
fixComparisons++;
|
||||
}
|
||||
|
||||
// Defective comparisons ~= N*(N+1)/2; fixed = N.
|
||||
assertTrue("MOAD-0001: defective comparisons >> fixed (quadratic vs linear)",
|
||||
defComparisons > fixComparisons * 100,
|
||||
"defective=" + defComparisons + " fixed=" + fixComparisons);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0002: An Intertangled Defect ────────────────────────────────────
|
||||
|
||||
static void testMoad0002_IntertangledDefect() {
|
||||
System.out.println("── MOAD-0002: An Intertangled Defect ──");
|
||||
|
||||
// Medium scale: 500 "sessions", each trying to maintain independent config.
|
||||
int sessionCount = 500;
|
||||
|
||||
// Defective: all sessions share GLOBAL — last writer wins.
|
||||
Moad0002Algorithm.resetGlobal();
|
||||
int lastVolume = -1;
|
||||
for (int i = 0; i < sessionCount; i++) {
|
||||
DefectiveAudioSystem audio = new DefectiveAudioSystem();
|
||||
audio.setVolume(i); // each session sets its own volume
|
||||
lastVolume = i;
|
||||
}
|
||||
// Every session now sees the last writer's volume — isolation is impossible.
|
||||
DefectiveAudioSystem probe = new DefectiveAudioSystem();
|
||||
assertEqual("MOAD-0002 defective: all sessions share last writer's volume",
|
||||
lastVolume, probe.getVolume());
|
||||
|
||||
// Count how many sessions would see their own volume vs the shared value
|
||||
Moad0002Algorithm.resetGlobal();
|
||||
int isolated = 0;
|
||||
for (int i = 0; i < sessionCount; i++) {
|
||||
DefectiveAudioSystem s = new DefectiveAudioSystem();
|
||||
s.setVolume(i);
|
||||
// After setting, check immediately — but another session will overwrite
|
||||
// (In a concurrent model, most would fail; sequentially, all would pass
|
||||
// only if we check before the next set — which is the race window.)
|
||||
}
|
||||
// Defective cannot prove isolation — the last set wins for all.
|
||||
DefectiveAudioSystem last = new DefectiveAudioSystem();
|
||||
last.setVolume(999);
|
||||
DefectiveAudioSystem first = new DefectiveAudioSystem();
|
||||
// first sees 999, not whatever it "set" earlier
|
||||
assertEqual("MOAD-0002 defective: first session trampled by last (cannot coexist)",
|
||||
999, first.getVolume());
|
||||
|
||||
// Fixed: N independent Context objects all coexist.
|
||||
Context[] ctxs = new Context[sessionCount];
|
||||
FixedAudioSystem[] systems = new FixedAudioSystem[sessionCount];
|
||||
for (int i = 0; i < sessionCount; i++) {
|
||||
ctxs[i] = new Context(i, i * 2, "locale-" + i);
|
||||
systems[i] = new FixedAudioSystem(ctxs[i]);
|
||||
}
|
||||
// Each system reads its own volume — all coexist independently.
|
||||
boolean allIsolated = true;
|
||||
for (int i = 0; i < sessionCount; i++) {
|
||||
if (systems[i].getVolume() != i) { allIsolated = false; break; }
|
||||
}
|
||||
assertTrue("MOAD-0002 fixed: all " + sessionCount + " sessions maintain independent volume",
|
||||
allIsolated, "some session saw another session's volume");
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0003: A Leaked Context ───────────────────────────────────────────
|
||||
|
||||
static void testMoad0003_LeakedContext() {
|
||||
System.out.println("── MOAD-0003: A Leaked Context ──");
|
||||
|
||||
int requestCount = 500; // simulate 500 requests on the same pooled thread
|
||||
|
||||
// Defective: alternate authenticated and anonymous requests.
|
||||
// Anonymous requests should see null; defective sees stale identity.
|
||||
int leaks = 0;
|
||||
Moad0003Algorithm.reset();
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
if (i % 2 == 0) {
|
||||
// Authenticated request — sets ThreadLocal but never removes it
|
||||
Moad0003Algorithm.handleDefective("user-" + i);
|
||||
} else {
|
||||
// Anonymous request — should see null, but sees "user-N" (leaked)
|
||||
String identity = Moad0003Algorithm.handleDefectiveAnonymous();
|
||||
if (identity != null) leaks++;
|
||||
}
|
||||
}
|
||||
Moad0003Algorithm.reset();
|
||||
|
||||
// Every anonymous request (250 of them) should have seen a leaked identity
|
||||
assertEqual("MOAD-0003 defective: all anonymous requests leak auth identity",
|
||||
requestCount / 2, leaks);
|
||||
|
||||
// Fixed: same alternating pattern — zero leaks
|
||||
int fixLeaks = 0;
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
if (i % 2 == 0) {
|
||||
Moad0003Algorithm.handleFixed("user-" + i);
|
||||
} else {
|
||||
String identity = Moad0003Algorithm.handleFixedAnonymous();
|
||||
if (identity != null) fixLeaks++;
|
||||
}
|
||||
}
|
||||
|
||||
assertEqual("MOAD-0003 fixed: zero leaks across " + requestCount + " requests",
|
||||
0, fixLeaks);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0004: A Logged Secret ────────────────────────────────────────────
|
||||
|
||||
static void testMoad0004_LoggedSecret() {
|
||||
System.out.println("── MOAD-0004: A Logged Secret ──");
|
||||
|
||||
// Simulate 1000 requests being logged
|
||||
int requestCount = 1000;
|
||||
int defLeaks = 0;
|
||||
int fixLeaks = 0;
|
||||
|
||||
Map<String, String> headers = Moad0004Algorithm.sampleHeaders();
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
Result def = Moad0004Algorithm.logDefective(headers);
|
||||
Result fix = Moad0004Algorithm.logFixed(headers);
|
||||
defLeaks += def.credentialLeakCount;
|
||||
fixLeaks += fix.credentialLeakCount;
|
||||
}
|
||||
|
||||
// Defective: 3 credential headers × 1000 requests = 3000 credential exposures
|
||||
assertEqual("MOAD-0004 defective: 3000 credential exposures across 1000 requests",
|
||||
3000, defLeaks);
|
||||
assertEqual("MOAD-0004 fixed: zero credential exposures across 1000 requests",
|
||||
0, fixLeaks);
|
||||
|
||||
// Verify specific sensitive tokens never appear in fixed logs
|
||||
Result fixSample = Moad0004Algorithm.logFixed(headers);
|
||||
assertNotContains("MOAD-0004 fixed: bearer token absent from log",
|
||||
fixSample.logLine, "eyJhbGciOiJSUzI1NiJ9.secret.token");
|
||||
assertNotContains("MOAD-0004 fixed: session cookie absent from log",
|
||||
fixSample.logLine, "abc123def456");
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0005: A Thundering Herd ─────────────────────────────────────────
|
||||
|
||||
static void testMoad0005_ThunderingHerd() {
|
||||
System.out.println("── MOAD-0005: A Thundering Herd ──");
|
||||
|
||||
// Medium scale: simulate 500 callers all missing the cache simultaneously.
|
||||
int callerCount = 500;
|
||||
|
||||
int defComputes = Moad0005Algorithm.simulateDefectiveConcurrentMiss(callerCount, "resource");
|
||||
int fixComputes = Moad0005Algorithm.simulateFixedConcurrentMiss(callerCount, "resource");
|
||||
|
||||
// Defective: 500 redundant computes — thundering herd
|
||||
assertEqual("MOAD-0005 defective: 500 concurrent misses → 500 computes (herd)",
|
||||
callerCount, defComputes);
|
||||
// Fixed: exactly 1 compute — herd suppressed
|
||||
assertEqual("MOAD-0005 fixed: 500 concurrent misses → 1 compute (herd suppressed)",
|
||||
1, fixComputes);
|
||||
|
||||
// Amplification factor proves the severity
|
||||
assertTrue("MOAD-0005: defective wastes 500× more compute than fixed",
|
||||
defComputes >= callerCount,
|
||||
"defComputes=" + defComputes + " callerCount=" + callerCount);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0006: A Glass Safe ───────────────────────────────────────────────
|
||||
|
||||
static void testMoad0006_GlassSafe() {
|
||||
System.out.println("── MOAD-0006: A Glass Safe ──");
|
||||
|
||||
DefectiveCredentialStore defStore = new DefectiveCredentialStore();
|
||||
FixedCredentialStore fixStore = new FixedCredentialStore();
|
||||
|
||||
// Store 500 user passwords
|
||||
int userCount = 500;
|
||||
String[] passwords = new String[userCount];
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
passwords[i] = "password-for-user-" + i;
|
||||
defStore.storePassword("user-" + i, passwords[i]);
|
||||
fixStore.storePassword("user-" + i, passwords[i]);
|
||||
}
|
||||
|
||||
// Defective: every password extractable from DB dump
|
||||
int defExtracted = 0;
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
String extracted = defStore.extractPassword("user-" + i);
|
||||
if (passwords[i].equals(extracted)) defExtracted++;
|
||||
}
|
||||
assertEqual("MOAD-0006 defective: all 500 passwords extractable from DB",
|
||||
userCount, defExtracted);
|
||||
|
||||
// Fixed: no extraction — verify still works, but extract is impossible
|
||||
int fixVerified = 0;
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
if (fixStore.verify("user-" + i, passwords[i])) fixVerified++;
|
||||
}
|
||||
assertEqual("MOAD-0006 fixed: all 500 passwords still verify correctly",
|
||||
userCount, fixVerified);
|
||||
|
||||
// Fixed: no two users with same password share same hash (unique salts)
|
||||
String sharedPw = "shared-secret";
|
||||
FixedCredentialStore sharedStore = new FixedCredentialStore();
|
||||
sharedStore.storePassword("alice", sharedPw);
|
||||
sharedStore.storePassword("bob", sharedPw);
|
||||
assertNotEqualBytes("MOAD-0006 fixed: same password → different hashes (salt randomization)",
|
||||
sharedStore.rawHash("alice"), sharedStore.rawHash("bob"));
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0007: A Flatland Defect ─────────────────────────────────────────
|
||||
|
||||
static void testMoad0007_FlatlandDefect() {
|
||||
System.out.println("── MOAD-0007: A Flatland Defect ──");
|
||||
|
||||
int N = 2000; // 2000 spatial objects
|
||||
List<SpatialObject> scene = Moad0007Algorithm.buildScene(N, 10000.0);
|
||||
SpatialObject[] index = Moad0007Algorithm.buildIndex(scene);
|
||||
|
||||
// Query: narrow range that matches only ~10% of objects
|
||||
double lo = 9100.0, hi = 9200.0;
|
||||
|
||||
Moad0007Algorithm.Result defResult = Moad0007Algorithm.queryDefective(scene, lo, hi);
|
||||
Moad0007Algorithm.Result fixResult = Moad0007Algorithm.queryFixed(index, lo, hi);
|
||||
|
||||
// Correctness: same hits
|
||||
assertEqual("MOAD-0007: both return same hit count",
|
||||
defResult.hits.size(), fixResult.hits.size());
|
||||
|
||||
// Defective: visits all N=2000 objects
|
||||
assertEqual("MOAD-0007 defective: visits all N=2000 objects",
|
||||
N, defResult.probeCount);
|
||||
|
||||
// Fixed: visits O(log N + k) objects — far fewer than N
|
||||
int maxFixed = (int)(Math.ceil(Math.log(N) / Math.log(2))) + fixResult.hits.size() + 2;
|
||||
assertTrue("MOAD-0007 fixed: visits only O(log N + k) = " + maxFixed + " objects (not all N)",
|
||||
fixResult.probeCount <= maxFixed,
|
||||
"probeCount=" + fixResult.probeCount + " maxExpected=" + maxFixed);
|
||||
|
||||
// Probe ratio proves the speedup
|
||||
double ratio = (double) defResult.probeCount / fixResult.probeCount;
|
||||
assertTrue("MOAD-0007: defective visits 10x+ more objects than fixed at N=2000",
|
||||
ratio > 10.0,
|
||||
"defective=" + defResult.probeCount + " fixed=" + fixResult.probeCount + " ratio=" + ratio);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0009: A Metered Heart ────────────────────────────────────────────
|
||||
|
||||
static void testMoad0009_MeteredHeart() {
|
||||
System.out.println("── MOAD-0009: A Metered Heart ──");
|
||||
|
||||
// Medium scale: 1000 timer ticks, 10 events — 990 wasted firings (99% waste)
|
||||
int ticks = 1000;
|
||||
int eventCount = 10;
|
||||
List<Event> events = Moad0009Algorithm.buildEvents(eventCount, ticks);
|
||||
|
||||
Moad0009Algorithm.Result defResult = Moad0009Algorithm.runDefectiveScheduler(ticks, events);
|
||||
Moad0009Algorithm.Result fixResult = Moad0009Algorithm.runFixedEventDriven(events);
|
||||
|
||||
// Correctness: both process the same events
|
||||
assertEqual("MOAD-0009: both process all 10 events",
|
||||
eventCount, defResult.eventsProcessed);
|
||||
assertEqual("MOAD-0009 fixed: processes all 10 events",
|
||||
eventCount, fixResult.eventsProcessed);
|
||||
|
||||
// Defective: 1000 firings for 10 events = 990 wasted
|
||||
assertEqual("MOAD-0009 defective: 1000 firings for 10 events",
|
||||
ticks, defResult.firings);
|
||||
int wasted = defResult.firings - defResult.eventsProcessed;
|
||||
assertEqual("MOAD-0009 defective: 990 wasted firings (99% waste)", 990, wasted);
|
||||
|
||||
// Fixed: exactly 10 firings — zero waste
|
||||
assertEqual("MOAD-0009 fixed: exactly 10 firings (zero waste)",
|
||||
eventCount, fixResult.firings);
|
||||
|
||||
// Efficiency ratio: fixed is 100× more efficient in firings
|
||||
double ratio = (double) defResult.firings / fixResult.firings;
|
||||
assertTrue("MOAD-0009: defective fires 100× more often than fixed",
|
||||
ratio >= 100.0, "ratio=" + ratio);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── MOAD-0011: A Catastrophic Inheritance ────────────────────────────────
|
||||
|
||||
static void testMoad0011_CatastrophicInheritance() {
|
||||
System.out.println("── MOAD-0011: A Catastrophic Inheritance ──");
|
||||
|
||||
// Structural: all known-bad PCRE patterns detected
|
||||
assertTrue("MOAD-0011: DEFECTIVE_PATTERN_BLEACH detected as catastrophic",
|
||||
Moad0011Algorithm.hasCatastrophicStructure(Moad0011Algorithm.DEFECTIVE_PATTERN_BLEACH),
|
||||
"expected catastrophic");
|
||||
assertTrue("MOAD-0011: DEFECTIVE_PATTERN_CSS detected as catastrophic",
|
||||
Moad0011Algorithm.hasCatastrophicStructure(Moad0011Algorithm.DEFECTIVE_PATTERN_CSS),
|
||||
"expected catastrophic");
|
||||
|
||||
// Fixed patterns pass structural check
|
||||
assertFalse("MOAD-0011: FIXED_PATTERN_BLEACH passes structural check",
|
||||
Moad0011Algorithm.hasCatastrophicStructure(Moad0011Algorithm.FIXED_PATTERN_BLEACH),
|
||||
"expected safe");
|
||||
assertFalse("MOAD-0011: FIXED_PATTERN_CSS passes structural check",
|
||||
Moad0011Algorithm.hasCatastrophicStructure(Moad0011Algorithm.FIXED_PATTERN_CSS),
|
||||
"expected safe");
|
||||
|
||||
// Behavioral at N=12: defective exponential vs fixed linear
|
||||
String adversarial12 = Moad0011Algorithm.adversarialInput(12);
|
||||
Moad0011Algorithm.Result def = Moad0011Algorithm.matchDefective(adversarial12);
|
||||
Moad0011Algorithm.Result fix = Moad0011Algorithm.matchFixed(adversarial12);
|
||||
|
||||
// Both correctly reject the non-matching input
|
||||
assertFalse("MOAD-0011 defective: correctly rejects adversarial N=12", def.matched, "expected no match");
|
||||
assertFalse("MOAD-0011 fixed: correctly rejects adversarial N=12", fix.matched, "expected no match");
|
||||
|
||||
// Steps ratio: defective >> fixed
|
||||
double ratio = (double) def.steps / fix.steps;
|
||||
assertTrue("MOAD-0011: defective 100x+ more steps than fixed at N=12",
|
||||
ratio > 100.0,
|
||||
"defSteps=" + def.steps + " fixSteps=" + fix.steps + " ratio=" + ratio);
|
||||
|
||||
System.out.printf(" INFO: N=12 adversarial: defective=%d steps, fixed=%d steps (%.0fx)%n",
|
||||
def.steps, fix.steps, ratio);
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
static void assertEqual(String label, int expected, int actual) {
|
||||
if (expected == actual) {
|
||||
System.out.printf(" PASS: %s%n", label);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.printf(" FAIL: %s — expected %d, got %d%n", label, expected, actual);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(String label, boolean condition, String detail) {
|
||||
if (condition) {
|
||||
System.out.printf(" PASS: %s%n", label);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.printf(" FAIL: %s — %s%n", label, detail);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void assertFalse(String label, boolean condition, String detail) {
|
||||
assertTrue(label, !condition, detail);
|
||||
}
|
||||
|
||||
static void assertNotContains(String label, String haystack, String needle) {
|
||||
if (!haystack.contains(needle)) {
|
||||
System.out.printf(" PASS: %s%n", label);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.printf(" FAIL: %s — '%s' found in log%n", label, needle);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
static void assertNotEqualBytes(String label, byte[] a, byte[] b) {
|
||||
if (!java.util.Arrays.equals(a, b)) {
|
||||
System.out.printf(" PASS: %s%n", label);
|
||||
passed++;
|
||||
} else {
|
||||
System.out.printf(" FAIL: %s — arrays are equal%n", label);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue