Improve testing harness to allow tests on real devices (#197)

* test_cli: provide return values to patched objects to avoid warning about non-awaited calls

* test_cli: restore alias after testing

* smartstrip: remove internal update() calls for turn_{on,off}, set_led

* Make sure power is always a float

* Fix discovery tests

* Make tests runnable on real devices

* Add a note about running tests on a real device

* test_strip: run update against the parent device
This commit is contained in:
Teemu R
2021-09-19 23:45:48 +02:00
committed by GitHub
parent b088596205
commit 1803a83ae6
14 changed files with 70 additions and 24 deletions

View File

@@ -147,7 +147,7 @@ def get_device_for_file(file):
with open(p) as f:
sysinfo = json.load(f)
model = basename(file)
p = device_for_file(model)(host="123.123.123.123")
p = device_for_file(model)(host="127.0.0.123")
p.protocol = FakeTransportProtocol(sysinfo)
asyncio.run(p.update())
return p
@@ -168,21 +168,29 @@ def dev(request):
asyncio.run(d.update())
if d.model in file:
return d
raise Exception("Unable to find type for %s" % ip)
else:
pytest.skip(f"skipping file {file}")
return get_device_for_file(file)
def pytest_addoption(parser):
parser.addoption("--ip", action="store", default=None, help="run against device")
parser.addoption(
"--ip", action="store", default=None, help="run against device on given ip"
)
def pytest_collection_modifyitems(config, items):
if not config.getoption("--ip"):
print("Testing against fixtures.")
return
else:
print("Running against ip %s" % config.getoption("--ip"))
requires_dummy = pytest.mark.skip(
reason="test requires to be run against dummy data"
)
for item in items:
if "requires_dummy" in item.keywords:
item.add_marker(requires_dummy)
# allow mocks to be awaited

View File

@@ -124,7 +124,7 @@ LIGHT_STATE_SCHEMA = Schema(
"dft_on_state": Optional(
{
"brightness": All(int, Range(min=0, max=100)),
"color_temp": All(int, Range(min=2000, max=9000)),
"color_temp": All(int, Range(min=0, max=9000)),
"hue": All(int, Range(min=0, max=255)),
"mode": str,
"saturation": All(int, Range(min=0, max=255)),

View File

@@ -66,6 +66,7 @@ async def test_hsv(dev, turn_on):
await dev.set_hsv(hue=1, saturation=1, value=1)
await dev.update()
hue, saturation, brightness = dev.hsv
assert hue == 1
assert saturation == 1
@@ -134,6 +135,7 @@ async def test_variable_temp_state_information(dev):
async def test_try_set_colortemp(dev, turn_on):
await handle_turn_on(dev, turn_on)
await dev.set_color_temp(2700)
await dev.update()
assert dev.color_temp == 2700
@@ -179,9 +181,11 @@ async def test_dimmable_brightness(dev, turn_on):
assert dev.is_dimmable
await dev.set_brightness(50)
await dev.update()
assert dev.brightness == 50
await dev.set_brightness(10)
await dev.update()
assert dev.brightness == 10
with pytest.raises(ValueError):

View File

@@ -18,7 +18,7 @@ async def test_state(dev, turn_on):
await handle_turn_on(dev, turn_on)
runner = CliRunner()
res = await runner.invoke(state, obj=dev)
print(res.output)
await dev.update()
if dev.is_on:
assert "Device state: ON" in res.output
@@ -32,6 +32,8 @@ async def test_alias(dev):
res = await runner.invoke(alias, obj=dev)
assert f"Alias: {dev.alias}" in res.output
old_alias = dev.alias
new_alias = "new alias"
res = await runner.invoke(alias, [new_alias], obj=dev)
assert f"Setting alias to {new_alias}" in res.output
@@ -39,6 +41,8 @@ async def test_alias(dev):
res = await runner.invoke(alias, obj=dev)
assert f"Alias: {new_alias}" in res.output
await dev.set_alias(old_alias)
async def test_raw_command(dev):
runner = CliRunner()
@@ -63,11 +67,13 @@ async def test_emeter(dev: SmartDevice, mocker):
assert "== Emeter ==" in res.output
monthly = mocker.patch.object(dev, "get_emeter_monthly")
monthly.return_value = []
res = await runner.invoke(emeter, ["--year", "1900"], obj=dev)
assert "For year" in res.output
monthly.assert_called()
daily = mocker.patch.object(dev, "get_emeter_daily")
daily.return_value = []
res = await runner.invoke(emeter, ["--month", "1900-12"], obj=dev)
assert "For month" in res.output
daily.assert_called()

View File

@@ -8,14 +8,14 @@ from .conftest import bulb, dimmer, lightstrip, plug, pytestmark, strip
@plug
async def test_type_detection_plug(dev: SmartDevice):
d = Discover._get_device_class(dev.protocol.discovery_data)("localhost")
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_plug
assert d.device_type == DeviceType.Plug
@bulb
async def test_type_detection_bulb(dev: SmartDevice):
d = Discover._get_device_class(dev.protocol.discovery_data)("localhost")
d = Discover._get_device_class(dev._last_update)("localhost")
# TODO: light_strip is a special case for now to force bulb tests on it
if not d.is_light_strip:
assert d.is_bulb
@@ -24,21 +24,21 @@ async def test_type_detection_bulb(dev: SmartDevice):
@strip
async def test_type_detection_strip(dev: SmartDevice):
d = Discover._get_device_class(dev.protocol.discovery_data)("localhost")
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_strip
assert d.device_type == DeviceType.Strip
@dimmer
async def test_type_detection_dimmer(dev: SmartDevice):
d = Discover._get_device_class(dev.protocol.discovery_data)("localhost")
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_dimmer
assert d.device_type == DeviceType.Dimmer
@lightstrip
async def test_type_detection_lightstrip(dev: SmartDevice):
d = Discover._get_device_class(dev.protocol.discovery_data)("localhost")
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_light_strip
assert d.device_type == DeviceType.LightStrip

View File

@@ -32,6 +32,7 @@ async def test_get_emeter_realtime(dev):
@has_emeter
@pytest.mark.requires_dummy
async def test_get_emeter_daily(dev):
if dev.is_strip:
pytest.skip("Disabled for strips temporarily")
@@ -54,6 +55,7 @@ async def test_get_emeter_daily(dev):
@has_emeter
@pytest.mark.requires_dummy
async def test_get_emeter_monthly(dev):
if dev.is_strip:
pytest.skip("Disabled for strips temporarily")

View File

@@ -20,9 +20,11 @@ async def test_led(dev):
original = dev.led
await dev.set_led(False)
await dev.update()
assert not dev.led
await dev.set_led(True)
await dev.update()
assert dev.led
await dev.set_led(original)

View File

@@ -13,6 +13,7 @@ async def test_state_info(dev):
assert isinstance(dev.state_information, dict)
@pytest.mark.requires_dummy
async def test_invalid_connection(dev):
with patch.object(FakeTransportProtocol, "query", side_effect=SmartDeviceException):
with pytest.raises(SmartDeviceException):
@@ -49,18 +50,22 @@ async def test_state(dev, turn_on):
orig_state = dev.is_on
if orig_state:
await dev.turn_off()
await dev.update()
assert not dev.is_on
assert dev.is_off
await dev.turn_on()
await dev.update()
assert dev.is_on
assert not dev.is_off
else:
await dev.turn_on()
await dev.update()
assert dev.is_on
assert not dev.is_off
await dev.turn_off()
await dev.update()
assert not dev.is_on
assert dev.is_off
@@ -71,9 +76,11 @@ async def test_alias(dev):
assert isinstance(original, str)
await dev.set_alias(test_alias)
await dev.update()
assert dev.alias == test_alias
await dev.set_alias(original)
await dev.update()
assert dev.alias == original

View File

@@ -15,20 +15,24 @@ async def test_children_change_state(dev, turn_on):
orig_state = plug.is_on
if orig_state:
await plug.turn_off()
assert not plug.is_on
assert plug.is_off
await dev.update()
assert plug.is_on is False
assert plug.is_off is True
await plug.turn_on()
assert plug.is_on
assert not plug.is_off
await dev.update()
assert plug.is_on is True
assert plug.is_off is False
else:
await plug.turn_on()
assert plug.is_on
assert not plug.is_off
await dev.update()
assert plug.is_on is True
assert plug.is_off is False
await plug.turn_off()
assert not plug.is_on
assert plug.is_off
await dev.update()
assert plug.is_on is False
assert plug.is_off is True
@strip