mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 10:44:04 +00:00
Fix potential infinite loop if incomplete lists returned (#920)
Fixes the test framework to handle fixtures with incomplete lists better by checking for completeness and overriding the sum. Also adds a pytest-timeout dev dependency with timeout set to 10 seconds. Finally fixes smartprotocol to prevent an infinite loop if incomplete lists ever happens in the real world. Co-authored-by: Teemu R. <tpr@iki.fi>
This commit is contained in:
@@ -58,6 +58,8 @@ def pytest_configure():
|
||||
|
||||
|
||||
def pytest_sessionfinish(session, exitstatus):
|
||||
if not pytest.fixtures_missing_methods:
|
||||
return
|
||||
msg = "\n"
|
||||
for fixture, methods in sorted(pytest.fixtures_missing_methods.items()):
|
||||
method_list = ", ".join(methods)
|
||||
|
@@ -28,6 +28,8 @@ class FakeSmartTransport(BaseTransport):
|
||||
*,
|
||||
list_return_size=10,
|
||||
component_nego_not_included=False,
|
||||
warn_fixture_missing_methods=True,
|
||||
fix_incomplete_fixture_lists=True,
|
||||
):
|
||||
super().__init__(
|
||||
config=DeviceConfig(
|
||||
@@ -46,6 +48,8 @@ class FakeSmartTransport(BaseTransport):
|
||||
for comp in self.info["component_nego"]["component_list"]
|
||||
}
|
||||
self.list_return_size = list_return_size
|
||||
self.warn_fixture_missing_methods = warn_fixture_missing_methods
|
||||
self.fix_incomplete_fixture_lists = fix_incomplete_fixture_lists
|
||||
|
||||
@property
|
||||
def default_port(self):
|
||||
@@ -220,6 +224,18 @@ class FakeSmartTransport(BaseTransport):
|
||||
if (params and (start_index := params.get("start_index")))
|
||||
else 0
|
||||
)
|
||||
# Fixtures generated before _handle_response_lists was implemented
|
||||
# could have incomplete lists.
|
||||
if (
|
||||
len(result[list_key]) < result["sum"]
|
||||
and self.fix_incomplete_fixture_lists
|
||||
):
|
||||
result["sum"] = len(result[list_key])
|
||||
if self.warn_fixture_missing_methods:
|
||||
pytest.fixtures_missing_methods.setdefault(
|
||||
self.fixture_name, set()
|
||||
).add(f"{method} (incomplete '{list_key}' list)")
|
||||
|
||||
result[list_key] = result[list_key][
|
||||
start_index : start_index + self.list_return_size
|
||||
]
|
||||
@@ -244,9 +260,10 @@ class FakeSmartTransport(BaseTransport):
|
||||
"method": method,
|
||||
}
|
||||
# Reduce warning spam by consolidating and reporting at the end of the run
|
||||
if self.fixture_name not in pytest.fixtures_missing_methods:
|
||||
pytest.fixtures_missing_methods[self.fixture_name] = set()
|
||||
pytest.fixtures_missing_methods[self.fixture_name].add(method)
|
||||
if self.warn_fixture_missing_methods:
|
||||
pytest.fixtures_missing_methods.setdefault(
|
||||
self.fixture_name, set()
|
||||
).add(method)
|
||||
return retval
|
||||
elif method in ["set_qs_info", "fw_download"]:
|
||||
return {"error_code": 0}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from ..credentials import Credentials
|
||||
@@ -242,3 +244,49 @@ async def test_smart_protocol_lists_multiple_request(mocker, list_sum, batch_siz
|
||||
)
|
||||
assert query_spy.call_count == expected_count
|
||||
assert resp == response
|
||||
|
||||
|
||||
async def test_incomplete_list(mocker, caplog):
|
||||
"""Test for handling incomplete lists returned from queries."""
|
||||
info = {
|
||||
"get_preset_rules": {
|
||||
"start_index": 0,
|
||||
"states": [
|
||||
{
|
||||
"brightness": 50,
|
||||
},
|
||||
{
|
||||
"brightness": 100,
|
||||
},
|
||||
],
|
||||
"sum": 7,
|
||||
}
|
||||
}
|
||||
caplog.set_level(logging.ERROR)
|
||||
transport = FakeSmartTransport(
|
||||
info,
|
||||
"dummy-name",
|
||||
component_nego_not_included=True,
|
||||
warn_fixture_missing_methods=False,
|
||||
)
|
||||
protocol = SmartProtocol(transport=transport)
|
||||
resp = await protocol.query({"get_preset_rules": None})
|
||||
assert resp
|
||||
assert resp["get_preset_rules"]["sum"] == 2 # FakeTransport fixes sum
|
||||
assert caplog.text == ""
|
||||
|
||||
# Test behaviour without FakeTranport fix
|
||||
transport = FakeSmartTransport(
|
||||
info,
|
||||
"dummy-name",
|
||||
component_nego_not_included=True,
|
||||
warn_fixture_missing_methods=False,
|
||||
fix_incomplete_fixture_lists=False,
|
||||
)
|
||||
protocol = SmartProtocol(transport=transport)
|
||||
resp = await protocol.query({"get_preset_rules": None})
|
||||
assert resp["get_preset_rules"]["sum"] == 7
|
||||
assert (
|
||||
"Device 127.0.0.123 returned empty results list for method get_preset_rules"
|
||||
in caplog.text
|
||||
)
|
||||
|
Reference in New Issue
Block a user