From 3e0701bcbae030c4e87f278c9ba0fc2b73253633 Mon Sep 17 00:00:00 2001 From: markmental Date: Wed, 4 Nov 2026 12:24:58 -0500 Subject: [PATCH] Fix invalid examples in usage, add regression testing to catch this going forward --- ipvarch.c | 2 +- tests/cases.py | 2 ++ tests/runner.py | 27 ++++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/ipvarch.c b/ipvarch.c index b15114b..3c6e1ee 100644 --- a/ipvarch.c +++ b/ipvarch.c @@ -62,7 +62,7 @@ static void usage(void) printf("Examples:\n"); printf(" ipvarch 192.168.1.1\n"); printf(" ipvarch 2001:db8::1\n"); - printf(" ipvarch --plan-network 172.16.4.0/16\n"); + printf(" ipvarch --plan-network 172.16.0.0/16\n"); printf(" ipvarch --random-ipv4network private /24\n"); return; } diff --git a/tests/cases.py b/tests/cases.py index dd55794..67b83c4 100644 --- a/tests/cases.py +++ b/tests/cases.py @@ -202,5 +202,7 @@ GROUPS = [ case("--help", ["--help"], must=["Usage:"]), case("help", ["help"], must=["Usage:"]), case("no arguments", [], exit=1, must=["Usage:"]), + case("usage examples are plannable", ["--help"], + validator="usage-examples"), ]), ] diff --git a/tests/runner.py b/tests/runner.py index 265e3d2..a26e2ef 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -23,6 +23,7 @@ from cases import GROUPS # noqa: E402 REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SAN_BIN = os.path.join("/tmp/opencode", "ipvarch-asan") +_usage_bin = None # ---------------------------------------------------------------- table @@ -178,12 +179,32 @@ def _random_check(out, want_private): return True, "" +def v_usage_examples(out): + """Every / example in usage output must be planned OK + when fed back to the binary.""" + bin_path = _usage_bin + rows = [] + for m in re.finditer(r"(\d{1,3}(?:\.\d{1,3}){3})/(\d+)", out): + rows.append((m.group(1), int(m.group(2)))) + if not rows: + return False, "usage shows no / examples" + for net, prefix in rows: + rc, out2, err2 = run_bin(bin_path, ["--plan-network", + "%s/%d" % (net, prefix)]) + if rc != 0 or "IPVarch Network Plan" not in out2: + return False, ("usage example %s/%d is not a plannable network " + "(exit=%d, stderr=%r)" % + (net, prefix, rc, err2.strip())) + return True, "" + + VALIDATORS = { "plan": v_plan, "plan-gateway": v_plan, "table": v_table_layout, "random-private": lambda out: _random_check(out, True), "random-public": lambda out: _random_check(out, False), + "usage-examples": v_usage_examples, } # ---------------------------------------------------------------- run @@ -250,6 +271,7 @@ def build_sanitize(repo): def main(): + global _usage_bin ap = argparse.ArgumentParser(description="IPVarch test runner") ap.add_argument("--bin", default=os.path.join(REPO, "ipvarch")) ap.add_argument("--sanitize", action="store_true", @@ -270,6 +292,8 @@ def main(): else: bin_path = args.bin + _usage_bin = bin_path + if not os.path.exists(bin_path): print("error: binary not found: %s (run 'make' first)" % bin_path, file=sys.stderr) @@ -280,7 +304,8 @@ def main(): ran_any = False for gname, cases in GROUPS: - if args.filter and args.filter not in gname: + if args.filter and args.filter not in gname and \ + not any(args.filter in case["name"] for case in cases): continue print("== %s ==" % gname) for case in cases: