diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 6960e2f9..9d5c634b 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -59,14 +59,9 @@ jobs: run: | sudo apt install libssl-dev libfann-dev portaudio19-dev libpulse-dev pip install -r requirements/test.txt - pip install ./test/end2end/session/skill-ovos-hello-world - pip install ./test/end2end/session/skill-ovos-fallback-unknown - pip install ./test/end2end/session/skill-ovos-fallback-unknownv1 - pip install ./test/end2end/session/skill-converse_test - name: Run unittests run: | pytest --cov=ovos_workshop --cov-report xml test/unittests - pytest --cov-append --cov=ovos_workshop --cov-report xml test/end2end # NOTE: additional pytest invocations should also add the --cov-append flag # or they will overwrite previous invocations' coverage reports # (for an example, see OVOS Skill Manager's workflow) diff --git a/ovos_workshop/skills/ovos.py b/ovos_workshop/skills/ovos.py index c75253f3..3787d325 100644 --- a/ovos_workshop/skills/ovos.py +++ b/ovos_workshop/skills/ovos.py @@ -915,9 +915,15 @@ def _register_system_event_handlers(self): if self.stop_is_implemented: self.add_event('mycroft.stop', self.__handle_stop, speak_errors=False) - self.add_event('skill.converse.ping', self._handle_converse_ack, + # TODO: deprectate 0.0.9 + self.add_event("skill.converse.ping", self._handle_converse_ack, speak_errors=False) - self.add_event('skill.converse.request', self._handle_converse_request, + self.add_event(f"{self.skill_id}.converse.ping", self._handle_converse_ack, + speak_errors=False) + # TODO: deprecate 0.0.9 + self.add_event("skill.converse.request", self._handle_converse_request, + speak_errors=False) + self.add_event(f"{self.skill_id}.converse.request", self._handle_converse_request, speak_errors=False) self.add_event(f"{self.skill_id}.activate", self.handle_activate, speak_errors=False) @@ -938,7 +944,9 @@ def _register_system_event_handlers(self): self.add_event('mycroft.skills.settings.changed', self.handle_settings_change, speak_errors=False) + # TODO: deprecate 0.0.9 self.add_event("skill.converse.get_response", self.__handle_get_response, speak_errors=False) + self.add_event(f"{self.skill_id}.converse.get_response", self.__handle_get_response, speak_errors=False) def _send_public_api(self, message: Message): """ @@ -1017,8 +1025,13 @@ def _handle_converse_ack(self, message: Message): may override the property self.converse_is_implemented to enable or disable converse support. Note that this does not affect a skill's `active` status. - @param message: `skill.converse.ping` Message + @param message: `{self.skill_id}.converse.ping` Message """ + if message.msg_type == "skill.converse.ping": + log_deprecation("Support for message type `skill.converse.ping` is deprecated, use `{skill_id}.converse.ping`", "0.0.9") + if message.data.get("skill_id") != self.skill_id: + return # not for us! + self.bus.emit(message.reply( "skill.converse.pong", data={"skill_id": self.skill_id, @@ -1029,26 +1042,29 @@ def _handle_converse_request(self, message: Message): """ If this skill is requested and supports converse, handle the user input with `converse`. - @param message: `skill.converse.request` Message + @param message: `{self.skill_id}.converse.request` Message """ - skill_id = message.data['skill_id'] - if skill_id == self.skill_id: - try: - # converse can have multiple signatures - params = signature(self.converse).parameters - kwargs = {"message": message, - "utterances": message.data['utterances'], - "lang": message.data['lang']} - kwargs = {k: v for k, v in kwargs.items() if k in params} - result = self.converse(**kwargs) - self.bus.emit(message.reply('skill.converse.response', - {"skill_id": self.skill_id, - "result": result})) - except Exception as e: - LOG.error(e) - self.bus.emit(message.reply('skill.converse.response', - {"skill_id": self.skill_id, - "result": False})) + if message.msg_type == "skill.converse.request": + log_deprecation("Support for message type `skill.converse.request` is deprecated, use `{skill_id}.converse.request`", "0.0.9") + if message.data.get("skill_id") != self.skill_id: + return # not for us! + + try: + # converse can have multiple signatures + params = signature(self.converse).parameters + kwargs = {"message": message, + "utterances": message.data['utterances'], + "lang": message.data['lang']} + kwargs = {k: v for k, v in kwargs.items() if k in params} + result = self.converse(**kwargs) + self.bus.emit(message.reply('skill.converse.response', + {"skill_id": self.skill_id, + "result": result})) + except Exception as e: + LOG.error(e) + self.bus.emit(message.reply('skill.converse.response', + {"skill_id": self.skill_id, + "result": False})) def _handle_collect_resting(self, message: Optional[Message] = None): """ @@ -1545,10 +1561,14 @@ def converse(utterances, lang=None): return ans def __handle_get_response(self, message): - - skill_id = message.data["skill_id"] - if skill_id != self.skill_id: - return # not for us! + """ + Handle the response message to a previous get_response / speak call + sent from the intent service + """ + if message.msg_type == "skill.converse.get_response": + log_deprecation("Support for message type `skill.converse.get_response` is deprecated, use `{skill_id}.converse.get_response`", "0.0.9") + if message.data.get("skill_id") != self.skill_id: + return # not for us! # validate session_id to ensure this isnt another # user querying the skill at same time diff --git a/test/end2end/__init__.py b/test/end2end/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/end2end/session/__init__.py b/test/end2end/session/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/end2end/session/minicroft.py b/test/end2end/session/minicroft.py deleted file mode 100644 index d178b332..00000000 --- a/test/end2end/session/minicroft.py +++ /dev/null @@ -1,69 +0,0 @@ -from time import sleep -from ovos_bus_client.session import SessionManager, Session -from ovos_core.intent_services import IntentService -from ovos_core.skill_manager import SkillManager -from ovos_plugin_manager.skills import find_skill_plugins -from ovos_utils.log import LOG -from ovos_utils.messagebus import FakeBus -from ovos_utils.process_utils import ProcessState -from ovos_workshop.skills.fallback import FallbackSkill - - -class MiniCroft(SkillManager): - def __init__(self, skill_ids, *args, **kwargs): - bus = FakeBus() - super().__init__(bus, *args, **kwargs) - self.skill_ids = skill_ids - self.intent_service = self._register_intent_services() - - def _register_intent_services(self): - """Start up the all intent services and connect them as needed. - - Args: - bus: messagebus client to register the services on - """ - service = IntentService(self.bus) - # Register handler to trigger fallback system - self.bus.on( - 'mycroft.skills.fallback', - FallbackSkill.make_intent_failure_handler(self.bus) - ) - return service - - def load_plugin_skills(self): - LOG.info("loading skill plugins") - plugins = find_skill_plugins() - for skill_id, plug in plugins.items(): - LOG.debug(skill_id) - if skill_id not in self.skill_ids: - continue - if skill_id not in self.plugin_skills: - self._load_plugin_skill(skill_id, plug) - - def run(self): - """Load skills and update periodically from disk and internet.""" - self.status.set_alive() - - self.load_plugin_skills() - - self.status.set_ready() - - LOG.info("Skills all loaded!") - - def stop(self): - super().stop() - SessionManager.bus = None - SessionManager.sessions = {} - SessionManager.default_session = SessionManager.sessions["default"] = Session("default") - - -def get_minicroft(skill_id): - if isinstance(skill_id, str): - skill_id = [skill_id] - assert isinstance(skill_id, list) - croft1 = MiniCroft(skill_id) - croft1.start() - while croft1.status.state != ProcessState.READY: - sleep(0.2) - return croft1 - diff --git a/test/end2end/session/skill-converse_test/__init__.py b/test/end2end/session/skill-converse_test/__init__.py deleted file mode 100644 index 959ca9da..00000000 --- a/test/end2end/session/skill-converse_test/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -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): - """ - send "mycroft.skills.abort_question" and confirm only get_response is aborted - send "mycroft.skills.abort_execution" and confirm the full intent is aborted, except intent3 - 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.bus.on("test_activate", self.do_activate) - self.bus.on("test_deactivate", self.do_deactivate) - - def do_activate(self, message): - self.activate() - - def do_deactivate(self, message): - self.deactivate() - - @intent_file_handler("converse_on.intent") - def handle_converse_on(self, message): - self._converse = True - self.speak("on") - - @intent_file_handler("converse_off.intent") - def handle_converse_off(self, message): - self._converse = False - self.speak("off") - - def handle_intent_aborted(self): - self.speak("I am dead") - - @intent_file_handler("test_get_response.intent") - def handle_test_get_response(self, message): - ans = self.get_response("get", num_retries=1) - self.speak(ans or "ERROR") - - @intent_file_handler("test_get_response3.intent") - def handle_test_get_response3(self, message): - ans = self.get_response(num_retries=3) - self.speak(ans or "ERROR") - - @killable_intent(callback=handle_intent_aborted) - @intent_file_handler("test.intent") - def handle_test_abort_intent(self, message): - self.stop_called = False - self.my_special_var = "changed" - while True: - sleep(1) - self.speak("still here") - - @intent_file_handler("test2.intent") - @killable_intent(callback=handle_intent_aborted) - def handle_test_get_response_intent(self, message): - self.stop_called = False - self.my_special_var = "CHANGED" - ans = self.get_response("question", num_retries=99999) - self.log.debug("get_response returned: " + str(ans)) - if ans is None: - self.speak("question aborted") - - @killable_intent(msg="my.own.abort.msg", callback=handle_intent_aborted) - @intent_file_handler("test3.intent") - def handle_test_msg_intent(self, message): - self.stop_called = False - if self.my_special_var != "default": - self.speak("someone forgot to cleanup") - while True: - sleep(1) - self.speak("you can't abort me") - - def stop(self): - self.stop_called = True - - def converse(self, message): - return self._converse diff --git a/test/end2end/session/skill-converse_test/locale/en-us/converse_off.intent b/test/end2end/session/skill-converse_test/locale/en-us/converse_off.intent deleted file mode 100644 index 54299a48..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/converse_off.intent +++ /dev/null @@ -1 +0,0 @@ -no \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/converse_on.intent b/test/end2end/session/skill-converse_test/locale/en-us/converse_on.intent deleted file mode 100644 index 396a0ba2..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/converse_on.intent +++ /dev/null @@ -1 +0,0 @@ -yes \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/get.dialog b/test/end2end/session/skill-converse_test/locale/en-us/get.dialog deleted file mode 100644 index 1dd99dc4..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/get.dialog +++ /dev/null @@ -1 +0,0 @@ -give me an answer \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/question.dialog b/test/end2end/session/skill-converse_test/locale/en-us/question.dialog deleted file mode 100644 index f0fb83cc..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/question.dialog +++ /dev/null @@ -1 +0,0 @@ -this is a question \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/test.intent b/test/end2end/session/skill-converse_test/locale/en-us/test.intent deleted file mode 100644 index 30d74d25..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/test.intent +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/test2.intent b/test/end2end/session/skill-converse_test/locale/en-us/test2.intent deleted file mode 100644 index 5161aff4..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/test2.intent +++ /dev/null @@ -1 +0,0 @@ -test again \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/test3.intent b/test/end2end/session/skill-converse_test/locale/en-us/test3.intent deleted file mode 100644 index 1fec3fd2..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/test3.intent +++ /dev/null @@ -1 +0,0 @@ -one more test \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/test_get_response.intent b/test/end2end/session/skill-converse_test/locale/en-us/test_get_response.intent deleted file mode 100644 index 88cde324..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/test_get_response.intent +++ /dev/null @@ -1 +0,0 @@ -test get response \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/locale/en-us/test_get_response3.intent b/test/end2end/session/skill-converse_test/locale/en-us/test_get_response3.intent deleted file mode 100644 index 484272aa..00000000 --- a/test/end2end/session/skill-converse_test/locale/en-us/test_get_response3.intent +++ /dev/null @@ -1,2 +0,0 @@ -3 prompts -three prompts \ No newline at end of file diff --git a/test/end2end/session/skill-converse_test/setup.py b/test/end2end/session/skill-converse_test/setup.py deleted file mode 100644 index 211ae750..00000000 --- a/test/end2end/session/skill-converse_test/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from setuptools import setup -from os import getenv, path, walk - - -def find_resource_files(): - resource_base_dirs = ("locale", "ui", "vocab", "dialog", "regex") - base_dir = path.dirname(__file__) - package_data = ["skill.json"] - for res in resource_base_dirs: - if path.isdir(path.join(base_dir, res)): - for (directory, _, files) in walk(path.join(base_dir, res)): - if files: - package_data.append( - path.join(directory.replace(base_dir, "").lstrip('/'), - '*')) - return package_data - - -# skill_id=package_name:SkillClass -PLUGIN_ENTRY_POINT = 'ovos-tskill-abort.openvoiceos=ovos_tskill_abort:TestAbortSkill' - -setup( - # this is the package name that goes on pip - name='ovos-tskill-abort', - version='0.0.1', - description='this is a OVOS test skill for the killable_intents decorator', - url='https://github.com/OpenVoiceOS/skill-abort-test', - author='JarbasAi', - author_email='jarbasai@mailfence.com', - license='Apache-2.0', - package_dir={"ovos_tskill_abort": ""}, - package_data={'ovos_tskill_abort': find_resource_files()}, - packages=['ovos_tskill_abort'], - include_package_data=True, - install_requires=["ovos-workshop"], - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} -) diff --git a/test/end2end/session/skill-ovos-fallback-unknown/__init__.py b/test/end2end/session/skill-ovos-fallback-unknown/__init__.py deleted file mode 100644 index ad46bfff..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknown/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from ovos_workshop.skills.fallback import FallbackSkill -from ovos_workshop.decorators import fallback_handler - - -class UnknownSkill(FallbackSkill): - - @fallback_handler(priority=100) - def handle_fallback(self, message): - self.speak_dialog('unknown') - return True diff --git a/test/end2end/session/skill-ovos-fallback-unknown/locale/en-us/unknown.dialog b/test/end2end/session/skill-ovos-fallback-unknown/locale/en-us/unknown.dialog deleted file mode 100755 index 3f31d7bb..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknown/locale/en-us/unknown.dialog +++ /dev/null @@ -1,9 +0,0 @@ -I'm sorry, I don't understand. -I don't know what that means. -I don't understand, but I'm learning new things everyday. -Sorry, I didn't catch that. -Sorry, I don't understand. -I don't understand. -I'm not sure I understood you. -You might have to say that a different way. -Please rephrase your request. diff --git a/test/end2end/session/skill-ovos-fallback-unknown/setup.py b/test/end2end/session/skill-ovos-fallback-unknown/setup.py deleted file mode 100755 index 3ff59d18..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknown/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from setuptools import setup -from os import walk, path - -URL = "https://github.com/OpenVoiceOS/skill-ovos-fallback-unknown" -SKILL_CLAZZ = "UnknownSkill" # needs to match __init__.py class name -PYPI_NAME = "ovos-skill-fallback-unknown" # pip install PYPI_NAME - -# below derived from github url to ensure standard skill_id -SKILL_AUTHOR, SKILL_NAME = URL.split(".com/")[-1].split("/") -SKILL_PKG = SKILL_NAME.lower().replace('-', '_') -PLUGIN_ENTRY_POINT = f'{SKILL_NAME.lower()}.{SKILL_AUTHOR.lower()}={SKILL_PKG}:{SKILL_CLAZZ}' -# skill_id=package_name:SkillClass - - -def find_resource_files(): - resource_base_dirs = ("locale", "ui", "vocab", "dialog", "regex", "skill") - base_dir = path.dirname(__file__) - package_data = ["*.json"] - for res in resource_base_dirs: - if path.isdir(path.join(base_dir, res)): - for (directory, _, files) in walk(path.join(base_dir, res)): - if files: - package_data.append( - path.join(directory.replace(base_dir, "").lstrip('/'), - '*')) - return package_data - - -setup( - name=PYPI_NAME, - version="0.0.0", - package_dir={SKILL_PKG: ""}, - package_data={SKILL_PKG: find_resource_files()}, - packages=[SKILL_PKG], - include_package_data=True, - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} -) diff --git a/test/end2end/session/skill-ovos-fallback-unknownv1/__init__.py b/test/end2end/session/skill-ovos-fallback-unknownv1/__init__.py deleted file mode 100644 index 0b9382fe..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknownv1/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from ovos_workshop.skills.fallback import FallbackSkillV1 -from ovos_workshop.decorators import fallback_handler - - -# explicitly use class with compat for older cores -# this is usually auto detected, just done here for unittests -class UnknownSkill(FallbackSkillV1): - - @fallback_handler(priority=100) - def handle_fallback(self, message): - self.speak_dialog('unknown') - return True diff --git a/test/end2end/session/skill-ovos-fallback-unknownv1/locale/en-us/unknown.dialog b/test/end2end/session/skill-ovos-fallback-unknownv1/locale/en-us/unknown.dialog deleted file mode 100755 index 3f31d7bb..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknownv1/locale/en-us/unknown.dialog +++ /dev/null @@ -1,9 +0,0 @@ -I'm sorry, I don't understand. -I don't know what that means. -I don't understand, but I'm learning new things everyday. -Sorry, I didn't catch that. -Sorry, I don't understand. -I don't understand. -I'm not sure I understood you. -You might have to say that a different way. -Please rephrase your request. diff --git a/test/end2end/session/skill-ovos-fallback-unknownv1/setup.py b/test/end2end/session/skill-ovos-fallback-unknownv1/setup.py deleted file mode 100755 index a6505f19..00000000 --- a/test/end2end/session/skill-ovos-fallback-unknownv1/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from setuptools import setup -from os import walk, path - -URL = "https://github.com/OpenVoiceOS/skill-ovos-fallback-unknownv1" -SKILL_CLAZZ = "UnknownSkill" # needs to match __init__.py class name -PYPI_NAME = "ovos-skill-fallback-unknown-v1" # pip install PYPI_NAME - -# below derived from github url to ensure standard skill_id -SKILL_AUTHOR, SKILL_NAME = URL.split(".com/")[-1].split("/") -SKILL_PKG = SKILL_NAME.lower().replace('-', '_') -PLUGIN_ENTRY_POINT = f'{SKILL_NAME.lower()}.{SKILL_AUTHOR.lower()}={SKILL_PKG}:{SKILL_CLAZZ}' -# skill_id=package_name:SkillClass - - -def find_resource_files(): - resource_base_dirs = ("locale", "ui", "vocab", "dialog", "regex", "skill") - base_dir = path.dirname(__file__) - package_data = ["*.json"] - for res in resource_base_dirs: - if path.isdir(path.join(base_dir, res)): - for (directory, _, files) in walk(path.join(base_dir, res)): - if files: - package_data.append( - path.join(directory.replace(base_dir, "").lstrip('/'), - '*')) - return package_data - - -setup( - name=PYPI_NAME, - version="0.0.0", - package_dir={SKILL_PKG: ""}, - package_data={SKILL_PKG: find_resource_files()}, - packages=[SKILL_PKG], - include_package_data=True, - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} -) diff --git a/test/end2end/session/skill-ovos-hello-world/MANIFEST.in b/test/end2end/session/skill-ovos-hello-world/MANIFEST.in deleted file mode 100644 index b9ecb580..00000000 --- a/test/end2end/session/skill-ovos-hello-world/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -recursive-include dialog * -recursive-include vocab * -recursive-include locale * -recursive-include res * -recursive-include ui * -include *.json -include *.txt \ No newline at end of file diff --git a/test/end2end/session/skill-ovos-hello-world/__init__.py b/test/end2end/session/skill-ovos-hello-world/__init__.py deleted file mode 100644 index df0104ba..00000000 --- a/test/end2end/session/skill-ovos-hello-world/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from ovos_utils.intents import IntentBuilder -from ovos_workshop.decorators import intent_handler -from ovos_workshop.skills import OVOSSkill - - -class HelloWorldSkill(OVOSSkill): - - @intent_handler(IntentBuilder("HelloWorldIntent").require("HelloWorldKeyword")) - def handle_hello_world_intent(self, message): - self.speak_dialog("hello.world") diff --git a/test/end2end/session/skill-ovos-hello-world/locale/en-us/dialog/hello.world.dialog b/test/end2end/session/skill-ovos-hello-world/locale/en-us/dialog/hello.world.dialog deleted file mode 100644 index 811f098f..00000000 --- a/test/end2end/session/skill-ovos-hello-world/locale/en-us/dialog/hello.world.dialog +++ /dev/null @@ -1,3 +0,0 @@ -Hello world -Hello -Hi to you too diff --git a/test/end2end/session/skill-ovos-hello-world/locale/en-us/vocab/HelloWorldKeyword.voc b/test/end2end/session/skill-ovos-hello-world/locale/en-us/vocab/HelloWorldKeyword.voc deleted file mode 100644 index 5ffa264b..00000000 --- a/test/end2end/session/skill-ovos-hello-world/locale/en-us/vocab/HelloWorldKeyword.voc +++ /dev/null @@ -1,2 +0,0 @@ -hello world -greetings diff --git a/test/end2end/session/skill-ovos-hello-world/setup.py b/test/end2end/session/skill-ovos-hello-world/setup.py deleted file mode 100755 index 6bc5a62e..00000000 --- a/test/end2end/session/skill-ovos-hello-world/setup.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python3 -from setuptools import setup -from os import walk, path - -URL = "https://github.com/OpenVoiceOS/skill-ovos-hello-world" -SKILL_CLAZZ = "HelloWorldSkill" # needs to match __init__.py class name - -# below derived from github url to ensure standard skill_id -SKILL_AUTHOR, SKILL_NAME = URL.split(".com/")[-1].split("/") -SKILL_PKG = SKILL_NAME.lower().replace('-', '_') -PLUGIN_ENTRY_POINT = f'{SKILL_NAME.lower()}.{SKILL_AUTHOR.lower()}={SKILL_PKG}:{SKILL_CLAZZ}' -# skill_id=package_name:SkillClass - - -def find_resource_files(): - resource_base_dirs = ("locale", "ui", "vocab", "dialog", "regex", "skill") - base_dir = path.dirname(__file__) - package_data = ["*.json"] - for res in resource_base_dirs: - if path.isdir(path.join(base_dir, res)): - for (directory, _, files) in walk(path.join(base_dir, res)): - if files: - package_data.append( - path.join(directory.replace(base_dir, "").lstrip('/'), - '*')) - return package_data - - -setup( - name="ovos-skill-hello-world", - version="0.0.0", - long_description="test", - description='OVOS hello world skill plugin', - author_email='jarbasai@mailfence.com', - license='Apache-2.0', - package_dir={SKILL_PKG: ""}, - package_data={SKILL_PKG: find_resource_files()}, - packages=[SKILL_PKG], - include_package_data=True, - keywords='ovos skill plugin', - entry_points={'ovos.plugin.skill': PLUGIN_ENTRY_POINT} -) diff --git a/test/end2end/session/test_complete_failure.py b/test/end2end/session/test_complete_failure.py deleted file mode 100644 index 313c6cbf..00000000 --- a/test/end2end/session/test_complete_failure.py +++ /dev/null @@ -1,260 +0,0 @@ -import time -from time import sleep -from unittest import TestCase, skip - -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 = "skill-ovos-hello-world.openvoiceos" - self.core = get_minicroft(self.skill_id) - - def test_complete_failure(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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}, - {"session": SessionManager.default_session.serialize()}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - # Converse - "skill.converse.ping", - "skill.converse.pong", - # FallbackV1 - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - # complete intent failure - "mycroft.audio.play_sound", - "complete_intent_failure", - "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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], "default") - - # verify ping/pong answer from hello world skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # high prio fallback - self.assertEqual(messages[3].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[3].data["fallback_range"], [0, 5]) - self.assertEqual(messages[4].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[4].data["handler"], "fallback") - self.assertEqual(messages[5].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[5].data["handler"], "fallback") - self.assertEqual(messages[6].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[6].data["handled"]) - - # medium prio fallback - self.assertEqual(messages[7].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[7].data["fallback_range"], [5, 90]) - self.assertEqual(messages[8].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[8].data["handler"], "fallback") - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[9].data["handler"], "fallback") - self.assertEqual(messages[10].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[10].data["handled"]) - - # low prio fallback - self.assertEqual(messages[11].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[11].data["fallback_range"], [90, 101]) - self.assertEqual(messages[12].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[12].data["handler"], "fallback") - self.assertEqual(messages[13].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[13].data["handler"], "fallback") - self.assertEqual(messages[14].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[14].data["handled"]) - - # complete intent failure - self.assertEqual(messages[15].msg_type, "mycroft.audio.play_sound") - self.assertEqual(messages[15].data["uri"], "snd/error.mp3") - self.assertEqual(messages[16].msg_type, "complete_intent_failure") - - # 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") - - @skip("TODO works if run standalone, otherwise has side effects in other tests") - def test_complete_failure_lang_detect(self): - SessionManager.sessions = {} - SessionManager.default_session = SessionManager.sessions["default"] = Session("default") - SessionManager.default_session.lang = "en-us" - - stt_lang_detect = "pt-pt" - - 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) - - SessionManager.default_session.valid_languages = ["en-us", stt_lang_detect, "fr-fr"] - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}, - {"session": SessionManager.default_session.serialize(), - "stt_lang": stt_lang_detect, # lang detect plugin - "detected_lang": "not-valid" # text lang detect - }) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - "ovos.session.update_default", # language changed - "skill.converse.ping", - "skill.converse.pong", - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - "mycroft.audio.play_sound", - "complete_intent_failure", - "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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["stt_lang"], stt_lang_detect) - self.assertEqual(m.context["detected_lang"], "not-valid") - - # verify session lang updated with pt-pt from lang disambiguation step - self.assertEqual(messages[1].msg_type, "ovos.session.update_default") - self.assertEqual(messages[1].data["session_data"]["session_id"], "default") - self.assertEqual(messages[1].data["session_data"]["lang"], stt_lang_detect) - - # verify ping/pong answer from hello world skill - self.assertEqual(messages[2].msg_type, "skill.converse.ping") - self.assertEqual(messages[3].msg_type, "skill.converse.pong") - self.assertEqual(messages[3].data["skill_id"], self.skill_id) - self.assertEqual(messages[3].context["skill_id"], self.skill_id) - self.assertFalse(messages[3].data["can_handle"]) - - # verify fallback is triggered with pt-pt from lang disambiguation step - self.assertEqual(messages[4].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[4].data["lang"], stt_lang_detect) - - # high prio fallback - self.assertEqual(messages[4].data["fallback_range"], [0, 5]) - self.assertEqual(messages[5].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[5].data["handler"], "fallback") - self.assertEqual(messages[6].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[6].data["handler"], "fallback") - self.assertEqual(messages[7].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[7].data["handled"]) - - # medium prio fallback - self.assertEqual(messages[8].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[8].data["lang"], stt_lang_detect) - self.assertEqual(messages[8].data["fallback_range"], [5, 90]) - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[9].data["handler"], "fallback") - self.assertEqual(messages[10].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[10].data["handler"], "fallback") - self.assertEqual(messages[11].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[11].data["handled"]) - - # low prio fallback - self.assertEqual(messages[12].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[12].data["lang"], stt_lang_detect) - self.assertEqual(messages[12].data["fallback_range"], [90, 101]) - self.assertEqual(messages[13].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[13].data["handler"], "fallback") - self.assertEqual(messages[14].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[14].data["handler"], "fallback") - self.assertEqual(messages[15].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[15].data["handled"]) - - # complete intent failure - self.assertEqual(messages[16].msg_type, "mycroft.audio.play_sound") - self.assertEqual(messages[16].data["uri"], "snd/error.mp3") - self.assertEqual(messages[17].msg_type, "complete_intent_failure") - - # verify default session is now updated - self.assertEqual(messages[18].msg_type, "ovos.session.update_default") - self.assertEqual(messages[18].data["session_data"]["session_id"], "default") - self.assertEqual(messages[18].data["session_data"]["lang"], "pt-pt") - self.assertEqual(SessionManager.default_session.lang, "pt-pt") - - SessionManager.default_session.lang = "en-us" diff --git a/test/end2end/session/test_converse.py b/test/end2end/session/test_converse.py deleted file mode 100644 index 984a7273..00000000 --- a/test/end2end/session/test_converse.py +++ /dev/null @@ -1,527 +0,0 @@ -import time -from time import sleep -from unittest import TestCase, skip - -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_session(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) - - # triggers intent from test skill to make it active - # verify active skills list (test) - def skill_converse_no(): - - nonlocal messages - - utt = Message("recognizer_loop:utterance", - {"utterances": ["no"]}) # converse returns False - 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", - "skill.converse.pong", - # skill selected - "intent.service.skills.activated", - f"{self.skill_id}.activate", - f"{self.skill_id}:converse_off.intent", - # skill executing - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete", - # session updated - "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:]: - 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}:converse_off.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_converse_off") - 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["meta"]["skill"], self.skill_id) - self.assertEqual(messages[10].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[10].data["name"], "TestAbortSkill.handle_converse_off") - - # verify default session is now updated - self.assertEqual(messages[11].msg_type, "ovos.session.update_default") - self.assertEqual(messages[11].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[11].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - # test that active skills list has been updated - self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(messages[11].data["session_data"]["active_skills"][0][0], self.skill_id) - - messages = [] - - skill_converse_no() - - # test hello world skill triggers, test skill says it does not want to converse - # verify active skills list (hello, test) - def hello_world(): - nonlocal messages - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}) - 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", - "skill.converse.pong", - "skill.converse.request", - "skill.converse.response", # does not want to converse - # skill selected - "intent.service.skills.activated", - f"{self.other_skill_id}.activate", - f"{self.other_skill_id}:HelloWorldIntent", - # skill executing - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete", - # session updated - "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:]: - 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 answer from skill that it does not want to converse - self.assertEqual(messages[4].msg_type, "skill.converse.request") - self.assertEqual(messages[4].data["skill_id"], self.skill_id) - self.assertEqual(messages[5].msg_type, "skill.converse.response") - self.assertEqual(messages[5].data["skill_id"], self.skill_id) - self.assertFalse(messages[5].data["result"]) # does not want to converse - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[6].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[6].data["skill_id"], self.other_skill_id) - self.assertEqual(messages[7].msg_type, f"{self.other_skill_id}.activate") - - # verify intent triggers - self.assertEqual(messages[8].msg_type, f"{self.other_skill_id}:HelloWorldIntent") - # verify skill_id is now present in every message.context - for m in messages[8:]: - self.assertEqual(m.context["skill_id"], self.other_skill_id) - - # verify intent execution - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[9].data["name"], "HelloWorldSkill.handle_hello_world_intent") - self.assertEqual(messages[10].msg_type, "enclosure.active_skill") - self.assertEqual(messages[10].data["skill_id"], self.other_skill_id) - self.assertEqual(messages[11].msg_type, "speak") - self.assertEqual(messages[11].data["lang"], "en-us") - self.assertFalse(messages[11].data["expect_response"]) - self.assertEqual(messages[11].data["meta"]["skill"], self.other_skill_id) - self.assertEqual(messages[12].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[12].data["name"], "HelloWorldSkill.handle_hello_world_intent") - - # verify default session is now updated - self.assertEqual(messages[13].msg_type, "ovos.session.update_default") - self.assertEqual(messages[13].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[13].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - # test that active skills list has been updated - self.assertEqual(sess.active_skills[0][0], self.other_skill_id) - self.assertEqual(sess.active_skills[1][0], self.skill_id) - self.assertEqual(messages[13].data["session_data"]["active_skills"][0][0], self.other_skill_id) - self.assertEqual(messages[13].data["session_data"]["active_skills"][1][0], self.skill_id) - - messages = [] - - hello_world() - - # trigger skill intent that makes it return True in next converse - # verify active skills list gets swapped (test, hello) - def skill_converse_yes(): - nonlocal messages - - utt = Message("recognizer_loop:utterance", - {"utterances": ["yes"]}) # converse returns True - 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", - "skill.converse.pong", - "skill.converse.request", - "skill.converse.response", # does not want to converse - # skill selected - "intent.service.skills.activated", - f"{self.skill_id}.activate", - f"{self.skill_id}:converse_on.intent", - # skill executing - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete", - # session updated - "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:]: - 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 answer from skill that it does not want to converse - self.assertEqual(messages[4].msg_type, "skill.converse.request") - self.assertEqual(messages[4].data["skill_id"], self.skill_id) - self.assertEqual(messages[5].msg_type, "skill.converse.response") - self.assertEqual(messages[5].data["skill_id"], self.skill_id) - self.assertFalse(messages[5].data["result"]) # do not want to converse - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[6].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[6].data["skill_id"], self.skill_id) - self.assertEqual(messages[7].msg_type, f"{self.skill_id}.activate") - - # verify intent triggers - self.assertEqual(messages[8].msg_type, f"{self.skill_id}:converse_on.intent") - # verify skill_id is now present in every message.context - for m in messages[8:]: - self.assertEqual(m.context["skill_id"], self.skill_id) - - # verify intent execution - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[9].data["name"], "TestAbortSkill.handle_converse_on") - self.assertEqual(messages[10].msg_type, "enclosure.active_skill") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, "speak") - self.assertEqual(messages[11].data["lang"], "en-us") - self.assertFalse(messages[11].data["expect_response"]) - self.assertEqual(messages[11].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[12].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[12].data["name"], "TestAbortSkill.handle_converse_on") - - # verify default session is now updated - self.assertEqual(messages[13].msg_type, "ovos.session.update_default") - self.assertEqual(messages[13].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[13].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - # test that active skills list has been updated - self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(sess.active_skills[1][0], self.other_skill_id) - self.assertEqual(messages[13].data["session_data"]["active_skills"][0][0], self.skill_id) - self.assertEqual(messages[13].data["session_data"]["active_skills"][1][0], self.other_skill_id) - - messages = [] - - skill_converse_yes() - - # test converse capture, hello world utterance wont reach hello world skill - def converse_capture(): - nonlocal messages - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}) - 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", - "skill.converse.pong", - "skill.converse.request", - "skill.converse.response", # CONVERSED - # skill selected - "intent.service.skills.activated", - f"{self.skill_id}.activate", - # session updated - "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: - print(m) - self.assertTrue(m in mtypes) - - # verify that "session" is injected - # (missing in utterance message) and kept in all messages - for m in messages[1:]: - 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 answer from skill that it does not want to converse - self.assertEqual(messages[4].msg_type, "skill.converse.request") - self.assertEqual(messages[4].data["skill_id"], self.skill_id) - self.assertEqual(messages[5].msg_type, "skill.converse.response") - self.assertEqual(messages[5].data["skill_id"], self.skill_id) - self.assertTrue(messages[5].data["result"]) # CONVERSED - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[6].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[6].data["skill_id"], self.skill_id) - self.assertEqual(messages[7].msg_type, f"{self.skill_id}.activate") - - # verify default session is now updated - self.assertEqual(messages[8].msg_type, "ovos.session.update_default") - self.assertEqual(messages[8].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[8].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - # test that active skills list has been updated - self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(sess.active_skills[1][0], self.other_skill_id) - self.assertEqual(messages[8].data["session_data"]["active_skills"][0][0], self.skill_id) - self.assertEqual(messages[8].data["session_data"]["active_skills"][1][0], self.other_skill_id) - - messages = [] - - converse_capture() - - def external_deactivate(): - nonlocal messages - - utt = Message("test_deactivate") - self.core.bus.emit(utt) - - self.assertEqual(SessionManager.default_session.active_skills[0][0], self.other_skill_id) - # confirm all expected messages are sent - expected_messages = [ - "test_deactivate", - "intent.service.skills.deactivate", - "intent.service.skills.deactivated", - "ovos-tskill-abort.openvoiceos.deactivate", - "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 skill is no longer in active skills - self.assertEqual(SessionManager.default_session.active_skills[0][0], self.other_skill_id) - self.assertEqual(len(SessionManager.default_session.active_skills), 1) - - # verify default session is now updated - self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") - self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[-1].data["session_data"]) - self.assertEqual(sess.session_id, "default") - # test that active skills list has been updated - self.assertEqual(len(sess.active_skills), 1) - self.assertEqual(sess.active_skills[0][0], self.other_skill_id) - self.assertEqual(len(messages[-1].data["session_data"]["active_skills"]), 1) - self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.other_skill_id) - - messages = [] - - external_deactivate() - - def external_activate(): - nonlocal messages - - self.assertEqual(SessionManager.default_session.active_skills[0][0], self.other_skill_id) - utt = Message("test_activate") - self.core.bus.emit(utt) - self.assertEqual(SessionManager.default_session.active_skills[0][0], self.skill_id) - # confirm all expected messages are sent - expected_messages = [ - "test_activate", - "intent.service.skills.activate", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - "ovos.session.update_default", - "active_skill_request", # backwards compat namespace classic core - "intent.service.skills.activated", - f"{self.skill_id}.activate", - "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 skill is again in active skills - self.assertEqual(SessionManager.default_session.active_skills[0][0], self.skill_id) - self.assertEqual(len(SessionManager.default_session.active_skills), 2) - - # verify default session is now updated - self.assertEqual(messages[-1].msg_type, "ovos.session.update_default") - self.assertEqual(messages[-1].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[-1].data["session_data"]) - self.assertEqual(sess.session_id, "default") - # test that active skills list has been updated - self.assertEqual(len(sess.active_skills), 2) - self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(len(messages[-1].data["session_data"]["active_skills"]), 2) - self.assertEqual(messages[-1].data["session_data"]["active_skills"][0][0], self.skill_id) - - messages = [] - - external_activate() - diff --git a/test/end2end/session/test_fallback.py b/test/end2end/session/test_fallback.py deleted file mode 100644 index 48f4d342..00000000 --- a/test/end2end/session/test_fallback.py +++ /dev/null @@ -1,232 +0,0 @@ -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 TestFallback(TestCase): - - def setUp(self): - self.skill_id = "skill-ovos-fallback-unknown.openvoiceos" - self.core = get_minicroft(self.skill_id) - - def test_fallback(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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}, - {"session": SessionManager.default_session.serialize(), # explicit default sess - "x": "xx"}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - # Converse - "skill.converse.ping", - "skill.converse.pong", - # FallbackV2 - "ovos.skills.fallback.ping", - "ovos.skills.fallback.pong", - # skill executing - f"ovos.skills.fallback.{self.skill_id}.request", - f"ovos.skills.fallback.{self.skill_id}.start", - "enclosure.active_skill", - "speak", - f"ovos.skills.fallback.{self.skill_id}.response", - # intent service post fallback - "intent.service.skills.activated", - f"{self.skill_id}.activate", - "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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["x"], "xx") - # verify active skills is empty until "intent.service.skills.activated" - for m in messages[:10]: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["session"]["active_skills"], []) - - # verify converse ping/pong answer from skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # verify fallback ping/pong answer from skill - self.assertEqual(messages[3].msg_type, "ovos.skills.fallback.ping") - self.assertEqual(messages[4].msg_type, "ovos.skills.fallback.pong") - self.assertEqual(messages[4].data["skill_id"], self.skill_id) - self.assertEqual(messages[4].context["skill_id"], self.skill_id) - self.assertTrue(messages[4].data["can_handle"]) - - # verify skill executes - self.assertEqual(messages[5].msg_type, f"ovos.skills.fallback.{self.skill_id}.request") - self.assertEqual(messages[5].data["skill_id"], self.skill_id) - self.assertEqual(messages[6].msg_type, f"ovos.skills.fallback.{self.skill_id}.start") - self.assertEqual(messages[7].msg_type, "enclosure.active_skill") - self.assertEqual(messages[7].data["skill_id"], self.skill_id) - self.assertEqual(messages[8].msg_type, "speak") - self.assertEqual(messages[8].data["meta"]["dialog"], "unknown") - self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[9].msg_type, f"ovos.skills.fallback.{self.skill_id}.response") - self.assertTrue(messages[9].data["result"]) - self.assertEqual(messages[9].data["fallback_handler"], "UnknownSkill.handle_fallback") - - # verify skill is activated - self.assertEqual(messages[10].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, f"{self.skill_id}.activate") - - # verify default session is now updated - self.assertEqual(messages[12].msg_type, "ovos.session.update_default") - self.assertEqual(messages[12].data["session_data"]["session_id"], "default") - - # test second message with no session resumes default active skills - messages = [] - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}) - self.core.bus.emit(utt) - wait_for_n_messages(len(expected_messages)) - self.assertEqual(len(expected_messages), len(messages)) - - # verify that contexts are kept around - for m in messages[1:]: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["session"]["active_skills"][0][0], self.skill_id) - - def test_fallback_with_session(self): - SessionManager.sessions = {} - SessionManager.default_session = SessionManager.sessions["default"] = Session("default") - SessionManager.default_session.lang = "en-us" - messages = [] - - sess = Session() - - 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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}, - {"session": sess.serialize(), # explicit sess - "x": "xx"}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - # Converse - "skill.converse.ping", - "skill.converse.pong", - # FallbackV2 - "ovos.skills.fallback.ping", - "ovos.skills.fallback.pong", - # skill executing - TODO "mycroft.skill.handler.start" + "mycroft.skill.handler.complete" should be added - f"ovos.skills.fallback.{self.skill_id}.request", - f"ovos.skills.fallback.{self.skill_id}.start", - "enclosure.active_skill", - "speak", - f"ovos.skills.fallback.{self.skill_id}.response", - # intent service post fallback - "intent.service.skills.activated", - f"{self.skill_id}.activate" - ] - 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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], sess.session_id) - self.assertEqual(m.context["x"], "xx") - - # verify converse ping/pong answer from skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # verify fallback ping/pong answer from skill - self.assertEqual(messages[3].msg_type, "ovos.skills.fallback.ping") - self.assertEqual(messages[4].msg_type, "ovos.skills.fallback.pong") - self.assertEqual(messages[4].data["skill_id"], self.skill_id) - self.assertEqual(messages[4].context["skill_id"], self.skill_id) - self.assertTrue(messages[4].data["can_handle"]) - - # verify skill executes - self.assertEqual(messages[5].msg_type, f"ovos.skills.fallback.{self.skill_id}.request") - self.assertEqual(messages[5].data["skill_id"], self.skill_id) - self.assertEqual(messages[6].msg_type, f"ovos.skills.fallback.{self.skill_id}.start") - self.assertEqual(messages[7].msg_type, "enclosure.active_skill") - self.assertEqual(messages[7].data["skill_id"], self.skill_id) - self.assertEqual(messages[8].msg_type, "speak") - self.assertEqual(messages[8].data["meta"]["dialog"], "unknown") - self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[9].msg_type, f"ovos.skills.fallback.{self.skill_id}.response") - self.assertTrue(messages[9].data["result"]) - self.assertEqual(messages[9].data["fallback_handler"], "UnknownSkill.handle_fallback") - - # verify skill is activated - self.assertEqual(messages[10].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, f"{self.skill_id}.activate") - - # test that active skills list has been updated - sess = SessionManager.sessions[sess.session_id] - self.assertEqual(sess.active_skills[0][0], self.skill_id) - # test that default session remains unchanged - self.assertEqual(SessionManager.default_session.active_skills, []) diff --git a/test/end2end/session/test_fallback_v1.py b/test/end2end/session/test_fallback_v1.py deleted file mode 100644 index 853ab1e4..00000000 --- a/test/end2end/session/test_fallback_v1.py +++ /dev/null @@ -1,177 +0,0 @@ -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 TestFallback(TestCase): - - def setUp(self): - self.skill_id = "skill-ovos-fallback-unknownv1.openvoiceos" - self.core = get_minicroft(self.skill_id) - - def test_fallback_v1(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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}, - {"session": SessionManager.default_session.serialize(), # explicit default sess - "x": "xx"}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - # Converse - "skill.converse.ping", - "skill.converse.pong", - # FallbackV1 - high prio - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - # FallbackV1 - medium prio - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - # FallbackV1 - low prio -> skill selected - "mycroft.skills.fallback", - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - # self activation from skill, instead of from core - "intent.service.skills.activate", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - "ovos.session.update_default", # because it comes from skill - # backwards compat activation for older cores - "active_skill_request", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - "ovos.session.update_default", # because it comes from skill - # report handling - "mycroft.skill.handler.complete", - "mycroft.skills.fallback.response", - # update default sess - "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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["x"], "xx") - # verify active skills is empty until "intent.service.skills.activated" - for m in messages[:16]: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["session"]["active_skills"], []) - - # verify converse ping/pong answer from skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # high prio fallback - self.assertEqual(messages[3].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[3].data["fallback_range"], [0, 5]) - self.assertEqual(messages[4].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[4].data["handler"], "fallback") - self.assertEqual(messages[5].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[5].data["handler"], "fallback") - self.assertEqual(messages[6].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[6].data["handled"]) - - # medium prio fallback - self.assertEqual(messages[7].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[7].data["fallback_range"], [5, 90]) - self.assertEqual(messages[8].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[8].data["handler"], "fallback") - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[9].data["handler"], "fallback") - self.assertEqual(messages[10].msg_type, "mycroft.skills.fallback.response") - self.assertFalse(messages[10].data["handled"]) - - # low prio fallback - self.assertEqual(messages[11].msg_type, "mycroft.skills.fallback") - self.assertEqual(messages[11].data["fallback_range"], [90, 101]) - self.assertEqual(messages[12].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[12].data["handler"], "fallback") - - # skill execution - self.assertEqual(messages[13].msg_type, "enclosure.active_skill") - self.assertEqual(messages[13].data["skill_id"], self.skill_id) - self.assertEqual(messages[14].msg_type, "speak") - self.assertEqual(messages[14].data["meta"]["dialog"], "unknown") - self.assertEqual(messages[14].data["meta"]["skill"], self.skill_id) - - # skill making itself active - self.assertEqual(messages[15].msg_type, "intent.service.skills.activate") - self.assertEqual(messages[15].data["skill_id"], self.skill_id) - self.assertEqual(messages[16].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[16].data["skill_id"], self.skill_id) - self.assertEqual(messages[17].msg_type, f"{self.skill_id}.activate") - self.assertEqual(messages[18].msg_type, 'ovos.session.update_default') - # skill making itself active again - backwards compat namespace - self.assertEqual(messages[19].msg_type, "active_skill_request") - self.assertEqual(messages[19].data["skill_id"], self.skill_id) - self.assertEqual(messages[20].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[20].data["skill_id"], self.skill_id) - self.assertEqual(messages[21].msg_type, f"{self.skill_id}.activate") - self.assertEqual(messages[22].msg_type, 'ovos.session.update_default') - - # fallback execution response - self.assertEqual(messages[23].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[23].data["handler"], "fallback") - self.assertEqual(messages[24].msg_type, "mycroft.skills.fallback.response") - self.assertTrue(messages[24].data["handled"]) - - # verify default session is now updated - self.assertEqual(messages[25].msg_type, "ovos.session.update_default") - self.assertEqual(messages[25].data["session_data"]["session_id"], "default") - - # test second message with no session resumes default active skills - messages = [] - utt = Message("recognizer_loop:utterance", - {"utterances": ["invalid"]}) - self.core.bus.emit(utt) - wait_for_n_messages(len(expected_messages)) - self.assertEqual(len(expected_messages), len(messages)) - - # verify that contexts are kept around - for m in messages[1:]: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["session"]["active_skills"][0][0], self.skill_id) - diff --git a/test/end2end/session/test_get_response.py b/test/end2end/session/test_get_response.py deleted file mode 100644 index 9b641e79..00000000 --- a/test/end2end/session/test_get_response.py +++ /dev/null @@ -1,722 +0,0 @@ -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") - - def on_speak(msg): - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_start")) - sleep(1) # simulate TTS playback - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_end")) - - self.core.bus.on("message", new_msg) - self.core.bus.on("speak", on_speak) - - # 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", - "skill.converse.get_response.enable", # start of get_response - "ovos.session.update_default", # sync get_response status - "enclosure.active_skill", - "speak", # 'mycroft.mic.listen' if no dialog passed to get_response - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - - # "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 - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - - "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") - - # enable get_response for this session - self.assertEqual(messages[8].msg_type, "skill.converse.get_response.enable") - self.assertEqual(messages[9].msg_type, "ovos.session.update_default") - - # question dialog - self.assertEqual(messages[10].msg_type, "enclosure.active_skill") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, "speak") - self.assertEqual(messages[11].data["lang"], "en-us") - self.assertTrue(messages[11].data["expect_response"]) # listen after dialog - self.assertEqual(messages[11].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[12].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[13].msg_type, "recognizer_loop:audio_output_end") - - # user response would be here - - # disable get_response for this session - self.assertEqual(messages[14].msg_type, "skill.converse.get_response.disable") - self.assertEqual(messages[15].msg_type, "ovos.session.update_default") - - # post self.get_response intent code - self.assertEqual(messages[16].msg_type, "enclosure.active_skill") - self.assertEqual(messages[16].data["skill_id"], self.skill_id) - self.assertEqual(messages[17].msg_type, "speak") - self.assertEqual(messages[17].data["lang"], "en-us") - self.assertFalse(messages[17].data["expect_response"]) - self.assertEqual(messages[17].data["utterance"], "ERROR") - self.assertEqual(messages[17].data["meta"]["skill"], self.skill_id) - - self.assertEqual(messages[18].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[19].msg_type, "recognizer_loop:audio_output_end") - - self.assertEqual(messages[20].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[20].data["name"], "TestAbortSkill.handle_test_get_response") - - # verify default session is now updated - self.assertEqual(messages[21].msg_type, "ovos.session.update_default") - self.assertEqual(messages[21].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[21].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): - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_start")) - sleep(1) # simulate TTS playback - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_end")) # end wait=True in self.speak - if msg.data["utterance"] == "give me an answer": - sleep(0.5) - utt = Message("recognizer_loop:utterance", - {"utterances": ["ok"]}, - {"session": SessionManager.default_session.serialize()}) - self.core.bus.emit(utt) - - self.core.bus.on("speak", 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", - "skill.converse.get_response.enable", # start of get_response - "ovos.session.update_default", # sync get_response status - "enclosure.active_skill", - "speak", # 'mycroft.mic.listen' if no dialog passed to get_response - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - - "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 - - "skill.converse.get_response.disable", # end of get_response - "ovos.session.update_default", # sync get_response status - - # intent code post self.get_response - "enclosure.active_skill", # from speak inside intent - "speak", # speak "ok" inside intent - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - "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.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") - - # enable get_response for this session - self.assertEqual(messages[8].msg_type, "skill.converse.get_response.enable") - self.assertEqual(messages[9].msg_type, "ovos.session.update_default") - - self.assertEqual(messages[10].msg_type, "enclosure.active_skill") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, "speak") - self.assertEqual(messages[11].data["utterance"], "give me an answer", ) - self.assertEqual(messages[11].data["lang"], "en-us") - self.assertTrue(messages[11].data["expect_response"]) # listen after dialog - self.assertEqual(messages[11].data["meta"]["skill"], self.skill_id) - # ovos-audio speak execution (simulated) - self.assertEqual(messages[12].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[13].msg_type, "recognizer_loop:audio_output_end") - - # check utterance goes through converse cycle - self.assertEqual(messages[14].msg_type, "recognizer_loop:utterance") - self.assertEqual(messages[15].msg_type, "skill.converse.ping") - self.assertEqual(messages[16].msg_type, "skill.converse.pong") - self.assertEqual(messages[17].msg_type, "skill.converse.pong") - - # captured utterance sent to get_response handler that is waiting - self.assertEqual(messages[18].msg_type, "skill.converse.get_response") - self.assertEqual(messages[18].data["skill_id"], self.skill_id) - self.assertEqual(messages[18].data["utterances"], ["ok"]) - - # converse pipeline activates the skill last_used timestamp - self.assertEqual(messages[19].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[20].msg_type, f"{self.skill_id}.activate") - self.assertEqual(messages[21].msg_type, "ovos.session.update_default") - - # disable get_response for this session - self.assertEqual(messages[22].msg_type, "skill.converse.get_response.disable") - self.assertEqual(messages[23].msg_type, "ovos.session.update_default") - - # post self.get_response intent code - self.assertEqual(messages[24].msg_type, "enclosure.active_skill") - self.assertEqual(messages[24].data["skill_id"], self.skill_id) - self.assertEqual(messages[25].msg_type, "speak") - self.assertEqual(messages[25].data["lang"], "en-us") - self.assertFalse(messages[25].data["expect_response"]) - self.assertEqual(messages[25].data["utterance"], "ok") - self.assertEqual(messages[25].data["meta"]["skill"], self.skill_id) - # ovos-audio speak execution (simulated) - self.assertEqual(messages[26].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[27].msg_type, "recognizer_loop:audio_output_end") - - self.assertEqual(messages[28].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[28].data["name"], "TestAbortSkill.handle_test_get_response") - - # verify default session is now updated - self.assertEqual(messages[29].msg_type, "ovos.session.update_default") - self.assertEqual(messages[29].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[29].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - def test_cancel_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): - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_start")) - sleep(1) # simulate TTS playback - self.core.bus.emit(msg.forward("recognizer_loop:audio_output_end")) # end wait=True in self.speak - if msg.data["utterance"] == "give me an answer": - sleep(0.5) - utt = Message("recognizer_loop:utterance", - {"utterances": ["cancel"]}, - {"session": SessionManager.default_session.serialize()}) - self.core.bus.emit(utt) - - self.core.bus.on("speak", 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", - "skill.converse.get_response.enable", # start of get_response - "ovos.session.update_default", # sync get_response status - "enclosure.active_skill", - "speak", # 'mycroft.mic.listen' if no dialog passed to get_response - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - - "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 - - "skill.converse.get_response.disable", # end of get_response - "ovos.session.update_default", # sync get_response status - - # intent code post self.get_response - "enclosure.active_skill", # from speak inside intent - "speak", # speak "ERROR" inside intent - "recognizer_loop:audio_output_start", - "recognizer_loop:audio_output_end", - "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.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") - - # enable get_response for this session - self.assertEqual(messages[8].msg_type, "skill.converse.get_response.enable") - self.assertEqual(messages[9].msg_type, "ovos.session.update_default") - - self.assertEqual(messages[10].msg_type, "enclosure.active_skill") - self.assertEqual(messages[10].data["skill_id"], self.skill_id) - self.assertEqual(messages[11].msg_type, "speak") - self.assertEqual(messages[11].data["utterance"], "give me an answer", ) - self.assertEqual(messages[11].data["lang"], "en-us") - self.assertTrue(messages[11].data["expect_response"]) # listen after dialog - self.assertEqual(messages[11].data["meta"]["skill"], self.skill_id) - # ovos-audio speak execution (simulated) - self.assertEqual(messages[12].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[13].msg_type, "recognizer_loop:audio_output_end") - - # check utterance goes through converse cycle - self.assertEqual(messages[14].msg_type, "recognizer_loop:utterance") - self.assertEqual(messages[15].msg_type, "skill.converse.ping") - self.assertEqual(messages[16].msg_type, "skill.converse.pong") - self.assertEqual(messages[17].msg_type, "skill.converse.pong") - - # captured utterance sent to get_response handler that is waiting - self.assertEqual(messages[18].msg_type, "skill.converse.get_response") - self.assertEqual(messages[18].data["skill_id"], self.skill_id) - self.assertEqual(messages[18].data["utterances"], ["cancel"]) # was canceled by user, returned None - - # converse pipeline activates the skill last_used timestamp - self.assertEqual(messages[19].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[20].msg_type, f"{self.skill_id}.activate") - self.assertEqual(messages[21].msg_type, "ovos.session.update_default") - - # disable get_response for this session - self.assertEqual(messages[22].msg_type, "skill.converse.get_response.disable") - self.assertEqual(messages[23].msg_type, "ovos.session.update_default") - - # post self.get_response intent code - self.assertEqual(messages[24].msg_type, "enclosure.active_skill") - self.assertEqual(messages[24].data["skill_id"], self.skill_id) - self.assertEqual(messages[25].msg_type, "speak") - self.assertEqual(messages[25].data["lang"], "en-us") - self.assertFalse(messages[25].data["expect_response"]) - self.assertEqual(messages[25].data["utterance"], "ERROR") - self.assertEqual(messages[25].data["meta"]["skill"], self.skill_id) - # ovos-audio speak execution (simulated) - self.assertEqual(messages[26].msg_type, "recognizer_loop:audio_output_start") - self.assertEqual(messages[27].msg_type, "recognizer_loop:audio_output_end") - - self.assertEqual(messages[28].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[28].data["name"], "TestAbortSkill.handle_test_get_response") - - # verify default session is now updated - self.assertEqual(messages[29].msg_type, "ovos.session.update_default") - self.assertEqual(messages[29].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[29].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - def test_with_reprompt(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) - - counter = 0 - - def answer_get_response(msg): - nonlocal counter - counter += 1 - if counter == 3: # answer on 3rd prompt only - sleep(0.5) - utt = Message("recognizer_loop:utterance", - {"utterances": ["ok"]}, - {"session": SessionManager.default_session.serialize()}) - self.core.bus.emit(utt) - - self.core.bus.on("mycroft.mic.listen", answer_get_response) - - # trigger get_response - utt = Message("recognizer_loop:utterance", - {"utterances": ["3 prompts"]}) - 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_response3.intent", - - # intent code before self.get_response - "mycroft.skill.handler.start", - "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 - "mycroft.mic.listen", - "mycroft.mic.listen", - - "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 - - "skill.converse.get_response.disable", # end of get_response - "ovos.session.update_default", # sync get_response status - - # intent code post self.get_response - "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") - - # 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_response3.intent") - - # verify intent execution - self.assertEqual(messages[7].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[7].data["name"], "TestAbortSkill.handle_test_get_response3") - - # enable get_response for this session - self.assertEqual(messages[8].msg_type, "skill.converse.get_response.enable") - self.assertEqual(messages[9].msg_type, "ovos.session.update_default") - - # 3 sound prompts (no dialog in this test) - self.assertEqual(messages[10].msg_type, "mycroft.mic.listen") - self.assertEqual(messages[11].msg_type, "mycroft.mic.listen") - 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") - self.assertEqual(messages[15].msg_type, "skill.converse.pong") - self.assertEqual(messages[16].msg_type, "skill.converse.pong") - - # 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"], ["ok"]) - - # 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") - - # post self.get_response intent code - self.assertEqual(messages[23].msg_type, "enclosure.active_skill") - self.assertEqual(messages[23].data["skill_id"], self.skill_id) - self.assertEqual(messages[24].msg_type, "speak") - self.assertEqual(messages[24].data["lang"], "en-us") - self.assertFalse(messages[24].data["expect_response"]) - self.assertEqual(messages[24].data["utterance"], "ok") - self.assertEqual(messages[24].data["meta"]["skill"], self.skill_id) - - self.assertEqual(messages[25].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[25].data["name"], "TestAbortSkill.handle_test_get_response3") - - # verify default session is now updated - self.assertEqual(messages[26].msg_type, "ovos.session.update_default") - self.assertEqual(messages[26].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[26].data["session_data"]) - self.assertEqual(sess.session_id, "default") diff --git a/test/end2end/session/test_session.py b/test/end2end/session/test_session.py deleted file mode 100644 index 30a349e6..00000000 --- a/test/end2end/session/test_session.py +++ /dev/null @@ -1,318 +0,0 @@ -import time -from time import sleep -from unittest import TestCase, skip - -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 = "skill-ovos-hello-world.openvoiceos" - self.core = get_minicroft(self.skill_id) - - def test_no_session(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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}) - 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", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - f"{self.skill_id}:HelloWorldIntent", - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete", - "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:]: - 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 hello world skill - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[3].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[3].data["skill_id"], self.skill_id) - self.assertEqual(messages[4].msg_type, f"{self.skill_id}.activate") - - # verify intent triggers - self.assertEqual(messages[5].msg_type, f"{self.skill_id}:HelloWorldIntent") - self.assertEqual(messages[5].data["intent_type"], f"{self.skill_id}:HelloWorldIntent") - # verify skill_id is now present in every message.context - for m in messages[5:]: - self.assertEqual(m.context["skill_id"], self.skill_id) - - # verify intent execution - self.assertEqual(messages[6].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[6].data["name"], "HelloWorldSkill.handle_hello_world_intent") - self.assertEqual(messages[7].msg_type, "enclosure.active_skill") - self.assertEqual(messages[7].data["skill_id"], self.skill_id) - self.assertEqual(messages[8].msg_type, "speak") - self.assertEqual(messages[8].data["lang"], "en-us") - self.assertFalse(messages[8].data["expect_response"]) - self.assertEqual(messages[8].data["meta"]["dialog"], "hello.world") - self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[9].data["name"], "HelloWorldSkill.handle_hello_world_intent") - - # verify default session is now updated - self.assertEqual(messages[10].msg_type, "ovos.session.update_default") - self.assertEqual(messages[10].data["session_data"]["session_id"], "default") - # test deserialization of payload - sess = Session.deserialize(messages[10].data["session_data"]) - self.assertEqual(sess.session_id, "default") - - # test that active skills list has been updated - self.assertEqual(sess.active_skills[0][0], self.skill_id) - self.assertEqual(messages[10].data["session_data"]["active_skills"][0][0], self.skill_id) - - def test_explicit_default_session(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) - - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}, - {"session": SessionManager.default_session.serialize(), # explicit - "xxx": "not-valid"}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - "skill.converse.ping", - "skill.converse.pong", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - f"{self.skill_id}:HelloWorldIntent", - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete", - "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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], "default") - self.assertEqual(m.context["xxx"], "not-valid") - - # verify ping/pong answer from hello world skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[3].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[3].data["skill_id"], self.skill_id) - self.assertEqual(messages[4].msg_type, f"{self.skill_id}.activate") - - # verify intent triggers - self.assertEqual(messages[5].msg_type, f"{self.skill_id}:HelloWorldIntent") - self.assertEqual(messages[5].data["intent_type"], f"{self.skill_id}:HelloWorldIntent") - # verify skill_id is now present in every message.context - for m in messages[5:]: - self.assertEqual(m.context["skill_id"], self.skill_id) - - # verify intent execution - self.assertEqual(messages[6].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[6].data["name"], "HelloWorldSkill.handle_hello_world_intent") - self.assertEqual(messages[7].msg_type, "enclosure.active_skill") - self.assertEqual(messages[7].data["skill_id"], self.skill_id) - self.assertEqual(messages[8].msg_type, "speak") - self.assertEqual(messages[8].data["lang"], "en-us") - self.assertFalse(messages[8].data["expect_response"]) - self.assertEqual(messages[8].data["meta"]["dialog"], "hello.world") - self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[9].data["name"], "HelloWorldSkill.handle_hello_world_intent") - - # verify default session is now updated - self.assertEqual(messages[10].msg_type, "ovos.session.update_default") - self.assertEqual(messages[10].data["session_data"]["session_id"], "default") - - # test deserialization of payload - sess = Session.deserialize(messages[10].data["session_data"]) - self.assertEqual(sess.session_id, "default") - self.assertEqual(sess.valid_languages, ["en-us"]) - - # test that active skills list has been updated - self.assertEqual(messages[10].data["session_data"]["active_skills"][0][0], self.skill_id) - self.assertEqual(sess.active_skills[0][0], self.skill_id) - - def test_explicit_session(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) - - sess = Session() - utt = Message("recognizer_loop:utterance", - {"utterances": ["hello world"]}, - {"session": sess.serialize(), # explicit - "xxx": "not-valid"}) - self.core.bus.emit(utt) - - # confirm all expected messages are sent - expected_messages = [ - "recognizer_loop:utterance", - "skill.converse.ping", - "skill.converse.pong", - "intent.service.skills.activated", - f"{self.skill_id}.activate", - f"{self.skill_id}:HelloWorldIntent", - "mycroft.skill.handler.start", - "enclosure.active_skill", - "speak", - "mycroft.skill.handler.complete" - ] - 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 contexts are kept around - for m in messages: - self.assertEqual(m.context["session"]["session_id"], sess.session_id) - self.assertEqual(m.context["xxx"], "not-valid") - - # verify ping/pong answer from hello world skill - self.assertEqual(messages[1].msg_type, "skill.converse.ping") - self.assertEqual(messages[2].msg_type, "skill.converse.pong") - self.assertEqual(messages[2].data["skill_id"], self.skill_id) - self.assertEqual(messages[2].context["skill_id"], self.skill_id) - self.assertFalse(messages[2].data["can_handle"]) - - # verify skill is activated by intent service (intent pipeline matched) - self.assertEqual(messages[3].msg_type, "intent.service.skills.activated") - self.assertEqual(messages[3].data["skill_id"], self.skill_id) - self.assertEqual(messages[4].msg_type, f"{self.skill_id}.activate") - - # verify intent triggers - self.assertEqual(messages[5].msg_type, f"{self.skill_id}:HelloWorldIntent") - self.assertEqual(messages[5].data["intent_type"], f"{self.skill_id}:HelloWorldIntent") - # verify skill_id is now present in every message.context - for m in messages[5:]: - self.assertEqual(m.context["skill_id"], self.skill_id) - - # verify intent execution - self.assertEqual(messages[6].msg_type, "mycroft.skill.handler.start") - self.assertEqual(messages[6].data["name"], "HelloWorldSkill.handle_hello_world_intent") - self.assertEqual(messages[7].msg_type, "enclosure.active_skill") - self.assertEqual(messages[7].data["skill_id"], self.skill_id) - self.assertEqual(messages[8].msg_type, "speak") - self.assertEqual(messages[8].data["lang"], "en-us") - self.assertFalse(messages[8].data["expect_response"]) - self.assertEqual(messages[8].data["meta"]["dialog"], "hello.world") - self.assertEqual(messages[8].data["meta"]["skill"], self.skill_id) - self.assertEqual(messages[9].msg_type, "mycroft.skill.handler.complete") - self.assertEqual(messages[9].data["name"], "HelloWorldSkill.handle_hello_world_intent") - - # test that active skills list has been updated - sess = SessionManager.sessions[sess.session_id] - self.assertEqual(sess.active_skills[0][0], self.skill_id) - # test that default session remains unchanged - self.assertEqual(SessionManager.default_session.active_skills, []) - diff --git a/test/unittests/test_skill.py b/test/unittests/test_skill.py index c1f25234..fc7ccb2f 100644 --- a/test/unittests/test_skill.py +++ b/test/unittests/test_skill.py @@ -136,8 +136,8 @@ def test_registered_events(self): # base skill class events exclusive to ovos-core if not is_classic_core(): - default_ovos = ["skill.converse.ping", - "skill.converse.request", + default_ovos = [f"{self.skill.skill_id}.converse.ping", + f"{self.skill.skill_id}.converse.request", "intent.service.skills.activated", "intent.service.skills.deactivated", f"{self.skill.skill_id}.activate",