java-topology/defects/hive/unit/HiveGenMRSeenOpsTest.java
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

185 lines
7 KiB
Java

package unit;
import java.util.*;
/**
* Unit tests for hive-0001 and hive-0002: GenMRProcContext seenOps CWE-407.
*
* hive-0001: taskToSeenOps maps Task → List<Operator>. isSeenOp() calls
* ArrayList.contains() — O(n) per lookup. Called O(T) times during
* MapReduce plan generation. Total O(T²) per task.
*
* hive-0002: seenFileSinkOps is List<FileSinkOperator>. contains() in
* GenMRFileSink1 is O(n) on each file sink encountered.
*
* Fix: List → HashSet for both. O(1) contains().
*
* Measurement: count element-level comparisons in contains().
*/
public class HiveGenMRSeenOpsTest {
// ── Minimal stub types ───────────────────────────────────────────────────
static class Task {
final String id;
Task(String id) { this.id = id; }
@Override public boolean equals(Object o) {
return o instanceof Task && ((Task) o).id.equals(id);
}
@Override public int hashCode() { return id.hashCode(); }
@Override public String toString() { return "Task(" + id + ")"; }
}
static class Operator {
final String id;
Operator(String id) { this.id = id; }
@Override public boolean equals(Object o) {
return o instanceof Operator && ((Operator) o).id.equals(id);
}
@Override public int hashCode() { return id.hashCode(); }
@Override public String toString() { return "Op(" + id + ")"; }
}
// ── DEFECTIVE: List<Operator> per task ───────────────────────────────────
static class DefectiveSeenOps {
final Map<Task, List<Operator>> taskToSeenOps = new HashMap<>();
long comparisons = 0;
boolean isSeenOp(Task task, Operator op) {
List<Operator> seen = taskToSeenOps.get(task);
if (seen == null) return false;
for (Operator s : seen) { // linear scan
comparisons++;
if (s.equals(op)) return true;
}
return false;
}
void addSeenOp(Task task, Operator op) {
taskToSeenOps.computeIfAbsent(task, k -> new ArrayList<>()).add(op);
}
}
// ── FIXED: Set<Operator> per task ────────────────────────────────────────
static class FixedSeenOps {
final Map<Task, Set<Operator>> taskToSeenOps = new HashMap<>();
long comparisons = 0;
boolean isSeenOp(Task task, Operator op) {
Set<Operator> seen = taskToSeenOps.get(task);
if (seen == null) return false;
comparisons++; // O(1) hash lookup
return seen.contains(op);
}
void addSeenOp(Task task, Operator op) {
taskToSeenOps.computeIfAbsent(task, k -> new HashSet<>()).add(op);
}
}
// ── Simulate MapReduce plan generation ───────────────────────────────────
// For T table-scan operators per task: each is added then checked T times.
public static long simulateDefective(int T) {
Task task = new Task("mapTask");
DefectiveSeenOps ctx = new DefectiveSeenOps();
List<Operator> ops = new ArrayList<>();
for (int i = 0; i < T; i++) ops.add(new Operator("op_" + i));
for (Operator op : ops) {
// Check if seen (not yet) then add — models isSeenOp + addSeenOp
if (!ctx.isSeenOp(task, op)) ctx.addSeenOp(task, op);
}
// Second pass: re-check all (models merged task path through same operators)
for (Operator op : ops) {
ctx.isSeenOp(task, op);
}
return ctx.comparisons;
}
public static long simulateFixed(int T) {
Task task = new Task("mapTask");
FixedSeenOps ctx = new FixedSeenOps();
List<Operator> ops = new ArrayList<>();
for (int i = 0; i < T; i++) ops.add(new Operator("op_" + i));
for (Operator op : ops) {
if (!ctx.isSeenOp(task, op)) ctx.addSeenOp(task, op);
}
for (Operator op : ops) {
ctx.isSeenOp(task, op);
}
return ctx.comparisons;
}
// ── Tests ────────────────────────────────────────────────────────────────
static void testCorrectnessMatch() {
Task task = new Task("t1");
DefectiveSeenOps def = new DefectiveSeenOps();
FixedSeenOps fix = new FixedSeenOps();
List<Operator> ops = new ArrayList<>();
for (int i = 0; i < 10; i++) ops.add(new Operator("op_" + i));
for (Operator op : ops) {
boolean d = def.isSeenOp(task, op); def.addSeenOp(task, op);
boolean f = fix.isSeenOp(task, op); fix.addSeenOp(task, op);
assert d == f : "isSeenOp result mismatch before add";
}
for (Operator op : ops) {
assert def.isSeenOp(task, op) == fix.isSeenOp(task, op)
: "isSeenOp result mismatch after add";
}
System.out.println("PASS testCorrectnessMatch");
}
static void testDefectiveGrowsQuadratically() {
long prev = -1;
for (int T : new int[]{20, 40, 80}) {
long c = simulateDefective(T);
if (prev > 0) {
double ratio = (double) c / prev;
assert ratio > 2.5
: "defective comparisons should grow >2.5x when T doubles; got " + ratio;
}
prev = c;
}
System.out.println("PASS testDefectiveGrowsQuadratically");
}
static void testFixedGrowsLinearly() {
long prev = -1;
for (int T : new int[]{20, 40, 80}) {
long c = simulateFixed(T);
if (prev > 0) {
double ratio = (double) c / prev;
// Should be very close to 2x (linear) — allow 2.0±0.2
assert ratio < 2.25
: "fixed comparisons should grow ≈2x when T doubles; got " + ratio;
}
prev = c;
}
System.out.println("PASS testFixedGrowsLinearly");
}
static void testRatioAtScale() {
int T = 100;
long def_c = simulateDefective(T);
long fix_c = simulateFixed(T);
double ratio = (double) def_c / fix_c;
assert ratio > 20
: "at T=100, defective should be >20x slower; ratio=" + ratio;
System.out.printf("PASS testRatioAtScale (defective=%d, fixed=%d, ratio=%.1fx)%n",
def_c, fix_c, ratio);
}
public static void main(String[] args) {
testCorrectnessMatch();
testDefectiveGrowsQuadratically();
testFixedGrowsLinearly();
testRatioAtScale();
System.out.println("All hive-0001/0002 tests passed.");
}
}