feat: initial otko import
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
CI / lint (push) Has been cancelled
CI / test (macos-latest, 3.10) (push) Has been cancelled
CI / test (macos-latest, 3.11) (push) Has been cancelled
CI / test (macos-latest, 3.12) (push) Has been cancelled
CI / test (ubuntu-latest, 3.10) (push) Has been cancelled
CI / test (ubuntu-latest, 3.11) (push) Has been cancelled
CI / test (ubuntu-latest, 3.12) (push) Has been cancelled
CI / test (windows-latest, 3.10) (push) Has been cancelled
CI / test (windows-latest, 3.11) (push) Has been cancelled
CI / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
commit
612936a00b
540 changed files with 174136 additions and 0 deletions
0
tests/tools/__init__.py
Normal file
0
tests/tools/__init__.py
Normal file
35
tests/tools/fixtures/minimal.mat
Normal file
35
tests/tools/fixtures/minimal.mat
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Minimal synthetic .mat fixture for snapshot tests
|
||||
|
||||
BOOK: Test_Book
|
||||
|
||||
|
||||
MATERIAL: TestMaterial
|
||||
COMMENT: A minimal test material.\n
|
||||
QUESTION: Material:#CB#(TestMat)
|
||||
VALUE: TestMat
|
||||
HELP: The material type identifier.
|
||||
TKWIDGET: TK_UpdateInfoBar
|
||||
IMAGE: img/test.png
|
||||
QUESTION: Formulation#CB#(Stress-Strain,Force-Deformation)
|
||||
VALUE: Stress-Strain
|
||||
DEPENDENCIES: (Stress-Strain,RESTORE,Elastic_modulus_E,#CURRENT#,HIDE,Stiffness_K,#CURRENT#)
|
||||
DEPENDENCIES: (Force-Deformation,HIDE,Elastic_modulus_E,#CURRENT#,RESTORE,Stiffness_K,#CURRENT#)
|
||||
QUESTION: Elastic_modulus_E#UNITS#
|
||||
VALUE: 30 GPa
|
||||
HELP: Young modulus of elasticity.
|
||||
QUESTION: Stiffness_K#UNITS#
|
||||
VALUE: 4000 kN/m
|
||||
QUESTION: Gap_size
|
||||
VALUE: 0.0#WIDTH#(10)
|
||||
HELP: Gap size value.
|
||||
QUESTION: Combo_sub(Val_A,Val_B,Val_C)
|
||||
VALUE: #N# 3 1.0 2.0 3.0
|
||||
TKWIDGET: TK_MaterialWikiInfo
|
||||
QUESTION: _#CB#(_)
|
||||
VALUE:
|
||||
STATE: HIDDEN
|
||||
TKWIDGET: TK_MaterialTester
|
||||
QUESTION: Set_as_variable#CB#(0,1)
|
||||
VALUE: 0
|
||||
END MATERIAL
|
||||
|
||||
178
tests/tools/fixtures/minimal_expected.json
Normal file
178
tests/tools/fixtures/minimal_expected.json
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
{
|
||||
"mat_books": [
|
||||
{
|
||||
"name": "Test_Book",
|
||||
"source": "mat",
|
||||
"entries": [
|
||||
{
|
||||
"name": "TestMaterial",
|
||||
"book": "Test_Book",
|
||||
"source_type": "MATERIAL",
|
||||
"comment": "A minimal test material.\\n",
|
||||
"image": "",
|
||||
"tkwidgets": [],
|
||||
"fields": [
|
||||
{
|
||||
"name": "Material",
|
||||
"widget_type": "CB",
|
||||
"options": [
|
||||
"TestMat"
|
||||
],
|
||||
"default": "TestMat",
|
||||
"width_hint": null,
|
||||
"help_text": "The material type identifier.",
|
||||
"image": "img/test.png",
|
||||
"state": null,
|
||||
"tkwidgets": [
|
||||
"TK_UpdateInfoBar"
|
||||
],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Formulation",
|
||||
"widget_type": "CB",
|
||||
"options": [
|
||||
"Stress-Strain",
|
||||
"Force-Deformation"
|
||||
],
|
||||
"default": "Stress-Strain",
|
||||
"width_hint": null,
|
||||
"help_text": "",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [],
|
||||
"dependencies": [
|
||||
{
|
||||
"trigger": "Stress-Strain",
|
||||
"actions": [
|
||||
{
|
||||
"action": "RESTORE",
|
||||
"field": "Elastic_modulus_E",
|
||||
"target": "#CURRENT#"
|
||||
},
|
||||
{
|
||||
"action": "HIDE",
|
||||
"field": "Stiffness_K",
|
||||
"target": "#CURRENT#"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"trigger": "Force-Deformation",
|
||||
"actions": [
|
||||
{
|
||||
"action": "HIDE",
|
||||
"field": "Elastic_modulus_E",
|
||||
"target": "#CURRENT#"
|
||||
},
|
||||
{
|
||||
"action": "RESTORE",
|
||||
"field": "Stiffness_K",
|
||||
"target": "#CURRENT#"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Elastic_modulus_E",
|
||||
"widget_type": "UNITS",
|
||||
"options": [],
|
||||
"default": "30 GPa",
|
||||
"width_hint": null,
|
||||
"help_text": "Young modulus of elasticity.",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Stiffness_K",
|
||||
"widget_type": "UNITS",
|
||||
"options": [],
|
||||
"default": "4000 kN/m",
|
||||
"width_hint": null,
|
||||
"help_text": "",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Gap_size",
|
||||
"widget_type": "SCALAR",
|
||||
"options": [],
|
||||
"default": "0.0",
|
||||
"width_hint": 10,
|
||||
"help_text": "Gap size value.",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Combo_sub",
|
||||
"widget_type": "TUPLE",
|
||||
"options": [
|
||||
"Val_A",
|
||||
"Val_B",
|
||||
"Val_C"
|
||||
],
|
||||
"default": "#N# 3 1.0 2.0 3.0",
|
||||
"width_hint": null,
|
||||
"help_text": "",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [
|
||||
"TK_MaterialWikiInfo"
|
||||
],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "_",
|
||||
"widget_type": "CB",
|
||||
"options": [
|
||||
"_"
|
||||
],
|
||||
"default": "",
|
||||
"width_hint": null,
|
||||
"help_text": "",
|
||||
"image": "",
|
||||
"state": "HIDDEN",
|
||||
"tkwidgets": [
|
||||
"TK_MaterialTester"
|
||||
],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
},
|
||||
{
|
||||
"name": "Set_as_variable",
|
||||
"widget_type": "CB",
|
||||
"options": [
|
||||
"0",
|
||||
"1"
|
||||
],
|
||||
"default": "0",
|
||||
"width_hint": null,
|
||||
"help_text": "",
|
||||
"image": "",
|
||||
"state": null,
|
||||
"tkwidgets": [],
|
||||
"dependencies": [],
|
||||
"section_title": null
|
||||
}
|
||||
],
|
||||
"condtype": "",
|
||||
"condmeshtype": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cnd_books": []
|
||||
}
|
||||
390
tests/tools/test_parse_schemas.py
Normal file
390
tests/tools/test_parse_schemas.py
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
"""
|
||||
Tests for tools/gidopensees_import/parse_schemas.py.
|
||||
|
||||
Fast tests run without gidopensees present (snapshot + unit tests).
|
||||
Integration tests that read the real .mat/.cnd files are skipped when the
|
||||
files cannot be found, so CI still passes in environments without the
|
||||
upstream repo checked out.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from tools.gidopensees_import.parse_schemas import (
|
||||
ParseError,
|
||||
_parse_dependencies,
|
||||
_parse_question,
|
||||
_parse_value,
|
||||
build_catalog,
|
||||
parse_mat,
|
||||
)
|
||||
from tools.gidopensees_import.schema_model import CatalogSpec
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Paths
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
MINIMAL_MAT = FIXTURES / "minimal.mat"
|
||||
MINIMAL_EXPECTED = FIXTURES / "minimal_expected.json"
|
||||
|
||||
GIDOPENSEES = Path("d:/GitHub/gidopensees")
|
||||
REAL_MAT = GIDOPENSEES / "OpenSees.mat"
|
||||
REAL_CND = GIDOPENSEES / "OpenSees.cnd"
|
||||
|
||||
_have_real = REAL_MAT.exists() and REAL_CND.exists()
|
||||
real_files = pytest.mark.skipif(not _have_real, reason="gidopensees repo not present")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unit tests — _parse_question
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseQuestion:
|
||||
def test_cb_with_options(self) -> None:
|
||||
name, wtype, opts = _parse_question("Formulation#CB#(Stress-Strain,Force-Deformation)")
|
||||
assert name == "Formulation"
|
||||
assert wtype == "CB"
|
||||
assert opts == ["Stress-Strain", "Force-Deformation"]
|
||||
|
||||
def test_cb_single_underscore(self) -> None:
|
||||
name, wtype, opts = _parse_question("_#CB#(_)")
|
||||
assert name == "_"
|
||||
assert wtype == "CB"
|
||||
assert opts == ["_"]
|
||||
|
||||
def test_units(self) -> None:
|
||||
name, wtype, opts = _parse_question("Elastic_modulus_E#UNITS#")
|
||||
assert name == "Elastic_modulus_E"
|
||||
assert wtype == "UNITS"
|
||||
assert opts == []
|
||||
|
||||
def test_mat_with_books(self) -> None:
|
||||
name, wtype, opts = _parse_question(
|
||||
"Ux_material#MAT#(Standard_Uniaxial_Materials,Uniaxial_Steel_Materials)"
|
||||
)
|
||||
assert name == "Ux_material"
|
||||
assert wtype == "MAT"
|
||||
assert "Standard_Uniaxial_Materials" in opts
|
||||
|
||||
def test_tuple(self) -> None:
|
||||
name, wtype, opts = _parse_question(
|
||||
"Cyclic_data(Max_compressive_strain,Max_tensile_strain,Number_of_cycles)"
|
||||
)
|
||||
assert name == "Cyclic_data"
|
||||
assert wtype == "TUPLE"
|
||||
assert opts == ["Max_compressive_strain", "Max_tensile_strain", "Number_of_cycles"]
|
||||
|
||||
def test_plain_scalar(self) -> None:
|
||||
name, wtype, opts = _parse_question("Parameter_R0")
|
||||
assert name == "Parameter_R0"
|
||||
assert wtype == "SCALAR"
|
||||
assert opts == []
|
||||
|
||||
def test_label_with_trailing_colon(self) -> None:
|
||||
name, wtype, opts = _parse_question("Material:#CB#(Steel02)")
|
||||
assert name == "Material"
|
||||
assert wtype == "CB"
|
||||
assert opts == ["Steel02"]
|
||||
|
||||
def test_plain_label_colon(self) -> None:
|
||||
name, wtype, _opts = _parse_question("Layer_name:")
|
||||
assert name == "Layer_name"
|
||||
assert wtype == "SCALAR"
|
||||
|
||||
def test_cb_no_options(self) -> None:
|
||||
name, wtype, opts = _parse_question("Section:#CB#(Fiber)")
|
||||
assert name == "Section"
|
||||
assert wtype == "CB"
|
||||
assert opts == ["Fiber"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unit tests — _parse_value
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseValue:
|
||||
def test_simple(self) -> None:
|
||||
val, width = _parse_value("30 GPa")
|
||||
assert val == "30 GPa"
|
||||
assert width is None
|
||||
|
||||
def test_width_suffix(self) -> None:
|
||||
val, width = _parse_value("0.0#WIDTH#(10)")
|
||||
assert val == "0.0"
|
||||
assert width == 10
|
||||
|
||||
def test_number_width(self) -> None:
|
||||
val, width = _parse_value("20#WIDTH#(10)")
|
||||
assert val == "20"
|
||||
assert width == 10
|
||||
|
||||
def test_n_prefix(self) -> None:
|
||||
val, width = _parse_value("#N# 3 -0.005 0.005 5")
|
||||
assert val == "#N# 3 -0.005 0.005 5"
|
||||
assert width is None
|
||||
|
||||
def test_empty(self) -> None:
|
||||
val, width = _parse_value("")
|
||||
assert val == ""
|
||||
assert width is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unit tests — _parse_dependencies
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestParseDependencies:
|
||||
_path = Path("test.mat")
|
||||
|
||||
def test_single_group_restore_hide(self) -> None:
|
||||
s = "(Stress-Strain,RESTORE,Mat_type,#CURRENT#,HIDE,Stiffness_K,#CURRENT#)"
|
||||
rules = _parse_dependencies(s, self._path, 1)
|
||||
assert len(rules) == 1
|
||||
rule = rules[0]
|
||||
assert rule.trigger == "Stress-Strain"
|
||||
assert len(rule.actions) == 2
|
||||
assert rule.actions[0].action == "RESTORE"
|
||||
assert rule.actions[0].field == "Mat_type"
|
||||
assert rule.actions[1].action == "HIDE"
|
||||
assert rule.actions[1].field == "Stiffness_K"
|
||||
|
||||
def test_two_groups_same_line(self) -> None:
|
||||
s = "(0,SET,Rz_material,#CURRENT#) (1,RESTORE,Rz_material,#CURRENT#)"
|
||||
rules = _parse_dependencies(s, self._path, 1)
|
||||
assert len(rules) == 2
|
||||
assert rules[0].trigger == "0"
|
||||
assert rules[0].actions[0].action == "SET"
|
||||
assert rules[1].trigger == "1"
|
||||
assert rules[1].actions[0].action == "RESTORE"
|
||||
|
||||
def test_set_with_literal_value(self) -> None:
|
||||
s = "(1,SET,Void_width_dv,0.0m,SET,Number_of_voids,0)"
|
||||
rules = _parse_dependencies(s, self._path, 1)
|
||||
assert len(rules) == 1
|
||||
assert rules[0].actions[0].target == "0.0m"
|
||||
assert rules[0].actions[1].target == "0"
|
||||
|
||||
def test_no_groups_raises(self) -> None:
|
||||
with pytest.raises(ParseError):
|
||||
_parse_dependencies("no parentheses here", self._path, 5)
|
||||
|
||||
def test_literal_integer_trigger(self) -> None:
|
||||
s = "(1,RESTORE,Gap_length,#CURRENT#)"
|
||||
rules = _parse_dependencies(s, self._path, 1)
|
||||
assert rules[0].trigger == "1"
|
||||
|
||||
def test_fix_xyz_multi_action(self) -> None:
|
||||
s = "(Fix_XYZ,RESTORE,X-Translation,1,RESTORE,Y-Translation,1,RESTORE,Z-Translation,1)"
|
||||
rules = _parse_dependencies(s, self._path, 1)
|
||||
assert len(rules) == 1
|
||||
assert rules[0].trigger == "Fix_XYZ"
|
||||
assert len(rules[0].actions) == 3
|
||||
for act in rules[0].actions:
|
||||
assert act.target == "1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Snapshot test — minimal fixture
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSnapshotMinimal:
|
||||
def test_minimal_mat_matches_golden(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
cat = CatalogSpec(mat_books=books)
|
||||
actual = json.loads(cat.model_dump_json())
|
||||
expected = json.loads(MINIMAL_EXPECTED.read_text(encoding="utf-8"))
|
||||
assert actual == expected
|
||||
|
||||
def test_minimal_has_one_book(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
assert len(books) == 1
|
||||
assert books[0].name == "Test_Book"
|
||||
|
||||
def test_minimal_entry_fields(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
assert entry.name == "TestMaterial"
|
||||
assert entry.comment.startswith("A minimal test material")
|
||||
field_names = [f.name for f in entry.fields]
|
||||
assert "Elastic_modulus_E" in field_names
|
||||
assert "Stiffness_K" in field_names
|
||||
assert "Gap_size" in field_names
|
||||
assert "Combo_sub" in field_names
|
||||
|
||||
def test_formulation_two_deps(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
form = next(f for f in entry.fields if f.name == "Formulation")
|
||||
assert len(form.dependencies) == 2
|
||||
triggers = {d.trigger for d in form.dependencies}
|
||||
assert "Stress-Strain" in triggers
|
||||
assert "Force-Deformation" in triggers
|
||||
|
||||
def test_gap_size_width_hint(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
gap = next(f for f in entry.fields if f.name == "Gap_size")
|
||||
assert gap.default == "0.0"
|
||||
assert gap.width_hint == 10
|
||||
|
||||
def test_hidden_button_field(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
hidden = [f for f in entry.fields if f.state == "HIDDEN"]
|
||||
assert any(f.name == "_" and "TK_MaterialTester" in f.tkwidgets for f in hidden)
|
||||
|
||||
def test_tkwidget_on_first_field(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
mat_field = entry.fields[0]
|
||||
assert mat_field.name == "Material"
|
||||
assert "TK_UpdateInfoBar" in mat_field.tkwidgets
|
||||
assert mat_field.image == "img/test.png"
|
||||
|
||||
def test_tuple_field(self) -> None:
|
||||
books = parse_mat(MINIMAL_MAT)
|
||||
entry = books[0].entries[0]
|
||||
combo = next(f for f in entry.fields if f.name == "Combo_sub")
|
||||
assert combo.widget_type == "TUPLE"
|
||||
assert combo.options == ["Val_A", "Val_B", "Val_C"]
|
||||
assert combo.default == "#N# 3 1.0 2.0 3.0"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Integration tests — real gidopensees files
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@real_files
|
||||
class TestRealFiles:
|
||||
def test_mat_book_count(self, catalog: CatalogSpec) -> None:
|
||||
assert len(catalog.mat_books) == 14
|
||||
|
||||
def test_cnd_book_count(self, catalog: CatalogSpec) -> None:
|
||||
assert len(catalog.cnd_books) == 6
|
||||
|
||||
def test_mat_material_count(self, catalog: CatalogSpec) -> None:
|
||||
total = sum(len(b.entries) for b in catalog.mat_books)
|
||||
assert total == 58
|
||||
|
||||
def test_cnd_condition_count(self, catalog: CatalogSpec) -> None:
|
||||
total = sum(len(b.entries) for b in catalog.cnd_books)
|
||||
assert total >= 39
|
||||
|
||||
def test_steel02_field_names(self, catalog: CatalogSpec) -> None:
|
||||
steel2 = _find_entry(catalog, "Steel02")
|
||||
assert steel2 is not None
|
||||
fnames = {f.name for f in steel2.fields}
|
||||
required = {
|
||||
"Steel_grade",
|
||||
"Yield_Stress_Fy",
|
||||
"Strain-hardening_ratio_b",
|
||||
"Parameter_R0",
|
||||
"Parameter_cR1",
|
||||
"Parameter_cR2",
|
||||
}
|
||||
assert required.issubset(fnames), f"Missing: {required - fnames}"
|
||||
|
||||
def test_steel02_formulation_three_deps(self, catalog: CatalogSpec) -> None:
|
||||
steel2 = _find_entry(catalog, "Steel02")
|
||||
assert steel2 is not None
|
||||
form = next(f for f in steel2.fields if f.name == "Formulation")
|
||||
assert len(form.dependencies) == 3, (
|
||||
f"Expected 3 dependency rules on Formulation, got {len(form.dependencies)}"
|
||||
)
|
||||
|
||||
def test_steel02_formulation_dep_triggers(self, catalog: CatalogSpec) -> None:
|
||||
steel2 = _find_entry(catalog, "Steel02")
|
||||
assert steel2 is not None
|
||||
form = next(f for f in steel2.fields if f.name == "Formulation")
|
||||
triggers = {d.trigger for d in form.dependencies}
|
||||
assert triggers == {"Stress-Strain", "Force-Deformation", "Moment-Rotation"}
|
||||
|
||||
def test_concrete04_exists(self, catalog: CatalogSpec) -> None:
|
||||
entry = _find_entry(catalog, "Concrete04_(Popovics_concrete)")
|
||||
assert entry is not None
|
||||
assert entry.book == "Uniaxial_Concrete_Materials"
|
||||
|
||||
def test_all_mat_entries_have_book(self, catalog: CatalogSpec) -> None:
|
||||
for book in catalog.mat_books:
|
||||
for entry in book.entries:
|
||||
assert entry.book == book.name, (
|
||||
f"{entry.name!r} has book={entry.book!r}, expected {book.name!r}"
|
||||
)
|
||||
|
||||
def test_all_fields_have_valid_widget_type(self, catalog: CatalogSpec) -> None:
|
||||
valid = {"CB", "UNITS", "MAT", "SCALAR", "TUPLE"}
|
||||
for book in catalog.mat_books:
|
||||
for entry in book.entries:
|
||||
for f in entry.fields:
|
||||
assert f.widget_type in valid, (
|
||||
f"{entry.name}.{f.name}: unknown widget_type={f.widget_type!r}"
|
||||
)
|
||||
|
||||
def test_point_restraints_condtype(self, catalog: CatalogSpec) -> None:
|
||||
entry = _find_cnd_entry(catalog, "Point_Restraints")
|
||||
assert entry is not None
|
||||
assert "point" in entry.condtype.lower()
|
||||
assert "node" in entry.condmeshtype.lower()
|
||||
|
||||
def test_zerolength_mat_fields(self, catalog: CatalogSpec) -> None:
|
||||
entry = _find_cnd_entry(catalog, "Point_ZeroLength")
|
||||
assert entry is not None
|
||||
mat_fields = [f for f in entry.fields if f.widget_type == "MAT"]
|
||||
assert len(mat_fields) >= 6 # Ux,Uy,Uz,Rx,Ry,Rz materials
|
||||
|
||||
def test_no_empty_field_names(self, catalog: CatalogSpec) -> None:
|
||||
for book in catalog.mat_books + catalog.cnd_books:
|
||||
for entry in book.entries:
|
||||
for f in entry.fields:
|
||||
assert f.name, f"{entry.name} has a field with empty name"
|
||||
|
||||
def test_dependencies_actions_are_valid(self, catalog: CatalogSpec) -> None:
|
||||
valid_actions = {"RESTORE", "HIDE", "SET"}
|
||||
for book in catalog.mat_books + catalog.cnd_books:
|
||||
for entry in book.entries:
|
||||
for fspec in entry.fields:
|
||||
for rule in fspec.dependencies:
|
||||
for act in rule.actions:
|
||||
assert act.action in valid_actions, (
|
||||
f"{entry.name}.{fspec.name}: invalid action {act.action!r}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures (pytest)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def catalog() -> CatalogSpec:
|
||||
return build_catalog(REAL_MAT, REAL_CND)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _find_entry(catalog: CatalogSpec, name: str) -> object | None:
|
||||
for book in catalog.mat_books:
|
||||
for entry in book.entries:
|
||||
if entry.name == name:
|
||||
return entry
|
||||
return None
|
||||
|
||||
|
||||
def _find_cnd_entry(catalog: CatalogSpec, name: str) -> object | None:
|
||||
for book in catalog.cnd_books:
|
||||
for entry in book.entries:
|
||||
if entry.name == name:
|
||||
return entry
|
||||
return None
|
||||
Loading…
Reference in a new issue