tests: add type annotations to top-level test files (#1688)

Add type annotations (parameter types and `-> None` return types) to all
top-level test functions across 22 test files. This enables mypy to
check test function bodies, catching type errors that were previously
hidden.
This commit is contained in:
ZeliardM
2026-10-04 10:36:01 -04:00
committed by GitHub
parent eed64fe013
commit b36c795cbc
22 changed files with 557 additions and 392 deletions

View File

@@ -3,6 +3,7 @@ import inspect
import pkgutil
import sys
from datetime import UTC, datetime, timedelta, timezone
from types import ModuleType
from unittest.mock import AsyncMock
from zoneinfo import ZoneInfo
@@ -77,9 +78,9 @@ temp_control_smart = parametrize(
interfaces = pytest.mark.parametrize("interface", kasa.interfaces.__all__)
def _get_subclasses(of_class, package):
def _get_subclasses(of_class: type, package: ModuleType) -> set[type]:
"""Get all the subclasses of a given class."""
subclasses = set()
subclasses: set[type] = set()
# iter_modules returns ModuleInfo: (module_finder, name, ispkg)
for _, modname, ispkg in pkgutil.iter_modules(package.__path__):
importlib.import_module("." + modname, package=package.__name__)
@@ -100,7 +101,7 @@ def _get_subclasses(of_class, package):
@interfaces
def test_feature_attributes(interface):
def test_feature_attributes(interface: str) -> None:
"""Test that all common derived classes define the FeatureAttributes."""
klass = getattr(kasa.interfaces, interface)
@@ -126,7 +127,7 @@ def test_feature_attributes(interface):
@led
async def test_led_module(dev: Device, mocker: MockerFixture):
async def test_led_module(dev: Device, mocker: MockerFixture) -> None:
"""Test fan speed feature."""
led_module = dev.modules.get(Module.Led)
assert led_module
@@ -153,7 +154,7 @@ async def test_led_module(dev: Device, mocker: MockerFixture):
@light_effect
async def test_light_effect_module(dev: Device, mocker: MockerFixture):
async def test_light_effect_module(dev: Device, mocker: MockerFixture) -> None:
"""Test fan speed feature."""
light_effect_module = dev.modules[Module.LightEffect]
assert light_effect_module
@@ -205,7 +206,7 @@ async def test_light_effect_module(dev: Device, mocker: MockerFixture):
@light_effect
async def test_light_effect_brightness(dev: Device, mocker: MockerFixture):
async def test_light_effect_brightness(dev: Device, mocker: MockerFixture) -> None:
"""Test that light module uses light_effect for brightness when active."""
light_module = dev.modules[Module.Light]
@@ -230,7 +231,7 @@ async def test_light_effect_brightness(dev: Device, mocker: MockerFixture):
@dimmable
async def test_light_brightness(dev: Device):
async def test_light_brightness(dev: Device) -> None:
"""Test brightness setter and getter."""
assert isinstance(dev, Device)
light = next(get_parent_and_child_modules(dev, Module.Light))
@@ -253,7 +254,7 @@ async def test_light_brightness(dev: Device):
@variable_temp
async def test_light_color_temp(dev: Device):
async def test_light_color_temp(dev: Device) -> None:
"""Test color temp setter and getter."""
assert isinstance(dev, Device)
@@ -292,7 +293,7 @@ async def test_light_color_temp(dev: Device):
@light
async def test_light_set_state(dev: Device):
async def test_light_set_state(dev: Device) -> None:
"""Test brightness setter and getter."""
assert isinstance(dev, Device)
light = next(get_parent_and_child_modules(dev, Module.Light))
@@ -319,7 +320,7 @@ async def test_light_set_state(dev: Device):
@light_preset
async def test_light_preset_module(dev: Device, mocker: MockerFixture):
async def test_light_preset_module(dev: Device, mocker: MockerFixture) -> None:
"""Test light preset module."""
preset_mod = next(get_parent_and_child_modules(dev, Module.LightPreset))
assert preset_mod
@@ -370,7 +371,7 @@ async def test_light_preset_module(dev: Device, mocker: MockerFixture):
@light_preset
async def test_light_preset_save(dev: Device, mocker: MockerFixture):
async def test_light_preset_save(dev: Device, mocker: MockerFixture) -> None:
"""Test saving a new preset value."""
preset_mod = next(get_parent_and_child_modules(dev, Module.LightPreset))
assert preset_mod
@@ -393,7 +394,7 @@ async def test_light_preset_save(dev: Device, mocker: MockerFixture):
@temp_control_smart
async def test_thermostat(dev: Device, mocker: MockerFixture):
async def test_thermostat(dev: Device, mocker: MockerFixture) -> None:
"""Test saving a new preset value."""
therm_mod = next(get_parent_and_child_modules(dev, Module.Thermostat))
assert therm_mod
@@ -426,7 +427,7 @@ async def test_thermostat(dev: Device, mocker: MockerFixture):
@time
async def test_set_time(dev: Device):
async def test_set_time(dev: Device) -> None:
"""Test setting the device time."""
time_mod = dev.modules[Module.Time]
@@ -463,7 +464,9 @@ async def test_set_time(dev: Device):
assert time_mod.time == original_time
async def test_time_post_update_no_time_uses_utc_unit(monkeypatch: pytest.MonkeyPatch):
async def test_time_post_update_no_time_uses_utc_unit(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""If neither get_timezone nor get_time are present, timezone falls back to UTC."""
from kasa.iot.modules.time import Time as TimeModule
@@ -476,7 +479,7 @@ async def test_time_post_update_no_time_uses_utc_unit(monkeypatch: pytest.Monkey
async def test_time_post_update_uses_offset_when_index_missing_unit(
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture
):
) -> None:
"""When index present but zone not on host, fall back to offset-based guess."""
from zoneinfo import ZoneInfoNotFoundError
@@ -547,7 +550,7 @@ async def test_time_post_update_unsynced_clock_uses_utc(
assert time_mod.time.year == 2000
async def test_time_get_time_exception_returns_none_unit(mocker: MockerFixture):
async def test_time_get_time_exception_returns_none_unit(mocker: MockerFixture) -> None:
"""Cover Time.get_time exception path (unit test of iot Time)."""
from kasa.iot.modules.time import Time as TimeModule
@@ -557,7 +560,7 @@ async def test_time_get_time_exception_returns_none_unit(mocker: MockerFixture):
assert await TimeModule.get_time(inst) is None
async def test_time_get_time_success_unit(mocker: MockerFixture):
async def test_time_get_time_success_unit(mocker: MockerFixture) -> None:
"""Cover the success path of Time.get_time."""
from kasa.iot.modules.time import Time as TimeModule
@@ -589,7 +592,7 @@ async def test_time_get_time_success_unit(mocker: MockerFixture):
async def test_time_post_update_with_time_no_tz_uses_guess_unit(
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture
):
) -> None:
"""When get_time is present but get_timezone is missing, use offset-based guess (dst_expected None)."""
from kasa.iot.modules.time import Time as TimeModule
@@ -620,7 +623,7 @@ async def test_time_post_update_with_time_no_tz_uses_guess_unit(
async def test_time_set_time_wraps_exception_unit(
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture
):
) -> None:
"""Cover exception wrapping in Time.set_time (unit test of iot Time)."""
from kasa.iot.modules.time import Time as TimeModule
@@ -673,7 +676,7 @@ async def test_smart_time_set_time_no_region_added_when_tzname_none_unit(
async def test_smartcam_time_post_update_fallback_parses_timezone_str_unit(
monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture
):
) -> None:
"""Exercise smartcam Time._post_update_hook fallback when ZoneInfo not found, parsing 'timezone' string."""
from zoneinfo import ZoneInfoNotFoundError