(
results: list[Result],
loop_stats: LoopStats,
args: argparse.Namespace,
)
| 385 | |
| 386 | |
| 387 | def _build_report( |
| 388 | results: list[Result], |
| 389 | loop_stats: LoopStats, |
| 390 | args: argparse.Namespace, |
| 391 | ) -> dict: |
| 392 | ok = [r for r in results if r.ok] |
| 393 | fail = [r for r in results if not r.ok] |
| 394 | lat = [r.latency_ms for r in ok] |
| 395 | total = len(results) |
| 396 | elapsed = float(args.duration) |
| 397 | |
| 398 | status_hist: dict[int, int] = {} |
| 399 | err_hist: dict[str, int] = {} |
| 400 | for r in fail: |
| 401 | status_hist[r.status] = status_hist.get(r.status, 0) + 1 |
| 402 | if r.err: |
| 403 | err_hist[r.err] = err_hist.get(r.err, 0) + 1 |
| 404 | |
| 405 | report = { |
| 406 | "mode": "open-loop" if args.rps else "closed-loop", |
| 407 | "url": args.url, |
| 408 | "duration_s": args.duration, |
| 409 | "concurrency": args.concurrency, |
| 410 | "target_rps": args.rps, |
| 411 | "simple_ratio": args.simple_ratio, |
| 412 | "seed": args.seed, |
| 413 | "total": total, |
| 414 | "ok": len(ok), |
| 415 | "fail": len(fail), |
| 416 | "rps_achieved": total / elapsed if elapsed else 0.0, |
| 417 | "status_hist": status_hist, |
| 418 | "err_hist": dict(list(err_hist.items())[:10]), |
| 419 | "latency_ms": pcts(lat) if lat else {}, |
| 420 | "response_bytes_mean": (statistics.mean(r.bytes for r in ok) if ok else 0.0), |
| 421 | } |
| 422 | if args.rps: |
| 423 | report["scheduler"] = { |
| 424 | "scheduled": loop_stats.scheduled, |
| 425 | "dropped": loop_stats.dropped, |
| 426 | "in_flight_peak": loop_stats.in_flight_peak, |
| 427 | } |
| 428 | if args.bucket_seconds: |
| 429 | report["buckets"] = _bucket_stats(results, args.bucket_seconds, elapsed) |
| 430 | return report |
| 431 | |
| 432 | |
| 433 | def _print_report(report: dict) -> None: |
no test coverage detected