From 03dfc29d679f1b0066f72c15c89cb19caff92166 Mon Sep 17 00:00:00 2001 From: "aikido-autofix[bot]" <119856028+aikido-autofix[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:25:56 +0000 Subject: [PATCH] fix(security): autofix Dangerous use of assert --- merge-scoredirector-results.py | 38 ++++++---- summarize-moveprovider.py | 126 +++++++++++++++++++-------------- summarize-scoredirector.py | 52 +++++++------- 3 files changed, 120 insertions(+), 96 deletions(-) diff --git a/merge-scoredirector-results.py b/merge-scoredirector-results.py index 984a15dc..7ee2bd06 100644 --- a/merge-scoredirector-results.py +++ b/merge-scoredirector-results.py @@ -168,14 +168,14 @@ def merge_files(paths: list, expected_jdk: "str | None" = None) -> list: def _selftest() -> None: # JMH itself used 3.392 for 100 iterations; recovering that number validates the quantile. - assert abs(t_quantile(CONFIDENCE_TAIL, 99) - 3.392) < 0.001, t_quantile(CONFIDENCE_TAIL, 99) + if not abs(t_quantile(CONFIDENCE_TAIL, 99) - 3.392) < 0.001: raise AssertionError(t_quantile(CONFIDENCE_TAIL, 99)) for degrees_of_freedom, expected in ((1, 636.619), (10, 4.587), (19, 3.883), (25, 3.725)): actual = t_quantile(CONFIDENCE_TAIL, degrees_of_freedom) - assert abs(actual - expected) < max(0.001, expected * 1e-5), (degrees_of_freedom, actual) + if not abs(actual - expected) < max(0.001, expected * 1e-5): raise AssertionError((degrees_of_freedom, actual)) - assert percentile([1.0, 2.0, 3.0], 0.0) == 1.0 - assert percentile([1.0, 2.0, 3.0], 50.0) == 2.0 - assert percentile([1.0, 2.0, 3.0], 100.0) == 3.0 + if not percentile([1.0, 2.0, 3.0], 0.0) == 1.0: raise AssertionError() + if not percentile([1.0, 2.0, 3.0], 50.0) == 2.0: raise AssertionError() + if not percentile([1.0, 2.0, 3.0], 100.0) == 3.0: raise AssertionError() def fork(*values, jvm="/jdk25/bin/java", version="25.0.4.1"): return {"benchmark": "Bench.run", "params": {"csExample": "CLOUD_BALANCING"}, @@ -187,22 +187,30 @@ def fork(*values, jvm="/jdk25/bin/java", version="25.0.4.1"): "rawData": [list(values)]}} merged = merge_entries([fork(100.0), fork(102.0), fork(98.0), fork(104.0)]) - assert merged["forks"] == 4 and merged["measurementIterations"] == 1 - assert merged["primaryMetric"]["score"] == 101.0 + if not (merged["forks"] == 4 and merged["measurementIterations"] == 1): + raise AssertionError expected_error = t_quantile(CONFIDENCE_TAIL, 3) * statistics.stdev([100.0, 102.0, 98.0, 104.0]) / 2 - assert abs(merged["primaryMetric"]["scoreError"] - expected_error) < 1e-9 + if not (abs(merged["primaryMetric"]["scoreError"] - expected_error) < 1e-9): + raise AssertionError low, high = merged["primaryMetric"]["scoreConfidence"] - assert abs(high - low - 2 * expected_error) < 1e-9 - assert merged["primaryMetric"]["rawData"] == [[100.0], [102.0], [98.0], [104.0]], "fork order is kept" - assert merged["primaryMetric"]["scorePercentiles"]["0.0"] == 98.0 - assert merged["primaryMetric"]["scoreUnit"] == "ops/s", "unrelated fields survive" + if not (abs(high - low - 2 * expected_error) < 1e-9): + raise AssertionError + if not (merged["primaryMetric"]["rawData"] == [[100.0], [102.0], [98.0], [104.0]]): + raise AssertionError + if not (merged["primaryMetric"]["scorePercentiles"]["0.0"] == 98.0): + raise AssertionError + if not (merged["primaryMetric"]["scoreUnit"] == "ops/s"): + raise AssertionError # Iterations inside a fork are not independent, so they must not inflate the sample count. spread = merge_entries([fork(100.0, 100.0, 100.0), fork(104.0, 104.0, 104.0)]) - assert spread["measurementIterations"] == 3 and spread["forks"] == 2 - assert spread["primaryMetric"]["score"] == 102.0 + if not (spread["measurementIterations"] == 3 and spread["forks"] == 2): + raise AssertionError() + if not (spread["primaryMetric"]["score"] == 102.0): + raise AssertionError() two_fork_error = t_quantile(CONFIDENCE_TAIL, 1) * statistics.stdev([100.0, 104.0]) / math.sqrt(2) - assert abs(spread["primaryMetric"]["scoreError"] - two_fork_error) < 1e-9 + if not (abs(spread["primaryMetric"]["scoreError"] - two_fork_error) < 1e-9): + raise AssertionError() for broken, reason in ( ([fork(100.0), fork(102.0, jvm="/jdk21/bin/java")], "a different JVM"), diff --git a/summarize-moveprovider.py b/summarize-moveprovider.py index fc7c4194..0751f859 100644 --- a/summarize-moveprovider.py +++ b/summarize-moveprovider.py @@ -351,69 +351,85 @@ def _selftest() -> None: # Regression: new (slow) is strictly below old (fast). delta, verdict, _ = evaluate_row(fast, slow, COMMIT_BAND) - assert verdict == REGRESSION, verdict - assert delta < 0 + if verdict != REGRESSION: raise AssertionError(verdict) + if delta >= 0: raise AssertionError() # Improvement: new (fast) is strictly above old (slow). delta, verdict, _ = evaluate_row(slow, fast, COMMIT_BAND) - assert verdict == IMPROVEMENT, verdict - assert delta > 0 + if verdict != IMPROVEMENT: raise AssertionError(verdict) + if delta <= 0: raise AssertionError() # Within tolerance: old vs. a slightly higher score, well inside either band. delta, verdict, _ = evaluate_row(fast, same, COMMIT_BAND) - assert verdict == TOLERANCE, verdict + if verdict != TOLERANCE: raise AssertionError(verdict) # NaN confidence interval outside tolerance: can't prove improvement or regression. delta, verdict, _ = evaluate_row(fast, noisy, COMMIT_BAND) - assert verdict == UNDETERMINED, verdict + if not verdict == UNDETERMINED: raise AssertionError(verdict) # The band belongs to the scenario, and a band is only ever a noise floor: a delta inside it is # not reported, whatever the confidence intervals say. drawOnly used to be looser than commitMove # to absorb the JIT compilation lottery; it no longer is, so the only thing to pin here is that # the band, not the confidence interval, decides the small-delta case. - assert evaluate_row(fast, noisy, TOLERANCE_PCT[COMMIT_MOVE])[1] == UNDETERMINED - assert evaluate_row(fast, noisy, 20.0)[1] == TOLERANCE, "a wide enough band swallows any delta" - assert set(TOLERANCE_PCT) == set(SCENARIOS), "every scenario needs a band" + if not evaluate_row(fast, noisy, TOLERANCE_PCT[COMMIT_MOVE])[1] == UNDETERMINED: raise AssertionError() + if not evaluate_row(fast, noisy, 20.0)[1] == TOLERANCE: raise AssertionError("a wide enough band swallows any delta") + if not set(TOLERANCE_PCT) == set(SCENARIOS): raise AssertionError("every scenario needs a band") for scenario, band in TOLERANCE_PCT.items(): - assert band > 0, f"{scenario} needs a positive band" + if not band > 0: raise AssertionError(f"{scenario} needs a positive band") # Missing side never crashes and is reported distinctly. delta, verdict, high_error = evaluate_row(None, fast, COMMIT_BAND) - assert verdict == MISSING and delta is None and high_error is False + if not (verdict == MISSING and delta is None and high_error is False): + raise AssertionError() # High relative error only annotates; it must not override a tolerance/regression verdict. high_err_side = {"score": 100.0, "score_error": 5.0, "conf_lo": 90.0, "conf_hi": 110.0, "moved_values": 2.0} delta, verdict, high_error = evaluate_row(fast, high_err_side, COMMIT_BAND) - assert verdict == TOLERANCE and high_error is True + if not (verdict == TOLERANCE and high_error is True): + raise AssertionError() # The counter is a rate in the primary unit, so values/op is the quotient of the two scores. entry = {"secondaryMetrics": {MOVED_VALUES_KEY: {"score": 300.0}}} - assert read_moved_values(entry, 100.0) == 3.0 - assert read_moved_values({"secondaryMetrics": {}}, 100.0) is None - assert read_moved_values(entry, 0.0) is None - assert read_moved_values({"secondaryMetrics": {MOVED_VALUES_KEY: {"score": "NaN"}}}, 100.0) is None + if not (read_moved_values(entry, 100.0) == 3.0): + raise AssertionError() + if not (read_moved_values({"secondaryMetrics": {}}, 100.0) is None): + raise AssertionError() + if not (read_moved_values(entry, 0.0) is None): + raise AssertionError() + if not (read_moved_values({"secondaryMetrics": {MOVED_VALUES_KEY: {"score": "NaN"}}}, 100.0) is None): + raise AssertionError() # ns/value: 100 ops/s moving 2 values each is 1e9 / 200 ns for each value. - assert ns_per_value(fast) == 1e9 / 200 - assert ns_per_value(None) is None - assert ns_per_value({"score": 100.0, "moved_values": None}) is None - assert ns_per_value({"score": 100.0, "moved_values": 0.0}) is None + if not ns_per_value(fast) == 1e9 / 200: + raise AssertionError() + if not ns_per_value(None) is None: + raise AssertionError() + if not ns_per_value({"score": 100.0, "moved_values": None}) is None: + raise AssertionError() + if not ns_per_value({"score": 100.0, "moved_values": 0.0}) is None: + raise AssertionError() # The workload guard needs both sides, and ignores a difference within tolerance. heavier = dict(fast, moved_values=4.0) # 0.2 % apart: the two sides never agree to the last bit, and that must not raise the flag. barely = dict(fast, moved_values=2.004) - assert workload_changed(fast, heavier) is True - assert workload_changed(fast, barely) is False - assert workload_changed(fast, fast) is False - assert workload_changed(fast, dict(fast, moved_values=None)) is False - assert workload_changed(None, fast) is False - assert WORKLOAD_CHANGED in format_values_per_move(fast, heavier) - assert format_values_per_move(fast, fast) == "2.0" - assert format_values_per_move(None, None) == ABSENT + if not workload_changed(fast, heavier) is True: + raise AssertionError() + if not workload_changed(fast, barely) is False: + raise AssertionError() + if not workload_changed(fast, fast) is False: + raise AssertionError() + if not workload_changed(fast, dict(fast, moved_values=None)) is False: + raise AssertionError() + if not workload_changed(None, fast) is False: + raise AssertionError() + if not WORKLOAD_CHANGED in format_values_per_move(fast, heavier): + raise AssertionError() + if not format_values_per_move(fast, fast) == "2.0": raise AssertionError + if not format_values_per_move(None, None) == ABSENT: raise AssertionError # A workload change annotates only; it must never fail the build. - assert WORKLOAD_CHANGED not in {v.emoji for v in _FAILING_VERDICTS} + if WORKLOAD_CHANGED in {v.emoji for v in _FAILING_VERDICTS}: raise AssertionError # Row order, over every provider the benchmark has, from a deliberately scrambled input. basic = ["change", "swap", "assign", "unassign", @@ -427,14 +443,14 @@ def _selftest() -> None: # Sorted for each family on its own, which is how render_report splits them before rendering. for prefix, names in (("basic:", basic), ("list:", listvar)): wanted_family = [prefix + n for n in names] - assert sorted(sorted(wanted_family), key=provider_sort_key) == wanted_family, \ - sorted(sorted(wanted_family), key=provider_sort_key) + if not sorted(sorted(wanted_family), key=provider_sort_key) == wanted_family: + raise AssertionError(sorted(sorted(wanted_family), key=provider_sort_key)) wanted = ["basic:" + n for n in basic] + ["list:" + n for n in listvar] # Every known provider must resolve to a real operation, or it silently lands in the tail. for provider_key in wanted: - assert provider_sort_key(provider_key)[1] < len(OPERATION_ORDER), provider_key + if provider_sort_key(provider_key)[1] >= len(OPERATION_ORDER): raise AssertionError(provider_key) # An unknown name sorts last rather than into the middle of a group. - assert provider_sort_key("basic:brand_new")[1] == len(OPERATION_ORDER) + if provider_sort_key("basic:brand_new")[1] != len(OPERATION_ORDER): raise AssertionError() # One row for each provider, split into the two families, and either scenario can fail the build. baseline = {("basic:change", COMMIT_MOVE): fast, ("basic:change", DRAW_ONLY): fast, @@ -443,53 +459,53 @@ def _selftest() -> None: sut[("list:list_change", DRAW_ONLY)] = slow expect = ["basic:change", "list:list_change"] rows = build_rows(expect, baseline, sut, {}) - assert len(rows) == len(expect) - assert [r[0] for r in rows] == expect - assert set(rows[0][1]) == set(SCENARIOS) + if len(rows) != len(expect): raise AssertionError() + if [r[0] for r in rows] != expect: raise AssertionError() + if not set(rows[0][1]) == set(SCENARIOS): raise AssertionError report, exit_code = render_report(rows, "v1.0.0", "main", "TimefoldAI") - assert exit_code == 1, "a regression in drawOnly alone must still fail" - assert "#### Basic variable" in report and "#### List variable" in report - assert report.count("| Move provider |") == 2 + if not exit_code == 1: raise AssertionError("a regression in drawOnly alone must still fail") + if not ("#### Basic variable" in report and "#### List variable" in report): raise AssertionError + if not report.count("| Move provider |") == 2: raise AssertionError # Both ranking lines, for both families. - assert report.count("Highest generation ns/value:") == 2 - assert report.count("Highest commit ns/value:") == 2 + if not report.count("Highest generation ns/value:") == 2: raise AssertionError + if not report.count("Highest commit ns/value:") == 2: raise AssertionError # The column explainer, with one row for each of the four columns. - assert "#### What the columns mean" in report + if not "#### What the columns mean" in report: raise AssertionError for column in (f"`{COMMIT_MOVE}`", f"`{DRAW_ONLY}`", "`Values/move`", "`ns/value`"): - assert f"| {column} |" in report, column - assert str(DRAW_ONLY_DRAWS) in report + if not f"| {column} |" in report: raise AssertionError(column) + if not str(DRAW_ONLY_DRAWS) in report: raise AssertionError # The legend is sectioned, and every mark a label cannot explain by itself gets its own bullet. for heading in ("#### What the columns mean", "#### Noise bands", "#### What the marks mean", "#### Notes"): - assert heading in report, heading + if not heading in report: raise AssertionError(heading) for mark in (f"{UNDETERMINED.emoji} {UNDETERMINED.label}:", f"{HIGH_ERROR}:", f"{WORKLOAD_CHANGED}:"): - assert f"- {mark}" in report, mark + if not f"- {mark}" in report: raise AssertionError(mark) # A ⚠️ cell names both its margins below the table; a clean table prints no such line. - assert error_notes(rows) == [] - assert f"{HIGH_ERROR} CHANGE" not in report + if not error_notes(rows) == []: raise AssertionError() + if f"{HIGH_ERROR} CHANGE" in report: raise AssertionError() noisy_sut = dict(sut) noisy_sut[("basic:change", COMMIT_MOVE)] = high_err_side noisy_rows = build_rows(expect, baseline, noisy_sut, {}) notes = error_notes(noisy_rows) - assert len(notes) == 1, notes - assert notes[0] == f"{HIGH_ERROR} CHANGE `{COMMIT_MOVE}`: ± 1.0 % old · ± 5.0 % new, against a +0.0 % delta", notes[0] + if not len(notes) == 1: raise AssertionError(notes) + if not notes[0] == f"{HIGH_ERROR} CHANGE `{COMMIT_MOVE}`: ± 1.0 % old · ± 5.0 % new, against a +0.0 % delta": raise AssertionError(notes[0]) noisy_report, _ = render_report(noisy_rows, "v1.0.0", "main", "TimefoldAI") - assert notes[0] in noisy_report + if not notes[0] in noisy_report: raise AssertionError() # A margin on a zero score is no percentage at all; it prints as absent rather than crashing. zero = {"score": 0.0, "score_error": 1.0, "conf_lo": math.nan, "conf_hi": math.nan, "moved_values": 2.0} zero_rows = build_rows(["basic:change"], {("basic:change", COMMIT_MOVE): high_err_side, ("basic:change", DRAW_ONLY): fast}, {("basic:change", COMMIT_MOVE): zero, ("basic:change", DRAW_ONLY): fast}, {}) - assert error_notes(zero_rows) == [ - f"{HIGH_ERROR} CHANGE `{COMMIT_MOVE}`: ± 5.0 % old · {ABSENT} new, against a -100.0 % delta"] + if not error_notes(zero_rows) == [ + f"{HIGH_ERROR} CHANGE `{COMMIT_MOVE}`: ± 5.0 % old · {ABSENT} new, against a -100.0 % delta"]: raise AssertionError # An old baseline jar still reports singleDraw; the alias joins it to commitMove. - assert SCENARIO_ALIASES.get("singleDraw") == COMMIT_MOVE - assert "manyDraws" not in SCENARIO_ALIASES, "manyDraws has no counterpart and must drop out" + if not SCENARIO_ALIASES.get("singleDraw") == COMMIT_MOVE: raise AssertionError + if "manyDraws" in SCENARIO_ALIASES: raise AssertionError("manyDraws has no counterpart and must drop out") print("summarize-moveprovider.py: selftest OK") diff --git a/summarize-scoredirector.py b/summarize-scoredirector.py index 918f8e5f..9089e381 100644 --- a/summarize-scoredirector.py +++ b/summarize-scoredirector.py @@ -293,48 +293,48 @@ def side(score, error, lo, hi, forks=()): # together resolves far better than either side's own spread suggests. drifting_old = side(100.0, 0.0, math.nan, math.nan, [90, 95, 100, 105, 110]) drifting_new = side(100.0, 0.0, math.nan, math.nan, [90, 95, 100, 105, 110]) - assert resolution(drifting_old, drifting_new) == 0.0, "identical sides resolve perfectly" + if resolution(drifting_old, drifting_new) != 0.0: raise AssertionError("identical sides resolve perfectly") scattered_old = side(100.0, 0.0, math.nan, math.nan, [100, 100, 100, 100, 100]) scattered_new = side(100.0, 0.0, math.nan, math.nan, [90, 95, 100, 105, 110]) - assert resolution(scattered_old, scattered_new) > 10.0, "unpaired scatter is not hidden" + if resolution(scattered_old, scattered_new) <= 10.0: raise AssertionError("unpaired scatter is not hidden") # Anything that cannot be paired resolves to nothing rather than to a wrong number. - assert math.isnan(resolution(fast, slow)), "no rawData, no resolution" - assert math.isnan(resolution(drifting_old, side(100.0, 0.0, 0.0, 0.0, [100, 100]))) - assert math.isnan(resolution(side(1.0, 0.0, 0.0, 0.0, [0.0, 1.0, 2.0]), drifting_old)) + if not math.isnan(resolution(fast, slow)): raise AssertionError("no rawData, no resolution") + if not math.isnan(resolution(drifting_old, side(100.0, 0.0, 0.0, 0.0, [100, 100]))): raise AssertionError() + if not math.isnan(resolution(side(1.0, 0.0, 0.0, 0.0, [0.0, 1.0, 2.0]), drifting_old)): raise AssertionError() - assert is_unresolved(TOLERANCE_PCT + 0.1) and not is_unresolved(TOLERANCE_PCT - 0.1) - assert not is_unresolved(math.nan), "unknown resolution must not raise the flag" + if not (is_unresolved(TOLERANCE_PCT + 0.1) and not is_unresolved(TOLERANCE_PCT - 0.1)): raise AssertionError() + if is_unresolved(math.nan): raise AssertionError("unknown resolution must not raise the flag") # Regression: new (slow) is strictly below old (fast). delta, verdict, _ = evaluate_row(fast, slow) - assert verdict == REGRESSION, verdict - assert delta < 0 + if verdict != REGRESSION: raise AssertionError(verdict) + if not delta < 0: raise AssertionError() # Improvement: new (fast) is strictly above old (slow). delta, verdict, _ = evaluate_row(slow, fast) - assert verdict == IMPROVEMENT, verdict - assert delta > 0 + if verdict != IMPROVEMENT: raise AssertionError(verdict) + if not delta > 0: raise AssertionError() # Within tolerance: old vs. a slightly higher score, well inside the band. delta, verdict, _ = evaluate_row(fast, same) - assert verdict == TOLERANCE, verdict + if verdict != TOLERANCE: raise AssertionError(verdict) # NaN confidence interval outside tolerance: can't prove improvement or regression. delta, verdict, _ = evaluate_row(fast, noisy) - assert verdict == UNDETERMINED, verdict + if not (verdict == UNDETERMINED): raise AssertionError(verdict) # Missing side never crashes and is reported distinctly. delta, verdict, resolution_pct = evaluate_row(None, fast) - assert verdict == MISSING and delta is None and math.isnan(resolution_pct) + if not (verdict == MISSING and delta is None and math.isnan(resolution_pct)): raise AssertionError() # A coarse resolution only annotates; it must not override the verdict. coarse_old = side(100.0, 0.0, math.nan, math.nan, [100, 100, 100, 100, 100]) coarse_new = side(100.0, 0.0, math.nan, math.nan, [80, 90, 100, 110, 120]) delta, verdict, resolution_pct = evaluate_row(coarse_old, coarse_new) - assert verdict == TOLERANCE and is_unresolved(resolution_pct), resolution_pct + if not (verdict == TOLERANCE and is_unresolved(resolution_pct)): raise AssertionError(resolution_pct) - assert format_resolution(math.nan) == ABSENT - assert format_resolution(1.34) == "± 1.3 %" + if not (format_resolution(math.nan) == ABSENT): raise AssertionError() + if not (format_resolution(1.34) == "± 1.3 %"): raise AssertionError() # Row order: alphabetical, from a deliberately scrambled input. expect = ["vehicle_routing", "cloud_balancing", "examination"] @@ -342,31 +342,31 @@ def side(score, error, lo, hi, forks=()): sut = dict(baseline) sut["examination"] = slow rows = build_rows(expect, baseline, sut, {}) - assert [r[0] for r in rows] == sorted(expect) + if not [r[0] for r in rows] == sorted(expect): raise AssertionError report, exit_code = render_report(rows, "v1.0.0", "main", "TimefoldAI") - assert exit_code == 1, "a regression must fail the build" - assert "| Example | Throughput | Resolution |" in report - assert report.count("| Example |") == 1 + if not exit_code == 1: raise AssertionError("a regression must fail the build") + if not "| Example | Throughput | Resolution |" in report: raise AssertionError + if not report.count("| Example |") == 1: raise AssertionError # The legend is sectioned. for heading in ("#### Noise band", "#### Resolution", "#### What the marks mean", "#### Notes"): - assert heading in report, heading + if not heading in report: raise AssertionError(heading) for mark in (f"{UNDETERMINED.emoji} {UNDETERMINED.label}:", f"{UNRESOLVED}:"): - assert f"- {mark}" in report, mark + if f"- {mark}" not in report: raise AssertionError(mark) # A missing side renders as a distinct row rather than vanishing. missing_rows = build_rows(["cloud_balancing", "examination"], {"cloud_balancing": fast}, {"cloud_balancing": fast}, {}) missing_report, missing_exit = render_report(missing_rows, "v1.0.0", "main", "TimefoldAI") - assert missing_exit == 1 - assert f"{MISSING.emoji} {ABSENT} → {ABSENT}" in missing_report + if missing_exit != 1: raise AssertionError() + if f"{MISSING.emoji} {ABSENT} → {ABSENT}" not in missing_report: raise AssertionError() # load_side reads whichever *Example param is present, case-insensitively, and works for either # benchmark class's param name. for param_name in ("csExample", "csJustifiedExample"): params = {param_name: "CLOUD_BALANCING"} example_key = next(key for key in params if key.endswith("Example")) - assert params[example_key].lower() == "cloud_balancing", param_name + if params[example_key].lower() != "cloud_balancing": raise AssertionError(param_name) print("summarize-scoredirector.py: selftest OK")