Files
doslang-mirror/src/ferrolang_vm/test_cli.py
T
coolguy eb85e9fa3d dev: run host syntax gates before starting DOSBox-X
There is an Open Watcom install on this host (C:\WATCOM19), and its Windows
build compiles the compiler's own sources in about a second. Every declaration
mismatch in the unification commits was found that way; each one would
otherwise have cost a DOSBox-X boot and a full compiler build to surface, with
a DOS-side message that names the wrong thing.

Add tools/tests/test_host_syntax.py: compile all twelve sources with the flags
build-dos.bat uses (-za -wx -wcd=202) and fail on any diagnostic. It skips when
Watcom is absent, so the suite still runs elsewhere. Two structural checks come
with it -- that build-dos.bat, the Makefile and the test agree on the source
list, and that no .c under fec/src is compiled by nothing. Both would have
caught check.c and emit_c.c quietly leaving the build when the M7 wrappers
included them textually.

ferro-test now runs these and the 8.3 name check before starting the VM, and
stops if they fail.

This is not verification and does not claim to be: wcc386 targets 32-bit where
the real build is 16-bit large model, so it sees syntax, types and declarations
and nothing about code generation. The DOS build and the milestone suite remain
the gate. It only moves the cheap failures earlier.
2026-08-17 01:57:06 +09:00

91 lines
4.2 KiB
Python

"""Developer test entry point backed by a disposable DOSBox-X run."""
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
from .dosboxx import DosboxError, setup
from .registry import MAX_MILESTONE, MILESTONES
def main() -> int:
parser = argparse.ArgumentParser(
prog="ferro-test",
description="Fast Ferro development tests in DOSBox-X/Open Watcom.",
)
commands = parser.add_subparsers(dest="command", required=True)
prepare = commands.add_parser("setup", help="download and verify pinned development tools")
prepare.add_argument("--accept-watcom-license", action="store_true",
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=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")
run.add_argument("--keep-failed", action="store_true",
help="keep the disposable DOS filesystem after failures")
run.add_argument("--show-dos", action="store_true",
help="show DOSBox-X and wait for a key before closing")
run.add_argument("--dos-log", action="store_true",
help="print the captured DOS console after the run")
run.add_argument("--trace-dos", action="store_true",
help="do not redirect case command output")
run.add_argument("-k", dest="select", metavar="EXPR",
help="run only cases whose id matches this pytest -k expression")
args, extra = parser.parse_known_args()
try:
if args.command == "setup":
dosbox, watcom = setup(accept_watcom_license=args.accept_watcom_license)
print(f"DOSBox-X: {dosbox}")
print(f"Open Watcom: {watcom}")
return 0
if args.only:
os.environ["FERRO_TEST_ONLY"] = args.only
else:
os.environ["FERRO_TEST_THROUGH"] = args.through
for enabled, name in (
(args.keep_failed, "FERRO_TEST_KEEP_FAILED"),
(args.show_dos, "FERRO_TEST_SHOW_DOS"),
(args.trace_dos, "FERRO_TEST_TRACE_DOS"),
(args.dos_log, "FERRO_TEST_DOS_LOG"),
):
if enabled:
os.environ[name] = "1"
import pytest
from .paths import ROOT
# The package can be imported from a different checkout than the one the
# shell is sitting in -- an editable install plus a git worktree is enough
# to silently build and test the wrong tree. Say which tree this is.
print(f"ferro-test: building {ROOT}", file=sys.stderr)
tests = ROOT / "tools" / "tests"
# The host gates run first and take under a second. A missing declaration
# or an 8.3-illegal name would otherwise be found only after a DOSBox-X
# boot and a full compiler build, and the DOS-side message for either is
# unhelpful. They do not replace the DOS run; they precede it.
gates = [os.fspath(tests / name) for name in
("test_host_syntax.py", "test_dos_names.py")]
if pytest.main([*gates, "-q", "--no-header"]) != 0:
print("ferro-test: host gates failed; not starting DOSBox-X",
file=sys.stderr)
return 1
test_file = os.fspath(tests / "test_milestones_dosboxx.py")
pytest_args = [test_file, "--tb=short", "-v" if args.verbose else "-q"]
if args.select:
pytest_args.extend(["-k", args.select])
if args.dos_log:
pytest_args.append("-s")
pytest_args.extend(extra)
return int(pytest.main(pytest_args))
except (DosboxError, ValueError) as exc:
print(f"ferro-test: {exc}", file=sys.stderr)
return 2
if __name__ == "__main__":
raise SystemExit(main())