Skip to content

Commit cea049b

Browse files
committed
added more tests
1 parent f92a0cb commit cea049b

File tree

6 files changed

+116
-46
lines changed

6 files changed

+116
-46
lines changed

custom_components/pyscript/eval.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,11 +1381,7 @@ def completions(self, root):
13811381
try:
13821382
for attr in var.__dir__():
13831383
if attr.lower().startswith(attr_root) and (attr_root != "" or attr[0:1] != "_"):
1384-
value = getattr(var, attr, None)
1385-
if callable(value) or isinstance(value, EvalFunc):
1386-
words.add(f"{name}.{attr}")
1387-
else:
1388-
words.add(f"{name}.{attr}")
1384+
words.add(f"{name}.{attr}")
13891385
except Exception:
13901386
pass
13911387
for keyw in set(keyword.kwlist) - {"yield"}:
@@ -1398,11 +1394,7 @@ def completions(self, root):
13981394
sym_table.update(self.global_sym_table.items())
13991395
for name, value in sym_table.items():
14001396
if name.lower().startswith(root):
1401-
if callable(value) or isinstance(value, EvalFunc):
1402-
# used to be f"{name}(", but Jupyter doesn't always do the right thing with that
1403-
words.add(name)
1404-
else:
1405-
words.add(name)
1397+
words.add(name)
14061398
return words
14071399

14081400
async def eval(self, new_state_vars=None):

custom_components/pyscript/jupyter_kernel.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,6 @@ def encode(msg):
321321
# _LOGGER.debug("send %s: %s", msg_type, parts)
322322
for this_stream in stream if isinstance(stream, set) else {stream}:
323323
await this_stream.send_multipart(parts)
324-
# else:
325-
# _LOGGER.debug("send skipping msg_type %s since socket is None", msg_type)
326324

327325
async def shell_handler(self, shell_socket, wire_msg):
328326
"""Handle shell messages."""

custom_components/pyscript/trigger.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,6 @@ def dt_now():
2626
return dt.datetime.now()
2727

2828

29-
def isleap(year):
30-
"""Return True or False if year is a leap year."""
31-
return (year % 4) == 0 and (year % 100) != 0 or (year % 400) == 0
32-
33-
34-
def days_in_mon(month, year):
35-
"""Return numbers of days in month month of year year, 1 <= month <= 12."""
36-
dom = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
37-
38-
month -= 1
39-
if (month == 1) and isleap(year):
40-
return dom[month] + 1
41-
return dom[month]
42-
43-
4429
def parse_time_offset(offset_str):
4530
"""Parse a time offset."""
4631
match = re.split(r"([-+]?\s*\d*\.?\d+(?:[eE][-+]?\d+)?)\s*(\w*)", offset_str)

tests/test_function.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ def func4(trigger_type=None, event_type=None, **kwargs):
315315
#
316316
no_such_function("xyz")
317317
318+
@state_trigger("True or pyscript.f5var1")
319+
def func5(var_name=None, value=None):
320+
global seq_num
321+
322+
seq_num += 1
323+
log.info(f"func5 var = {var_name}, value = {value}")
324+
pyscript.done = [seq_num, var_name, value]
318325
""",
319326
)
320327
seq_num = 0
@@ -457,3 +464,14 @@ def func4(trigger_type=None, event_type=None, **kwargs):
457464
]
458465

459466
assert "name 'no_such_function' is not defined" in caplog.text
467+
468+
#
469+
# test deleting a state variable; func5() triggers on any change to pyscript.f5var1
470+
#
471+
seq_num += 1
472+
hass.states.async_set("pyscript.f5var1", 0)
473+
assert literal_eval(await wait_until_done(notify_q)) == [seq_num, "pyscript.f5var1", "0"]
474+
475+
seq_num += 1
476+
hass.states.async_remove("pyscript.f5var1")
477+
assert literal_eval(await wait_until_done(notify_q)) == [seq_num, "pyscript.f5var1", None]

tests/test_init.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,54 @@ def func7():
143143
)
144144

145145

146+
async def test_syntax_error(hass, caplog):
147+
"""Test syntax error in pyscript file."""
148+
149+
await setup_script(
150+
hass,
151+
None,
152+
dt(2020, 7, 1, 11, 59, 59, 999999),
153+
"""
154+
@service
155+
def func1()
156+
pass
157+
""",
158+
)
159+
assert "SyntaxError: invalid syntax (hello.py, line 3)" in caplog.text
160+
161+
162+
async def test_syntax_error2(hass, caplog):
163+
"""Test syntax error in pyscript file."""
164+
165+
await setup_script(
166+
hass,
167+
None,
168+
dt(2020, 7, 1, 11, 59, 59, 999999),
169+
"""
170+
xyz def 123
171+
""",
172+
)
173+
assert "SyntaxError: invalid syntax (hello.py, line 2)" in caplog.text
174+
175+
176+
async def test_runtime_error(hass, caplog):
177+
"""Test run-time error in pyscript file."""
178+
179+
await setup_script(
180+
hass,
181+
None,
182+
dt(2020, 7, 1, 11, 59, 59, 999999),
183+
"""
184+
@service
185+
def func1():
186+
pass
187+
188+
xyz
189+
""",
190+
)
191+
assert "NameError: name 'xyz' is not defined" in caplog.text
192+
193+
146194
async def test_service_description(hass):
147195
"""Test service description defined in doc_string."""
148196

tests/test_jupyter.py

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pytest_homeassistant.async_mock import mock_open, patch
1616

1717
from homeassistant import loader
18+
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
1819
from homeassistant.setup import async_setup_component
1920

2021
SECRET_KEY = b"0123456789abcdef"
@@ -232,9 +233,10 @@ async def shell_msg(sock, msg_type, msg_content, execute=False):
232233

233234

234235
async def test_jupyter_kernel_msgs(hass, caplog):
235-
"""Test Jupyter kernel."""
236+
"""Test Jupyter kernel messages."""
236237
sock, _ = await setup_script(hass, [dt(2020, 7, 1, 11, 0, 0, 0)], "")
237238

239+
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
238240
#
239241
# test the heartbeat loopback with some long and short messages
240242
# also send messages to stdin and iopub, which ignore them
@@ -264,11 +266,13 @@ async def test_jupyter_kernel_msgs(hass, caplog):
264266
#
265267
# test completions
266268
#
267-
reply = await shell_msg(sock, "complete_request", {"code": "whi", "cursor_pos": 3})
269+
code = "whi"
270+
reply = await shell_msg(sock, "complete_request", {"code": code, "cursor_pos": len(code)})
268271
assert reply["header"]["msg_type"] == "complete_reply"
269272
assert reply["content"]["matches"] == ["while"]
270273

271-
reply = await shell_msg(sock, "complete_request", {"code": "pyscr", "cursor_pos": 5})
274+
code = "pyscr"
275+
reply = await shell_msg(sock, "complete_request", {"code": code, "cursor_pos": len(code)})
272276
assert reply["header"]["msg_type"] == "complete_reply"
273277
assert reply["content"]["matches"] == [
274278
"pyscript",
@@ -313,36 +317,47 @@ async def test_jupyter_kernel_msgs(hass, caplog):
313317
#
314318
# test code execution
315319
#
316-
reply = await shell_msg(sock, "execute_request", {"code": "1 + 2"}, execute=True)
317-
assert reply["content"]["data"]["text/plain"] == "3"
320+
reply = await shell_msg(sock, "execute_request", {"code": "x = 123; x + 1 + 2"}, execute=True)
321+
assert reply["content"]["data"]["text/plain"] == "126"
322+
323+
reply = await shell_msg(sock, "execute_request", {"code": "import math; x + 5"}, execute=True)
324+
assert reply["content"]["data"]["text/plain"] == "128"
325+
326+
#
327+
# do a reload to make sure our global context is preserved
328+
#
329+
await hass.services.async_call("pyscript", "reload", {}, blocking=True)
330+
331+
reply = await shell_msg(sock, "execute_request", {"code": "x + 10"}, execute=True)
332+
assert reply["content"]["data"]["text/plain"] == "133"
333+
334+
#
335+
# test completion of object attribute now that we've loaded math above
336+
#
337+
code = "import math; math.sq"
338+
reply = await shell_msg(sock, "complete_request", {"code": code, "cursor_pos": len(code)})
339+
assert reply["header"]["msg_type"] == "complete_reply"
340+
assert reply["content"]["matches"] == ["math.sqrt"]
318341

319342
#
320343
# run-time error
321344
#
322-
reply = await shell_msg(sock, "execute_request", {"code": "x"}, execute=True)
323-
assert reply["content"]["evalue"] == "name 'x' is not defined"
345+
reply = await shell_msg(sock, "execute_request", {"code": "xyz"}, execute=True)
346+
assert reply["content"]["evalue"] == "name 'xyz' is not defined"
324347

325348
#
326349
# syntax error
327350
#
328351
reply = await shell_msg(sock, "execute_request", {"code": "1 + "}, execute=True)
329352
assert reply["content"]["evalue"] == "invalid syntax (jupyter_0, line 1)"
330353

331-
#
332-
# test stdout; doesn't work yet since iopub messages can be OoO
333-
# doesn't work yet...
334-
#
335-
reply = await shell_msg(sock, "execute_request", {"code": "print('hello'); 123"}, execute=True)
336-
assert reply["header"]["msg_type"] == "execute_result"
337-
assert reply["content"]["data"]["text/plain"] == "123"
338-
# stdout_msg = await get_iopub_msg(sock, "stream")
339-
# assert reply["content"]["text"] == "hello"
354+
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
340355

341356
await shutdown(sock)
342357

343358

344359
async def test_jupyter_kernel_port_close(hass, caplog):
345-
"""Test Jupyter kernel."""
360+
"""Test Jupyter kernel closing ports."""
346361
sock, port_nums = await setup_script(hass, [dt(2020, 7, 1, 11, 0, 0, 0)], "")
347362

348363
#
@@ -382,11 +397,12 @@ async def test_jupyter_kernel_port_close(hass, caplog):
382397
await sock["stdin_port"].send(msg)
383398
assert await sock["hb_port"].recv() == msg
384399

400+
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
385401
await shutdown(sock)
386402

387403

388404
async def test_jupyter_kernel_redefine_func(hass, caplog):
389-
"""Test Jupyter kernel."""
405+
"""Test Jupyter kernel redefining trigger function."""
390406
sock, _ = await setup_script(hass, [dt(2020, 7, 1, 11, 0, 0, 0)], "")
391407

392408
reply = await shell_msg(
@@ -427,7 +443,7 @@ def func():
427443

428444

429445
async def test_jupyter_kernel_global_ctx_func(hass, caplog):
430-
"""Test Jupyter kernel."""
446+
"""Test Jupyter kernel global_ctx functions."""
431447
sock, _ = await setup_script(hass, [dt(2020, 7, 1, 11, 0, 0, 0)], "")
432448

433449
reply = await shell_msg(sock, "execute_request", {"code": "pyscript.get_global_ctx()"}, execute=True)
@@ -445,3 +461,16 @@ async def test_jupyter_kernel_global_ctx_func(hass, caplog):
445461
assert literal_eval(reply["content"]["data"]["text/plain"]) == 456
446462

447463
await shutdown(sock)
464+
465+
466+
async def test_jupyter_kernel_stdout(hass, caplog):
467+
"""Test Jupyter kernel stdout."""
468+
sock, _ = await setup_script(hass, [dt(2020, 7, 1, 11, 0, 0, 0)], "")
469+
470+
reply = await shell_msg(sock, "execute_request", {"code": "log.info('hello'); 123"}, execute=True)
471+
assert reply["header"]["msg_type"] == "execute_result"
472+
assert reply["content"]["data"]["text/plain"] == "123"
473+
stdout_msg = await get_iopub_msg(sock, "stream")
474+
assert stdout_msg["content"]["text"] == "hello\n"
475+
476+
await shutdown(sock)

0 commit comments

Comments
 (0)