From 2c27e9f28236ad6a0626f78913b00bfb2569b41c Mon Sep 17 00:00:00 2001 From: bong-water-water-bong Date: Fri, 13 Nov 2026 01:37:28 -0400 Subject: [PATCH] tests: seven helpers treated --help as a path, and one of them WROTE a file named --help `tools/ci/tool_help_check.py --check` covers `--root tools`, so `tests/` and `onnx/` were never asked. Asking them: ``` tests/laya_route_cases.py --help wrote a file named `--help` into the checkout tests/fake_lemonade.py --help ValueError: invalid literal for int(): '--help' tests/fake_spawn_exec.py --help ValueError: invalid literal for int(): '--help' tests/batch_check_state_ci.py --help FileNotFoundError: '--help' tests/embed_forward_ci.py --help FileNotFoundError: '--help' tests/gptoss_forward_ci.py --help FileNotFoundError: '--help' tests/serve_spec_lookup_test.py --help FileNotFoundError: '--help' onnx/tests/onnx_conv_integer_types_test.py --help FileNotFoundError: '--help' ``` Eight of 61. **The first is the one that matters**: `--help` is not a crash there, it is a *write* -- the generator takes `sys.argv[1]` as its output path and `json.dump`s into it, so asking a tool what it does dirties the checkout with a file called `--help`. The other seven are the same bug read-only: the first positional is taken as a runner path or a port with no check. Each now answers with its own usage line and **rc 2**, touching nothing: ``` $ python3 tests/laya_route_cases.py --help # in an empty directory rc=2 Writes tests/laya_route_cases.json: 200 hand-labelled chat requests ... $ ls -A # nothing created ``` **Measured through the gate, not by eye** -- `tool_help_check --check` per root: ``` --root tools rc 0 (95 asked, 7 not asked) unchanged --root tests rc 0 (54 asked, 6 not asked) was 7 crashes of 54 --root onnx rc 0 ( 7 asked, 0 not asked) was 1 crash of 7 ``` Two ctest rows added (`tool_help_check_tests`, `tool_help_check_onnx`, 36.53 s and 1.90 s) so the next one of these is a red row rather than a stray file in someone's checkout, and the flags are checked without a pipe (`rc=2` read directly; `| head` reports `head`'s status, not the tool's). `copyright --check` rc 0. --- CMakeLists.txt | 7 +++++++ onnx/tests/onnx_conv_integer_types_test.py | 3 +++ tests/batch_check_state_ci.py | 3 +++ tests/embed_forward_ci.py | 3 +++ tests/fake_lemonade.py | 3 +++ tests/fake_spawn_exec.py | 3 +++ tests/gptoss_forward_ci.py | 3 +++ tests/laya_route_cases.py | 3 +++ tests/serve_spec_lookup_test.py | 3 +++ 9 files changed, 31 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dbee52ac6..10881beec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,13 @@ add_test(NAME dupdef_check COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/dupdef_c # row keeps the next one from landing. add_test(NAME tool_help_check COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/tool_help_check.py --check --root tools WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +# The same question, asked of the two trees the row above does not cover: `tests/` had 7 helpers that treated the +# flag as a path (4 crashed with FileNotFoundError, 2 as int(), and tests/laya_route_cases.py WROTE a file named +# `--help` into the checkout), plus one onnx runner path. They answer with a usage line and rc 2 now. +add_test(NAME tool_help_check_tests COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/tool_help_check.py --check --root tests + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +add_test(NAME tool_help_check_onnx COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/tool_help_check.py --check --root onnx + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) add_test(NAME tool_help_check_selftest COMMAND python3 ${CMAKE_SOURCE_DIR}/tools/ci/tool_help_check.py --self-test WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) diff --git a/onnx/tests/onnx_conv_integer_types_test.py b/onnx/tests/onnx_conv_integer_types_test.py index 6cf9c0d77..40124e42f 100755 --- a/onnx/tests/onnx_conv_integer_types_test.py +++ b/onnx/tests/onnx_conv_integer_types_test.py @@ -79,6 +79,9 @@ def run(runner, model, tensor, dtype, out_dir): def main(argv): + if len(argv) > 1 and argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0] if __doc__ else "usage: onnx_conv_integer_types_test.py [runner]") + return 2 runner = argv[1] if len(argv) > 1 else "build/ort_run" types = {"uint8": TensorProto.UINT8, "int8": TensorProto.INT8} results = {} diff --git a/tests/batch_check_state_ci.py b/tests/batch_check_state_ci.py index 66a00ca52..0c27541aa 100755 --- a/tests/batch_check_state_ci.py +++ b/tests/batch_check_state_ci.py @@ -508,6 +508,9 @@ def tmp_root(): def main(): + if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0]) + return 2 if len(sys.argv) not in (2, 3) or (len(sys.argv) == 3 and sys.argv[2] not in ("lfm2", "lfm3", "qwen2", "qwen2vl", "qwen2_untied", "qwen3_5", "hy_v3", "hy_v3_sgate")): print(__doc__.strip().splitlines()[2]) return 2 diff --git a/tests/embed_forward_ci.py b/tests/embed_forward_ci.py index 3d531d50c..88a852acc 100644 --- a/tests/embed_forward_ci.py +++ b/tests/embed_forward_ci.py @@ -153,6 +153,9 @@ def run(binary, d, ids, out): def main(): + if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0]) + return 2 if len(sys.argv) != 2: print(__doc__.strip().splitlines()[2]) return 2 diff --git a/tests/fake_lemonade.py b/tests/fake_lemonade.py index 539bee389..ebc2dbdb7 100755 --- a/tests/fake_lemonade.py +++ b/tests/fake_lemonade.py @@ -21,6 +21,9 @@ import json, sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): + print("usage: fake_lemonade.py ") + sys.exit(2) port, record = int(sys.argv[1]), sys.argv[2] diff --git a/tests/fake_spawn_exec.py b/tests/fake_spawn_exec.py index e2e611ea7..6782bb3a3 100644 --- a/tests/fake_spawn_exec.py +++ b/tests/fake_spawn_exec.py @@ -23,6 +23,9 @@ import json, os, sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): + print("usage: fake_spawn_exec.py ") + sys.exit(2) port = int(sys.argv[1]) RECORD = os.environ.get("SPAWN_RECORD") diff --git a/tests/gptoss_forward_ci.py b/tests/gptoss_forward_ci.py index e1b8d3d46..729b57a8b 100644 --- a/tests/gptoss_forward_ci.py +++ b/tests/gptoss_forward_ci.py @@ -253,6 +253,9 @@ def tmp_root(): def main(): + if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0]) + return 2 if len(sys.argv) != 2: print(__doc__.strip().splitlines()[2]) return 2 diff --git a/tests/laya_route_cases.py b/tests/laya_route_cases.py index b94bfe9ac..d427a77fb 100644 --- a/tests/laya_route_cases.py +++ b/tests/laya_route_cases.py @@ -184,6 +184,9 @@ def main(): instr = LONG_INSTR[i % len(LONG_INSTR)] cases.append({"class": "long_doc", "text": f"{instr}\n\n{body(k, r)}"}) assert all(sum(c["class"] == k for c in cases) == 50 for k in ("code", "prose", "short", "long_doc")) + if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0]) + sys.exit(2) out = sys.argv[1] if len(sys.argv) > 1 else "tests/laya_route_cases.json" json.dump({"about": "RFC #186: labelled requests for the Laya request classifier; regenerate with tests/laya_route_cases.py", "classes": ["code", "prose", "short", "long_doc"], "cases": cases}, open(out, "w"), indent=1, ensure_ascii=False) diff --git a/tests/serve_spec_lookup_test.py b/tests/serve_spec_lookup_test.py index e42f66342..54d6ce050 100644 --- a/tests/serve_spec_lookup_test.py +++ b/tests/serve_spec_lookup_test.py @@ -224,6 +224,9 @@ def start(binary, model, port, extra, log): def main(): + if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): + print(__doc__.strip().splitlines()[0]) + return 2 if len(sys.argv) != 2: print(__doc__.strip().splitlines()[2]) return 2