2026-10-01 17:43:48 +08:00
|
|
|
|
"""core 包测试。
|
|
|
|
|
|
|
2026-10-01 18:48:43 +08:00
|
|
|
|
TODO(L0):TestRandom / TestValidate 实现后去掉 skip 标记(Result 已完成)。
|
2026-10-01 17:43:48 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skip(reason="未实现:见 docs/03-roadmap.md L0")
|
|
|
|
|
|
class TestRandom:
|
|
|
|
|
|
def test_get_rng_is_reproducible(self):
|
|
|
|
|
|
from mcm_toolkit.core import get_rng
|
|
|
|
|
|
|
|
|
|
|
|
assert get_rng(42).random() == get_rng(42).random()
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_rng_passes_through_generator(self):
|
|
|
|
|
|
from mcm_toolkit.core import get_rng
|
|
|
|
|
|
|
|
|
|
|
|
g = np.random.default_rng(0)
|
|
|
|
|
|
assert get_rng(g) is g
|
|
|
|
|
|
|
|
|
|
|
|
def test_set_seed_makes_global_reproducible(self):
|
|
|
|
|
|
from mcm_toolkit.core import set_seed
|
|
|
|
|
|
|
|
|
|
|
|
set_seed(7)
|
|
|
|
|
|
a = np.random.default_rng(7).random()
|
|
|
|
|
|
set_seed(7)
|
|
|
|
|
|
b = np.random.default_rng(7).random()
|
|
|
|
|
|
assert a == b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skip(reason="未实现:见 docs/03-roadmap.md L0")
|
|
|
|
|
|
class TestValidate:
|
|
|
|
|
|
def test_check_same_length_raises_with_detail(self):
|
|
|
|
|
|
from mcm_toolkit.core import check_same_length
|
|
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ValueError, match=r"2.*3|长度"):
|
|
|
|
|
|
check_same_length([1, 2], [1, 2, 3])
|
|
|
|
|
|
|
|
|
|
|
|
def test_check_array_rejects_nan(self):
|
|
|
|
|
|
from mcm_toolkit.core import check_array
|
|
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
|
check_array([1.0, np.nan, 2.0], finite=True)
|
|
|
|
|
|
|
|
|
|
|
|
def test_check_probability_bounds(self):
|
|
|
|
|
|
from mcm_toolkit.core import check_probability
|
|
|
|
|
|
|
|
|
|
|
|
assert check_probability(0.5) == 0.5
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
|
check_probability(1.5)
|
|
|
|
|
|
|
|
|
|
|
|
def test_check_square_matrix(self):
|
|
|
|
|
|
from mcm_toolkit.core import check_square_matrix
|
|
|
|
|
|
|
|
|
|
|
|
check_square_matrix([[1, 2], [3, 4]])
|
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
|
check_square_matrix([[1, 2, 3], [4, 5, 6]])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_result_repr_is_readable():
|
|
|
|
|
|
from mcm_toolkit.core import Result
|
|
|
|
|
|
|
|
|
|
|
|
text = repr(Result("demo", params={"a": 1}, metrics={"r2": 0.9}))
|
2026-10-01 18:48:43 +08:00
|
|
|
|
# 只断言关键信息在场,不断言完整字符串——否则以后调格式会让测试集体变红
|
2026-10-01 17:43:48 +08:00
|
|
|
|
assert "demo" in text and "r2" in text
|
2026-10-01 18:48:43 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_result_repr_stays_short_and_safe():
|
|
|
|
|
|
"""默认值、失败结果、十万级数组:repr 都不能崩,也不能刷屏。"""
|
|
|
|
|
|
from mcm_toolkit.core import Result
|
|
|
|
|
|
|
|
|
|
|
|
assert "empty" in repr(Result("empty"))
|
|
|
|
|
|
|
|
|
|
|
|
failed = repr(Result("boom", success=False, message="求解未收敛"))
|
|
|
|
|
|
assert "success=False" in failed
|
|
|
|
|
|
assert "求解未收敛" in failed
|
|
|
|
|
|
|
|
|
|
|
|
noisy = repr(Result("mc", data=np.arange(100_000)))
|
|
|
|
|
|
assert "ndarray(100000)" in noisy
|
|
|
|
|
|
assert len(noisy) < 200, "data 不应被整个打印出来"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_result_summary_is_markdown_table():
|
|
|
|
|
|
from mcm_toolkit.core import Result
|
|
|
|
|
|
|
|
|
|
|
|
result = Result("topsis", params={"n": 3}, metrics={"best": 0.83})
|
|
|
|
|
|
text = result.summary()
|
|
|
|
|
|
lines = text.splitlines()
|
|
|
|
|
|
|
|
|
|
|
|
assert lines[0] == "### topsis"
|
|
|
|
|
|
assert lines[2] == "| 项 | 值 |"
|
|
|
|
|
|
assert "| success | True |" in lines
|
|
|
|
|
|
assert "| params.n | 3 |" in lines
|
|
|
|
|
|
assert "| metrics.best | 0.83 |" in lines
|
|
|
|
|
|
assert result.summary() == text, "summary 必须无副作用、可重复调用"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_result_summary_appends_message_on_failure():
|
|
|
|
|
|
from mcm_toolkit.core import Result
|
|
|
|
|
|
|
|
|
|
|
|
text = Result("gm11", success=False, message="样本太少").summary()
|
|
|
|
|
|
assert "| success | False |" in text
|
|
|
|
|
|
assert text.rstrip().endswith("> 样本太少")
|