java-topology/defects/bazel/unit/BazelFeatureSelectionAlgorithmTest.java

151 lines
5.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.*;
/**
* Models Bazel FeatureSelection.run() provides-conflict check.
*
* SLOW: O(P×S×L) — ImmutableList.contains() O(L) per selectable in P×S nested loops.
* FAST: O(P×S) — HashSet built once from enabledList; O(1) per lookup.
*
* CWE-407: src/main/java/com/google/devtools/build/lib/rules/cpp/FeatureSelection.java:159
*/
public class BazelFeatureSelectionAlgorithmTest {
// -------------------------------------------------------------------------
// Slow — ImmutableList.contains() O(L) per lookup
// -------------------------------------------------------------------------
static class SlowFeatureSelection {
long cmpOps = 0;
boolean listContains(List<String> list, String val) {
for (String s : list) {
cmpOps++;
if (s.equals(val)) return true;
}
return false;
}
/**
* O(P × S × L): for each provide × selectable, check enabledList membership.
* Returns count of conflict checks performed.
*/
int run(List<String> enabledList,
Map<String, List<String>> providesMap) {
int conflicts = 0;
for (Map.Entry<String, List<String>> entry : providesMap.entrySet()) {
for (String selectable : entry.getValue()) {
if (listContains(enabledList, selectable)) {
conflicts++;
}
}
}
return conflicts;
}
long ops(List<String> enabledList, Map<String, List<String>> providesMap) {
cmpOps = 0;
run(enabledList, providesMap);
return cmpOps;
}
}
// -------------------------------------------------------------------------
// Fast — HashSet built once before loop
// -------------------------------------------------------------------------
static class FastFeatureSelection {
long cmpOps = 0;
int run(List<String> enabledList,
Map<String, List<String>> providesMap) {
Set<String> enabledSet = new HashSet<>(enabledList);
cmpOps += enabledList.size(); // build cost
int conflicts = 0;
for (Map.Entry<String, List<String>> entry : providesMap.entrySet()) {
for (String selectable : entry.getValue()) {
cmpOps++;
if (enabledSet.contains(selectable)) {
conflicts++;
}
}
}
return conflicts;
}
long ops(List<String> enabledList, Map<String, List<String>> providesMap) {
cmpOps = 0;
run(enabledList, providesMap);
return cmpOps;
}
}
// -------------------------------------------------------------------------
// Build test data
// -------------------------------------------------------------------------
static List<String> makeEnabledList(int l) {
List<String> list = new ArrayList<>();
for (int i = 0; i < l; i++) list.add("feature_" + i);
return list;
}
static Map<String, List<String>> makeProvidesMap(int p, int s, int overlapStart) {
Map<String, List<String>> map = new LinkedHashMap<>();
for (int i = 0; i < p; i++) {
List<String> sels = new ArrayList<>();
for (int j = 0; j < s; j++) {
// Some selectables are in enabledList (conflicts), some not
sels.add("feature_" + (overlapStart + i * s + j));
}
map.put("provide_" + i, sels);
}
return map;
}
public static void main(String[] args) {
SlowFeatureSelection slow = new SlowFeatureSelection();
FastFeatureSelection fast = new FastFeatureSelection();
int passed = 0, total = 0;
System.out.println("=== bazel-0003: FeatureSelection ImmutableList.contains O(P×S×L) ===");
// (L, P, S) configurations matching realistic Bazel C++ toolchain sizes
int[][] configs = {
{50, 7, 6}, // small toolchain
{80, 7, 6}, // realistic (agent's estimate)
{150, 10, 8}, // large toolchain
{300, 15, 10} // extreme
};
for (int[] cfg : configs) {
int l = cfg[0], p = cfg[1], s = cfg[2];
List<String> enabled = makeEnabledList(l);
Map<String, List<String>> provides = makeProvidesMap(p, s, 0);
long sv = slow.ops(enabled, provides);
long fv = fast.ops(enabled, provides);
double ratio = (double) sv / Math.max(fv, 1);
total++;
boolean ok = sv > fv && ratio >= 5.0;
System.out.printf("L=%3d P=%2d S=%2d slow=%7d fast=%5d ratio=%6.1fx %s%n",
l, p, s, sv, fv, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both count same conflicts
List<String> enabled = makeEnabledList(80);
Map<String, List<String>> provides = makeProvidesMap(7, 6, 0);
int sc = slow.run(enabled, provides);
int fc = fast.run(enabled, provides);
total++;
boolean correct = sc == fc;
System.out.printf("conflict count matches (%d): %s%n", sc, correct ? "PASS" : "FAIL");
if (correct) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}