Skip to content
Closed
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 31 additions & 124 deletions tests/util/test_unit_conversion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Test Home Assistant eneergy utility functions."""
import inspect

import pytest

from homeassistant.const import (
Expand All @@ -16,6 +18,7 @@
UnitOfVolumetricFlux,
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util import unit_conversion
from homeassistant.util.unit_conversion import (
BaseUnitConverter,
DataRateConverter,
Expand All @@ -32,59 +35,31 @@
)

INVALID_SYMBOL = "bob"
_CONVERTERS: list[type[BaseUnitConverter]] = [
obj
for _, obj in inspect.getmembers(unit_conversion)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice that we don't need to update this container, but it's not very readable what the contents is besides the type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it might be better to add a test to ensure all converters are tested instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MartinHjelmare I've opened #86271 as an alternative.
I think it is better to keep the generated list just for comparing against the manual list and not for actually running the tests.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good!

if inspect.isclass(obj)
and issubclass(obj, BaseUnitConverter)
and obj != BaseUnitConverter
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another possibility is to make this list explicit, and then add a unit test to ensure that the explicit list contains all converters.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes I wonder if we should use the Hypothesis library in HA for cases like these.



def _get_valid_unit(converter: type[BaseUnitConverter]) -> str:
"""Get a valid unit from the converter, different from the normalized unit."""
return next(
filter(
Comment thread
MartinHjelmare marked this conversation as resolved.
Outdated
lambda v: v != converter.NORMALIZED_UNIT,
sorted(converter.VALID_UNITS),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filtering and then sorting, means you need to sort fewer items, than sorting and then filtering.

)
)


@pytest.mark.parametrize(
"converter,valid_unit",
[
(DataRateConverter, UnitOfDataRate.GIBIBYTES_PER_SECOND),
(DistanceConverter, UnitOfLength.KILOMETERS),
(DistanceConverter, UnitOfLength.METERS),
(DistanceConverter, UnitOfLength.CENTIMETERS),
(DistanceConverter, UnitOfLength.MILLIMETERS),
(DistanceConverter, UnitOfLength.MILES),
(DistanceConverter, UnitOfLength.YARDS),
(DistanceConverter, UnitOfLength.FEET),
(DistanceConverter, UnitOfLength.INCHES),
(ElectricCurrentConverter, UnitOfElectricCurrent.AMPERE),
(ElectricCurrentConverter, UnitOfElectricCurrent.MILLIAMPERE),
(EnergyConverter, UnitOfEnergy.WATT_HOUR),
(EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR),
(EnergyConverter, UnitOfEnergy.MEGA_WATT_HOUR),
(EnergyConverter, UnitOfEnergy.GIGA_JOULE),
(InformationConverter, UnitOfInformation.GIGABYTES),
(MassConverter, UnitOfMass.GRAMS),
(MassConverter, UnitOfMass.KILOGRAMS),
(MassConverter, UnitOfMass.MICROGRAMS),
(MassConverter, UnitOfMass.MILLIGRAMS),
(MassConverter, UnitOfMass.OUNCES),
(MassConverter, UnitOfMass.POUNDS),
(PowerConverter, UnitOfPower.WATT),
(PowerConverter, UnitOfPower.KILO_WATT),
(PressureConverter, UnitOfPressure.PA),
(PressureConverter, UnitOfPressure.HPA),
(PressureConverter, UnitOfPressure.MBAR),
(PressureConverter, UnitOfPressure.INHG),
(PressureConverter, UnitOfPressure.KPA),
(PressureConverter, UnitOfPressure.CBAR),
(PressureConverter, UnitOfPressure.MMHG),
(PressureConverter, UnitOfPressure.PSI),
(SpeedConverter, UnitOfVolumetricFlux.INCHES_PER_DAY),
(SpeedConverter, UnitOfVolumetricFlux.INCHES_PER_HOUR),
(SpeedConverter, UnitOfVolumetricFlux.MILLIMETERS_PER_DAY),
(SpeedConverter, UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR),
(SpeedConverter, UnitOfSpeed.FEET_PER_SECOND),
(SpeedConverter, UnitOfSpeed.KILOMETERS_PER_HOUR),
(SpeedConverter, UnitOfSpeed.KNOTS),
(SpeedConverter, UnitOfSpeed.METERS_PER_SECOND),
(SpeedConverter, UnitOfSpeed.MILES_PER_HOUR),
(TemperatureConverter, UnitOfTemperature.CELSIUS),
(TemperatureConverter, UnitOfTemperature.FAHRENHEIT),
(TemperatureConverter, UnitOfTemperature.KELVIN),
(VolumeConverter, UnitOfVolume.LITERS),
(VolumeConverter, UnitOfVolume.MILLILITERS),
(VolumeConverter, UnitOfVolume.GALLONS),
(VolumeConverter, UnitOfVolume.FLUID_OUNCES),
(converter, valid_unit)
for converter in _CONVERTERS
for valid_unit in sorted(converter.VALID_UNITS)
],
)
def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str) -> None:
Expand All @@ -94,21 +69,7 @@ def test_convert_same_unit(converter: type[BaseUnitConverter], valid_unit: str)

@pytest.mark.parametrize(
"converter,valid_unit",
[
(DataRateConverter, UnitOfDataRate.GIBIBYTES_PER_SECOND),
(DistanceConverter, UnitOfLength.KILOMETERS),
(ElectricCurrentConverter, UnitOfElectricCurrent.AMPERE),
(EnergyConverter, UnitOfEnergy.KILO_WATT_HOUR),
(InformationConverter, UnitOfInformation.GIBIBYTES),
(MassConverter, UnitOfMass.GRAMS),
(PowerConverter, UnitOfPower.WATT),
(PressureConverter, UnitOfPressure.PA),
(SpeedConverter, UnitOfSpeed.KILOMETERS_PER_HOUR),
(TemperatureConverter, UnitOfTemperature.CELSIUS),
(TemperatureConverter, UnitOfTemperature.FAHRENHEIT),
(TemperatureConverter, UnitOfTemperature.KELVIN),
(VolumeConverter, UnitOfVolume.LITERS),
],
[(converter, _get_valid_unit(converter)) for converter in _CONVERTERS],
)
def test_convert_invalid_unit(
converter: type[BaseUnitConverter], valid_unit: str
Expand All @@ -124,24 +85,8 @@ def test_convert_invalid_unit(
@pytest.mark.parametrize(
"converter,from_unit,to_unit",
[
(
DataRateConverter,
UnitOfDataRate.BYTES_PER_SECOND,
UnitOfDataRate.BITS_PER_SECOND,
),
(DistanceConverter, UnitOfLength.KILOMETERS, UnitOfLength.METERS),
(EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR),
(
InformationConverter,
UnitOfInformation.GIBIBYTES,
UnitOfInformation.GIGABYTES,
),
(MassConverter, UnitOfMass.GRAMS, UnitOfMass.KILOGRAMS),
(PowerConverter, UnitOfPower.WATT, UnitOfPower.KILO_WATT),
(PressureConverter, UnitOfPressure.HPA, UnitOfPressure.INHG),
(SpeedConverter, UnitOfSpeed.KILOMETERS_PER_HOUR, UnitOfSpeed.MILES_PER_HOUR),
(TemperatureConverter, UnitOfTemperature.CELSIUS, UnitOfTemperature.FAHRENHEIT),
(VolumeConverter, UnitOfVolume.GALLONS, UnitOfVolume.LITERS),
(converter, converter.NORMALIZED_UNIT, _get_valid_unit(converter))
for converter in _CONVERTERS
],
)
def test_convert_nonnumeric_value(
Expand All @@ -153,55 +98,17 @@ def test_convert_nonnumeric_value(


@pytest.mark.parametrize(
"converter,from_unit,to_unit,expected",
"converter,from_unit,to_unit",
[
(
DataRateConverter,
UnitOfDataRate.BITS_PER_SECOND,
UnitOfDataRate.BYTES_PER_SECOND,
8,
),
(DistanceConverter, UnitOfLength.KILOMETERS, UnitOfLength.METERS, 1 / 1000),
(
ElectricCurrentConverter,
UnitOfElectricCurrent.AMPERE,
UnitOfElectricCurrent.MILLIAMPERE,
1 / 1000,
),
(EnergyConverter, UnitOfEnergy.WATT_HOUR, UnitOfEnergy.KILO_WATT_HOUR, 1000),
(InformationConverter, UnitOfInformation.BITS, UnitOfInformation.BYTES, 8),
(PowerConverter, UnitOfPower.WATT, UnitOfPower.KILO_WATT, 1000),
(
PressureConverter,
UnitOfPressure.HPA,
UnitOfPressure.INHG,
pytest.approx(33.86389),
),
(
SpeedConverter,
UnitOfSpeed.KILOMETERS_PER_HOUR,
UnitOfSpeed.MILES_PER_HOUR,
pytest.approx(1.609343),
),
(
TemperatureConverter,
UnitOfTemperature.CELSIUS,
UnitOfTemperature.FAHRENHEIT,
1 / 1.8,
),
(
VolumeConverter,
UnitOfVolume.GALLONS,
UnitOfVolume.LITERS,
pytest.approx(0.264172),
),
(converter, converter.NORMALIZED_UNIT, _get_valid_unit(converter))
for converter in _CONVERTERS
],
)
def test_get_unit_ratio(
converter: type[BaseUnitConverter], from_unit: str, to_unit: str, expected: float
converter: type[BaseUnitConverter], from_unit: str, to_unit: str
) -> None:
"""Test unit ratio."""
assert converter.get_unit_ratio(from_unit, to_unit) == expected
assert converter.get_unit_ratio(from_unit, to_unit)


@pytest.mark.parametrize(
Expand Down