java-topology/defects/pandas/unit/PandasTest.java

108 lines
4 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.*;
/**
* Standalone unit tests for pandas CWE-407 defects.
*
* pandas-0001: Styler render — O(R×H) hidden_rows list membership in render loops
* Simulates: [r for r in range(len(index)) if r not in self.hidden_rows]
* and the body-cell loop: if r not in self.hidden_rows (O(H) per row).
* slow(): list.contains(r) per row iteration — O(R×H)
* fast(): HashSet.contains(r) per row iteration — O(R)
* Assert: slowOps >= fastOps * 10 for R=2000 rows, H=500 hidden rows
*/
public class PandasTest {
// ── pandas-0001 ───────────────────────────────────────────────────────────
/**
* Slow path: hidden rows stored as a List<Integer>.
* Each membership test scans up to H entries — O(H) per row.
* Total for R rows: O(R × H).
*
* @param totalRows total row count R
* @param hiddenRows set of hidden row indices (stored as list)
* @return op count (each element comparison in list.contains() = 1 op)
*/
static long slowRenderRows(int totalRows, List<Integer> hiddenRows) {
long ops = 0;
List<Integer> visibleRows = new ArrayList<>();
for (int r = 0; r < totalRows; r++) {
// Mirrors: if r not in self.hidden_rows — linear scan
boolean found = false;
for (int i = 0; i < hiddenRows.size(); i++) {
ops++;
if (hiddenRows.get(i) == r) {
found = true;
break;
}
}
if (!found) {
visibleRows.add(r);
}
}
return ops;
}
/**
* Fast path: hidden rows stored as a HashSet<Integer>.
* Each membership test is O(1). Total for R rows: O(R + H).
*
* @param totalRows total row count R
* @param hiddenRows set of hidden row indices (stored as list — converted once)
* @return op count (1 op per row for set.contains + 1 op per hidden row to build set)
*/
static long fastRenderRows(int totalRows, List<Integer> hiddenRows) {
long ops = 0;
List<Integer> visibleRows = new ArrayList<>();
// Build set once: O(H)
Set<Integer> hiddenSet = new HashSet<>(hiddenRows.size() * 2);
for (int h : hiddenRows) {
hiddenSet.add(h);
ops++;
}
for (int r = 0; r < totalRows; r++) {
ops++; // O(1) set.contains
if (!hiddenSet.contains(r)) {
visibleRows.add(r);
}
}
return ops;
}
static void testRenderRows() {
int R = 2000; // total rows
int H = 500; // hidden rows (scattered through the middle — maximizes scan depth)
List<Integer> hiddenRows = new ArrayList<>(H);
// Hidden rows in the range [R/4, R/4+H) — they appear late in the list
for (int i = R / 4; i < R / 4 + H; i++) hiddenRows.add(i);
long slowOps = slowRenderRows(R, hiddenRows);
long fastOps = fastRenderRows(R, hiddenRows);
System.out.printf(
"pandas-0001 R=%-5d H=%-4d slowOps=%-8d fastOps=%-6d ratio=%.1fx%n",
R, H, slowOps, fastOps, (double) slowOps / fastOps
);
assert slowOps > fastOps * 10 :
"pandas-0001 FAIL: expected slowOps > 10×fastOps, got " + slowOps + " vs " + fastOps;
System.out.println("pandas-0001 PASS");
}
// ── main ──────────────────────────────────────────────────────────────────
public static void main(String[] args) {
int pass = 0, total = 1;
try { testRenderRows(); pass++; } catch (AssertionError e) { System.err.println(e.getMessage()); }
System.out.printf("%n%d/%d PASS%n", pass, total);
if (pass != total) System.exit(1);
}
}