Files
pyflowx/tests/cli/test_pymake.py
T
zhou 5e561b4b3a refactor: 重构CliRunner,新增cmd工厂函数优化任务定义
1. 新增cmd工厂函数,简化TaskSpec创建并自动推导名称
2. 重构CliRunner,将graphs参数替换为tasks+aliases,支持扁平任务注册与别名映射
3. 替换所有cli工具中的旧版任务定义方式,使用新API简化代码
4. 补充对应测试用例,适配新的运行器API
2026-06-28 17:52:52 +08:00

217 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for cli.pymake module."""
from __future__ import annotations
from unittest.mock import patch
import pytest
from pyflowx.cli import pymake
from pyflowx.conditions import Constants
# ---------------------------------------------------------------------- #
# maturin_build_cmd
# ---------------------------------------------------------------------- #
class TestMaturinBuildCmd:
"""Test maturin_build_cmd function."""
def test_returns_list(self) -> None:
"""Should return a list."""
cmd = pymake.maturin_build_cmd()
assert isinstance(cmd, list)
def test_contains_maturin_build(self) -> None:
"""Should contain 'maturin' and 'build'."""
cmd = pymake.maturin_build_cmd()
assert "maturin" in cmd
assert "build" in cmd
def test_contains_release_flag(self) -> None:
"""Should contain release flag '-r'."""
cmd = pymake.maturin_build_cmd()
assert "-r" in cmd
def test_windows_includes_target(self) -> None:
"""On Windows, should include target-specific flags."""
cmd = pymake.maturin_build_cmd()
if Constants.IS_WINDOWS:
assert "--target" in cmd
assert "x86_64-win7-windows-msvc" in cmd
assert "-Zbuild-std" in cmd
assert "-i" in cmd
assert "python3.8" in cmd
else:
# On non-Windows, should not include Windows-specific flags
assert "--target" not in cmd
def test_does_not_mutate_on_multiple_calls(self) -> None:
"""Multiple calls should return independent lists."""
cmd1 = pymake.maturin_build_cmd()
cmd2 = pymake.maturin_build_cmd()
assert cmd1 == cmd2
# Mutating one should not affect the other
cmd1.append("extra")
assert "extra" not in cmd2
def test_non_windows_excludes_target_flags(self) -> None:
"""On non-Windows, should not include Windows-specific flags (覆盖 22->32 分支)."""
from unittest.mock import patch
with patch.object(pymake.Constants, "IS_WINDOWS", False):
cmd = pymake.maturin_build_cmd()
assert "maturin" in cmd
assert "build" in cmd
assert "-r" in cmd
assert "--target" not in cmd
assert "-Zbuild-std" not in cmd
# ---------------------------------------------------------------------- #
# TaskSpec definitions
# ---------------------------------------------------------------------- #
def _find_task(name: str) -> pymake.px.TaskSpec:
"""从 pymake.tasks 或单任务别名变量中查找指定名称的 TaskSpec."""
for spec in pymake.tasks:
if spec.name == name:
return spec
# 单任务别名变量(_doc/_lint/_tox
alias_map = {"doc": pymake._doc, "lint": pymake._lint, "tox": pymake._tox}
if name in alias_map:
return alias_map[name]
raise KeyError(f"任务 {name!r} 未找到")
class TestTaskSpecDefinitions:
"""Test that all TaskSpec definitions are valid."""
def test_uv_build_spec(self) -> None:
"""uv_build spec should be properly defined."""
spec = _find_task("uv_build")
assert spec.name == "uv_build"
assert spec.cmd == ["uv", "build"]
assert spec.skip_if_missing is False
def test_maturin_build_spec(self) -> None:
"""maturin_build spec should be properly defined."""
spec = _find_task("maturin_build")
assert spec.name == "maturin_build"
assert isinstance(spec.cmd, list)
assert spec.skip_if_missing is False
def test_uv_sync_spec(self) -> None:
"""uv_sync spec should be properly defined."""
spec = _find_task("uv_sync")
assert spec.name == "uv_sync"
assert spec.cmd == ["uv", "sync"]
assert spec.skip_if_missing is False
def test_git_clean_spec(self) -> None:
"""git_clean spec should be properly defined."""
spec = _find_task("git_clean")
assert spec.name == "git_clean"
assert spec.cmd == ["gitt", "c"]
assert spec.skip_if_missing is False
def test_test_spec(self) -> None:
"""test spec should be properly defined."""
spec = _find_task("test")
assert spec.name == "test"
assert isinstance(spec.cmd, list)
assert "pytest" in spec.cmd
assert "-m" in spec.cmd
assert "not slow" in spec.cmd
assert spec.skip_if_missing is False
def test_test_fast_spec(self) -> None:
"""test_fast spec should be properly defined."""
spec = _find_task("test_fast")
assert spec.name == "test_fast"
assert isinstance(spec.cmd, list)
assert "pytest" in spec.cmd
assert "-n" not in spec.cmd # test_fast doesn't use parallel
assert spec.skip_if_missing is False
def test_test_coverage_spec(self) -> None:
"""test_coverage spec should be properly defined."""
spec = _find_task("test_coverage")
assert spec.name == "test_coverage"
assert isinstance(spec.cmd, list)
assert "pytest" in spec.cmd
assert "--cov" in spec.cmd
assert spec.skip_if_missing is False
def test_ruff_lint_spec(self) -> None:
"""lint spec should be properly defined."""
spec = _find_task("lint")
assert spec.name == "lint"
assert isinstance(spec.cmd, list)
assert "ruff" in spec.cmd
assert "check" in spec.cmd
assert spec.skip_if_missing is False
def test_doc_spec(self) -> None:
"""doc spec should be properly defined."""
spec = _find_task("doc")
assert spec.name == "doc"
assert isinstance(spec.cmd, list)
assert "sphinx-build" in spec.cmd
assert spec.skip_if_missing is False
def test_hatch_publish_spec(self) -> None:
"""publish_python spec should be properly defined."""
spec = _find_task("publish_python")
assert spec.name == "publish_python"
assert spec.cmd == ["hatch", "publish"]
assert spec.skip_if_missing is False
def test_twine_publish_spec(self) -> None:
"""twine_publish spec should be properly defined."""
spec = _find_task("twine_publish")
assert spec.name == "twine_publish"
assert isinstance(spec.cmd, list)
assert "twine" in spec.cmd
assert "upload" in spec.cmd
assert spec.skip_if_missing is False
def test_tox_spec(self) -> None:
"""tox spec should be properly defined."""
spec = _find_task("tox")
assert spec.name == "tox"
assert spec.cmd == ["tox", "-p", "auto"]
assert spec.skip_if_missing is False
# ---------------------------------------------------------------------- #
# main function
# ---------------------------------------------------------------------- #
class TestMain:
"""Test main function."""
def test_main_calls_run_cli(self) -> None:
"""main() should create a CliRunner and call run_cli()."""
with pytest.raises(SystemExit) as exc_info:
pymake.main()
# run_cli() calls sys.exit(), so we should get SystemExit
# The exit code depends on whether any commands are available
assert exc_info.value.code in (0, 1, 2)
def test_main_with_list_argument(self) -> None:
"""main() should handle --list argument."""
with patch("sys.argv", ["pymake", "--list"]), pytest.raises(SystemExit) as exc_info:
pymake.main()
assert exc_info.value.code == 0
def test_main_creates_runner_with_multiple_commands(self) -> None:
"""main() should create a CliRunner with multiple commands."""
# We can't easily test the runner creation without mocking,
# but we can verify that main() doesn't raise an error for --list
with patch("sys.argv", ["pymake", "--list"]), pytest.raises(SystemExit):
pymake.main()
def test_main_with_no_args_shows_help(self) -> None:
"""main() with no args should show help and exit with failure."""
with patch("sys.argv", ["pymake"]), pytest.raises(SystemExit) as exc_info:
pymake.main()
assert exc_info.value.code == 1