From 4ad3e3097bd60d37f2fdbe193e1d6c47a9a02947 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Sun, 16 Aug 2026 22:11:36 +0900 Subject: [PATCH] refactor: derive the milestone bounds from the registry The highest supported milestone was spelled out in six places across four files: range(1, 7) and default="m6" in test_cli.py, the same pair in test_milestones_dosboxx.py, and through=6 in both registry.py and suite.py. Registering M7 meant finding all six, and missing one failed silently. Derive MAX_MILESTONE and MILESTONES from CASES instead, and move the mN selector parser to registry.milestone_number so the pytest module stops carrying its own copy. Adding cases for a new milestone is now enough for ferro-test to accept --through/--only for it. No behaviour change: MAX_MILESTONE evaluates to 6, ferro-test still advertises {m1..m6} with default m6, and the case snapshot is unchanged. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BScg8CF1sAAM2zVHAu5zvW --- src/ferrolang_vm/registry.py | 17 ++++++++++++++++- src/ferrolang_vm/test_cli.py | 9 ++++----- tools/tests/test_milestones_dosboxx.py | 16 +++------------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/ferrolang_vm/registry.py b/src/ferrolang_vm/registry.py index 99eecc4..5478bbc 100644 --- a/src/ferrolang_vm/registry.py +++ b/src/ferrolang_vm/registry.py @@ -187,7 +187,22 @@ CASES: list[Case] = [ "okstatic", "oktemp", "oktrim", "okwcall")], ] -def all_cases(*, through: int = 6, only: int | None = None) -> list[Case]: +MAX_MILESTONE: int = max(case.milestone for case in CASES) +MILESTONES: tuple[str, ...] = tuple(f"m{number}" + for number in range(1, MAX_MILESTONE + 1)) + + +def milestone_number(name: str) -> int: + """Parse an ``mN`` selector against the milestones the registry knows about.""" + if not name.startswith("m") or not name[1:].isdigit(): + raise ValueError(f"invalid milestone: {name}") + value = int(name[1:]) + if value not in range(1, MAX_MILESTONE + 1): + raise ValueError(f"unsupported milestone: {name}") + return value + + +def all_cases(*, through: int = MAX_MILESTONE, only: int | None = None) -> list[Case]: if only is not None: return [case for case in CASES if case.milestone == only] return [case for case in CASES if case.milestone <= through] diff --git a/src/ferrolang_vm/test_cli.py b/src/ferrolang_vm/test_cli.py index 8a9d1e3..e20f744 100644 --- a/src/ferrolang_vm/test_cli.py +++ b/src/ferrolang_vm/test_cli.py @@ -7,9 +7,7 @@ import sys from pathlib import Path from .dosboxx import DosboxError, setup - - -MILESTONES = tuple(f"m{number}" for number in range(1, 7)) +from .registry import MAX_MILESTONE, MILESTONES def main() -> int: @@ -23,8 +21,9 @@ def main() -> int: help="confirm acceptance of the Sybase Open Watcom Public License") run = commands.add_parser("run", help="build once and run milestone pytest cases") selection = run.add_mutually_exclusive_group() - selection.add_argument("--through", choices=MILESTONES, default="m6", - help="run cumulatively through this milestone (default: m6)") + selection.add_argument("--through", choices=MILESTONES, default=f"m{MAX_MILESTONE}", + help="run cumulatively through this milestone " + f"(default: m{MAX_MILESTONE})") selection.add_argument("--only", choices=MILESTONES, help="run only this milestone's cases") run.add_argument("-v", "--verbose", action="store_true", help="show every pytest case") diff --git a/tools/tests/test_milestones_dosboxx.py b/tools/tests/test_milestones_dosboxx.py index 32d2a01..c386b62 100644 --- a/tools/tests/test_milestones_dosboxx.py +++ b/tools/tests/test_milestones_dosboxx.py @@ -6,23 +6,13 @@ import warnings import pytest from ferrolang_vm.dosboxx import SuiteRun, run_suite -from ferrolang_vm.registry import all_cases +from ferrolang_vm.registry import MAX_MILESTONE, all_cases, milestone_number from ferrolang_vm.suite import Case - -def _number(name: str) -> int: - if not name.startswith("m") or not name[1:].isdigit(): - raise ValueError(f"invalid milestone: {name}") - value = int(name[1:]) - if value not in range(1, 7): - raise ValueError(f"unsupported milestone: {name}") - return value - - ONLY = os.environ.get("FERRO_TEST_ONLY") CASES = all_cases( - through=_number(os.environ.get("FERRO_TEST_THROUGH", "m6")), - only=_number(ONLY) if ONLY else None, + through=milestone_number(os.environ.get("FERRO_TEST_THROUGH", f"m{MAX_MILESTONE}")), + only=milestone_number(ONLY) if ONLY else None, )