Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
28 changes: 16 additions & 12 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from asyncclick.testing import CliRunner

from kasa import (
Device,
DeviceConfig,
SmartProtocol,
)
Expand Down Expand Up @@ -68,14 +69,14 @@ def _track_session(self, *args, **kwargs):
await session.close()


def load_fixture(foldername, filename):
def load_fixture(foldername: str, filename: str) -> str:
"""Load a fixture."""
path = Path(Path(__file__).parent / "fixtures" / foldername / filename)
with path.open() as fdp:
return fdp.read()


async def handle_turn_on(dev, turn_on):
async def handle_turn_on(dev: Device, turn_on: bool) -> None:
if turn_on:
await dev.turn_on()
else:
Expand Down Expand Up @@ -110,15 +111,16 @@ async def reset(self) -> None:
yield protocol


def pytest_configure():
pytest.fixtures_missing_methods = {}
def pytest_configure() -> None:
pytest.fixtures_missing_methods = {} # type: ignore[attr-defined]


def pytest_sessionfinish(session, exitstatus):
if not pytest.fixtures_missing_methods:
def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
fixtures_missing: dict = getattr(pytest, "fixtures_missing_methods", {})
if not fixtures_missing:
return
msg = "\n"
for fixture, methods in sorted(pytest.fixtures_missing_methods.items()):
for fixture, methods in sorted(fixtures_missing.items()):
method_list = ", ".join(methods)
msg += f"Fixture {fixture} missing: {method_list}\n"

Expand All @@ -128,7 +130,7 @@ def pytest_sessionfinish(session, exitstatus):
)


def pytest_addoption(parser):
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--ip", action="store", default=None, help="run against device on given ip"
)
Expand All @@ -140,7 +142,9 @@ def pytest_addoption(parser):
)


def pytest_collection_modifyitems(config, items):
def pytest_collection_modifyitems(
config: pytest.Config, items: list[pytest.Item]
) -> None:
if not config.getoption("--ip"):
print("Testing against fixtures.")
# pytest_socket doesn't work properly in windows with asyncio
Expand All @@ -161,11 +165,11 @@ def pytest_collection_modifyitems(config, items):


@pytest.fixture(autouse=True, scope="session")
def asyncio_sleep_fixture(request): # noqa: PT004
def asyncio_sleep_fixture(request: pytest.FixtureRequest): # noqa: PT004
"""Patch sleep to prevent tests actually waiting."""
orig_asyncio_sleep = asyncio.sleep

async def _asyncio_sleep(*_, **__):
async def _asyncio_sleep(*_, **__) -> None:
await orig_asyncio_sleep(0)

if request.config.getoption("--ip"):
Expand All @@ -176,7 +180,7 @@ async def _asyncio_sleep(*_, **__):


@pytest.fixture(autouse=True, scope="session")
def mock_datagram_endpoint(request): # noqa: PT004
def mock_datagram_endpoint(request: pytest.FixtureRequest):
"""Mock create_datagram_endpoint so it doesn't perform io."""

async def _create_datagram_endpoint(protocol_factory, *_, **__):
Expand Down
24 changes: 14 additions & 10 deletions tests/device_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def parametrize_subtract(params: pytest.MarkDecorator, subtract: pytest.MarkDeco


def parametrize(
desc,
desc: str,
*,
model_filter=None,
protocol_filter=None,
Expand Down Expand Up @@ -377,7 +377,7 @@ def parametrize(
vacuum = parametrize("vacuums", device_type_filter=[DeviceType.Vacuum])


def check_categories():
def check_categories() -> None:
"""Check that every fixture file is categorized."""
categorized_fixtures = set(
dimmer_iot.args[1]
Expand Down Expand Up @@ -410,7 +410,7 @@ def check_categories():
check_categories()


def device_for_fixture_name(model, protocol):
def device_for_fixture_name(model: str, protocol: str):
if protocol in {"SMART", "SMART.CHILD"}:
return SmartDevice
elif protocol in {"SMARTCAM", "SMARTCAM.CHILD"}:
Expand Down Expand Up @@ -449,7 +449,9 @@ async def _update_and_close(d) -> Device:
return d


async def _discover_update_and_close(ip, username, password) -> Device:
async def _discover_update_and_close(
ip: str, username: str | None, password: str | None
) -> Device:
if username and password:
credentials = Credentials(username=username, password=password)
else:
Expand All @@ -459,7 +461,7 @@ async def _discover_update_and_close(ip, username, password) -> Device:


async def get_device_for_fixture(
fixture_data: FixtureInfo, *, verbatim=False, update_after_init=True
fixture_data: FixtureInfo, *, verbatim: bool = False, update_after_init: bool = True
) -> Device:
# if the wanted file is not an absolute path, prepend the fixtures directory

Expand All @@ -484,7 +486,9 @@ class DummyParent:
fixture_data.data, fixture_data.name, verbatim=verbatim
)
else:
d.protocol = FakeIotProtocol(fixture_data.data, verbatim=verbatim)
d.protocol = FakeIotProtocol(
fixture_data.data, fixture_data.name, verbatim=verbatim
)

discovery_data = None
if "discovery_result" in fixture_data.data:
Expand All @@ -502,21 +506,21 @@ class DummyParent:
return d


async def get_device_for_fixture_protocol(fixture, protocol):
async def get_device_for_fixture_protocol(fixture: str, protocol: str):
finfo = FixtureInfo(name=fixture, protocol=protocol, data={})
for fixture_info in FIXTURE_DATA:
if finfo == fixture_info:
return await get_device_for_fixture(fixture_info)


def get_fixture_info(fixture, protocol):
def get_fixture_info(fixture: str, protocol: str):
finfo = FixtureInfo(name=fixture, protocol=protocol, data={})
for fixture_info in FIXTURE_DATA:
if finfo == fixture_info:
return fixture_info


def get_nearest_fixture_to_ip(dev):
def get_nearest_fixture_to_ip(dev: Device):
if isinstance(dev, SmartDevice):
protocol_fixtures = filter_fixtures("", protocol_filter={"SMART"})
elif isinstance(dev, SmartCamDevice):
Expand Down Expand Up @@ -554,7 +558,7 @@ def get_nearest_fixture_to_ip(dev):


@pytest.fixture(params=filter_fixtures("main devices"), ids=idgenerator)
async def dev(request) -> AsyncGenerator[Device, None]:
async def dev(request: pytest.FixtureRequest) -> AsyncGenerator[Device, None]:
"""Device fixture.

Provides a device (given --ip) or parametrized fixture for the supported devices.
Expand Down
23 changes: 12 additions & 11 deletions tests/discovery_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, TypedDict

import pytest
from pytest_mock import MockerFixture

from kasa.transports.xortransport import XorEncryption

Expand Down Expand Up @@ -48,8 +49,8 @@ class DiscoveryResponse(TypedDict):


def _make_unsupported(
device_family,
encrypt_type,
device_family: str,
encrypt_type: str,
*,
https: bool = False,
omit_keys: dict[str, Any] | None = None,
Expand Down Expand Up @@ -112,7 +113,7 @@ def _make_unsupported(


def parametrize_discovery(
desc, *, data_root_filter=None, protocol_filter=None, model_filter=None
desc: str, *, data_root_filter=None, protocol_filter=None, model_filter=None
):
filtered_fixtures = filter_fixtures(
desc,
Expand Down Expand Up @@ -141,7 +142,7 @@ def parametrize_discovery(
),
ids=idgenerator,
)
async def discovery_mock(request, mocker):
async def discovery_mock(request: pytest.FixtureRequest, mocker: MockerFixture):
"""Mock discovery and patch protocol queries to use Fake protocols."""
fi: FixtureInfo = request.param
fixture_info = FixtureInfo(fi.name, fi.protocol, copy.deepcopy(fi.data))
Expand Down Expand Up @@ -238,7 +239,7 @@ def _datagram(self) -> bytes:
return dm


def patch_discovery(fixture_infos: dict[str, FixtureInfo], mocker):
def patch_discovery(fixture_infos: dict[str, FixtureInfo], mocker: MockerFixture):
"""Mock discovery and patch protocol queries to use Fake protocols."""
discovery_mocks = {
ip: create_discovery_mock(ip, fixture_info.data)
Expand Down Expand Up @@ -271,7 +272,7 @@ async def process_callback_queue(finished_event: asyncio.Event) -> None:
await exception_queue.put(None)
callback_queue.task_done()

async def wait_for_coro():
async def wait_for_coro() -> None:
await callback_queue.join()
if ex := exception_queue.get_nowait():
raise ex
Expand All @@ -286,7 +287,7 @@ def _run_callback_task(self, coro: Coroutine) -> None:
)

# do_discover_mock
async def mock_discover(self):
async def mock_discover(self) -> None:
"""Call datagram_received for all mock fixtures.

Handles test cases modifying the ip and hostname of the first fixture
Expand Down Expand Up @@ -333,7 +334,7 @@ async def _query(self, request, retry_count: int = 3):
mocker.patch("kasa.IotProtocol.query", _query)
mocker.patch("kasa.SmartProtocol.query", _query)

def _getaddrinfo(host, *_, **__):
def _getaddrinfo(host: str, *_, **__):
nonlocal first_host, first_ip
first_host = host # Store the hostname used by discover single
first_ip = list(discovery_mocks.values())[
Expand All @@ -358,7 +359,7 @@ def _getaddrinfo(host, *_, **__):
),
ids=idgenerator,
)
def discovery_data(request, mocker):
def discovery_data(request: pytest.FixtureRequest, mocker: MockerFixture):
"""Return raw discovery file contents as JSON. Used for discovery tests."""
fixture_info = request.param
fixture_data = copy.deepcopy(fixture_info.data)
Expand All @@ -383,12 +384,12 @@ def discovery_data(request, mocker):
@pytest.fixture(
params=UNSUPPORTED_DEVICES.values(), ids=list(UNSUPPORTED_DEVICES.keys())
)
def unsupported_device_info(request, mocker):
def unsupported_device_info(request: pytest.FixtureRequest, mocker: MockerFixture):
"""Return unsupported devices for cli and discovery tests."""
discovery_data = request.param
host = "127.0.0.1"

async def mock_discover(self):
async def mock_discover(self) -> None:
if discovery_data:
data = (
b"\x02\x00\x00\x01\x01[\x00\x00\x00\x00\x00\x00W\xcev\xf8"
Expand Down
Loading
Loading