diff --git a/README.md b/README.md index dd9938f..27ac202 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ cargo install --path crates/irtt-cli --features tui Or run it directly without installing: ```sh -cargo run -p irtt-cli -- +cargo run -p irtt-cli --bin irtt-cli -- ``` For space-sensitive packaging, such as OpenWrt packages, distributors can ship @@ -52,9 +52,29 @@ only `irtt-rs` and symlink or hardlink applet names such as `irtt-cli`, `irtt-tui`, and future server applet names to it. Applet dispatch is based on the invoked binary name. -cargo-dist release archives currently bundle all enabled applets for a target. -That is separate from space-sensitive package layouts, which may still ship one -binary plus applet-name symlinks or hardlinks. +cargo-dist release archives are currently configured with the `full` feature +set, so target archives bundle the enabled applet binaries: `irtt-rs`, +`irtt-cli`, and `irtt-tui`. That is separate from space-sensitive package +layouts, which may still ship one dispatcher binary plus applet-name symlinks +or hardlinks. cargo-dist is not currently configured to create those links. + +## Build verification + +Useful local release/package sanity checks: + +```sh +cargo fmt --check +cargo test -p irtt-cli +cargo clippy --workspace --all-targets -- -D warnings +cargo build -p irtt-cli --release +cargo build -p irtt-cli --no-default-features --release +cargo build -p irtt-cli --features tui --release +cargo build -p irtt-cli --all-features --release +``` + +The no-default-features build only provides the `irtt-rs` dispatcher binary; +the default build provides `irtt-rs` and `irtt-cli`; `tui`, `full`, and +all-features builds also provide `irtt-tui`. ## CLI usage @@ -101,7 +121,7 @@ For available options: ```sh irtt-cli --help -irtt-tui --help +irtt-tui --help # when built or installed with the tui feature ``` ## Event row output diff --git a/scripts/compare_irtt_clients.py b/scripts/compare_irtt_clients.py index caa86c1..540a801 100755 --- a/scripts/compare_irtt_clients.py +++ b/scripts/compare_irtt_clients.py @@ -9,6 +9,7 @@ the local Rust client output. from __future__ import annotations import argparse +import csv import datetime as dt import json import math @@ -147,7 +148,7 @@ def parse_args() -> argparse.Namespace: default=DEFAULT_IRTT_RS_COMMAND, help=( "Command prefix or direct path for the Rust client. Arguments for " - "target, duration, interval, and machine output are appended. " + "target, duration, interval, and CSV event output are appended. " f"Default: {DEFAULT_IRTT_RS_COMMAND!r}" ), ) @@ -386,8 +387,10 @@ def run_case( case.duration, "--interval", case.interval, - "--output", - "machine", + "--format", + "csv", + "--columns", + "event,seq,effective_rtt_us,raw_rtt_us,adjusted_rtt_us,server_processing_us,client_send_wall_ns", *args.rust_extra_arg, ] @@ -402,7 +405,7 @@ def run_case( rust_samples = case_dir / "irtt-rs-rtt-samples.csv" upstream_samples = case_dir / "upstream-rtt-samples.csv" - rust_metrics = parse_rust_machine(case_dir / "irtt-rs.stdout", rust_samples) + rust_metrics = parse_rust_csv(case_dir / "irtt-rs.stdout", rust_samples) upstream_metrics = parse_upstream(case_dir / "upstream.stdout", upstream_json, upstream_samples) comparison = classify_difference(upstream_metrics, rust_metrics) @@ -529,16 +532,7 @@ def wait_process(proc: subprocess.Popen[bytes] | None) -> int: return proc.wait() -def parse_kv_line(line: str) -> dict[str, str]: - fields: dict[str, str] = {} - for token in shlex.split(line): - if "=" in token: - key, value = token.split("=", 1) - fields[key] = value - return fields - - -def parse_rust_machine(path: Path, samples_path: Path) -> Metrics: +def parse_rust_csv(path: Path, samples_path: Path) -> Metrics: metrics = Metrics(source="irtt-rs", samples_path=str(samples_path)) effective: list[float] = [] raw: list[float] = [] @@ -552,11 +546,11 @@ def parse_rust_machine(path: Path, samples_path: Path) -> Metrics: metrics.parse_notes.append(f"missing {path}") return metrics - for line in path.read_text(encoding="utf-8", errors="replace").splitlines(): - if not line.strip(): - continue - fields = parse_kv_line(line) + text = path.read_text(encoding="utf-8", errors="replace") + for fields in csv.DictReader(line for line in text.splitlines() if line.strip()): event = fields.get("event") + if event is None: + continue seq = parse_int(fields.get("seq")) if event == "echo_reply": if seq is not None: