Skip to content

Commit

Permalink
unittests/get_response (#361)
Browse files Browse the repository at this point in the history
  • Loading branch information
JarbasAl authored Oct 2, 2023
1 parent 51c288e commit 9a3a92d
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 8 deletions.
2 changes: 1 addition & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ovos-plugin-manager<0.1.0, >=0.0.24a9
ovos-config~=0.0,>=0.0.11a13
ovos-lingua-franca>=0.4.7
ovos-backend-client>=0.1.0a12
ovos-workshop<0.1.0, >=0.0.13a7
ovos-workshop<0.1.0, >=0.0.13a8

# provides plugins and classic machine learning framework
ovos-classifiers<0.1.0, >=0.0.0a37
2 changes: 1 addition & 1 deletion test/end2end/session/skill-converse_test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def handle_intent_aborted(self):
@intent_file_handler("test_get_response.intent")
def handle_test_get_response(self, message):
ans = self.get_response("get")
self.speak(ans)
self.speak(ans or "ERROR")

@killable_intent(callback=handle_intent_aborted)
@intent_file_handler("test.intent")
Expand Down
249 changes: 249 additions & 0 deletions test/end2end/session/test_get_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
import time
from time import sleep
from unittest import TestCase

from ovos_bus_client.message import Message
from ovos_bus_client.session import SessionManager, Session
from .minicroft import get_minicroft


class TestSessions(TestCase):

def setUp(self):
self.skill_id = "ovos-tskill-abort.openvoiceos"
self.other_skill_id = "skill-ovos-hello-world.openvoiceos"
self.core = get_minicroft([self.skill_id, self.other_skill_id])

def test_no_response(self):
SessionManager.sessions = {}
SessionManager.default_session = SessionManager.sessions["default"] = Session("default")
SessionManager.default_session.lang = "en-us"

messages = []

def new_msg(msg):
nonlocal messages
m = Message.deserialize(msg)
if m.msg_type in ["ovos.skills.settings_changed"]:
return # skip these, only happen in 1st run
messages.append(m)
print(len(messages), msg)

def wait_for_n_messages(n):
nonlocal messages
t = time.time()
while len(messages) < n:
sleep(0.1)
if time.time() - t > 10:
raise RuntimeError("did not get the number of expected messages under 10 seconds")

self.core.bus.on("message", new_msg)

# trigger get_response
utt = Message("recognizer_loop:utterance",
{"utterances": ["test get response"]})
self.core.bus.emit(utt)

# confirm all expected messages are sent
expected_messages = [
"recognizer_loop:utterance", # no session
"skill.converse.ping", # default session injected
"skill.converse.pong", # test skill
"skill.converse.pong", # hello world skill

# skill selected
"intent.service.skills.activated",
f"{self.skill_id}.activate",
f"{self.skill_id}:test_get_response.intent",

# skill executing
"mycroft.skill.handler.start",
"enclosure.active_skill",
"speak", # 'mycroft.mic.listen' if no dialog passed to get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status
# "recognizer_loop:utterance" would be here if user answered
"skill.converse.get_response.disable", # end of get_response
"ovos.session.update_default", # sync get_response status
"enclosure.active_skill", # from speak inside intent
"speak", # speak "ERROR" inside intent
"mycroft.skill.handler.complete", # original intent finished executing

# session updated at end of intent pipeline
"ovos.session.update_default"
]
wait_for_n_messages(len(expected_messages))

self.assertEqual(len(expected_messages), len(messages))

mtypes = [m.msg_type for m in messages]
for m in expected_messages:
self.assertTrue(m in mtypes)

# verify that "session" is injected
# (missing in utterance message) and kept in all messages
for m in messages[1:]:
print(m.msg_type, m.context["session"]["session_id"])
self.assertEqual(m.context["session"]["session_id"], "default")

# verify that "lang" is injected by converse.ping
# (missing in utterance message) and kept in all messages
self.assertEqual(messages[1].msg_type, "skill.converse.ping")
for m in messages[1:]:
self.assertEqual(m.context["lang"], "en-us")

# verify "pong" answer from both skills
self.assertEqual(messages[2].msg_type, "skill.converse.pong")
self.assertEqual(messages[3].msg_type, "skill.converse.pong")
self.assertEqual(messages[2].data["skill_id"], messages[2].context["skill_id"])
self.assertEqual(messages[3].data["skill_id"], messages[3].context["skill_id"])
# assert it reports converse method has been implemented by skill
if messages[2].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[2].data["can_handle"])
self.assertFalse(messages[3].data["can_handle"])
if messages[3].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[3].data["can_handle"])
self.assertFalse(messages[2].data["can_handle"])

# verify skill is activated by intent service (intent pipeline matched)
self.assertEqual(messages[4].msg_type, "intent.service.skills.activated")
self.assertEqual(messages[4].data["skill_id"], self.skill_id)
self.assertEqual(messages[5].msg_type, f"{self.skill_id}.activate")

# verify intent triggers
self.assertEqual(messages[6].msg_type, f"{self.skill_id}:test_get_response.intent")
# verify skill_id is now present in every message.context
for m in messages[6:]:
self.assertEqual(m.context["skill_id"], self.skill_id)

# verify intent execution
self.assertEqual(messages[7].msg_type, "mycroft.skill.handler.start")
self.assertEqual(messages[7].data["name"], "TestAbortSkill.handle_test_get_response")
self.assertEqual(messages[8].msg_type, "enclosure.active_skill")
self.assertEqual(messages[8].data["skill_id"], self.skill_id)
self.assertEqual(messages[9].msg_type, "speak")
self.assertEqual(messages[9].data["lang"], "en-us")
self.assertTrue(messages[9].data["expect_response"]) # listen after dialog
self.assertEqual(messages[9].data["meta"]["skill"], self.skill_id)

# enable get_response for this session
self.assertEqual(messages[10].msg_type, "skill.converse.get_response.enable")
self.assertEqual(messages[11].msg_type, "ovos.session.update_default")

# user response would be here

# disable get_response for this session
self.assertEqual(messages[12].msg_type, "skill.converse.get_response.disable")
self.assertEqual(messages[13].msg_type, "ovos.session.update_default")

# post self.get_response intent code
self.assertEqual(messages[14].msg_type, "enclosure.active_skill")
self.assertEqual(messages[14].data["skill_id"], self.skill_id)
self.assertEqual(messages[15].msg_type, "speak")
self.assertEqual(messages[15].data["lang"], "en-us")
self.assertFalse(messages[15].data["expect_response"])
self.assertEqual(messages[15].data["utterance"], "ERROR")
self.assertEqual(messages[15].data["meta"]["skill"], self.skill_id)

self.assertEqual(messages[16].msg_type, "mycroft.skill.handler.complete")
self.assertEqual(messages[16].data["name"], "TestAbortSkill.handle_test_get_response")

# verify default session is now updated
self.assertEqual(messages[17].msg_type, "ovos.session.update_default")
self.assertEqual(messages[17].data["session_data"]["session_id"], "default")
# test deserialization of payload
sess = Session.deserialize(messages[17].data["session_data"])
self.assertEqual(sess.session_id, "default")

def test_with_response(self):
SessionManager.sessions = {}
SessionManager.default_session = SessionManager.sessions["default"] = Session("default")
SessionManager.default_session.lang = "en-us"

messages = []

def new_msg(msg):
nonlocal messages
m = Message.deserialize(msg)
if m.msg_type in ["ovos.skills.settings_changed"]:
return # skip these, only happen in 1st run
messages.append(m)
print(len(messages), msg)

def wait_for_n_messages(n):
nonlocal messages
t = time.time()
while len(messages) < n:
sleep(0.1)
if time.time() - t > 10:
raise RuntimeError("did not get the number of expected messages under 10 seconds")

self.core.bus.on("message", new_msg)

def answer_get_response(msg):
sleep(0.5)
utt = Message("recognizer_loop:utterance",
{"utterances": ["ok"]},
{"session": SessionManager.default_session.serialize()})
self.core.bus.emit(utt)

self.core.bus.on("skill.converse.get_response.enable", answer_get_response)

# trigger get_response
utt = Message("recognizer_loop:utterance",
{"utterances": ["test get response"]})
self.core.bus.emit(utt)

# confirm all expected messages are sent
expected_messages = [
"recognizer_loop:utterance", # no session
"skill.converse.ping", # default session injected
"skill.converse.pong", # test skill
"skill.converse.pong", # hello world skill

# skill selected
"intent.service.skills.activated",
f"{self.skill_id}.activate",
f"{self.skill_id}:test_get_response.intent",

# intent code before self.get_response
"mycroft.skill.handler.start",
"enclosure.active_skill",
"speak", # 'mycroft.mic.listen' if no dialog passed to get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status

"recognizer_loop:utterance", # answer to get_response from user,
# converse pipeline start
"skill.converse.ping",
"skill.converse.pong",
"skill.converse.pong",
"skill.converse.get_response", # returning user utterance to running intent self.get_response
# skill selected by converse pipeline
"intent.service.skills.activated",
f"{self.skill_id}.activate",
"ovos.session.update_default", # sync skill activated by converse

# intent code post self.get_response
"skill.converse.get_response.disable", # end of get_response
"ovos.session.update_default", # sync get_response status
"enclosure.active_skill", # from speak inside intent
"speak", # speak "ok" inside intent
"mycroft.skill.handler.complete", # original intent finished executing

# session updated at end of intent pipeline
"ovos.session.update_default"
]
wait_for_n_messages(len(expected_messages))

self.assertEqual(len(expected_messages), len(messages))

mtypes = [m.msg_type for m in messages]
for m in expected_messages:
self.assertTrue(m in mtypes)

# verify that "session" is injected
# (missing in utterance message) and kept in all messages
for m in messages[1:]:
print(m.msg_type, m.context["session"]["session_id"])
self.assertEqual(m.context["session"]["session_id"], "default")
2 changes: 1 addition & 1 deletion test/integrationtests/test_workshop.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def test_get_response(self):

self.assertIn(start_msg, self.bus.emitted_msgs)
self.assertIn(speak_msg, self.bus.emitted_msgs)
self.assertIn(activate_msg, self.bus.emitted_msgs)
#self.assertIn(activate_msg, self.bus.emitted_msgs)

# check that get_response loop is aborted
# but intent continues executing
Expand Down
12 changes: 7 additions & 5 deletions test/unittests/skills/test_mycroft_skill_get_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def test_wait_cancel(self):
def is_cancel(utterance):
return utterance == 'cancel'

response = skill._wait_response(is_cancel, validator, on_fail, 1)
response = skill._wait_response(is_cancel, validator, on_fail, 1, Message(""))
self.assertEqual(response, None)
converser.join()

Expand Down Expand Up @@ -149,10 +149,11 @@ def validator(*args, **kwargs):

expected_response = 'ice creamr please'
skill._wait_response.return_value = expected_response
m = Message("")
response = skill.get_response('what do you want',
validator=validator)
validator=validator, message=m)
skill._wait_response.assert_called_with(AnyCallable(), validator,
AnyCallable(), -1)
AnyCallable(), -1, m)

def test_converse_detection(self):
"""Ensure validator is passed on."""
Expand All @@ -164,9 +165,10 @@ def validator(*args, **kwargs):
self.assertTrue(skill.converse_is_implemented)

self.assertFalse(skill.converse_is_implemented)
skill.get_response('what do you want', validator=validator)
m = Message("")
skill.get_response('what do you want', validator=validator, message=m)
skill._wait_response.assert_called_with(AnyCallable(), validator,
AnyCallable(), -1)
AnyCallable(), -1, m)
self.assertFalse(skill.converse_is_implemented)


Expand Down

0 comments on commit 9a3a92d

Please sign in to comment.