188 lines
6.0 KiB
Python
188 lines
6.0 KiB
Python
|
|
import pytest
|
|
from pathlib import Path
|
|
from navaltoolbox import (
|
|
Hull,
|
|
Vessel,
|
|
StabilityCalculator,
|
|
CriteriaContext,
|
|
ScriptEngine,
|
|
)
|
|
|
|
|
|
class TestRhaiScripting:
|
|
"""Tests for Rhai scripting integration."""
|
|
|
|
@pytest.fixture
|
|
def dtmb5415_vessel(self):
|
|
"""Load DTMB5415 hull and create vessel."""
|
|
stl_path = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "rust"
|
|
/ "tests"
|
|
/ "data"
|
|
/ "dtmb5415.stl"
|
|
)
|
|
if not stl_path.exists():
|
|
pytest.skip("DTMB5415 STL file not found")
|
|
|
|
hull = Hull(str(stl_path))
|
|
return Vessel(hull)
|
|
|
|
def test_reproduce_is_code_2008_error(self, dtmb5415_vessel):
|
|
"""
|
|
Reproduce the 'Function not found' error for IS Code 2008 script.
|
|
|
|
Specifically checking:
|
|
- find_max_gz
|
|
- area_under_curve
|
|
"""
|
|
# 1. Setup stability result
|
|
displacement = 8635000.0 # kg
|
|
lcg = 71.670
|
|
tcg = 0.0
|
|
vcg = 7.555
|
|
cog = (lcg, tcg, vcg)
|
|
|
|
calc = StabilityCalculator(dtmb5415_vessel, 1025.0)
|
|
|
|
# Calculate full curve 0-180
|
|
heels = list(range(0, 70, 5))
|
|
result = calc.complete_stability(displacement, cog, heels)
|
|
|
|
# 2. Create Context
|
|
ctx = CriteriaContext.from_result(
|
|
result,
|
|
vessel_name="DTMB 5415",
|
|
loading_condition="Design Load"
|
|
)
|
|
|
|
# 3. Initialize Engine
|
|
engine = ScriptEngine()
|
|
|
|
# 4. Load and run IS Code 2008 script
|
|
script_path = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "rules"
|
|
/ "is_code_2008_general.rhai"
|
|
)
|
|
|
|
if not script_path.exists():
|
|
pytest.skip(f"Script not found at {script_path}")
|
|
|
|
print(f"Running script: {script_path}")
|
|
|
|
# This is expected to fail currently
|
|
try:
|
|
result = engine.run_script_file(str(script_path), ctx)
|
|
print("Script execution successful!")
|
|
print(f"Overall Pass: {result.overall_pass}")
|
|
for crit in result.criteria:
|
|
print(
|
|
f" - {crit.name}: "
|
|
f"{crit.status} ({crit.actual_value:.3f} {crit.unit})"
|
|
)
|
|
|
|
except ValueError as e:
|
|
pytest.fail(f"Script execution failed: {e}")
|
|
|
|
def test_minimal_reproduction(self, dtmb5415_vessel):
|
|
"""Minimal reproduction of specific function calls."""
|
|
displacement = 8635000.0
|
|
cog = (71.670, 0.0, 7.555)
|
|
calc = StabilityCalculator(dtmb5415_vessel, 1025.0)
|
|
heels = [0, 10, 20, 30, 40, 50, 60]
|
|
result = calc.complete_stability(displacement, cog, heels)
|
|
|
|
ctx = CriteriaContext.from_result(result, "Test", "Test")
|
|
engine = ScriptEngine()
|
|
|
|
# Test find_max_gz
|
|
script_max_gz = """
|
|
fn check(ctx) {
|
|
let max = ctx.find_max_gz();
|
|
print("Max GZ: " + max);
|
|
return #{ name: "Test", status: "PASS" };
|
|
}
|
|
"""
|
|
engine.run_script(script_max_gz, ctx)
|
|
|
|
# Test area_under_curve
|
|
script_area = """
|
|
fn check(ctx) {
|
|
let area = ctx.area_under_curve(0.0, 30.0);
|
|
print("Area: " + area);
|
|
return #{ name: "Test", status: "PASS" };
|
|
}
|
|
"""
|
|
engine.run_script(script_area, ctx)
|
|
|
|
def test_is_code_2008_values_dtmb5415(self, dtmb5415_vessel):
|
|
"""Verify numerical values of IS Code 2008 criteria on DTMB5415."""
|
|
displacement = 8635000.0
|
|
cog = (71.670, 0.0, 7.555)
|
|
calc = StabilityCalculator(dtmb5415_vessel, 1025.0)
|
|
heels = list(range(0, 70, 5))
|
|
result = calc.complete_stability(displacement, cog, heels)
|
|
|
|
ctx = CriteriaContext.from_result(result, "DTMB 5415", "Test")
|
|
engine = ScriptEngine()
|
|
|
|
script_path = (
|
|
Path(__file__).parent.parent.parent
|
|
/ "rules"
|
|
/ "is_code_2008_general.rhai"
|
|
)
|
|
|
|
if not script_path.exists():
|
|
pytest.skip("IS Code script not found")
|
|
|
|
res = engine.run_script_file(str(script_path), ctx)
|
|
|
|
assert res.overall_pass is True, (
|
|
"DTMB5415 should pass all general criteria"
|
|
)
|
|
assert len(res.criteria) == 6
|
|
|
|
crit_names = [c.name for c in res.criteria]
|
|
assert any("Area 0-30°" in n for n in crit_names)
|
|
assert any("GZ at 30°+" in n for n in crit_names)
|
|
assert any("Initial GM₀" in n for n in crit_names)
|
|
|
|
for crit in res.criteria:
|
|
assert crit.status == "PASS"
|
|
if "GM₀" in crit.name:
|
|
assert abs(crit.actual_value - result.hydrostatics.gmt) < 0.01
|
|
|
|
def test_run_all_rules(self, dtmb5415_vessel):
|
|
"""Verify that all scripts in rules/ directory can be executed."""
|
|
displacement = 8635000.0
|
|
cog = (71.670, 0.0, 7.555)
|
|
calc = StabilityCalculator(dtmb5415_vessel, 1025.0)
|
|
heels = list(range(0, 70, 5))
|
|
result = calc.complete_stability(displacement, cog, heels)
|
|
|
|
ctx = CriteriaContext.from_result(result, "Test", "Test")
|
|
engine = ScriptEngine()
|
|
|
|
rules_dir = Path(__file__).parent.parent.parent / "rules"
|
|
scripts = list(rules_dir.glob("*.rhai"))
|
|
|
|
assert len(scripts) > 0, "No rule scripts found"
|
|
|
|
for script in scripts:
|
|
print(f"Testing script: {script.name}")
|
|
try:
|
|
res = engine.run_script_file(str(script), ctx)
|
|
# Basic check that we got a result
|
|
assert res.overall_pass is not None
|
|
except Exception as e:
|
|
# template.rhai might fail if it expects parameters
|
|
# we didn't set, but we should check if it's a
|
|
# syntax error vs runtime error
|
|
print(f"Script {script.name} failed: {e}")
|
|
# We expect success for IMO scripts. Template might fail logic
|
|
# but should run.
|
|
if "im" in script.name:
|
|
pytest.fail(f"IMO script {script.name} failed: {e}")
|