Files
2026-07-13 13:11:15 +08:00

148 lines
6.3 KiB
Python

import contextlib
import io
import subprocess
import sys
import unittest
from unittest import mock
from tests.python.support.paths import add_repo_path, repo_path
add_repo_path("skills/cad/scripts")
from step import cli
class StepCliTests(unittest.TestCase):
def test_requires_explicit_target(self) -> None:
with self.assertRaises(SystemExit) as cm:
cli.main([])
self.assertEqual(2, cm.exception.code)
def test_passes_targets_in_order_without_kind_for_generated_sources(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(0, cli.main(["parts/second.py", "parts/first.py"]))
generate.assert_called_once()
self.assertEqual(["parts/second.py", "parts/first.py"], generate.call_args.args[0])
self.assertIsNone(generate.call_args.kwargs["direct_step_kind"])
self.assertFalse(generate.call_args.kwargs["step_options"].has_metadata)
self.assertIsNone(generate.call_args.kwargs["output"])
self.assertFalse(generate.call_args.kwargs["skip_step_write"])
self.assertFalse(generate.call_args.kwargs["verbose"])
def test_passes_direct_step_kind(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(0, cli.main(["--kind", "assembly", "assemblies/second.step", "assemblies/first.step"]))
generate.assert_called_once()
self.assertEqual(["assemblies/second.step", "assemblies/first.step"], generate.call_args.args[0])
self.assertEqual("assembly", generate.call_args.kwargs["direct_step_kind"])
self.assertFalse(generate.call_args.kwargs["step_options"].has_metadata)
self.assertIsNone(generate.call_args.kwargs["output"])
self.assertFalse(generate.call_args.kwargs["verbose"])
def test_passes_verbose_flag(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(0, cli.main(["parts/sample.py", "--verbose"]))
self.assertTrue(generate.call_args.kwargs["verbose"])
def test_passes_skip_step_write_flag(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(0, cli.main(["parts/sample.py", "--skip-step-write"]))
self.assertTrue(generate.call_args.kwargs["skip_step_write"])
def test_passes_output_flag(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(0, cli.main(["parts/sample.py", "-o", "STEP/sample.step"]))
self.assertEqual("STEP/sample.step", generate.call_args.kwargs["output"])
def test_output_flag_rejects_multiple_targets(self) -> None:
with self.assertRaises(SystemExit) as cm:
cli.main(["parts/first.py", "parts/second.py", "-o", "STEP/first.step"])
self.assertEqual(2, cm.exception.code)
def test_passes_import_metadata_flags(self) -> None:
with mock.patch.object(cli, "generate_step_targets", return_value=0) as generate:
self.assertEqual(
0,
cli.main(
[
"--kind",
"part",
"imports/sample_part.step",
"--stl",
"../meshes/sample_part.stl",
"--3mf",
"../meshes/sample_part.3mf",
"--glb",
"../meshes/sample_part.glb",
"--mesh-tolerance",
"0.2",
"--mesh-angular-tolerance",
"0.25",
]
),
)
generate.assert_called_once()
self.assertEqual(["imports/sample_part.step"], generate.call_args.args[0])
self.assertEqual("part", generate.call_args.kwargs["direct_step_kind"])
options = generate.call_args.kwargs["step_options"]
self.assertEqual("../meshes/sample_part.stl", options.stl)
self.assertEqual("../meshes/sample_part.3mf", options.three_mf)
self.assertEqual("../meshes/sample_part.glb", options.glb)
self.assertEqual(0.2, options.mesh_tolerance)
self.assertEqual(0.25, options.mesh_angular_tolerance)
def test_rejects_invalid_numeric_flag(self) -> None:
with self.assertRaises(SystemExit) as cm:
cli.main(["imports/sample_part.step", "--mesh-tolerance", "nan"])
self.assertEqual(2, cm.exception.code)
def test_rejects_removed_inspect_flag(self) -> None:
with self.assertRaises(SystemExit) as cm:
cli.main(["parts/sample.step", "--inspect", "refs {cad_token} --facts"])
self.assertEqual(2, cm.exception.code)
def test_help_has_current_step_flags_only(self) -> None:
stream = io.StringIO()
with self.assertRaises(SystemExit) as cm, contextlib.redirect_stdout(stream):
cli.main(["--help"])
self.assertEqual(0, cm.exception.code)
help_text = stream.getvalue()
self.assertIn("--kind", help_text)
self.assertIn("--stl", help_text)
self.assertIn("--3mf", help_text)
self.assertIn("--glb", help_text)
self.assertIn("--skip-step-write", help_text)
self.assertIn("--output", help_text)
self.assertIn("--mesh-tolerance", help_text)
self.assertIn("--verbose", help_text)
def test_cli_does_not_reserve_common_module_name(self) -> None:
skill_root = repo_path("skills/cad")
code = (
"import sys; sys.path.insert(0, 'scripts'); import step.cli; "
"print('common' in sys.modules); "
"print('OCP.OCP' in sys.modules); "
"print('cadpy.step_scene' in sys.modules)"
)
result = subprocess.run(
[sys.executable, "-c", code],
cwd=skill_root,
check=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
self.assertEqual("", result.stderr)
self.assertEqual(0, result.returncode)
self.assertEqual(["False", "False", "False"], result.stdout.strip().splitlines())
if __name__ == "__main__":
unittest.main()