I love how stupid this project is
si units and redefining speed metric as thrust/weight ratio
This commit is contained in:
@@ -28,16 +28,16 @@ def seeded_repo(repo):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def walking():
|
||||
def road_vehicle():
|
||||
return Entity(
|
||||
name="Walking",
|
||||
name="Road Vehicle",
|
||||
dimension="platform",
|
||||
description="Bipedal locomotion",
|
||||
description="Generic wheeled road vehicle",
|
||||
dependencies=[
|
||||
Dependency("environment", "ground_surface", "true", None, "requires"),
|
||||
Dependency("environment", "gravity", "true", None, "requires"),
|
||||
Dependency("physical", "mass_kg", "150", "kg", "range_max"),
|
||||
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
|
||||
Dependency("physical", "mass", "36000", "kg", "range_max"),
|
||||
Dependency("physical", "mass", "50", "kg", "range_min"),
|
||||
Dependency("environment", "medium", "ground", None, "requires"),
|
||||
],
|
||||
)
|
||||
@@ -52,8 +52,7 @@ def bicycle():
|
||||
dependencies=[
|
||||
Dependency("environment", "ground_surface", "true", None, "requires"),
|
||||
Dependency("environment", "gravity", "true", None, "requires"),
|
||||
Dependency("physical", "mass_kg", "30", "kg", "range_max"),
|
||||
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
|
||||
Dependency("physical", "mass", "30", "kg", "range_max"),
|
||||
Dependency("environment", "medium", "ground", None, "requires"),
|
||||
],
|
||||
)
|
||||
@@ -67,8 +66,7 @@ def spaceship():
|
||||
description="Vehicle designed for space travel",
|
||||
dependencies=[
|
||||
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
|
||||
Dependency("physical", "mass_kg", "5000", "kg", "range_min"),
|
||||
Dependency("force", "power_density_required_w_kg", "500", "W/kg", "range_min"),
|
||||
Dependency("physical", "mass", "5000", "kg", "range_min"),
|
||||
Dependency("environment", "medium", "space", None, "requires"),
|
||||
],
|
||||
)
|
||||
@@ -83,7 +81,7 @@ def solar_sail():
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "radiation_pressure", None, "requires"),
|
||||
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
|
||||
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
|
||||
Dependency("force", "power_density", "0.01", "W/kg", "provides"),
|
||||
Dependency("environment", "medium", "space", None, "requires"),
|
||||
],
|
||||
)
|
||||
@@ -97,7 +95,7 @@ def solar_radiation():
|
||||
description="Photon flux from a nearby star",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "radiation_pressure", None, "provides"),
|
||||
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
|
||||
Dependency("physical", "mass", "0", "kg", "range_min"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -110,8 +108,8 @@ def human_pedalling():
|
||||
description="Human-powered via pedalling",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "biological", None, "requires"),
|
||||
Dependency("force", "power_density_w_kg", "1.5", "W/kg", "provides"),
|
||||
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
|
||||
Dependency("force", "power_density", "1.5", "W/kg", "provides"),
|
||||
Dependency("physical", "mass", "0", "kg", "range_min"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -124,8 +122,8 @@ def food_calories():
|
||||
description="Metabolic energy from food",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "biological", None, "provides"),
|
||||
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
|
||||
Dependency("physical", "mass", "0", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "720000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -138,8 +136,8 @@ def nuclear_thermal_drive():
|
||||
description="Nuclear fission reactor for thrust",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"),
|
||||
Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"),
|
||||
Dependency("physical", "mass_kg", "1500", "kg", "range_min"),
|
||||
Dependency("force", "power_density", "50", "W/kg", "provides"),
|
||||
Dependency("physical", "mass", "1500", "kg", "range_min"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -152,8 +150,8 @@ def nuclear_fuel():
|
||||
description="Enriched uranium fuel rods",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "nuclear_thermal", None, "provides"),
|
||||
Dependency("physical", "mass_kg", "500", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density_wh_kg", "500000", "Wh/kg", "provides"),
|
||||
Dependency("physical", "mass", "500", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "1800000000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -166,8 +164,8 @@ def hydrogen_engine():
|
||||
description="Hydrogen combustion engine",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
|
||||
Dependency("force", "power_density_w_kg", "1500", "W/kg", "provides"),
|
||||
Dependency("physical", "mass_kg", "25", "kg", "range_min"),
|
||||
Dependency("force", "power_density", "1500", "W/kg", "provides"),
|
||||
Dependency("physical", "mass", "25", "kg", "range_min"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -180,9 +178,9 @@ def ice_engine():
|
||||
description="Spark-ignition engine",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
|
||||
Dependency("force", "power_density_w_kg", "1000", "W/kg", "provides"),
|
||||
Dependency("force", "power_density", "1000", "W/kg", "provides"),
|
||||
Dependency("environment", "atmosphere", "standard", None, "requires"),
|
||||
Dependency("physical", "mass_kg", "40", "kg", "range_min"),
|
||||
Dependency("physical", "mass", "40", "kg", "range_min"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -195,8 +193,8 @@ def gasoline():
|
||||
description="Liquid hydrocarbon fuel",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
|
||||
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density_wh_kg", "1500", "Wh/kg", "provides"),
|
||||
Dependency("physical", "mass", "10", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "5400000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -209,8 +207,8 @@ def hydrogen():
|
||||
description="Compressed hydrogen gas",
|
||||
dependencies=[
|
||||
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
|
||||
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density_wh_kg", "600", "Wh/kg", "provides"),
|
||||
Dependency("physical", "mass", "5", "kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "2160000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -221,10 +219,10 @@ def urban_domain():
|
||||
name="urban_commuting",
|
||||
description="Daily city travel",
|
||||
metric_bounds=[
|
||||
MetricBound("speed", weight=0.25, norm_min=5, norm_max=120),
|
||||
MetricBound("cost_efficiency", weight=0.25, norm_min=0.01, norm_max=2.0, lower_is_better=True),
|
||||
MetricBound("power_density", weight=0.25, norm_min=1, norm_max=2000),
|
||||
MetricBound("cost_efficiency", weight=0.25, norm_min=1e-5, norm_max=2e-3, lower_is_better=True),
|
||||
MetricBound("safety", weight=0.25, norm_min=0.0, norm_max=1.0),
|
||||
MetricBound("availability", weight=0.15, norm_min=0.0, norm_max=1.0),
|
||||
MetricBound("range_fuel", weight=0.10, norm_min=5, norm_max=500),
|
||||
MetricBound("range_fuel", weight=0.10, norm_min=5000, norm_max=500000),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -13,10 +13,10 @@ def test_compatible_ground_combo(bicycle, human_pedalling, food_calories):
|
||||
assert result.status != "p1_fail", f"Unexpected block: {result.violations}"
|
||||
|
||||
|
||||
def test_solar_sail_blocks_with_walking(walking, solar_sail, solar_radiation):
|
||||
"""Walking (ground) + Solar Sail (space) should be blocked by medium mutex."""
|
||||
def test_solar_sail_blocks_with_road_vehicle(road_vehicle, solar_sail, solar_radiation):
|
||||
"""Road Vehicle (ground) + Solar Sail (space) should be blocked by medium mutex."""
|
||||
resolver = ConstraintResolver()
|
||||
combo = Combination(entities=[walking, solar_sail, solar_radiation])
|
||||
combo = Combination(entities=[road_vehicle, solar_sail, solar_radiation])
|
||||
result = resolver.resolve(combo)
|
||||
assert result.status == "p1_fail"
|
||||
assert any("mutually exclusive" in v for v in result.violations)
|
||||
@@ -41,49 +41,6 @@ def test_nuclear_drive_blocks_with_bicycle(bicycle, nuclear_thermal_drive, nucle
|
||||
assert any("mass" in v.lower() for v in result.violations)
|
||||
|
||||
|
||||
def test_power_density_mismatch_blocks():
|
||||
"""A platform needing 500 W/kg and a source providing 0.01 W/kg → block."""
|
||||
platform = Entity(
|
||||
name="HeavyPlatform", dimension="platform",
|
||||
dependencies=[
|
||||
Dependency("force", "power_density_required_w_kg", "500", "W/kg", "range_min"),
|
||||
],
|
||||
)
|
||||
power = Entity(
|
||||
name="TinyPower", dimension="actuator",
|
||||
dependencies=[
|
||||
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
|
||||
],
|
||||
)
|
||||
resolver = ConstraintResolver()
|
||||
combo = Combination(entities=[platform, power])
|
||||
result = resolver.resolve(combo)
|
||||
assert result.status == "p1_fail"
|
||||
assert any("power density deficit" in v for v in result.violations)
|
||||
|
||||
|
||||
def test_power_density_under_powered_warning():
|
||||
"""Power source slightly below requirement → warning, not block."""
|
||||
platform = Entity(
|
||||
name="MedPlatform", dimension="platform",
|
||||
dependencies=[
|
||||
Dependency("force", "power_density_required_w_kg", "100", "W/kg", "range_min"),
|
||||
],
|
||||
)
|
||||
power = Entity(
|
||||
name="WeakPower", dimension="actuator",
|
||||
dependencies=[
|
||||
Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"),
|
||||
],
|
||||
)
|
||||
resolver = ConstraintResolver()
|
||||
combo = Combination(entities=[platform, power])
|
||||
result = resolver.resolve(combo)
|
||||
# Under-powered but within 100x → warning, not block
|
||||
assert result.status != "p1_fail"
|
||||
assert any("under-powered" in w for w in result.warnings)
|
||||
|
||||
|
||||
def test_requires_vs_excludes():
|
||||
"""Direct requires/excludes contradiction."""
|
||||
a = Entity(
|
||||
@@ -122,17 +79,17 @@ def test_hydrogen_bicycle_valid(bicycle, hydrogen_engine, hydrogen):
|
||||
|
||||
|
||||
def test_energy_density_deficit_blocks():
|
||||
"""A platform needing 2000 Wh/kg paired with a 200 Wh/kg battery → blocked."""
|
||||
"""A platform needing 7200000 J/kg paired with a 720000 J/kg battery → blocked."""
|
||||
platform = Entity(
|
||||
name="Spaceship", dimension="platform",
|
||||
dependencies=[
|
||||
Dependency("physical", "energy_density_wh_kg", "2000", "Wh/kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "7200000", "J/kg", "range_min"),
|
||||
],
|
||||
)
|
||||
storage = Entity(
|
||||
name="Battery", dimension="energy_storage",
|
||||
dependencies=[
|
||||
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
|
||||
Dependency("physical", "energy_density", "720000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
resolver = ConstraintResolver()
|
||||
@@ -143,17 +100,17 @@ def test_energy_density_deficit_blocks():
|
||||
|
||||
|
||||
def test_energy_density_under_density_warning():
|
||||
"""A platform needing 400 Wh/kg paired with a 200 Wh/kg battery → conditional."""
|
||||
"""A platform needing 1440000 J/kg paired with a 720000 J/kg battery → conditional."""
|
||||
platform = Entity(
|
||||
name="Airplane", dimension="platform",
|
||||
dependencies=[
|
||||
Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "1440000", "J/kg", "range_min"),
|
||||
],
|
||||
)
|
||||
storage = Entity(
|
||||
name="Battery", dimension="energy_storage",
|
||||
dependencies=[
|
||||
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
|
||||
Dependency("physical", "energy_density", "720000", "J/kg", "provides"),
|
||||
],
|
||||
)
|
||||
resolver = ConstraintResolver()
|
||||
@@ -168,14 +125,14 @@ def test_energy_density_no_constraint_if_no_provider():
|
||||
platform = Entity(
|
||||
name="Spaceship", dimension="platform",
|
||||
dependencies=[
|
||||
Dependency("physical", "energy_density_wh_kg", "2000", "Wh/kg", "range_min"),
|
||||
Dependency("physical", "energy_density", "7200000", "J/kg", "range_min"),
|
||||
],
|
||||
)
|
||||
# Solar Sail-style: no energy_density_wh_kg declared
|
||||
# Solar Sail-style: no energy_density declared
|
||||
actuator = Entity(
|
||||
name="Solar Sail", dimension="actuator",
|
||||
dependencies=[
|
||||
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
|
||||
Dependency("force", "power_density", "0.01", "W/kg", "provides"),
|
||||
],
|
||||
)
|
||||
resolver = ConstraintResolver()
|
||||
|
||||
@@ -7,7 +7,7 @@ from physcom.llm.providers.mock import MockLLMProvider
|
||||
|
||||
|
||||
def test_pass1_filters_impossible_combos(seeded_repo):
|
||||
"""Pass 1 should block known-impossible combinations (e.g., solar sail + walking)."""
|
||||
"""Pass 1 should block known-impossible combinations (e.g., solar sail + road vehicle)."""
|
||||
domain = seeded_repo.get_domain("urban_commuting")
|
||||
resolver = ConstraintResolver()
|
||||
scorer = Scorer(domain)
|
||||
@@ -44,8 +44,8 @@ def test_pass4_with_mock_llm(seeded_repo):
|
||||
resolver = ConstraintResolver()
|
||||
scorer = Scorer(domain)
|
||||
mock_llm = MockLLMProvider(default_estimates={
|
||||
"speed": 50.0, "cost_efficiency": 0.5, "safety": 0.6,
|
||||
"availability": 0.7, "range_fuel": 200.0,
|
||||
"power_density": 500.0, "cost_efficiency": 5e-4, "safety": 0.6,
|
||||
"availability": 0.7, "range_fuel": 200000.0,
|
||||
})
|
||||
pipeline = Pipeline(seeded_repo, resolver, scorer, llm=mock_llm)
|
||||
|
||||
|
||||
@@ -346,7 +346,7 @@ def test_p4_fail_implausible(seeded_repo):
|
||||
# Low estimates → normalized scores avg <= 0.5 → MockLLMProvider returns (text, False)
|
||||
# Use threshold=0.0 so no combo gets p3_fail and all reach pass 4
|
||||
mock_llm = MockLLMProvider(default_estimates={
|
||||
"speed": 0.1, "cost_efficiency": 0.1, "safety": 0.1,
|
||||
"power_density": 0.1, "cost_efficiency": 0.1, "safety": 0.1,
|
||||
"availability": 0.1, "range_fuel": 0.1,
|
||||
})
|
||||
pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm)
|
||||
@@ -377,8 +377,8 @@ def test_p4_pass_plausible(seeded_repo):
|
||||
scorer = Scorer(domain)
|
||||
# High estimates → avg > 0.5 → MockLLMProvider returns (text, True)
|
||||
mock_llm = MockLLMProvider(default_estimates={
|
||||
"speed": 50.0, "cost_efficiency": 0.5, "safety": 0.6,
|
||||
"availability": 0.7, "range_fuel": 200.0,
|
||||
"power_density": 500.0, "cost_efficiency": 5e-4, "safety": 0.6,
|
||||
"availability": 0.7, "range_fuel": 200000.0,
|
||||
})
|
||||
pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm)
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ class TestScorer:
|
||||
Entity(name="ICE", dimension="actuator"),
|
||||
])
|
||||
combo.id = 1
|
||||
raw = {"speed": 60.0, "cost_efficiency": 0.5, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400}
|
||||
raw = {"power_density": 500.0, "cost_efficiency": 5e-4, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400000}
|
||||
result = scorer.score_combination(combo, raw)
|
||||
assert 0.0 < result.composite_score <= 1.0
|
||||
assert len(result.scores) == 5
|
||||
@@ -85,8 +85,8 @@ class TestScorer:
|
||||
scorer = Scorer(urban_domain)
|
||||
combo = Combination(entities=[])
|
||||
combo.id = 1
|
||||
raw = {"speed": 60.0, "cost_efficiency": 0.5, "safety": 0.0,
|
||||
"availability": 0.8, "range_fuel": 400}
|
||||
raw = {"power_density": 500.0, "cost_efficiency": 5e-4, "safety": 0.0,
|
||||
"availability": 0.8, "range_fuel": 400000}
|
||||
result = scorer.score_combination(combo, raw)
|
||||
assert result.composite_score == 0.0
|
||||
|
||||
@@ -95,13 +95,13 @@ class TestScorer:
|
||||
scorer = Scorer(urban_domain)
|
||||
combo = Combination(entities=[])
|
||||
combo.id = 1
|
||||
# cost_efficiency: norm_min=0.01, norm_max=2.0, lower_is_better=True
|
||||
# A low cost (0.02) should get a HIGH normalized score (near 1.0)
|
||||
# A high cost (1.9) should get a LOW normalized score (near 0.0)
|
||||
raw_cheap = {"speed": 60.0, "cost_efficiency": 0.02, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400}
|
||||
raw_expensive = {"speed": 60.0, "cost_efficiency": 1.9, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400}
|
||||
# cost_efficiency: norm_min=1e-5, norm_max=2e-3, lower_is_better=True
|
||||
# A low cost (2e-5) should get a HIGH normalized score (near 1.0)
|
||||
# A high cost (1.9e-3) should get a LOW normalized score (near 0.0)
|
||||
raw_cheap = {"power_density": 500.0, "cost_efficiency": 2e-5, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400000}
|
||||
raw_expensive = {"power_density": 500.0, "cost_efficiency": 1.9e-3, "safety": 0.7,
|
||||
"availability": 0.8, "range_fuel": 400000}
|
||||
result_cheap = scorer.score_combination(combo, raw_cheap)
|
||||
result_expensive = scorer.score_combination(combo, raw_expensive)
|
||||
|
||||
|
||||
102
tests/test_units.py
Normal file
102
tests/test_units.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""Tests for the unit-aware formatter."""
|
||||
|
||||
from physcom.units import format_quantity
|
||||
|
||||
|
||||
class TestFormatQuantity:
|
||||
def test_speed_km_h(self):
|
||||
"""33.33 m/s should display as ~120 km/h."""
|
||||
result = format_quantity(33.33, "m/s")
|
||||
assert "km/h" in result
|
||||
assert "120" in result
|
||||
|
||||
def test_speed_km_s(self):
|
||||
"""10000 m/s should display as 10 km/s."""
|
||||
result = format_quantity(10000, "m/s")
|
||||
assert "km/s" in result
|
||||
|
||||
def test_distance_km(self):
|
||||
"""5000 m should display as 5 km."""
|
||||
result = format_quantity(5000, "m")
|
||||
assert "km" in result
|
||||
assert "5" in result
|
||||
|
||||
def test_distance_ly(self):
|
||||
"""9.461e15 m should display as ~1 ly."""
|
||||
result = format_quantity(9.461e15, "m")
|
||||
assert "ly" in result
|
||||
|
||||
def test_distance_au(self):
|
||||
"""1.496e11 m should display as ~1 AU."""
|
||||
result = format_quantity(1.496e11, "m")
|
||||
assert "AU" in result
|
||||
|
||||
def test_mass_tons(self):
|
||||
"""5000 kg should display as 5 t."""
|
||||
result = format_quantity(5000, "kg")
|
||||
assert "t" in result
|
||||
|
||||
def test_mass_kg(self):
|
||||
"""50 kg should display as 50 kg."""
|
||||
result = format_quantity(50, "kg")
|
||||
assert "kg" in result
|
||||
|
||||
def test_energy_density_wh_kg(self):
|
||||
"""720000 J/kg should display as 200 Wh/kg."""
|
||||
result = format_quantity(720000, "J/kg")
|
||||
assert "Wh/kg" in result
|
||||
assert "200" in result
|
||||
|
||||
def test_time_days(self):
|
||||
"""86400 s should display as 1 d."""
|
||||
result = format_quantity(86400, "s")
|
||||
assert "d" in result
|
||||
|
||||
def test_time_years(self):
|
||||
"""3.156e7 s should display as ~1 yr."""
|
||||
result = format_quantity(3.156e7, "s")
|
||||
assert "yr" in result
|
||||
|
||||
def test_cost_per_km(self):
|
||||
"""$/m always displays as $/km."""
|
||||
result = format_quantity(0.001, "$/m")
|
||||
assert "$/km" in result
|
||||
|
||||
def test_cost_per_ton_km(self):
|
||||
"""$/(kg·m) always displays as $/t·km."""
|
||||
result = format_quantity(1e-6, "$/(kg\u00b7m)")
|
||||
assert "$/t\u00b7km" in result
|
||||
|
||||
def test_emissions_g_per_km(self):
|
||||
"""kg/m always displays as g/km."""
|
||||
result = format_quantity(1e-4, "kg/m")
|
||||
assert "g/km" in result
|
||||
|
||||
def test_none_returns_dash(self):
|
||||
assert format_quantity(None, "m/s") == "\u2014"
|
||||
|
||||
def test_dimensionless(self):
|
||||
"""0-1 unit should just return the number."""
|
||||
result = format_quantity(0.75, "0-1")
|
||||
assert result == "0.75"
|
||||
|
||||
def test_string_input(self):
|
||||
"""String values should be converted to float."""
|
||||
result = format_quantity("5000", "m")
|
||||
assert "km" in result
|
||||
|
||||
def test_non_numeric_string(self):
|
||||
"""Non-numeric string values returned as-is."""
|
||||
assert format_quantity("true", "m") == "true"
|
||||
|
||||
def test_zero(self):
|
||||
result = format_quantity(0, "m/s")
|
||||
assert "m/s" in result
|
||||
|
||||
def test_area_m2(self):
|
||||
result = format_quantity(50, "m\u00b2")
|
||||
assert "m\u00b2" in result
|
||||
|
||||
def test_power_density(self):
|
||||
result = format_quantity(5000, "W/kg")
|
||||
assert "kW/kg" in result
|
||||
Reference in New Issue
Block a user