diff --git a/negate/__main__.py b/negate/__main__.py index 1721b9b..5a3bf06 100644 --- a/negate/__main__.py +++ b/negate/__main__.py @@ -14,6 +14,8 @@ from typing import Any from negate.io.console import CLI_LOGGER, set_root_folder +from negate.io.blurb import BlurbText +from negate.extract.unified_core import ExtractionModule ROOT_FOLDER = Path(__file__).resolve().parent.parent CONFIG_PATH = ROOT_FOLDER / "config" @@ -106,7 +108,9 @@ def _load_model_choices() -> ModelChoices: return choices -def _build_parser(blurb: BlurbText, choices: ModelChoices, list_results: list[str], list_model: list[str], inference_pair: list[str]) -> argparse.ArgumentParser: +def _build_parser( + blurb: BlurbText, choices: ModelChoices, list_results: list[str], list_model: list[str], inference_pair: list[str] +) -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Negate CLI") subparsers = parser.add_subparsers(dest="cmd", required=True) @@ -115,11 +119,13 @@ def _build_parser(blurb: BlurbText, choices: ModelChoices, list_results: list[st train_parser.add_argument("-l", "--loop", action="store_true", help=blurb.loop) train_parser.add_argument("-f", "--features", choices=list_results, default=None, help=blurb.features_load) - process_parser = subparsers.add_parser("process", help="Run all decompose/extract module combinations") + module_list = ", ".join(mod.name for mod in ExtractionModule) + process_help = f"Run all decompose/extract module combinations. Available modules: {module_list}" + process_parser = subparsers.add_parser("process", help=process_help) process_parser.add_argument("path", help=blurb.unidentified_path) process_parser.add_argument("-v", "--verbose", action="store_true", help=blurb.verbose) process_parser.add_argument("--transposed", default=None, help="Comma-separated transposed indices") - process_parser.add_argument("--combination", default=None, help="Comma-separated module names") + process_parser.add_argument("--combination", default=None, help=f"Comma-separated module names from: {module_list}") process_parser.add_argument("--train", choices=["convnext", "xgboost"], default=None, help="Train model after processing") vit_help = f"Vison {blurb.model_desc} {choices.default_vit}".strip() diff --git a/negate/command.py b/negate/command.py index ff53286..ca0c6cf 100644 --- a/negate/command.py +++ b/negate/command.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Any import time as timer_module +from negate.io.console import configure_runtime_logging, CLI_LOGGER start_ns = timer_module.perf_counter() diff --git a/results/20260412_080531/results_real_20260412_080531.json b/results/20260412_080531/results_real_20260412_080531.json new file mode 100644 index 0000000..cec4f95 --- /dev/null +++ b/results/20260412_080531/results_real_20260412_080531.json @@ -0,0 +1,3 @@ +{ + "x_p": "{'image_mean_ff': 0.5551752934617227, 'image_std': 0.0977445244532872, 'image_mean': (28894107043657, 57788214087314), 'diff_mean': (25353770906193, 50707541812387), 'laplace_mean': (24083985868529, 48167971737058), 'sobel_mean': (26436775610593, 52873551221187), 'image_tc': (16, 16), 'diff_tc': (29, 29), 'laplace_tc': (25, 25), 'sobel_tc': (25, 25), 'spectral_tc': (16, 16)}" +} \ No newline at end of file diff --git a/tests/test_main_help.py b/tests/test_main_help.py new file mode 100644 index 0000000..db008a5 --- /dev/null +++ b/tests/test_main_help.py @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: MPL-2.0 AND LicenseRef-Commons-Clause-License-Condition-1.0 +# + +"""Test suite for CLI help text.""" + +import argparse +import sys +from io import StringIO +from negate.extract.unified_core import ExtractionModule + + +def test_process_help_includes_all_modules(): + """Verify process command help lists all ExtractionModule options.""" + from negate.__main__ import _load_blurb_text, _load_model_choices, _build_parser + + blurb = _load_blurb_text() + choices = _load_model_choices() + + parser = _build_parser( + blurb=blurb, + choices=choices, + list_results=[], + list_model=[], + inference_pair=[], + ) + + help_output = StringIO() + try: + parser.parse_args(["process", "-h"]) + except SystemExit: + pass + + help_text = parser.format_help() + process_subparser = None + + for action in parser._subparsers._actions: + if isinstance(action, argparse._SubParsersAction): + if "process" in action.choices: + process_subparser = action.choices["process"] + break + + assert process_subparser is not None, "process subparser not found" + + process_help = process_subparser.format_help() + + for module in ExtractionModule: + assert module.name in process_help, f"Module {module.name} not found in process help text" + + +def test_process_help_module_list_matches_enum(): + """Verify the module list in help matches ExtractionModule enum.""" + from negate.__main__ import _load_blurb_text, _load_model_choices, _build_parser + + blurb = _load_blurb_text() + choices = _load_model_choices() + + parser = _build_parser( + blurb=blurb, + choices=choices, + list_results=[], + list_model=[], + inference_pair=[], + ) + + for action in parser._subparsers._actions: + if isinstance(action, argparse._SubParsersAction): + if "process" in action.choices: + process_subparser = action.choices["process"] + break + + process_help = process_subparser.format_help() + + module_names = [mod.name for mod in ExtractionModule] + for module_name in module_names: + assert module_name in process_help, f"Module {module_name} missing from help" + + assert len(module_names) == len(ExtractionModule), "Module list count mismatch"