Move volume utility to unit_conversion (#78955)

* Move volume utility to unit_conversion

* Split tests
This commit is contained in:
epenet 2022-09-22 17:49:45 +02:00 committed by GitHub
parent 523d8d246b
commit c8491c4404
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 182 additions and 256 deletions

View file

@ -30,11 +30,7 @@ from homeassistant.helpers import entity_registry
from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.storage import STORAGE_DIR
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
from homeassistant.util import (
dt as dt_util,
temperature as temperature_util,
volume as volume_util,
)
from homeassistant.util import dt as dt_util, temperature as temperature_util
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
EnergyConverter,
@ -168,12 +164,12 @@ def _convert_volume_from_m3(to_unit: str, value: float | None) -> float | None:
"""Convert volume in m³ to to_unit."""
if value is None:
return None
return volume_util.convert(value, volume_util.NORMALIZED_UNIT, to_unit)
return VolumeConverter.convert(value, VolumeConverter.NORMALIZED_UNIT, to_unit)
def _convert_volume_to_m3(from_unit: str, value: float) -> float:
"""Convert volume in from_unit to m³."""
return volume_util.convert(value, from_unit, volume_util.NORMALIZED_UNIT)
return VolumeConverter.convert(value, from_unit, VolumeConverter.NORMALIZED_UNIT)
STATISTIC_UNIT_TO_UNIT_CLASS: dict[str | None, str] = {
@ -181,7 +177,7 @@ STATISTIC_UNIT_TO_UNIT_CLASS: dict[str | None, str] = {
PowerConverter.NORMALIZED_UNIT: PowerConverter.UNIT_CLASS,
PressureConverter.NORMALIZED_UNIT: PressureConverter.UNIT_CLASS,
temperature_util.NORMALIZED_UNIT: "temperature",
volume_util.NORMALIZED_UNIT: "volume",
VolumeConverter.NORMALIZED_UNIT: "volume",
}
STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = {
@ -189,7 +185,7 @@ STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = {
PowerConverter.NORMALIZED_UNIT: PowerConverter,
PressureConverter.NORMALIZED_UNIT: PressureConverter,
temperature_util.NORMALIZED_UNIT: TemperatureConverter,
volume_util.NORMALIZED_UNIT: VolumeConverter,
VolumeConverter.NORMALIZED_UNIT: VolumeConverter,
}
# Convert energy power, pressure, temperature and volume statistics from the
@ -201,7 +197,7 @@ STATISTIC_UNIT_TO_DISPLAY_UNIT_FUNCTIONS: dict[
PowerConverter.NORMALIZED_UNIT: _convert_power_from_w,
PressureConverter.NORMALIZED_UNIT: _convert_pressure_from_pa,
temperature_util.NORMALIZED_UNIT: _convert_temperature_from_c,
volume_util.NORMALIZED_UNIT: _convert_volume_from_m3,
VolumeConverter.NORMALIZED_UNIT: _convert_volume_from_m3,
}
# Convert energy and volume statistics from the display unit configured by the user
@ -209,7 +205,7 @@ STATISTIC_UNIT_TO_DISPLAY_UNIT_FUNCTIONS: dict[
# This is used to support adjusting statistics in the display unit
DISPLAY_UNIT_TO_STATISTIC_UNIT_FUNCTIONS: dict[str, Callable[[str, float], float]] = {
EnergyConverter.NORMALIZED_UNIT: _convert_energy_to_kwh,
volume_util.NORMALIZED_UNIT: _convert_volume_to_m3,
VolumeConverter.NORMALIZED_UNIT: _convert_volume_to_m3,
}
_LOGGER = logging.getLogger(__name__)

View file

@ -47,11 +47,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import entity_sources
from homeassistant.util import (
dt as dt_util,
temperature as temperature_util,
volume as volume_util,
)
from homeassistant.util import dt as dt_util, temperature as temperature_util
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
EnergyConverter,
@ -124,7 +120,8 @@ UNIT_CONVERSIONS: dict[str, dict[str, Callable]] = {
# Convert volume to cubic meter
SensorDeviceClass.GAS: {
VOLUME_CUBIC_METERS: lambda x: x,
VOLUME_CUBIC_FEET: volume_util.cubic_feet_to_cubic_meter,
VOLUME_CUBIC_FEET: lambda x: x
/ VolumeConverter.UNIT_CONVERSION[VOLUME_CUBIC_FEET],
},
}

View file

@ -21,10 +21,23 @@ from homeassistant.const import (
PRESSURE_PSI,
TEMP_CELSIUS,
UNIT_NOT_RECOGNIZED_TEMPLATE,
VOLUME_CUBIC_FEET,
VOLUME_CUBIC_METERS,
VOLUME_FLUID_OUNCE,
VOLUME_GALLONS,
VOLUME_LITERS,
VOLUME_MILLILITERS,
)
from . import temperature as temperature_util, volume as volume_util
from . import temperature as temperature_util
from .distance import FOOT_TO_M, IN_TO_M
# Volume conversion constants
_L_TO_CUBIC_METER = 0.001 # 1 L = 0.001 m³
_ML_TO_CUBIC_METER = 0.001 * _L_TO_CUBIC_METER # 1 mL = 0.001 L
_GALLON_TO_CUBIC_METER = 231 * pow(IN_TO_M, 3) # US gallon is 231 cubic inches
_FLUID_OUNCE_TO_CUBIC_METER = _GALLON_TO_CUBIC_METER / 128 # 128 fl. oz. in a US gallon
_CUBIC_FOOT_TO_CUBIC_METER = pow(FOOT_TO_M, 3)
class BaseUnitConverter:
@ -132,9 +145,25 @@ class TemperatureConverter(BaseUnitConverter):
convert = temperature_util.convert
class VolumeConverter(BaseUnitConverter):
class VolumeConverter(BaseUnitConverterWithUnitConversion):
"""Utility to convert volume values."""
UNIT_CLASS = "volume"
NORMALIZED_UNIT = VOLUME_CUBIC_METERS
VALID_UNITS = volume_util.VALID_UNITS
convert = volume_util.convert
# Units in terms of m³
UNIT_CONVERSION: dict[str, float] = {
VOLUME_LITERS: 1 / _L_TO_CUBIC_METER,
VOLUME_MILLILITERS: 1 / _ML_TO_CUBIC_METER,
VOLUME_GALLONS: 1 / _GALLON_TO_CUBIC_METER,
VOLUME_FLUID_OUNCE: 1 / _FLUID_OUNCE_TO_CUBIC_METER,
VOLUME_CUBIC_METERS: 1,
VOLUME_CUBIC_FEET: 1 / _CUBIC_FOOT_TO_CUBIC_METER,
}
VALID_UNITS: tuple[str, ...] = (
VOLUME_LITERS,
VOLUME_MILLILITERS,
VOLUME_GALLONS,
VOLUME_FLUID_OUNCE,
VOLUME_CUBIC_METERS,
VOLUME_CUBIC_FEET,
)

View file

@ -1,9 +1,7 @@
"""Volume conversion util functions."""
from __future__ import annotations
from numbers import Number
from homeassistant.const import (
from homeassistant.const import ( # pylint: disable=unused-import # noqa: F401
UNIT_NOT_RECOGNIZED_TEMPLATE,
VOLUME,
VOLUME_CUBIC_FEET,
@ -14,69 +12,40 @@ from homeassistant.const import (
VOLUME_MILLILITERS,
)
from .distance import FOOT_TO_M, IN_TO_M
from .unit_conversion import VolumeConverter
VALID_UNITS: tuple[str, ...] = (
VOLUME_LITERS,
VOLUME_MILLILITERS,
VOLUME_GALLONS,
VOLUME_FLUID_OUNCE,
VOLUME_CUBIC_METERS,
VOLUME_CUBIC_FEET,
)
L_TO_CUBIC_METER = 0.001 # 1 L = 0.001 m³
ML_TO_CUBIC_METER = 0.001 * L_TO_CUBIC_METER # 1 mL = 0.001 L
GALLON_TO_CUBIC_METER = 231 * pow(IN_TO_M, 3) # US gallon is 231 cubic inches
FLUID_OUNCE_TO_CUBIC_METER = GALLON_TO_CUBIC_METER / 128 # 128 fl. oz. in a US gallon
CUBIC_FOOT_TO_CUBIC_METER = pow(FOOT_TO_M, 3)
# Units in terms of m³
UNIT_CONVERSION: dict[str, float] = {
VOLUME_LITERS: 1 / L_TO_CUBIC_METER,
VOLUME_MILLILITERS: 1 / ML_TO_CUBIC_METER,
VOLUME_GALLONS: 1 / GALLON_TO_CUBIC_METER,
VOLUME_FLUID_OUNCE: 1 / FLUID_OUNCE_TO_CUBIC_METER,
VOLUME_CUBIC_METERS: 1,
VOLUME_CUBIC_FEET: 1 / CUBIC_FOOT_TO_CUBIC_METER,
}
NORMALIZED_UNIT = VOLUME_CUBIC_METERS
UNIT_CONVERSION = VolumeConverter.UNIT_CONVERSION
VALID_UNITS = VolumeConverter.VALID_UNITS
def liter_to_gallon(liter: float) -> float:
"""Convert a volume measurement in Liter to Gallon."""
# Need to add warning when core migration finished
return _convert(liter, VOLUME_LITERS, VOLUME_GALLONS)
def gallon_to_liter(gallon: float) -> float:
"""Convert a volume measurement in Gallon to Liter."""
# Need to add warning when core migration finished
return _convert(gallon, VOLUME_GALLONS, VOLUME_LITERS)
def cubic_meter_to_cubic_feet(cubic_meter: float) -> float:
"""Convert a volume measurement in cubic meter to cubic feet."""
# Need to add warning when core migration finished
return _convert(cubic_meter, VOLUME_CUBIC_METERS, VOLUME_CUBIC_FEET)
def cubic_feet_to_cubic_meter(cubic_feet: float) -> float:
"""Convert a volume measurement in cubic feet to cubic meter."""
# Need to add warning when core migration finished
return _convert(cubic_feet, VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS)
def convert(volume: float, from_unit: str, to_unit: str) -> float:
"""Convert a volume from one unit to another."""
if from_unit not in VALID_UNITS:
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(from_unit, VOLUME))
if to_unit not in VALID_UNITS:
raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(to_unit, VOLUME))
if not isinstance(volume, Number):
raise TypeError(f"{volume} is not of numeric type")
if from_unit == to_unit:
return volume
return _convert(volume, from_unit, to_unit)
# Need to add warning when core migration finished
return VolumeConverter.convert(volume, from_unit, to_unit)
def _convert(volume: float, from_unit: str, to_unit: str) -> float:

View file

@ -15,12 +15,19 @@ from homeassistant.const import (
PRESSURE_MMHG,
PRESSURE_PA,
PRESSURE_PSI,
VOLUME_CUBIC_FEET,
VOLUME_CUBIC_METERS,
VOLUME_FLUID_OUNCE,
VOLUME_GALLONS,
VOLUME_LITERS,
VOLUME_MILLILITERS,
)
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
EnergyConverter,
PowerConverter,
PressureConverter,
VolumeConverter,
)
INVALID_SYMBOL = "bob"
@ -42,6 +49,10 @@ INVALID_SYMBOL = "bob"
(PressureConverter, PRESSURE_CBAR),
(PressureConverter, PRESSURE_MMHG),
(PressureConverter, PRESSURE_PSI),
(VolumeConverter, VOLUME_LITERS),
(VolumeConverter, VOLUME_MILLILITERS),
(VolumeConverter, VOLUME_GALLONS),
(VolumeConverter, VOLUME_FLUID_OUNCE),
],
)
def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str) -> None:
@ -55,6 +66,7 @@ def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str)
(EnergyConverter, ENERGY_KILO_WATT_HOUR),
(PowerConverter, POWER_WATT),
(PressureConverter, PRESSURE_PA),
(VolumeConverter, VOLUME_LITERS),
],
)
def test_convert_invalid_unit(
@ -74,6 +86,7 @@ def test_convert_invalid_unit(
(EnergyConverter, ENERGY_WATT_HOUR, ENERGY_KILO_WATT_HOUR),
(PowerConverter, POWER_WATT, POWER_KILO_WATT),
(PressureConverter, PRESSURE_HPA, PRESSURE_INHG),
(VolumeConverter, VOLUME_GALLONS, VOLUME_LITERS),
],
)
def test_convert_nonnumeric_value(
@ -85,206 +98,128 @@ def test_convert_nonnumeric_value(
@pytest.mark.parametrize(
"converter,value,from_unit,expected,to_unit",
"value,from_unit,expected,to_unit",
[
(EnergyConverter, 10, ENERGY_WATT_HOUR, 0.01, ENERGY_KILO_WATT_HOUR),
(EnergyConverter, 10, ENERGY_WATT_HOUR, 0.00001, ENERGY_MEGA_WATT_HOUR),
(EnergyConverter, 10, ENERGY_KILO_WATT_HOUR, 10000, ENERGY_WATT_HOUR),
(EnergyConverter, 10, ENERGY_KILO_WATT_HOUR, 0.01, ENERGY_MEGA_WATT_HOUR),
(EnergyConverter, 10, ENERGY_MEGA_WATT_HOUR, 10000000, ENERGY_WATT_HOUR),
(EnergyConverter, 10, ENERGY_MEGA_WATT_HOUR, 10000, ENERGY_KILO_WATT_HOUR),
(PowerConverter, 10, POWER_KILO_WATT, 10000, POWER_WATT),
(PowerConverter, 10, POWER_WATT, 0.01, POWER_KILO_WATT),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(14.5037743897),
PRESSURE_PSI,
),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(29.5299801647),
PRESSURE_INHG,
),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(100000),
PRESSURE_PA,
),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(100),
PRESSURE_KPA,
),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(1000),
PRESSURE_MBAR,
),
(
PressureConverter,
1000,
PRESSURE_HPA,
pytest.approx(100),
PRESSURE_CBAR,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(14.5037743897),
PRESSURE_PSI,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(29.5299801647),
PRESSURE_INHG,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(100000),
PRESSURE_PA,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(1000),
PRESSURE_HPA,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(1000),
PRESSURE_MBAR,
),
(
PressureConverter,
100,
PRESSURE_KPA,
pytest.approx(100),
PRESSURE_CBAR,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(14.7346266155),
PRESSURE_PSI,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(101.59167),
PRESSURE_KPA,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(1015.9167),
PRESSURE_HPA,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(101591.67),
PRESSURE_PA,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(1015.9167),
PRESSURE_MBAR,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(101.59167),
PRESSURE_CBAR,
),
(
PressureConverter,
30,
PRESSURE_INHG,
pytest.approx(762.002),
PRESSURE_MMHG,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(0.580102),
PRESSURE_PSI,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(3.99966),
PRESSURE_KPA,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(39.9966),
PRESSURE_HPA,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(3999.66),
PRESSURE_PA,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(39.9966),
PRESSURE_MBAR,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(3.99966),
PRESSURE_CBAR,
),
(
PressureConverter,
30,
PRESSURE_MMHG,
pytest.approx(1.181099),
PRESSURE_INHG,
),
(10, ENERGY_WATT_HOUR, 0.01, ENERGY_KILO_WATT_HOUR),
(10, ENERGY_WATT_HOUR, 0.00001, ENERGY_MEGA_WATT_HOUR),
(10, ENERGY_KILO_WATT_HOUR, 10000, ENERGY_WATT_HOUR),
(10, ENERGY_KILO_WATT_HOUR, 0.01, ENERGY_MEGA_WATT_HOUR),
(10, ENERGY_MEGA_WATT_HOUR, 10000000, ENERGY_WATT_HOUR),
(10, ENERGY_MEGA_WATT_HOUR, 10000, ENERGY_KILO_WATT_HOUR),
],
)
def test_convert(
converter: type[BaseUnitConverter],
def test_energy_convert(
value: float,
from_unit: str,
expected: float,
to_unit: str,
) -> None:
"""Test conversion to other units."""
assert converter.convert(value, from_unit, to_unit) == expected
assert EnergyConverter.convert(value, from_unit, to_unit) == expected
@pytest.mark.parametrize(
"value,from_unit,expected,to_unit",
[
(10, POWER_KILO_WATT, 10000, POWER_WATT),
(10, POWER_WATT, 0.01, POWER_KILO_WATT),
],
)
def test_power_convert(
value: float,
from_unit: str,
expected: float,
to_unit: str,
) -> None:
"""Test conversion to other units."""
assert PowerConverter.convert(value, from_unit, to_unit) == expected
@pytest.mark.parametrize(
"value,from_unit,expected,to_unit",
[
(1000, PRESSURE_HPA, pytest.approx(14.5037743897), PRESSURE_PSI),
(1000, PRESSURE_HPA, pytest.approx(29.5299801647), PRESSURE_INHG),
(1000, PRESSURE_HPA, pytest.approx(100000), PRESSURE_PA),
(1000, PRESSURE_HPA, pytest.approx(100), PRESSURE_KPA),
(1000, PRESSURE_HPA, pytest.approx(1000), PRESSURE_MBAR),
(1000, PRESSURE_HPA, pytest.approx(100), PRESSURE_CBAR),
(100, PRESSURE_KPA, pytest.approx(14.5037743897), PRESSURE_PSI),
(100, PRESSURE_KPA, pytest.approx(29.5299801647), PRESSURE_INHG),
(100, PRESSURE_KPA, pytest.approx(100000), PRESSURE_PA),
(100, PRESSURE_KPA, pytest.approx(1000), PRESSURE_HPA),
(100, PRESSURE_KPA, pytest.approx(1000), PRESSURE_MBAR),
(100, PRESSURE_KPA, pytest.approx(100), PRESSURE_CBAR),
(30, PRESSURE_INHG, pytest.approx(14.7346266155), PRESSURE_PSI),
(30, PRESSURE_INHG, pytest.approx(101.59167), PRESSURE_KPA),
(30, PRESSURE_INHG, pytest.approx(1015.9167), PRESSURE_HPA),
(30, PRESSURE_INHG, pytest.approx(101591.67), PRESSURE_PA),
(30, PRESSURE_INHG, pytest.approx(1015.9167), PRESSURE_MBAR),
(30, PRESSURE_INHG, pytest.approx(101.59167), PRESSURE_CBAR),
(30, PRESSURE_INHG, pytest.approx(762.002), PRESSURE_MMHG),
(30, PRESSURE_MMHG, pytest.approx(0.580102), PRESSURE_PSI),
(30, PRESSURE_MMHG, pytest.approx(3.99966), PRESSURE_KPA),
(30, PRESSURE_MMHG, pytest.approx(39.9966), PRESSURE_HPA),
(30, PRESSURE_MMHG, pytest.approx(3999.66), PRESSURE_PA),
(30, PRESSURE_MMHG, pytest.approx(39.9966), PRESSURE_MBAR),
(30, PRESSURE_MMHG, pytest.approx(3.99966), PRESSURE_CBAR),
(30, PRESSURE_MMHG, pytest.approx(1.181099), PRESSURE_INHG),
],
)
def test_pressure_convert(
value: float,
from_unit: str,
expected: float,
to_unit: str,
) -> None:
"""Test conversion to other units."""
assert PressureConverter.convert(value, from_unit, to_unit) == expected
@pytest.mark.parametrize(
"value,from_unit,expected,to_unit",
[
(5, VOLUME_LITERS, pytest.approx(1.32086), VOLUME_GALLONS),
(5, VOLUME_GALLONS, pytest.approx(18.92706), VOLUME_LITERS),
(5, VOLUME_CUBIC_METERS, pytest.approx(176.5733335), VOLUME_CUBIC_FEET),
(500, VOLUME_CUBIC_FEET, pytest.approx(14.1584233), VOLUME_CUBIC_METERS),
(500, VOLUME_CUBIC_FEET, pytest.approx(14.1584233), VOLUME_CUBIC_METERS),
(500, VOLUME_CUBIC_FEET, pytest.approx(478753.2467), VOLUME_FLUID_OUNCE),
(500, VOLUME_CUBIC_FEET, pytest.approx(3740.25974), VOLUME_GALLONS),
(500, VOLUME_CUBIC_FEET, pytest.approx(14158.42329599), VOLUME_LITERS),
(500, VOLUME_CUBIC_FEET, pytest.approx(14158423.29599), VOLUME_MILLILITERS),
(500, VOLUME_CUBIC_METERS, 500, VOLUME_CUBIC_METERS),
(500, VOLUME_CUBIC_METERS, pytest.approx(16907011.35), VOLUME_FLUID_OUNCE),
(500, VOLUME_CUBIC_METERS, pytest.approx(132086.02617), VOLUME_GALLONS),
(500, VOLUME_CUBIC_METERS, 500000, VOLUME_LITERS),
(500, VOLUME_CUBIC_METERS, 500000000, VOLUME_MILLILITERS),
(500, VOLUME_FLUID_OUNCE, pytest.approx(0.52218967), VOLUME_CUBIC_FEET),
(500, VOLUME_FLUID_OUNCE, pytest.approx(0.014786764), VOLUME_CUBIC_METERS),
(500, VOLUME_FLUID_OUNCE, 3.90625, VOLUME_GALLONS),
(500, VOLUME_FLUID_OUNCE, pytest.approx(14.786764), VOLUME_LITERS),
(500, VOLUME_FLUID_OUNCE, pytest.approx(14786.764), VOLUME_MILLILITERS),
(500, VOLUME_GALLONS, pytest.approx(66.84027), VOLUME_CUBIC_FEET),
(500, VOLUME_GALLONS, pytest.approx(1.892706), VOLUME_CUBIC_METERS),
(500, VOLUME_GALLONS, 64000, VOLUME_FLUID_OUNCE),
(500, VOLUME_GALLONS, pytest.approx(1892.70589), VOLUME_LITERS),
(500, VOLUME_GALLONS, pytest.approx(1892705.89), VOLUME_MILLILITERS),
(500, VOLUME_LITERS, pytest.approx(17.65733), VOLUME_CUBIC_FEET),
(500, VOLUME_LITERS, 0.5, VOLUME_CUBIC_METERS),
(500, VOLUME_LITERS, pytest.approx(16907.011), VOLUME_FLUID_OUNCE),
(500, VOLUME_LITERS, pytest.approx(132.086), VOLUME_GALLONS),
(500, VOLUME_LITERS, 500000, VOLUME_MILLILITERS),
(500, VOLUME_MILLILITERS, pytest.approx(0.01765733), VOLUME_CUBIC_FEET),
(500, VOLUME_MILLILITERS, 0.0005, VOLUME_CUBIC_METERS),
(500, VOLUME_MILLILITERS, pytest.approx(16.907), VOLUME_FLUID_OUNCE),
(500, VOLUME_MILLILITERS, pytest.approx(0.132086), VOLUME_GALLONS),
(500, VOLUME_MILLILITERS, 0.5, VOLUME_LITERS),
],
)
def test_volume_convert(
value: float,
from_unit: str,
expected: float,
to_unit: str,
) -> None:
"""Test conversion to other units."""
assert VolumeConverter.convert(value, from_unit, to_unit) == expected