"""Validate TL DPS analyzer output by re-running the computation and checking ratios."""
from __future__ import annotations
import argparse
import math
from pathlib import Path
from server import DEFAULT_SAMPLE_PATH, analyze_logs
DEFAULT_TOLERANCE = 1e-3
def _resolve_input_path(sample: bool, input_path: Path | None) -> Path:
if sample:
return DEFAULT_SAMPLE_PATH
if input_path is not None:
return input_path
return DEFAULT_SAMPLE_PATH
def _assert_close(label: str, reported: float, expected: float, tolerance: float) -> None:
if not math.isfinite(reported) or not math.isfinite(expected):
raise AssertionError(f"{label}: non-finite value encountered")
if abs(reported - expected) > tolerance:
raise AssertionError(
f"{label}: reported={reported:.6f} expected={expected:.6f} tolerance={tolerance:.6f}"
)
def main() -> None:
parser = argparse.ArgumentParser(
description="Re-run the TL DPS analyzer and ensure DPS aligns with total damage / duration."
)
parser.add_argument(
"--input-path",
type=Path,
help="Optional file or directory containing TL logs.",
)
parser.add_argument(
"--sample",
action="store_true",
help="Use the bundled sample log instead of a custom path.",
)
parser.add_argument(
"--limit-runs",
type=int,
metavar="N",
help="When targeting a directory, only load the newest N log files.",
)
parser.add_argument(
"--tolerance",
type=float,
default=DEFAULT_TOLERANCE,
help=f"Absolute tolerance for DPS checks (default: {DEFAULT_TOLERANCE}).",
)
args = parser.parse_args()
if args.limit_runs is not None and args.limit_runs <= 0:
parser.error("--limit-runs must be a positive integer")
if args.tolerance <= 0:
parser.error("--tolerance must be positive")
input_path = _resolve_input_path(args.sample, args.input_path)
report = analyze_logs(input_path, limit_runs=args.limit_runs)
for run in report["runs"]:
duration = run["duration_seconds"]
expected = run["total_damage"] / duration if duration else 0.0
_assert_close(f"run {run['run_id']}", run["dps"], expected, args.tolerance)
summary = report.get("summary", {})
summary_duration = summary.get("combined_duration_seconds", 0.0)
expected_summary = summary.get("total_damage", 0) / summary_duration if summary_duration else 0.0
_assert_close("summary", summary.get("overall_dps", 0.0), expected_summary, args.tolerance)
print(
"Validation succeeded for",
f"{len(report['runs'])} run(s) at tolerance {args.tolerance} using {input_path}",
)
if __name__ == "__main__":
main()