Skip to content

test: cover converter CLI entry points#2387

Open
lonexreb wants to merge 1 commit intoNVIDIA-NeMo:mainfrom
lonexreb:test/2259-converter-cli-coverage
Open

test: cover converter CLI entry points#2387
lonexreb wants to merge 1 commit intoNVIDIA-NeMo:mainfrom
lonexreb:test/2259-converter-cli-coverage

Conversation

@lonexreb
Copy link
Copy Markdown

@lonexreb lonexreb commented May 4, 2026

Summary

Closes the test-coverage gap identified in #2259 — the CLI entry points
(parse_args + main) of the three converter scripts under
examples/converters/ are now exercised by tests, both quickly via
unit tests and end-to-end against real model artifacts in the existing
functional roundtrip test.

The existing tests/functional/test_converter_roundtrip.py only called
the underlying library functions (convert_dcp_to_hf,
export_model_from_megatron, merge_lora_to_hf,
export_lora_adapter_to_hf). The argparse + main() shells of
convert_dcp_to_hf.py, convert_megatron_to_hf.py, and
convert_lora_to_hf.py were never invoked, so regressions in their CLI
contracts (missing flags, broken config-vs-CLI overrides, wrong dispatch
on --adapter-only, etc.) shipped silently.

What's covered

New fast unit tests under tests/unit/converters/

These run without GPU, Ray, or model downloads. The underlying library
calls are mocked out so the tests stay in the L0 budget.

  • test_convert_dcp_to_hf_args.py — flags, defaults, unknown-flag
    exit code, plus main()'s local-tokenizer-vs-config-fallback branch
    selection and hf_overrides forwarding (including the YAML
    null → {} collapse).
  • test_convert_megatron_to_hf_args.py (marked mcore) — flags,
    defaults, the --no-strict toggle wiring through to the strict=
    kwarg, and the --hf-model-name override path.
  • test_convert_lora_to_hf_args.py — required-flag enforcement
    (parametrized over each), --adapter-only dispatch in both
    branches, and the refuse-to-overwrite contract on existing output
    paths.

Extended functional test (tests/functional/test_converter_roundtrip.py)

Added a STEP 10 that invokes each script's main() in-process via
sys.argv patching, against the same model artifacts produced earlier
in the test, and compares the resulting HF checkpoints against the
previously validated state dicts:

  • 10a) convert_dcp_to_hf.py CLI on the v1 DCP checkpoint (also
    exercises the local-tokenizer-path branch by saving the tokenizer
    next to the DCP ckpt).
  • 10b) convert_megatron_to_hf.py CLI on the Megatron checkpoint.
  • 10c) convert_lora_to_hf.py CLI — merged-model branch (no
    --adapter-only).
  • 10d) convert_lora_to_hf.py CLI — adapter-only branch
    (--adapter-only), verified by checking the resulting PEFT
    directory layout.

Test plan

  • ruff check tests/unit/converters/ tests/functional/test_converter_roundtrip.py — clean
  • ruff format — applied
  • python3 -m py_compile — clean
  • L0_Unit_Tests_Other (CI) — runs the new tests/unit/converters/ suite
  • L0_Unit_Tests with --mcore-only (CI) — runs
    test_convert_megatron_to_hf_args.py
  • tests/functional/test_converters.sh (CI) — runs the extended
    roundtrip including STEP 10

Notes

  • All three new unit-test files have the NVIDIA copyright header per
    repo convention.
  • Each main()-exercising unit test uses a fresh importlib-loaded
    module copy so sys.argv patches cannot leak across tests.
  • The functional test's new helper _run_script_main invokes main()
    in-process to keep STEP 10 inside the same Ray/CUDA context as the
    rest of the test.

Refs: #2259

The existing functional roundtrip test only exercised the inner library
functions (convert_dcp_to_hf, export_model_from_megatron, merge_lora_to_hf
and export_lora_adapter_to_hf). The argparse + main() surfaces of the
three converter scripts under examples/converters/ were never invoked,
so regressions in their CLI contracts (missing flags, broken
config-vs-CLI overrides, wrong dispatch on --adapter-only, etc.) shipped
silently.

Closes the gap on two fronts:

1. New fast unit tests under tests/unit/converters/ exercise parse_args()
   and main()'s argument-handling logic for all three scripts. The
   underlying library calls are mocked out so the tests run without GPU,
   Ray, or model downloads:
   - test_convert_dcp_to_hf_args.py: validates flags, defaults, the
     local-tokenizer-vs-config-fallback branch, and hf_overrides
     forwarding (including the YAML-null-collapses-to-empty-dict edge
     case).
   - test_convert_megatron_to_hf_args.py (mcore-marked): validates flags,
     --no-strict toggle wiring through to the strict= kwarg, and the
     --hf-model-name override path.
   - test_convert_lora_to_hf_args.py: validates required-flag
     enforcement, --adapter-only dispatch in both branches, and the
     refuse-to-overwrite contract on existing output paths.

2. Extended tests/functional/test_converter_roundtrip.py with a STEP 10
   that invokes each script's main() in-process via sys.argv patching
   against the same model artifacts produced earlier in the test, then
   compares the resulting HF checkpoints against the previously
   validated state dicts. This exercises the real end-to-end CLI path
   for convert_dcp_to_hf.py, convert_megatron_to_hf.py, and both
   branches of convert_lora_to_hf.py.

Refs: NVIDIA-NeMo#2259
Signed-off-by: lonexreb <reach2shubhankar@gmail.com>
@lonexreb lonexreb requested a review from a team as a code owner May 4, 2026 06:24
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 4, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants