42f9d36edf
One file per source area, exercising the pure/isolated parsing and CIDR- arithmetic functions already exposed via headers with no side effects -- parse_port_forward_spec() (protocol suffix parsing/validation, network resolution left to add_port_forward()), resolve_env_specs() (literal and --env-file parsing, ordering, error cases -- a small local RAII ScratchFile helper writes the --env-file fixtures under /tmp), network_subnet.h's CIDR validation/overlap/allocation/address-arithmetic functions, and parse_args() itself against synthetic argv's. Two real bugs found running parse_args() repeatedly in one process (never possible before -- a real invocation only ever calls it once), not assumed: 1. getopt_long's scanning position (`optind`) is process-global and never reset, so a second parse_args() call would silently resume scanning wherever the first one left off. Fixing this alone (optind = 1) wasn't enough on its own, either -- 2. -h/-V return out of the getopt_long loop early (their own `return 0` case), before a call ever completes its scan and lets getopt_long null out its own private `nextchar` pointer -- the *next* parse_args() call then resumed scanning through that stale pointer into the *previous* call's already-destroyed argv strings, misparsing its own fresh argv. glibc documents `optind = 0` (not 1) as the "fully reinitialize private state before rescanning a new argv" signal; switching to it fixed this for good, confirmed by 3 repeated runs each in both random and deterministic (--order lex) Catch2 ordering with zero flakiness either way. Neither bug could ever have surfaced in real usage (parse_args() is only ever called once per process from main()) -- purely a testability gap the new unit tests exposed, now fixed at the source rather than worked around in the test file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz