import argparse
import os
import platform
import shutil
import subprocess
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from fastfetch_configs import MODES, REPO_ROOT
MODE_NAMES = tuple(mode for mode, _ in MODES)
RUN_ORDER = ("default", "short", "long", "full")
def retch_binary(system=None):
if (system or platform.system()) == "Windows":
return r".\target\release\retch.exe"
return "./target/release/retch"
def retch_command(mode, system=None):
binary = retch_binary(system)
return binary if mode == "default" else f"{binary} --{mode}"
def fastfetch_command(mode):
return f"fastfetch -c benches/fastfetch/{mode}.jsonc"
def export_name(mode):
return f"hyperfine_{mode}.json"
def pairs(system=None, with_fastfetch=True):
out = []
for mode in RUN_ORDER:
cmds = [retch_command(mode, system)]
if with_fastfetch:
cmds.append(fastfetch_command(mode))
out.append((mode, cmds))
return out
def run(warmup, runs, out_dir, with_fastfetch=True):
out_dir = os.path.abspath(out_dir)
if with_fastfetch:
version = subprocess.run(["fastfetch", "--version"], capture_output=True, text=True)
print(f"fastfetch: {version.stdout.strip() or '(no version output)'}", flush=True)
written = []
for mode, cmds in pairs(with_fastfetch=with_fastfetch):
path = os.path.join(out_dir, export_name(mode))
print(f"\n=== {mode}: {' vs '.join(cmds)}", flush=True)
subprocess.run(["hyperfine", "--warmup", str(warmup), "--runs", str(runs),
"--export-json", path, *cmds], cwd=REPO_ROOT, check=True)
written.append(path)
return written
def _self_test():
from bench_labels import EXPECTED_LABELS, label_for_command
failures = []
def expect(name, cond, detail=""):
if not cond:
failures.append(f"{name}: {detail}")
expect("four modes, least to most verbose",
MODE_NAMES == ("short", "default", "long", "full"), str(MODE_NAMES))
expect("default runs first, then the rest least to most verbose",
RUN_ORDER == ("default", "short", "long", "full"), str(RUN_ORDER))
expect("the run order covers every mode exactly once",
sorted(RUN_ORDER) == sorted(MODE_NAMES), f"{RUN_ORDER} vs {MODE_NAMES}")
expect("pairs() follows the run order",
tuple(mode for mode, _ in pairs("Linux")) == RUN_ORDER,
str([mode for mode, _ in pairs("Linux")]))
for system in ("Linux", "Darwin", "Windows"):
labels = [label_for_command(c) for _, cmds in pairs(system) for c in cmds]
expect(f"{system}: every command maps to an expected series",
set(labels) == set(EXPECTED_LABELS),
f"produced {sorted(set(labels))}, expected {sorted(EXPECTED_LABELS)}")
expect(f"{system}: no two commands share a series", len(labels) == len(set(labels)),
str(labels))
for mode in MODE_NAMES:
path = os.path.join(REPO_ROOT, "benches", "fastfetch", f"{mode}.jsonc")
expect(f"{mode} config exists", os.path.isfile(path), path)
expect(f"{mode} export matches parse_criterion's glob",
export_name(mode).startswith("hyperfine_") and export_name(mode).endswith(".json"))
expect("default mode passes no flag", retch_command("default", "Linux") == "./target/release/retch")
expect("full mode passes --full", retch_command("full", "Windows").endswith("retch.exe --full"))
if failures:
for f in failures:
print(f" FAIL {f}", file=sys.stderr)
print(f"cli_bench.py self-test FAILED ({len(failures)})", file=sys.stderr)
return 1
print("cli_bench.py self-test passed")
return 0
def main(argv):
if argv == ["--self-test"]:
return _self_test()
ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
ap.add_argument("--warmup", type=int, default=3)
ap.add_argument("--runs", type=int, default=10)
ap.add_argument("--out-dir", default=".")
ap.add_argument("--no-fastfetch", action="store_true",
help="benchmark retch alone (fastfetch not installed)")
args = ap.parse_args(argv)
if not shutil.which("hyperfine"):
print("cli_bench: hyperfine not found (cargo install hyperfine)", file=sys.stderr)
return 1
with_ff = not args.no_fastfetch
if with_ff and not shutil.which("fastfetch"):
print("cli_bench: fastfetch not found; pass --no-fastfetch to benchmark retch alone",
file=sys.stderr)
return 1
run(args.warmup, args.runs, args.out_dir, with_ff)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))