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
- core/units: two dominant systems (Metric m/kN, Imperial ft/kip) with display conversion helpers, legacy 4-system migration in ProjectMeta - dialogs/docks: unit-aware material, section, case, load, grid and results labels; diagram renderer unit labels; render controls update - docs: add consistent_units.md; regen examples/*.osmodel artifacts - tests: update persistence/phase8/project/unit-labels for new systems
142 lines
5.2 KiB
Python
142 lines
5.2 KiB
Python
"""Unit-system label table tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from otko.core import UnitSystem, labels_for
|
|
from otko.core.units import (
|
|
QUANTITIES,
|
|
display_factor,
|
|
display_for,
|
|
display_label,
|
|
display_with_alias,
|
|
format_display,
|
|
format_rotation,
|
|
native_label,
|
|
to_display,
|
|
)
|
|
|
|
|
|
def test_metric_labels() -> None:
|
|
lab = labels_for(UnitSystem.METRIC)
|
|
assert lab.length == "m"
|
|
assert lab.force == "N"
|
|
assert lab.moment == "N·m"
|
|
assert lab.stress == "Pa"
|
|
assert lab.curvature == "1/m"
|
|
|
|
|
|
def test_imperial_labels() -> None:
|
|
lab = labels_for(UnitSystem.IMPERIAL)
|
|
assert lab.length == "in"
|
|
assert lab.force == "kip"
|
|
assert lab.moment == "kip·in"
|
|
assert lab.stress == "ksi"
|
|
assert lab.curvature == "1/in"
|
|
|
|
|
|
def test_only_two_unit_systems_remain() -> None:
|
|
"""The status-bar dropdown offers Metric and Imperial only."""
|
|
assert {u.name for u in UnitSystem} == {"METRIC", "IMPERIAL"}
|
|
|
|
|
|
def test_legacy_unit_strings_migrate_on_load() -> None:
|
|
"""Old .osmodel files (4-system era) load onto Metric/Imperial."""
|
|
from otko.core import ProjectMeta
|
|
|
|
assert ProjectMeta.model_validate({"units": "SI (m, N, kg, s, Pa)"}).units is UnitSystem.METRIC
|
|
assert ProjectMeta.model_validate({"units": "SI (mm, N, t, s, MPa)"}).units is UnitSystem.METRIC
|
|
assert (
|
|
ProjectMeta.model_validate({"units": "US (ft, kip, slug, s, ksf)"}).units
|
|
is UnitSystem.IMPERIAL
|
|
)
|
|
assert (
|
|
ProjectMeta.model_validate({"units": "US (in, kip, kip·s²/in, s, ksi)"}).units
|
|
is UnitSystem.IMPERIAL
|
|
)
|
|
|
|
|
|
def test_labels_for_covers_every_unit_system() -> None:
|
|
"""Every UnitSystem enum value must have a matching label bundle."""
|
|
for us in UnitSystem:
|
|
lab = labels_for(us)
|
|
# sanity: at least length / force populated.
|
|
assert lab.length and lab.force
|
|
|
|
|
|
def test_display_table_covers_taxonomy_per_system() -> None:
|
|
"""Every system exposes every taxonomy quantity (§5 key set)."""
|
|
for us in UnitSystem:
|
|
for q in QUANTITIES:
|
|
label, factor = display_for(us, q)
|
|
assert label
|
|
assert factor > 0
|
|
|
|
|
|
def test_display_identity_for_imperial() -> None:
|
|
"""Imperial stores = displays (no factors)."""
|
|
for q in QUANTITIES:
|
|
assert display_factor(UnitSystem.IMPERIAL, q) == 1.0
|
|
|
|
|
|
def test_display_spot_factors() -> None:
|
|
"""Spot-check Metric factors: N→kN, Pa→MPa, m→mm."""
|
|
assert display_for(UnitSystem.METRIC, "force") == ("kN", 1e-3)
|
|
assert display_for(UnitSystem.METRIC, "stress") == ("MPa", 1e-6)
|
|
assert display_for(UnitSystem.METRIC, "displacement") == ("mm", 1000.0)
|
|
assert display_for(UnitSystem.METRIC, "section_dim") == ("mm", 1000.0)
|
|
assert display_for(UnitSystem.METRIC, "area") == ("mm²", 1e6)
|
|
assert display_for(UnitSystem.METRIC, "inertia") == ("mm⁴", 1e12)
|
|
assert display_for(UnitSystem.IMPERIAL, "stress") == ("ksi", 1.0)
|
|
|
|
|
|
def test_rotation_always_factor_one() -> None:
|
|
"""Rotation stays rad everywhere; ° is formatting only."""
|
|
for us in UnitSystem:
|
|
assert display_for(us, "rotation") == ("rad", 1.0)
|
|
assert format_rotation(3.14159265).startswith("3.142")
|
|
assert "°" in format_rotation(3.14159265, use_deg=True)
|
|
|
|
|
|
def test_dimensionless_absent_from_table() -> None:
|
|
"""Strain/drift/damping never take factors — KeyError by design."""
|
|
for us in UnitSystem:
|
|
for q in ("strain", "drift", "damping_ratio", "mass_participation"):
|
|
with pytest.raises(KeyError):
|
|
display_for(us, q)
|
|
|
|
|
|
def test_to_display_applies_factor_on_read() -> None:
|
|
"""1 m → 1000 mm; Imperial identity; values never written back."""
|
|
assert to_display(1.0, UnitSystem.METRIC, "displacement") == pytest.approx(1000.0)
|
|
assert to_display(1.0, UnitSystem.IMPERIAL, "displacement") == pytest.approx(1.0)
|
|
assert to_display(2.0, UnitSystem.METRIC, "force") == pytest.approx(0.002)
|
|
assert format_display(1.0, UnitSystem.METRIC, "displacement") == "1000 mm"
|
|
assert display_label(UnitSystem.IMPERIAL, "moment") == "kip·in"
|
|
|
|
|
|
def test_native_labels_match_stored_units() -> None:
|
|
"""Labels next to native values must be native units, not prefixes."""
|
|
assert native_label(UnitSystem.METRIC, "force") == "N"
|
|
assert native_label(UnitSystem.METRIC, "stress") == "Pa"
|
|
assert native_label(UnitSystem.METRIC, "displacement") == "m"
|
|
assert native_label(UnitSystem.METRIC, "distributed_load") == "N/m"
|
|
assert native_label(UnitSystem.METRIC, "area") == "m²"
|
|
assert native_label(UnitSystem.METRIC, "inertia") == "m⁴"
|
|
assert native_label(UnitSystem.IMPERIAL, "force") == "kip"
|
|
assert native_label(UnitSystem.IMPERIAL, "stress") == "ksi"
|
|
assert native_label(UnitSystem.IMPERIAL, "length") == "in"
|
|
for us in UnitSystem:
|
|
for q in QUANTITIES:
|
|
assert native_label(us, q)
|
|
|
|
|
|
def test_small_value_lbf_alias() -> None:
|
|
"""0.004 kip renders as 4.0 lbf; large values stay in kip."""
|
|
val, label = display_with_alias(0.004, UnitSystem.IMPERIAL, "force")
|
|
assert label == "lbf"
|
|
assert val == pytest.approx(4.0)
|
|
val_big, label_big = display_with_alias(2.0, UnitSystem.IMPERIAL, "force")
|
|
assert label_big == "kip"
|
|
assert val_big == pytest.approx(2.0)
|