Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ovos_workshop/skill_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from time import time
from ovos_bus_client.client import MessageBusClient
from ovos_bus_client.message import Message
from ovos_bus_client.session import SessionManager
from ovos_config.config import Configuration
from ovos_config.locale import setup_locale
from ovos_plugin_manager.skills import find_skill_plugins, get_skill_directories
Expand Down Expand Up @@ -534,6 +535,12 @@ def _connect_to_core(self):
self.bus.run_in_thread()
self.bus.connected_event.wait()

# mirrors ovos-core's IntentService (ovos_core/intent_services/service.py),
# the only other caller of connect_to_bus in the stack; without this,
# SessionManager.bus stays None in standalone skill containers and
# SessionManager.wait_while_speaking()/speak(wait=True) silently no-op
SessionManager.connect_to_bus(self.bus)

self.bus.on("mycroft.ready", self.load_skill)
self.bus.on("skillmanager.activate", self.do_load)
self.bus.on("skillmanager.deactivate", self.do_unload)
Expand Down
48 changes: 46 additions & 2 deletions test/unittests/test_skill_launcher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import shutil
import unittest
import unittest.mock
import sys

from os import environ
Expand Down Expand Up @@ -157,5 +158,48 @@ def test_plugin_skill_loader_load(self):


class TestSkillContainer(unittest.TestCase):
# TODO
pass
def setUp(self):
from ovos_bus_client.session import SessionManager
SessionManager.bus = None

def tearDown(self):
from ovos_bus_client.session import SessionManager
SessionManager.bus = None

def test_connect_to_core_wires_session_manager_bus(self):
"""
Regression test: standalone SkillContainer must wire
SessionManager.connect_to_bus() when it owns/connects its bus,
mirroring ovos-core's IntentService (the only other caller of
SessionManager.connect_to_bus in the stack). Without this,
SessionManager.bus stays None and speak(wait=True) /
SessionManager.wait_while_speaking silently no-op in standalone
skill containers.
"""
from ovos_workshop.skill_launcher import SkillContainer
from ovos_bus_client.session import SessionManager

bus = FakeBus()
container = SkillContainer(skill_id="test_skill.test", bus=bus)
# avoid blocking on wait_for_core()/mycroft.skills.is_ready
container.load_skill = lambda message=None: None
bus.wait_for_response = lambda message, **kwargs: None

# exercise only the bus-wiring half of _connect_to_core; stub out
# the blocking wait_for_core() polling loop it defines internally
import ovos_workshop.skill_launcher as skill_launcher_mod
orig_thread_wait = None

# call the real method but short-circuit the blocking retry loop by
# patching threading.Event().wait used inside wait_for_core
with unittest.mock.patch.object(
skill_launcher_mod.threading, "Event") as mock_event_cls:
mock_event_cls.return_value.wait.side_effect = RuntimeError(
"stop retry loop")
try:
container._connect_to_core()
except RuntimeError:
pass

self.assertIsNotNone(SessionManager.bus)
self.assertIs(SessionManager.bus, bus)
Loading