Files

160 lines
5.4 KiB
Python
Raw Permalink Normal View History

2026-09-01 18:05:18 +08:00
"""Generator 公共接口契约测试(技术债计划 P2-⑩)。
BaseMoldGenerator 的公共契约原先以 `_` 私有方法形式暴露给 MultiSchemeMoldPlanner,
本文件锁定提取后的公共接口:方法存在性、参数覆写行为、废弃入口告警,
并用真实几何(OCC box)走一遍 generate_plan 集成路径。
OCC(pythonocc)仅 conda 环境提供(本地 gemold / 服务器 py_3.12),
无 OCC 的环境自动跳过本文件,不影响其余测试。
"""
import pytest
pytest.importorskip("OCC.Core.BRepPrimAPI", reason="需要 pythonocc 运行生成器契约测试")
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core.gp import gp_Pnt
from moldinsight.core.aluminum_foam_mold import AluminumFoamMoldGenerator
from moldinsight.core.base_mold_generator import BaseMoldGenerator
from moldinsight.core.mold_generator import MoldCavityGenerator
from moldinsight.core.multi_scheme_planner import MultiSchemeMoldPlanner
from moldinsight.services.material_service import MaterialService
# BaseMoldGenerator 上的公共契约(含子类必须可调用的基类默认实现)
BASE_CONTRACT_METHODS = (
"analyze_product_geometry",
"apply_shrinkage_compensation",
"apply_draft_angles",
"split_cavity_core",
"create_mold_block",
"split_mold_block_by_plane",
"get_parting_plane",
"get_face_normal",
"calculate_parting_line",
"apply_process_params",
)
# 两个子类共同实现的完整契约
SUBCLASS_CONTRACT_METHODS = BASE_CONTRACT_METHODS + (
"calculate_mold_size",
"build_undercut_regions",
"set_material",
"side_action_designer",
"generate_detailed_cavity_json",
"generate_cavity_key_info",
"generate_mold_cavities", # 已废弃但保留兼容
)
def _box(dx=50.0, dy=40.0, dz=30.0):
return BRepPrimAPI_MakeBox(
gp_Pnt(-dx / 2, -dy / 2, -dz / 2), gp_Pnt(dx / 2, dy / 2, dz / 2)
).Shape()
@pytest.mark.parametrize("method", SUBCLASS_CONTRACT_METHODS)
@pytest.mark.parametrize(
"generator_cls", [MoldCavityGenerator, AluminumFoamMoldGenerator]
)
def test_generator_exposes_public_contract(generator_cls, method):
generator = generator_cls()
attr = getattr(generator, method, None)
assert attr is not None, f"{generator_cls.__name__} 缺少公共接口 {method}"
assert callable(attr) or isinstance(attr, object)
def test_foam_generator_implements_generate_mold_block():
assert callable(AluminumFoamMoldGenerator().generate_mold_block)
def test_base_generate_mold_block_is_not_implemented():
with pytest.raises(NotImplementedError):
BaseMoldGenerator().generate_mold_block(None, {})
def test_apply_process_params_overrides_generator_state():
generator = MoldCavityGenerator(shrinkage_rate=0.005, draft_angle=2.0)
material = MaterialService.get_material("ABS")
generator.apply_process_params(
material,
{
"draft_angle": 1.5,
"shrinkage_rate": 1.2, # 百分数
"parting_precision": 0.05,
"cavity_match": 97.0,
},
)
assert generator.draft_angle == pytest.approx(1.5)
assert generator.shrinkage_rate == pytest.approx(0.012)
assert generator.parting_line_tolerance == pytest.approx(0.05)
assert generator.cavity_match_rate == pytest.approx(97.0)
def test_apply_process_params_defaults_to_material_shrinkage():
generator = MoldCavityGenerator(shrinkage_rate=0.005, draft_angle=2.0)
material = MaterialService.get_material("PP") # shrinkage 0.016
generator.apply_process_params(material, None)
assert generator.shrinkage_rate == pytest.approx(0.016)
assert generator.draft_angle == pytest.approx(2.0)
def test_generate_mold_cavities_is_deprecated():
generator = MoldCavityGenerator()
with pytest.warns(DeprecationWarning):
result = generator.generate_mold_cavities(_box())
assert result["cavity"] is not None
assert result["core"] is not None
def test_planner_generate_plan_injection_box():
planner = MultiSchemeMoldPlanner()
plan = planner.generate_plan(
_box(),
MaterialService.get_material("ABS"),
is_foam_material=False,
max_schemes=2,
)
schemes = plan["candidate_schemes"]
assert 1 <= len(schemes) <= 2
assert plan["best_scheme_id"] == schemes[0]["scheme_id"]
best = schemes[0]
assert best["cavity_data"]["mold_cavities"]["cavity"]["vertex_count"] > 0
assert best["cavity_data"]["mold_cavities"]["core"]["vertex_count"] > 0
assert best["cavity_data"]["metadata"]["scheme_id"] == best["scheme_id"]
assert best["parting"]["line"]
export_shapes = plan["_export_shapes"][best["scheme_id"]]
assert export_shapes["cavity"] is not None
assert export_shapes["product"] is not None
def test_planner_generate_plan_foam_box():
planner = MultiSchemeMoldPlanner()
plan = planner.generate_plan(
_box(120, 100, 60),
MaterialService.get_material("AlSi10Mg"),
is_foam_material=True,
max_schemes=1,
)
best = plan["candidate_schemes"][0]
assert plan["best_scheme_id"] == best["scheme_id"]
# 泡沫材料优先 Z 轴上下开模
assert best["axis"] == "Z"
assert best["cavity_data"]["metadata"]["foam_material"] == "AlSi10Mg"
assert best["cavity_data"]["mold_cavities"]["cavity"]["vertex_count"] > 0
export_shapes = plan["_export_shapes"][best["scheme_id"]]
assert export_shapes["cavity"] is not None