Skip to content

Commit

Permalink
unittests/get_response_cascade (#366)
Browse files Browse the repository at this point in the history
Co-authored-by: Swen Gross <[email protected]>
  • Loading branch information
JarbasAl and emphasize authored Oct 5, 2023
1 parent 4e0c8ad commit 6132f04
Show file tree
Hide file tree
Showing 3 changed files with 349 additions and 2 deletions.
20 changes: 18 additions & 2 deletions test/end2end/session/skill-converse_test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from time import sleep

from mycroft.skills import intent_file_handler
from ovos_workshop.decorators import killable_intent
from ovos_workshop.skills.ovos import OVOSSkill
from mycroft.skills import intent_file_handler
from time import sleep


class TestAbortSkill(OVOSSkill):
Expand All @@ -11,9 +12,11 @@ class TestAbortSkill(OVOSSkill):
send "my.own.abort.msg" and confirm intent3 is aborted
say "stop" and confirm all intents are aborted
"""

def initialize(self):
self.stop_called = False
self._converse = False
self.items = []
self.bus.on("test_activate", self.do_activate)
self.bus.on("test_deactivate", self.do_deactivate)

Expand Down Expand Up @@ -46,6 +49,19 @@ def handle_test_get_response3(self, message):
ans = self.get_response(num_retries=3)
self.speak(ans or "ERROR")

@intent_file_handler("test_get_response_cascade.intent")
def handle_test_get_response_cascade(self, message):
quit = False
self.items = []
self.speak("give me items", wait=True)
while not quit:
response = self.get_response(num_retries=0)
if response is None:
quit = True
else:
self.items.append(response)
self.bus.emit(message.forward("skill_items", {"items": self.items}))

@killable_intent(callback=handle_intent_aborted)
@intent_file_handler("test.intent")
def handle_test_abort_intent(self, message):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test get items
330 changes: 330 additions & 0 deletions test/end2end/session/test_get_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,333 @@ def answer_get_response(msg):
# test deserialization of payload
sess = Session.deserialize(messages[26].data["session_data"])
self.assertEqual(sess.session_id, "default")

def test_nested(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)

items = ["A", "B", "C"]

def answer_get_response(msg):
nonlocal items
sleep(0.5)
if not len(items):
utt = Message("recognizer_loop:utterance",
{"utterances": ["cancel"]},
{"session": SessionManager.default_session.serialize()})
else:
utt = Message("recognizer_loop:utterance",
{"utterances": [items[0]]},
{"session": SessionManager.default_session.serialize()})
self.core.bus.emit(utt)
items = items[1:]

self.core.bus.on("mycroft.mic.listen", answer_get_response)

# trigger get_response
utt = Message("recognizer_loop:utterance",
{"utterances": ["test get items"]})
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_cascade.intent",

# intent code before self.get_response
"mycroft.skill.handler.start",
"enclosure.active_skill",
"speak", # "give me items"

# first get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status
"mycroft.mic.listen", # no dialog in self.get_response
"recognizer_loop:utterance", # A
"skill.converse.ping",
"skill.converse.pong",
"skill.converse.pong",
"skill.converse.get_response", # A
"intent.service.skills.activated",
f"{self.skill_id}.activate",
"ovos.session.update_default", # sync skill trigger
"skill.converse.get_response.disable",
"ovos.session.update_default", # sync get_response status

# second get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status
"mycroft.mic.listen", # no dialog in self.get_response
"recognizer_loop:utterance", # B
"skill.converse.ping",
"skill.converse.pong",
"skill.converse.pong",
"skill.converse.get_response", # B
"intent.service.skills.activated",
f"{self.skill_id}.activate",
"ovos.session.update_default", # sync skill trigger
"skill.converse.get_response.disable",
"ovos.session.update_default", # sync get_response status

# 3rd get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status
"mycroft.mic.listen", # no dialog in self.get_response
"recognizer_loop:utterance", # C
"skill.converse.ping",
"skill.converse.pong",
"skill.converse.pong",
"skill.converse.get_response", # C
"intent.service.skills.activated",
f"{self.skill_id}.activate",
"ovos.session.update_default", # sync skill trigger
"skill.converse.get_response.disable",
"ovos.session.update_default", # sync get_response status

# cancel get_response
"skill.converse.get_response.enable", # start of get_response
"ovos.session.update_default", # sync get_response status
"mycroft.mic.listen", # no dialog in self.get_response
"recognizer_loop:utterance", # cancel
"skill.converse.ping",
"skill.converse.pong",
"skill.converse.pong",
"skill.converse.get_response", # cancel
"intent.service.skills.activated",
f"{self.skill_id}.activate",
"ovos.session.update_default", # sync skill trigger
"skill.converse.get_response.disable",
"ovos.session.update_default", # sync get_response status

"skill_items", # skill emitted message [A, B, C]

"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")

# converse intent pipeline
self.assertEqual(messages[1].msg_type, "skill.converse.ping")
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_cascade.intent")

# verify intent execution
self.assertEqual(messages[7].msg_type, "mycroft.skill.handler.start")
self.assertEqual(messages[7].data["name"], "TestAbortSkill.handle_test_get_response_cascade")

# post self.get_response intent code
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.assertFalse(messages[9].data["expect_response"])
self.assertEqual(messages[9].data["utterance"], "give me items")
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")

# 3 sound prompts (no dialog in this test)
self.assertEqual(messages[12].msg_type, "mycroft.mic.listen")

# check utterance goes through converse cycle
self.assertEqual(messages[13].msg_type, "recognizer_loop:utterance")
self.assertEqual(messages[14].msg_type, "skill.converse.ping")

# 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[15].data["can_handle"])
self.assertFalse(messages[16].data["can_handle"])
if messages[3].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[16].data["can_handle"])
self.assertFalse(messages[15].data["can_handle"])

# captured utterance sent to get_response handler that is waiting
self.assertEqual(messages[17].msg_type, "skill.converse.get_response")
self.assertEqual(messages[17].data["skill_id"], self.skill_id)
self.assertEqual(messages[17].data["utterances"], ["A"])

# converse pipeline activates the skill last_used timestamp
self.assertEqual(messages[18].msg_type, "intent.service.skills.activated")
self.assertEqual(messages[19].msg_type, f"{self.skill_id}.activate")
self.assertEqual(messages[20].msg_type, "ovos.session.update_default")

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

## response 2

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

# 3 sound prompts (no dialog in this test)
self.assertEqual(messages[25].msg_type, "mycroft.mic.listen")

# check utterance goes through converse cycle
self.assertEqual(messages[26].msg_type, "recognizer_loop:utterance")
self.assertEqual(messages[27].msg_type, "skill.converse.ping")

# 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[28].data["can_handle"])
self.assertFalse(messages[29].data["can_handle"])
if messages[3].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[29].data["can_handle"])
self.assertFalse(messages[28].data["can_handle"])

# captured utterance sent to get_response handler that is waiting
self.assertEqual(messages[30].msg_type, "skill.converse.get_response")
self.assertEqual(messages[30].data["skill_id"], self.skill_id)
self.assertEqual(messages[30].data["utterances"], ["B"])

# converse pipeline activates the skill last_used timestamp
self.assertEqual(messages[31].msg_type, "intent.service.skills.activated")
self.assertEqual(messages[32].msg_type, f"{self.skill_id}.activate")
self.assertEqual(messages[33].msg_type, "ovos.session.update_default")

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

## response 3

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

# 3 sound prompts (no dialog in this test)
self.assertEqual(messages[38].msg_type, "mycroft.mic.listen")

# check utterance goes through converse cycle
self.assertEqual(messages[39].msg_type, "recognizer_loop:utterance")
self.assertEqual(messages[40].msg_type, "skill.converse.ping")

# 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[41].data["can_handle"])
self.assertFalse(messages[42].data["can_handle"])
if messages[3].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[42].data["can_handle"])
self.assertFalse(messages[41].data["can_handle"])

# captured utterance sent to get_response handler that is waiting
self.assertEqual(messages[43].msg_type, "skill.converse.get_response")
self.assertEqual(messages[43].data["skill_id"], self.skill_id)
self.assertEqual(messages[43].data["utterances"], ["C"])

# converse pipeline activates the skill last_used timestamp
self.assertEqual(messages[44].msg_type, "intent.service.skills.activated")
self.assertEqual(messages[45].msg_type, f"{self.skill_id}.activate")
self.assertEqual(messages[46].msg_type, "ovos.session.update_default")

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

## response 3

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

# 3 sound prompts (no dialog in this test)
self.assertEqual(messages[51].msg_type, "mycroft.mic.listen")

# check utterance goes through converse cycle
self.assertEqual(messages[52].msg_type, "recognizer_loop:utterance")
self.assertEqual(messages[53].msg_type, "skill.converse.ping")

# 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[54].data["can_handle"])
self.assertFalse(messages[55].data["can_handle"])
if messages[3].data["skill_id"] == self.skill_id: # we dont know order of pong responses
self.assertTrue(messages[55].data["can_handle"])
self.assertFalse(messages[54].data["can_handle"])

# captured utterance sent to get_response handler that is waiting
self.assertEqual(messages[56].msg_type, "skill.converse.get_response")
self.assertEqual(messages[56].data["skill_id"], self.skill_id)
self.assertEqual(messages[56].data["utterances"], ["cancel"])

# converse pipeline activates the skill last_used timestamp
self.assertEqual(messages[57].msg_type, "intent.service.skills.activated")
self.assertEqual(messages[58].msg_type, f"{self.skill_id}.activate")
self.assertEqual(messages[59].msg_type, "ovos.session.update_default")

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

# intent return
self.assertEqual(messages[62].msg_type, "skill_items")
self.assertEqual(messages[62].data, {"items": ["A", "B", "C"]})

# report handler complete
self.assertEqual(messages[63].msg_type, "mycroft.skill.handler.complete")
self.assertEqual(messages[63].data["name"], "TestAbortSkill.handle_test_get_response_cascade")

self.assertEqual(messages[64].msg_type, "ovos.session.update_default")

0 comments on commit 6132f04

Please sign in to comment.