java-topology/defects/cura-0002/test/test_cura_0002.py
russell@unturf.com eb3be81588 cura+prusaslicer: 5-MOAD scan; cura-0002 CWE-407 SettingInheritanceManager List[str] 109x, prusaslicer-0004 CWE-407 FillRectilinear queue 64x
cura-0002: SettingInheritanceManager._settings_with_inheritance_warning is a List[str].
Queried with `in` and mutated with .append()/.remove() on every setting property change.
With S=1000 settings, each _onPropertyChanged call costs O(S). Fix: Set[str] for O(1)
membership. 109x speedup at S=1000.

prusaslicer-0004: chain_monotonic_regions() in FillRectilinear.cpp uses std::find to
search a work queue of MonotonicRegion* on every dequeue. Queue grows to O(R) regions;
10 ants each dequeue all R regions → O(10*R^2) total. Fix: std::vector<bool> in_queue
indexed by region offset for O(1) membership. 64x speedup at R=500.

prusaslicer-scan: MOADs 0002/0003/0004/0005 CLEAN (thread_local is RNG only; no
request-scoped leakage; no full credential logging; slicing is single-threaded).
2026-04-03 14:16:30 -04:00

101 lines
3.3 KiB
Python

"""
test_cura_0002.py — CWE-407 SettingInheritanceManager warning list O(S^2) vs O(S)
Simulates Cura's SettingInheritanceManager._onPropertyChanged membership check
pattern against a List[str] (defect) vs Set[str] (fix).
Benchmark at S=100 and S=1000. Assert speedup > 3x.
"""
import os
import time
import random
import string
os.environ["PYTHONUNBUFFERED"] = "1"
def generate_keys(n: int):
random.seed(42)
return ["setting_" + "".join(random.choices(string.ascii_lowercase, k=6)) for _ in range(n)]
def simulate_list_manager(keys: list, num_events: int) -> float:
"""Defect: List[str] with O(N) membership checks on every property change."""
warning_list = []
start = time.perf_counter()
for i in range(num_events):
key = keys[i % len(keys)]
# Simulates _onPropertyChanged: 4 membership checks per event
has_override = (i % 3 != 0) # 2/3 of events add, 1/3 remove
if key not in warning_list and has_override: # O(N)
warning_list.append(key)
elif key in warning_list and not has_override: # O(N)
warning_list.remove(key) # O(N)
parent_key = key + "_category"
if parent_key not in warning_list and has_override: # O(N)
warning_list.append(parent_key)
elif parent_key in warning_list and not has_override: # O(N)
warning_list.remove(parent_key) # O(N)
end = time.perf_counter()
return end - start
def simulate_set_manager(keys: list, num_events: int) -> float:
"""Fix: Set[str] with O(1) membership checks on every property change."""
warning_set = set()
start = time.perf_counter()
for i in range(num_events):
key = keys[i % len(keys)]
has_override = (i % 3 != 0)
if has_override: # O(1)
warning_set.add(key)
elif key in warning_set: # O(1)
warning_set.discard(key) # O(1)
parent_key = key + "_category"
if has_override: # O(1)
warning_set.add(parent_key)
elif parent_key in warning_set: # O(1)
warning_set.discard(parent_key) # O(1)
end = time.perf_counter()
return end - start
def run_benchmark(n: int, num_events: int, label: str) -> bool:
keys = generate_keys(n)
# Warm up
simulate_list_manager(keys[:10], 100)
simulate_set_manager(keys[:10], 100)
t_list = simulate_list_manager(keys, num_events)
t_set = simulate_set_manager(keys, num_events)
speedup = t_list / t_set if t_set > 0 else float("inf")
status = "PASS" if speedup >= 3.0 else "FAIL"
print(f"[{label}] N={n} events={num_events}: list={t_list*1000:.1f}ms set={t_set*1000:.1f}ms speedup={speedup:.1f}x [{status}]")
return speedup >= 3.0
if __name__ == "__main__":
import sys
all_pass = True
all_pass &= run_benchmark(n=100, num_events=5000, label="S=100")
all_pass &= run_benchmark(n=1000, num_events=10000, label="S=1000")
print()
if all_pass:
print("PASS — cura-0002 speedup confirmed > 3x for both scales")
sys.exit(0)
else:
print("FAIL — speedup below threshold")
sys.exit(1)