From 7c075654a14f19ad2fb8c867c6548bfe734c7ee0 Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Tue, 15 Oct 2024 02:10:59 +0100 Subject: [PATCH 1/5] Update coverage.yml --- .github/workflows/coverage.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 79ad2def72f..00968d89627 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -24,7 +24,7 @@ jobs: - name: Install test dependencies run: | pip install -r requirements/tests.txt - pip install ./test/unittests/common_query/ovos_tskill_fakewiki + pip install ./test/integrationtests/common_query/ovos_tskill_fakewiki pip install ./test/end2end/skill-ovos-hello-world pip install ./test/end2end/skill-ovos-fallback-unknown pip install ./test/end2end/skill-ovos-slow-fallback @@ -43,16 +43,11 @@ jobs: run: | pytest --cov=ovos_core --cov-report xml test/unittests pytest --cov-append --cov=ovos_core --cov-report xml test/end2end - pytest --cov-append --cov=ovos_core --cov-report xml test/integrationtests - name: Generate coverage report with padatious run: | sudo apt install libfann-dev pip install .[lgpl] pytest --cov-append --cov=ovos_core --cov-report xml test/unittests/skills - - name: Generate coverage report with utils 0.0.38 - run: | - pip install ovos-utils==0.0.38 - pytest --cov-append --cov=ovos_core --cov-report xml test/end2end - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: From 4e609c8b34cb8fbfa3fdd656a9131cf64287799d Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Tue, 15 Oct 2024 02:28:31 +0100 Subject: [PATCH 2/5] Update coverage.yml --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 00968d89627..81ff52dd464 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -38,7 +38,7 @@ jobs: pip install ./test/end2end/metadata-test-plugin - name: Install core repo run: | - pip install -e .[mycroft,deprecated] + pip install -e .[mycroft,deprecated,plugins] - name: Generate coverage report run: | pytest --cov=ovos_core --cov-report xml test/unittests From 6d2f8c79f6f4da700b892733b5b6305122f5831b Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:16:41 +0100 Subject: [PATCH 3/5] requirements:latest stable packages (#563) * requirements:latest stable packages * requirements:latest stable packages * requirements:latest stable packages * requirements:latest stable packages * requirements:latest stable packages * requirements:latest stable packages * fix import * fix imports * fix imports * move tests to ovos-workshop * dont require latest workshop, just allow it --- mycroft/__init__.py | 2 +- mycroft/enclosure/gui.py | 2 +- mycroft/gui/__init__.py | 2 +- mycroft/skills/__init__.py | 2 +- mycroft/skills/common_iot_skill.py | 2 +- mycroft/skills/core.py | 2 +- mycroft/skills/mycroft_skill/__init__.py | 2 +- mycroft/skills/mycroft_skill/mycroft_skill.py | 4 +- ovos_core/intent_services/fallback_service.py | 2 +- requirements/extra-deprecated.txt | 9 +- requirements/lgpl.txt | 4 +- requirements/mycroft.txt | 20 +- requirements/plugins.txt | 10 +- requirements/requirements.txt | 8 +- requirements/skills-audio.txt | 12 +- requirements/skills-desktop.txt | 2 +- requirements/skills-essential.txt | 14 +- requirements/skills-gui.txt | 2 +- requirements/skills-internet.txt | 16 +- requirements/skills-media.txt | 7 +- test/integrationtests/decorator_test_skill.py | 33 - .../test_common_query_skill.py | 62 -- test/integrationtests/test_context.py | 40 -- test/integrationtests/test_fallback_skill.py | 54 -- .../test_intent_service_interface.py | 145 ---- test/integrationtests/test_mycroft_skill.py | 641 ------------------ test/integrationtests/test_skill_api.py | 153 ----- test/integrationtests/test_skill_loader.py | 192 ------ 28 files changed, 61 insertions(+), 1383 deletions(-) delete mode 100644 test/integrationtests/decorator_test_skill.py delete mode 100644 test/integrationtests/test_common_query_skill.py delete mode 100644 test/integrationtests/test_context.py delete mode 100644 test/integrationtests/test_fallback_skill.py delete mode 100644 test/integrationtests/test_intent_service_interface.py delete mode 100644 test/integrationtests/test_mycroft_skill.py delete mode 100644 test/integrationtests/test_skill_api.py delete mode 100644 test/integrationtests/test_skill_loader.py diff --git a/mycroft/__init__.py b/mycroft/__init__.py index be517139c82..bcbc4755033 100644 --- a/mycroft/__init__.py +++ b/mycroft/__init__.py @@ -18,7 +18,7 @@ from ovos_workshop.intents import IntentBuilder, Intent from ovos_workshop.decorators import intent_handler, intent_file_handler, adds_context, removes_context -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill from ovos_workshop.skills.fallback import FallbackSkill from ovos_utils.log import LOG diff --git a/mycroft/enclosure/gui.py b/mycroft/enclosure/gui.py index bd6a24c2e43..74f167495e1 100644 --- a/mycroft/enclosure/gui.py +++ b/mycroft/enclosure/gui.py @@ -8,4 +8,4 @@ to be a drop in replacement for mycroft-core """ -from ovos_workshop.skills.base import SkillGUI +from ovos_workshop.skills.ovos import SkillGUI diff --git a/mycroft/gui/__init__.py b/mycroft/gui/__init__.py index 184ee89227b..d73250046c6 100644 --- a/mycroft/gui/__init__.py +++ b/mycroft/gui/__init__.py @@ -1,3 +1,3 @@ # backwards compat imports from ovos_bus_client.apis.gui import GUIInterface -from ovos_workshop.skills.base import SkillGUI +from ovos_workshop.skills.ovos import SkillGUI diff --git a/mycroft/skills/__init__.py b/mycroft/skills/__init__.py index 6b344d9bcac..393e01778b6 100644 --- a/mycroft/skills/__init__.py +++ b/mycroft/skills/__init__.py @@ -22,7 +22,7 @@ resting_screen_handler, skill_api_method) from ovos_workshop.skills.fallback import FallbackSkill -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill from mycroft.skills.common_iot_skill import CommonIoTSkill from mycroft.skills.common_play_skill import CommonPlaySkill, CPSMatchLevel from mycroft.skills.common_query_skill import CommonQuerySkill, CQSMatchLevel diff --git a/mycroft/skills/common_iot_skill.py b/mycroft/skills/common_iot_skill.py index 5768a131386..bcaec91b1de 100644 --- a/mycroft/skills/common_iot_skill.py +++ b/mycroft/skills/common_iot_skill.py @@ -24,7 +24,7 @@ from functools import total_ordering, wraps from itertools import count -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill from ovos_bus_client.message import Message, dig_for_message ENTITY = "ENTITY" diff --git a/mycroft/skills/core.py b/mycroft/skills/core.py index 48ec3dc8963..74e269183bc 100644 --- a/mycroft/skills/core.py +++ b/mycroft/skills/core.py @@ -20,7 +20,7 @@ # Import moved methods for backwards compatibility # This will need to remain here for quite some time since removing it # would break most of the skills out there. -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill from ovos_workshop.skills.fallback import FallbackSkill from mycroft.skills.mycroft_skill import resting_screen_handler, intent_handler, intent_file_handler, skill_api_method diff --git a/mycroft/skills/mycroft_skill/__init__.py b/mycroft/skills/mycroft_skill/__init__.py index 3b935737057..baee0f9e2c6 100644 --- a/mycroft/skills/mycroft_skill/__init__.py +++ b/mycroft/skills/mycroft_skill/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill from ovos_utils.events import get_handler_name from mycroft.skills.mycroft_skill.decorators import (intent_handler, intent_file_handler, diff --git a/mycroft/skills/mycroft_skill/mycroft_skill.py b/mycroft/skills/mycroft_skill/mycroft_skill.py index a56f5989623..350b8e7b867 100644 --- a/mycroft/skills/mycroft_skill/mycroft_skill.py +++ b/mycroft/skills/mycroft_skill/mycroft_skill.py @@ -17,7 +17,7 @@ # backwards compat imports, do not delete! from ovos_workshop.intents import Intent, IntentBuilder, IntentServiceInterface, to_alnum from ovos_utils.skills import get_non_properties -from ovos_workshop.skills.base import SkillGUI +from ovos_workshop.skills.ovos import SkillGUI from ovos_bus_client.message import Message, dig_for_message from mycroft.metrics import report_metric from ovos_bus_client.util.scheduler import EventScheduler, EventSchedulerInterface @@ -29,4 +29,4 @@ read_vocab_file, read_value_file, read_translated_file, load_vocabulary, load_regex) from mycroft.deprecated.skills.settings import SettingsMetaUploader -from ovos_workshop.skills.mycroft_skill import MycroftSkill +from ovos_workshop.skills.ovos import OVOSSkill as MycroftSkill diff --git a/ovos_core/intent_services/fallback_service.py b/ovos_core/intent_services/fallback_service.py index 7d07a7f055f..165a0b6bf45 100644 --- a/ovos_core/intent_services/fallback_service.py +++ b/ovos_core/intent_services/fallback_service.py @@ -23,7 +23,7 @@ from ovos_plugin_manager.templates.pipeline import IntentMatch, PipelinePlugin from ovos_utils import flatten_list from ovos_utils.log import LOG -from ovos_workshop.skills.fallback import FallbackMode +from ovos_workshop.permissions import FallbackMode FallbackRange = namedtuple('FallbackRange', ['start', 'stop']) diff --git a/requirements/extra-deprecated.txt b/requirements/extra-deprecated.txt index 891d8b60ac4..bfcea171d34 100644 --- a/requirements/extra-deprecated.txt +++ b/requirements/extra-deprecated.txt @@ -6,4 +6,11 @@ python-vlc>=1.1.2 pyalsaaudio>=0.8 pyserial>=3.0 pillow>=8.3 -pyaudio \ No newline at end of file +pyaudio + +# mycroft-core imports / default plugins +# for compat with mycroft namespace +ovos-listener>=0.0.3,<1.0.0 +ovos-tts-plugin-mimic>=0.2.8, <1.0.0 +ovos-ww-plugin-precise>=0.1, <1.0.0 +ovos-ww-plugin-pocketsphinx>=0.1, <1.0.0 \ No newline at end of file diff --git a/requirements/lgpl.txt b/requirements/lgpl.txt index 5979e90b3eb..74a4b7a01a4 100644 --- a/requirements/lgpl.txt +++ b/requirements/lgpl.txt @@ -1,2 +1,2 @@ -ovos_padatious>=0.1.0,<1.0.0 -fann2>=1.0.7, < 1.1.0 \ No newline at end of file +ovos_padatious>=0.1.2,<1.0.0 +fann2>=1.0.7, < 1.1.0 diff --git a/requirements/mycroft.txt b/requirements/mycroft.txt index 829fa9d06d9..2bceb8a0b5a 100644 --- a/requirements/mycroft.txt +++ b/requirements/mycroft.txt @@ -1,14 +1,6 @@ -# all ovos core modules, a full install like mycroft-core -ovos_PHAL[extras]>=0.0.5,<1.0.0 -ovos-audio[extras]>=0.0.0,<1.0.0 -ovos-gui[extras]>=0.0.4,<1.0.0 -ovos-messagebus>=0.0.1,<1.0.0 -ovos-dinkum-listener[extras]>=0.0.1,<1.0.0 - -# mycroft-core imports / default plugins -# for compat with mycroft namespace -ovos-listener>=0.0.3,<1.0.0 -ovos-tts-plugin-mimic>=0.2.8, <1.0.0 -ovos-ww-plugin-precise>=0.1, <1.0.0 -ovos-ww-plugin-pocketsphinx>=0.1, <1.0.0 - +# all ovos core modules, a full install like mycroft-core used to do +ovos_PHAL[extras]>=0.2.5,<1.0.0 +ovos-audio[extras]>=0.2.4,<1.0.0 +ovos-gui[extras]>=0.2.2,<1.0.0 +ovos-messagebus>=0.0.6,<1.0.0 +ovos-dinkum-listener[extras]>=0.2.1,<1.0.0 diff --git a/requirements/plugins.txt b/requirements/plugins.txt index e09e6187d13..e4305a65330 100644 --- a/requirements/plugins.txt +++ b/requirements/plugins.txt @@ -1,8 +1,8 @@ -ovos-utterance-corrections-plugin>=0.0.0, <1.0.0 -ovos-utterance-plugin-cancel>=0.0.1, <1.0.0 -ovos-bidirectional-translation-plugin>=0.0.1, <1.0.0 -ovos-translate-server-plugin>=0.0.1, <1.0.0 -ovos-utterance-normalizer>=0.1.0, <1.0.0 +ovos-utterance-corrections-plugin>=0.0.2, <1.0.0 +ovos-utterance-plugin-cancel>=0.2.0, <1.0.0 +ovos-bidirectional-translation-plugin>=0.1.0, <1.0.0 +ovos-translate-server-plugin>=0.0.2, <1.0.0 +ovos-utterance-normalizer>=0.2.1, <1.0.0 # still in alpha ovos-classifiers diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 6e08d1c1643..cc8bf73a38e 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -4,9 +4,9 @@ watchdog>=2.1, <3.0 combo-lock>=0.2.2, <0.3 padacioso>=0.2.2,<1.0.0 -ovos-adapt-parser>=0.1.1, <1.0.0 -ovos_ocp_pipeline_plugin>=0.1.0, <1.0.0 -ovos-common-query-pipeline-plugin>=0.1.0, <1.0.0 +ovos-adapt-parser>=0.1.2, <1.0.0 +ovos_ocp_pipeline_plugin>=0.1.2, <1.0.0 +ovos-common-query-pipeline-plugin>=0.1.2, <1.0.0 ovos-utils>=0.1.0,<1.0.0 ovos_bus_client>=0.1.0,<1.0.0 @@ -14,4 +14,4 @@ ovos-plugin-manager>=0.0.26,<1.0.0 ovos-config>=0.0.13,<1.0.0 ovos-lingua-franca>=0.4.7,<1.0.0 ovos-backend-client>=0.1.0,<2.0.0 -ovos-workshop>=0.0.16,<1.0.0 +ovos-workshop>=0.0.16,<2.0.0 \ No newline at end of file diff --git a/requirements/skills-audio.txt b/requirements/skills-audio.txt index 7e326349a4c..8516c95b9ba 100644 --- a/requirements/skills-audio.txt +++ b/requirements/skills-audio.txt @@ -1,7 +1,7 @@ # skills that run in audio enabled devices (require mic/speaker) -ovos-skill-boot-finished>=0.1.0,<1.0.0 -ovos-skill-audio-recording>=0.1.0,<1.0.0 -ovos-skill-dictation>=0.1.0,<1.0.0 -ovos-skill-parrot>=0.0.1,<1.0.0 -ovos-skill-volume>=0.0.2,<1.0.0 -ovos-skill-naptime>=0.2.3,<1.0.0 +ovos-skill-boot-finished>=0.2.1,<1.0.0 +ovos-skill-audio-recording>=0.2.2,<1.0.0 +ovos-skill-dictation>=0.2.0,<1.0.0 +ovos-skill-parrot>=0.1.2,<1.0.0 +ovos-skill-volume>=0.1.1,<1.0.0 +ovos-skill-naptime>=0.3.1,<1.0.0 diff --git a/requirements/skills-desktop.txt b/requirements/skills-desktop.txt index d4023d4d430..a5f4295e72d 100644 --- a/requirements/skills-desktop.txt +++ b/requirements/skills-desktop.txt @@ -1,2 +1,2 @@ # skills that require a linux desktop environment -ovos-skill-application-launcher>=0.1.0,<1.0.0 +ovos-skill-application-launcher>=0.2.1,<1.0.0 diff --git a/requirements/skills-essential.txt b/requirements/skills-essential.txt index 2bd437bcf1c..9eeea909940 100644 --- a/requirements/skills-essential.txt +++ b/requirements/skills-essential.txt @@ -1,9 +1,9 @@ # skills providing core functionality (offline) -ovos-skill-fallback-unknown>=0.0.4,<1.0.0 -ovos-skill-alerts>=0.0.1,<1.0.0 -ovos-skill-personal>=0.0.4,<1.0.0 -ovos-skill-date-time>=0.2.3,<1.0.0 -ovos-skill-hello-world>=0.0.4,<1.0.0 -skill-wordnet>=0.0.1,<1.0.0 +ovos-skill-fallback-unknown>=0.1.2,<1.0.0 +ovos-skill-alerts>=0.1.2,<1.0.0 +ovos-skill-personal>=0.1.4,<1.0.0 +ovos-skill-date-time>=0.3.2,<1.0.0 +ovos-skill-hello-world>=0.1.6,<1.0.0 +skill-wordnet>=0.0.4,<1.0.0 #skill-randomness>=0.0.1,<1.0.0 -ovos-skill-spelling>=0.1.0,<1.0.0 +ovos-skill-spelling>=0.2.2,<1.0.0 diff --git a/requirements/skills-gui.txt b/requirements/skills-gui.txt index ae894f4c1f9..c0b98856f07 100644 --- a/requirements/skills-gui.txt +++ b/requirements/skills-gui.txt @@ -1 +1 @@ -ovos-skill-homescreen>=0.0.3,<1.0.0 \ No newline at end of file +ovos-skill-homescreen>=1.0.2,<2.0.0 diff --git a/requirements/skills-internet.txt b/requirements/skills-internet.txt index 5d545d0b6cd..2bed72e9dc4 100644 --- a/requirements/skills-internet.txt +++ b/requirements/skills-internet.txt @@ -1,9 +1,9 @@ # skills that require internet connectivity, should not be installed in offline devices -ovos-skill-weather>=0.0.1,<1.0.0 -skill-ddg>=0.0.2,<1.0.0 -skill-wolfie>=0.2.0,<1.0.0 -ovos-skill-wikipedia>=0.5.0,<1.0.0 -skill-ovos-fallback-chatgpt>=0.0.2,<1.0.0 -ovos-skill-wikihow>=0.2.0,<1.0.0 -ovos-skill-speedtest>=0.1.0,<1.0.0 -ovos-skill-ip>=0.1.0,<1.0.0 +ovos-skill-weather>=0.1.1,<1.0.0 +skill-ddg>=0.1.2,<1.0.0 +skill-wolfie>=0.2.3,<1.0.0 +ovos-skill-wikipedia>=0.5.2,<1.0.0 +skill-ovos-fallback-chatgpt>=0.1.2,<1.0.0 +ovos-skill-wikihow>=0.2.3,<1.0.0 +ovos-skill-speedtest>=0.2.1,<1.0.0 +ovos-skill-ip>=0.2.2,<1.0.0 diff --git a/requirements/skills-media.txt b/requirements/skills-media.txt index 4bc45f639d8..1c77abca446 100644 --- a/requirements/skills-media.txt +++ b/requirements/skills-media.txt @@ -1,6 +1,5 @@ # skills for OCP, require audio playback plugins (usually mpv) -ovos-skill-somafm>=0.0.2,<1.0.0 -skill-news>=0.0.4,<1.0.0 +ovos-skill-somafm>=0.1.1,<1.0.0 +skill-news>=0.1.2,<1.0.0 ovos-skill-pyradios>=0.1.0,<1.0.0 -ovos-skill-local-media>=0.2.0,<1.0.0 -ovos-skill-youtube-music>=0.1.1,<1.0.0 +ovos-skill-local-media>=0.2.1,<1.0.0 \ No newline at end of file diff --git a/test/integrationtests/decorator_test_skill.py b/test/integrationtests/decorator_test_skill.py deleted file mode 100644 index 6eb8dd7c4bd..00000000000 --- a/test/integrationtests/decorator_test_skill.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2017 Mycroft AI Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# TODO - move test to ovos-workshop -from ovos_workshop.intents import IntentBuilder - -from ovos_workshop.skills.ovos import OVOSSkill -from ovos_workshop.decorators import intent_handler, intent_file_handler - - -class TestSkill(OVOSSkill): - """ Test skill for intent_handler decorator. """ - @intent_handler(IntentBuilder('a').require('Keyword').build()) - def handler(self, message): - pass - - @intent_file_handler('test.intent') - def handler2(self, message): - pass - - def stop(self): - pass diff --git a/test/integrationtests/test_common_query_skill.py b/test/integrationtests/test_common_query_skill.py deleted file mode 100644 index 0d70ff50eae..00000000000 --- a/test/integrationtests/test_common_query_skill.py +++ /dev/null @@ -1,62 +0,0 @@ -from unittest import TestCase, mock - -from ovos_bus_client.message import Message - -from ovos_workshop.skills.common_query_skill import CommonQuerySkill - -# TODO - move test to ovos-workshop - -class AnyCallable: - """Class matching any callable. - - Useful for assert_called_with arguments. - """ - def __eq__(self, other): - return callable(other) - - - -class TestCommonQuerySkill(TestCase): - def setUp(self): - self.skill = CQSTest() - self.bus = mock.Mock(name='bus') - self.skill.bind(self.bus) - self.skill.config_core = {'enclosure': {'platform': 'mycroft_mark_1'}} - - def test_lifecycle(self): - """Test startup and shutdown.""" - skill = CQSTest() - bus = mock.Mock(name='bus') - skill.bind(bus) - bus.on.assert_any_call('question:query', AnyCallable()) - bus.on.assert_any_call('question:action', AnyCallable()) - skill.shutdown() - - def test_common_test_skill_action(self): - """Test that the optional action is triggered.""" - query_action = self.bus.on.call_args_list[-2][0][1] - query_action(Message('query:action', data={ - 'phrase': 'What\'s the meaning of life', - 'skill_id': 'asdf'})) - self.skill.CQS_action.assert_not_called() - query_action(Message('query:action', data={ - 'phrase': 'What\'s the meaning of life', - 'skill_id': 'CQSTest'})) - self.skill.CQS_action.assert_called_once_with( - 'What\'s the meaning of life', {}) - - -class CQSTest(CommonQuerySkill): - """Simple skill for testing the CommonQuerySkill""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.CQS_match_query_phrase = mock.Mock(name='match_phrase') - self.CQS_action = mock.Mock(name='selected_action') - self.skill_id = 'CQSTest' - - def CQS_match_query_phrase(self, phrase): - pass - - def CQS_action(self, phrase, data): - pass diff --git a/test/integrationtests/test_context.py b/test/integrationtests/test_context.py deleted file mode 100644 index 549fce6b840..00000000000 --- a/test/integrationtests/test_context.py +++ /dev/null @@ -1,40 +0,0 @@ -from unittest import TestCase, mock - -# TODO - move to ovos-workshop -from ovos_workshop.decorators import adds_context, removes_context - - -class ContextSkillMock(mock.Mock): - """Mock class to apply decorators on.""" - @adds_context('DestroyContext') - def handler_adding_context(self): - pass - - @adds_context('DestroyContext', 'exterminate') - def handler_adding_context_with_words(self): - pass - - @removes_context('DestroyContext') - def handler_removing_context(self): - pass - - -class TestContextDecorators(TestCase): - def test_adding_context(self): - """Check that calling handler adds the correct Keyword.""" - skill = ContextSkillMock() - skill.handler_adding_context() - skill.set_context.assert_called_once_with('DestroyContext', '') - - def test_adding_context_with_words(self): - """Ensure that decorated handler adds Keyword and content.""" - skill = ContextSkillMock() - skill.handler_adding_context_with_words() - skill.set_context.assert_called_once_with('DestroyContext', - 'exterminate') - - def test_removing_context(self): - """Make sure the decorated handler removes the specified context.""" - skill = ContextSkillMock() - skill.handler_removing_context() - skill.remove_context.assert_called_once_with('DestroyContext') diff --git a/test/integrationtests/test_fallback_skill.py b/test/integrationtests/test_fallback_skill.py deleted file mode 100644 index b86974d23b1..00000000000 --- a/test/integrationtests/test_fallback_skill.py +++ /dev/null @@ -1,54 +0,0 @@ -from unittest import TestCase, mock - -from ovos_workshop.skills.fallback import FallbackSkillV1 as FallbackSkill - - -def setup_fallback(fb_class): - fb_skill = fb_class() - fb_skill.bind(mock.Mock(name='bus')) - fb_skill.initialize() - return fb_skill - - -# TODO - move to ovos-workshop -class TestFallbackSkill(TestCase): - def test_life_cycle(self): - """Test startup and shutdown of a fallback skill. - - Ensure that an added handler is removed as part of default shutdown. - """ - self.assertEqual(len(FallbackSkill.fallback_handlers), 0) - fb_skill = setup_fallback(SimpleFallback) - self.assertEqual(len(FallbackSkill.fallback_handlers), 1) - self.assertEqual(FallbackSkill.wrapper_map[0][0], - fb_skill.fallback_handler) - self.assertEqual(len(FallbackSkill.wrapper_map), 1) - - fb_skill.default_shutdown() - self.assertEqual(len(FallbackSkill.fallback_handlers), 0) - self.assertEqual(len(FallbackSkill.wrapper_map), 0) - - def test_manual_removal(self): - """Test that the call to remove_fallback() removes the handler""" - self.assertEqual(len(FallbackSkill.fallback_handlers), 0) - - # Create skill adding a single handler - fb_skill = setup_fallback(SimpleFallback) - self.assertEqual(len(FallbackSkill.fallback_handlers), 1) - - self.assertTrue(fb_skill.remove_fallback(fb_skill.fallback_handler)) - # Both internal trackers of handlers should be cleared now - self.assertEqual(len(FallbackSkill.fallback_handlers), 0) - self.assertEqual(len(FallbackSkill.wrapper_map), 0) - - # Removing after it's already been removed should fail - self.assertFalse(fb_skill.remove_fallback(fb_skill.fallback_handler)) - - -class SimpleFallback(FallbackSkill): - """Simple fallback skill used for test.""" - def initialize(self): - self.register_fallback(self.fallback_handler, 42) - - def fallback_handler(self): - pass diff --git a/test/integrationtests/test_intent_service_interface.py b/test/integrationtests/test_intent_service_interface.py deleted file mode 100644 index 61fea26ed5d..00000000000 --- a/test/integrationtests/test_intent_service_interface.py +++ /dev/null @@ -1,145 +0,0 @@ -import unittest -# TODO - move test to ovos-workshop -from ovos_workshop.intents import IntentBuilder, IntentServiceInterface - - -class MockEmitter: - def __init__(self): - self.reset() - - def emit(self, message): - self.types.append(message.msg_type) - self.results.append(message.data) - - def get_types(self): - return self.types - - def get_results(self): - return self.results - - def on(self, event, f): - pass - - def reset(self): - self.types = [] - self.results = [] - - -class KeywordRegistrationTest(unittest.TestCase): - def check_emitter(self, expected_message_data): - """Verify that the registration messages matches the expected.""" - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'register_vocab') - self.assertEqual( - sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(expected_message_data, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def setUp(self): - self.emitter = MockEmitter() - - def test_register_keyword(self): - intent_service = IntentServiceInterface(self.emitter) - intent_service.register_adapt_keyword('test_intent', 'test', lang='en-US') - entity_data = {'entity_value': 'test', 'entity_type': 'test_intent', 'lang': 'en-US'} - compatibility_data = {'start': 'test', 'end': 'test_intent'} - expected_data = {**entity_data, **compatibility_data} - self.check_emitter([expected_data]) - - def test_register_keyword_with_aliases(self): - # TODO 22.02: Remove compatibility data - intent_service = IntentServiceInterface(self.emitter) - intent_service.register_adapt_keyword('test_intent', 'test', - ['test2', 'test3'], - lang='en-US') - - entity_data = {'entity_value': 'test', 'entity_type': 'test_intent', 'lang': 'en-US'} - compatibility_data = {'start': 'test', 'end': 'test_intent'} - expected_initial_vocab = {**entity_data, **compatibility_data} - - alias_data = { - 'entity_value': 'test2', - 'entity_type': 'test_intent', - 'alias_of': 'test', - 'lang': 'en-US' - } - alias_compatibility = {'start': 'test2', 'end': 'test_intent'} - expected_alias1 = {**alias_data, **alias_compatibility} - - alias_data2 = { - 'entity_value': 'test3', - 'entity_type': 'test_intent', - 'alias_of': 'test', - 'lang': 'en-US' - } - alias_compatibility2 = {'start': 'test3', 'end': 'test_intent'} - expected_alias2 = {**alias_data2, **alias_compatibility2} - - self.check_emitter([expected_initial_vocab, - expected_alias1, - expected_alias2]) - - def test_register_regex(self): - intent_service = IntentServiceInterface(self.emitter) - intent_service.register_adapt_regex('.*', lang="en-us") - self.check_emitter([{'regex': '.*', 'lang': 'en-us'}]) - - -class KeywordIntentRegistrationTest(unittest.TestCase): - def check_emitter(self, expected_message_data): - """Verify that the registration messages matches the expected.""" - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'register_intent') - self.assertEqual( - sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(expected_message_data, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def setUp(self): - self.emitter = MockEmitter() - - def test_register_intent(self): - intent_service = IntentServiceInterface(self.emitter) - intent_service.register_adapt_keyword('testA', 'testA', lang='en-US') - intent_service.register_adapt_keyword('testB', 'testB', lang='en-US') - self.emitter.reset() - - intent = IntentBuilder("test").require("testA").optionally("testB") - intent_service.register_adapt_intent("test", intent) - expected_data = {'at_least_one': [], - 'name': 'test', - 'excludes': [], - 'optional': [('testB', 'testB')], - 'requires': [('testA', 'testA')]} - self.check_emitter([expected_data]) - - - -class UtteranceIntentRegistrationTest(unittest.TestCase): - def check_emitter(self, expected_message_data): - """Verify that the registration messages matches the expected.""" - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'padatious:register_intent') - - self.assertEqual( - sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(expected_message_data, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def setUp(self): - self.emitter = MockEmitter() - - def test_register_intent(self): - intent_service = IntentServiceInterface(self.emitter) - filename = "/tmp/test.intent" - with open(filename, "w") as f: - f.write("this is a test\ntest the intent") - - intent_service.register_padatious_intent('test', filename, lang='en-US') - expected_data = {'file_name': '/tmp/test.intent', 'lang': 'en-US', 'name': 'test', - 'samples': ['this is a test', 'test the intent']} - self.check_emitter([expected_data]) - diff --git a/test/integrationtests/test_mycroft_skill.py b/test/integrationtests/test_mycroft_skill.py deleted file mode 100644 index 09059179b94..00000000000 --- a/test/integrationtests/test_mycroft_skill.py +++ /dev/null @@ -1,641 +0,0 @@ -# -# Copyright 2017 Mycroft AI Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import json -import sys -import unittest -from datetime import datetime -from os.path import join, dirname, abspath -from unittest.mock import MagicMock, patch - -from ovos_bus_client.message import Message -from ovos_config import Configuration -from ovos_workshop.decorators import resting_screen_handler, intent_handler -from ovos_workshop.intents import IntentBuilder, open_intent_envelope -from ovos_workshop.skills.mycroft_skill import MycroftSkill -from ovos_workshop.skills.ovos import OVOSSkill -from copy import deepcopy -from ovos_config import LocalConf, DEFAULT_CONFIG - -# TODO - move test to ovos-workshop - -BASE_CONF = deepcopy(LocalConf(DEFAULT_CONFIG)) - - -class MockEmitter(object): - def __init__(self): - self.types = [] - self.results = [] - self.reset() - - def emit(self, message): - self.types.append(message.msg_type) - self.results.append(message.data) - - def get_types(self): - return self.types - - def get_results(self): - return self.results - - def on(self, event, f): - pass - - def reset(self): - self.types = [] - self.results = [] - - -def vocab_base_path(): - return join(dirname(__file__), '..', 'vocab_test') - - -class TestFunction(unittest.TestCase): - def test_resting_screen_handler(self): - class T(OVOSSkill): - @resting_screen_handler('humbug') - def f(self): - pass - - test_class = T() - self.assertTrue('resting_handler' in dir(test_class.f)) - self.assertEqual(test_class.f.resting_handler, 'humbug') - - -class TestOVOSSkill(unittest.TestCase): - emitter = MockEmitter() - regex_path = abspath(join(dirname(__file__), '../regex_test')) - vocab_path = abspath(join(dirname(__file__), '../vocab_test')) - - def setUp(self): - self.emitter.reset() - - def compare_dicts(self, d1, d2): - self.assertEqual(json.dumps(d1, sort_keys=True), - json.dumps(d2, sort_keys=True)) - - def check_emitter(self, result_list): - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'register_vocab') - self.assertEqual(sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(result_list, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def test_open_envelope(self): - name = 'Jerome' - intent = IntentBuilder(name).require('Keyword') - intent.name = name - m = Message("register_intent", intent.__dict__) - unpacked_intent = open_intent_envelope(m) - self.assertEqual(intent.__dict__, unpacked_intent.__dict__) - - def check_detach_intent(self): - self.assertTrue(len(self.emitter.get_types()) > 0) - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'detach_intent') - self.emitter.reset() - - def check_register_intent(self, result_list): - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'register_intent') - self.assertEqual(sorted(self.emitter.get_results()), - sorted(result_list)) - self.emitter.reset() - - def check_register_vocabulary(self, result_list): - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'register_vocab') - self.assertEqual(sorted(self.emitter.get_results()), - sorted(result_list)) - self.emitter.reset() - - def test_register_intent(self): - # Test register Intent object - s = SimpleSkill1(bus=self.emitter, skill_id="A") - expected = [{'at_least_one': [], - 'name': 'A:a', - 'excludes': [], - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - msg_data = self.emitter.get_results() - self.assertTrue(expected[0] in msg_data) - self.emitter.reset() - - # Test register IntentBuilder object - s = SimpleSkill2(bus=self.emitter, skill_id="A") - expected = [{'at_least_one': [], - 'name': 'A:a', - 'excludes': [], - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - - msg_data = self.emitter.get_results() - self.assertTrue(expected[0] in msg_data) - self.emitter.reset() - - # Test register IntentBuilder object - with self.assertRaises(ValueError): - s = SimpleSkill3(bus=self.emitter, skill_id="A") - - def test_enable_disable_intent(self): - """Test disable/enable intent.""" - # Setup basic test - s = SimpleSkill1(bus=self.emitter, skill_id="A") - - # check that intent was registered - expected = [{'at_least_one': [], - 'name': 'A:a', - 'excludes': [], - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - msg_data = self.emitter.get_results() - self.assertTrue(expected[0] in msg_data) - self.emitter.reset() - - # Test disable/enable cycle - s.disable_intent('a') - self.check_detach_intent() - s.enable_intent('a') - self.check_register_intent(expected) - - def test_enable_disable_intent_handlers(self): - """Test disable/enable intent.""" - # Setup basic test - s = SimpleSkill1(bus=self.emitter, skill_id="A") - expected = [{'at_least_one': [], - 'name': 'A:a', - 'excludes': [], - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - msg_data = self.emitter.get_results() - self.assertTrue(expected[0] in msg_data) - self.emitter.reset() - - # Test disable/enable cycle - msg = Message('test.msg', data={'intent_name': 'a'}) - intent_disabled = s.handle_disable_intent(msg) - self.assertTrue(intent_disabled) - self.check_detach_intent() - intent_enabled = s.handle_enable_intent(msg) - self.assertTrue(intent_enabled) - self.check_register_intent(expected) - - def test_register_vocab(self): - """Test disable/enable intent.""" - # Setup basic test - s = SimpleSkill1(bus=self.emitter, skill_id="A") - - # Normal vocaubulary - self.emitter.reset() - expected = [ - { - 'start': 'hello', - 'end': 'AHelloKeyword', - 'entity_value': 'hello', - 'lang': 'en-us', - 'entity_type': 'AHelloKeyword' - } - ] - s.register_vocabulary('hello', 'HelloKeyword') - self.check_register_vocabulary(expected) - # Regex - s.register_regex('weird (?P.+) stuff') - expected = [{'lang': 'en-us', 'regex': 'weird (?P.+) stuff'}] - self.check_register_vocabulary(expected) - - def check_register_object_file(self, types_list, result_list): - self.assertEqual(sorted(self.emitter.get_types()), - sorted(types_list)) - self.assertEqual(sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(result_list, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def test_register_intent_file(self): - self._test_intent_file(SimpleSkill4()) - - def test_register_intent_intent_file(self): - """Test register intent files using register_intent.""" - self._test_intent_file(SimpleSkill6()) - - def _test_intent_file(self, s): - s.res_dir = abspath(join(dirname(__file__), 'intent_file')) - s._startup(bus=self.emitter, skill_id="A") - - expected_types = [ - 'padatious:register_intent', - 'padatious:register_entity' - ] - - expected_results = [ - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test.intent'), - 'lang': 'en-us', - 'name': str(s.skill_id) + ':test.intent', - 'samples': [] - }, - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test_ent.entity'), - 'lang': 'en-us', - 'name': str(s.skill_id) + ':test_ent_87af9db6c8402bcfaa8ebc719ae4427c', - 'samples': [] - } - ] - self.check_register_object_file(expected_types, expected_results) - - def check_register_decorators(self, result_list): - self.assertEqual(sorted(self.emitter.get_results(), - key=lambda d: sorted(d.items())), - sorted(result_list, key=lambda d: sorted(d.items()))) - self.emitter.reset() - - def test_register_decorators(self): - """ Test decorated intents """ - path_orig = sys.path - sys.path.append(abspath(dirname(__file__))) - SimpleSkill5 = __import__('decorator_test_skill').TestSkill - s = SimpleSkill5() - s.res_dir = abspath(join(dirname(__file__), 'intent_file')) - s._startup(bus=self.emitter, skill_id="A") - - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'excludes': [], - 'requires': [('AKeyword', 'AKeyword')]}, - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test.intent'), - 'lang': 'en-us', - 'samples': [], - 'name': str(s.skill_id) + ':test.intent'} - ] - - self.check_register_decorators(expected) - # Restore sys.path - sys.path = path_orig - - def test_failing_set_context(self): - s = SimpleSkill1(bus=self.emitter, skill_id="A") - with self.assertRaises(ValueError): - s.set_context(1) - with self.assertRaises(ValueError): - s.set_context(1, 1) - with self.assertRaises(ValueError): - s.set_context('Kowabunga', 1) - - def test_set_context(self): - def check_set_context(result_list): - for msg_type in self.emitter.get_types(): - self.assertEqual(msg_type, 'add_context') - self.assertEqual(sorted(self.emitter.get_results()), - sorted(result_list)) - self.emitter.reset() - - s = SimpleSkill1(bus=self.emitter, skill_id="A") - self.emitter.reset() - # No context content - s.set_context('TurtlePower') - expected = [{'context': 'ATurtlePower', 'origin': '', 'word': ''}] - check_set_context(expected) - - # context with content - s.set_context('Technodrome', 'Shredder') - expected = [{'context': 'ATechnodrome', 'origin': '', - 'word': 'Shredder'}] - check_set_context(expected) - - # UTF-8 context - s.set_context('Smörgåsbord€15') - expected = [{'context': 'ASmörgåsbord€15', 'origin': '', 'word': ''}] - check_set_context(expected) - - self.emitter.reset() - - def test_failing_remove_context(self): - s = SimpleSkill1(bus=self.emitter, skill_id="A") - with self.assertRaises(ValueError): - s.remove_context(1) - - def test_remove_context(self): - def check_remove_context(result_list): - for type in self.emitter.get_types(): - self.assertEqual(type, 'remove_context') - self.assertEqual(sorted(self.emitter.get_results()), - sorted(result_list)) - self.emitter.reset() - - s = SimpleSkill1(bus=self.emitter, skill_id="A") - self.emitter.reset() - s.remove_context('Donatello') - expected = [{'context': 'ADonatello'}] - check_remove_context(expected) - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_skill_location(self): - s = SimpleSkill1() - self.assertEqual(s.location, BASE_CONF.get('location')) - self.assertEqual(s.location_pretty, - BASE_CONF['location']['city']['name']) - self.assertEqual(s.location_timezone, - BASE_CONF['location']['timezone']['code']) - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_add_event(self): - emitter = MagicMock() - s = SimpleSkill1(bus=emitter, skill_id="A") - s.add_event('handler1', s.handler) - # Check that the handler was registered with the emitter - self.assertEqual(emitter.on.call_args[0][0], 'handler1') - # Check that the handler was stored in the skill - self.assertTrue('handler1' in [e[0] for e in s.events]) - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_remove_event(self): - emitter = MagicMock() - s = SimpleSkill1(bus=emitter, skill_id="A") - s.add_event('handler1', s.handler) - self.assertTrue('handler1' in [e[0] for e in s.events]) - # Remove event handler - s.remove_event('handler1') - # make sure it's not in the event list anymore - self.assertTrue('handler1' not in [e[0] for e in s.events]) - # Check that the handler was registered with the emitter - self.assertEqual(emitter.remove_all_listeners.call_args[0][0], - 'handler1') - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_add_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1(bus=emitter, skill_id="A") - - s.schedule_event(s.handler, datetime.now(), name='datetime_handler') - # Check that the handler was registered with the emitter - self.assertEqual(emitter.once.call_args[0][0], 'A:datetime_handler') - sched_events = [e[0] for e in s.event_scheduler.events] - self.assertTrue('A:datetime_handler' in sched_events) - - s.schedule_event(s.handler, 1, name='int_handler') - # Check that the handler was registered with the emitter - self.assertEqual(emitter.once.call_args[0][0], 'A:int_handler') - sched_events = [e[0] for e in s.event_scheduler.events] - self.assertTrue('A:int_handler' in sched_events) - - s.schedule_event(s.handler, .5, name='float_handler') - # Check that the handler was registered with the emitter - self.assertEqual(emitter.once.call_args[0][0], 'A:float_handler') - sched_events = [e[0] for e in s.event_scheduler.events] - self.assertTrue('A:float_handler' in sched_events) - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_remove_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1(bus=emitter, skill_id="A") - s.schedule_event(s.handler, datetime.now(), name='sched_handler1') - # Check that the handler was registered with the emitter - events = [e[0] for e in s.event_scheduler.events] - print(events) - self.assertTrue('A:sched_handler1' in events) - s.cancel_scheduled_event('sched_handler1') - # Check that the handler was removed - self.assertEqual(emitter.remove_all_listeners.call_args[0][0], - 'A:sched_handler1') - events = [e[0] for e in s.event_scheduler.events] - self.assertTrue('A:sched_handler1' not in events) - - @patch.dict(Configuration._Configuration__patch, BASE_CONF) - def test_run_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1(bus=emitter, skill_id="A") - with patch.object(s, '_settings', - create=True, value=MagicMock()): - s.schedule_event(s.handler, datetime.now(), name='sched_handler1') - # Check that the handler was registered with the emitter - emitter.once.call_args[0][1](Message('message')) - # Check that the handler was run - self.assertTrue(s.handler_run) - # Check that the handler was removed from the list of registred - # handler - self.assertTrue('A:sched_handler1' not in [e[0] for e in s.events]) - - def test_voc_match(self): - s = SimpleSkill1() - s.root_dir = abspath(dirname(__file__)) - - self.assertTrue(s.voc_match("turn off the lights", "turn_off_test")) - self.assertTrue(s.voc_match("would you please turn off the lights", - "turn_off_test")) - self.assertFalse(s.voc_match("return office", "turn_off_test")) - self.assertTrue(s.voc_match("switch off the lights", "turn_off_test")) - self.assertFalse(s.voc_match("", "turn_off_test")) - self.assertFalse(s.voc_match("switch", "turn_off_test")) - self.assertFalse(s.voc_match("My hovercraft is full of eels", - "turn_off_test")) - - self.assertTrue(s.voc_match("turn off the lights", "turn_off2_test")) - self.assertFalse(s.voc_match("return office", "turn_off2_test")) - self.assertTrue(s.voc_match("switch off the lights", "turn_off2_test")) - self.assertFalse(s.voc_match("", "turn_off_test")) - self.assertFalse(s.voc_match("switch", "turn_off_test")) - self.assertFalse(s.voc_match("My hovercraft is full of eels", - "turn_off_test")) - - def test_voc_match_exact(self): - s = SimpleSkill1() - s.root_dir = abspath(dirname(__file__)) - - self.assertTrue(s.voc_match("yes", "yes", exact=True)) - self.assertFalse(s.voc_match("yes please", "yes", exact=True)) - self.assertTrue(s.voc_match("switch off", "turn_off_test", - exact=True)) - self.assertFalse(s.voc_match("would you please turn off the lights", - "turn_off_test", exact=True)) - - def test_translate_locations(self): - """Assert that the a translatable list can be loaded from dialog and - locale. - """ - # Check that translatables can be loaded from the dialog directory - s = SimpleSkill1() - s.res_dir = abspath(join(dirname(__file__), - 'translate', 'in-dialog/')) - self.assertEqual(s.lang, "en-us") - lst = s.translate_list('good_things') - self.assertIsInstance(lst, list) - vals = s.translate_namedvalues('named_things') - self.assertIsInstance(vals, dict) - template = s.translate_template('test', - data={'thing': 'test framework'}) - self.assertEqual(template, - ['Oh look it\'s my favourite test framework']) - # Check that translatables can be loaded from locale folder - s = SimpleSkill1() - s.res_dir = abspath(join(dirname(__file__), - 'translate', 'in-locale')) - lst = s.translate_list('good_things') - self.assertIsInstance(lst, list) - vals = s.translate_namedvalues('named_things') - self.assertIsInstance(vals, dict) - template = s.translate_template('test', - data={'thing': 'test framework'}) - self.assertEqual(template, - ['Oh look it\'s my favourite test framework']) - - # Check loading in a non-en-us language - s = SimpleSkill1() - s.config_core['lang'] = 'de-de' - s.res_dir = abspath(join(dirname(__file__), - 'translate', 'in-locale')) - lst = s.translate_list('good_things') - self.assertEqual(lst, ['sonne', 'mycroft', 'zahne']) - vals = s.translate_namedvalues('named_things') - self.assertEqual(vals['blau'], '2') - template = s.translate_template('test', - data={'thing': 'test framework'}) - self.assertEqual(template, - ['Aber setzen sie sich herr test framework']) - - # Restore lang to en-us - s.config_core['lang'] = 'en-us' - - def test_native_langs(self): - s = _TestSkill() - lang = s.config_core['lang'] - secondary = s.config_core['secondary_langs'] - - s.config_core['lang'] = 'en-US' - s.config_core['secondary_langs'] = ['en', 'en-us', 'en-AU', - 'es', 'pt-PT'] - self.assertEqual(s.lang, 'en-us') - self.assertEqual(s.secondary_langs, ['en', 'en-au', 'es', - 'pt-pt']) - self.assertEqual(len(s.native_langs), len(set(s.native_langs))) - self.assertEqual(set(s.native_langs), {'en-us', 'en-au', 'pt-pt'}) - s.config_core['lang'] = lang - s.config_core['secondary_langs'] = secondary - - -class TestIntentCollisions(unittest.TestCase): - def test_two_intents_with_same_name(self): - emitter = MockEmitter() - skill = SameIntentNameSkill() - skill.bind(emitter) - with self.assertRaises(ValueError): - skill.initialize() - - def test_two_anonymous_intent_decorators(self): - """Two anonymous intent handlers should be ok.""" - emitter = MockEmitter() - skill = SameAnonymousIntentDecoratorsSkill() - skill.bind(emitter) - skill._register_decorated() - self.assertEqual(len(skill.intent_service.registered_intents), 2) - - -class _TestSkill(OVOSSkill): - pass - - -class SimpleSkill1(MycroftSkill): - """ Test skill for normal intent builder syntax """ - - def initialize(self): - self.handler_run = False - i = IntentBuilder('a').require('Keyword').build() - self.register_intent(i, self.handler) - - def handler(self, message): - self.handler_run = True - - def stop(self): - pass - - -class SimpleSkill2(_TestSkill): - """ Test skill for intent builder without .build() """ - - def initialize(self): - i = IntentBuilder('a').require('Keyword') - self.register_intent(i, self.handler) - - def handler(self, message): - pass - - def stop(self): - pass - - -class SimpleSkill3(_TestSkill): - """ Test skill for invalid Intent for register_intent """ - - def initialize(self): - self.register_intent('string', self.handler) - - def handler(self, message): - pass - - def stop(self): - pass - - -class SimpleSkill4(_TestSkill): - """ Test skill for padatious intent """ - - def initialize(self): - self.register_intent_file('test.intent', self.handler) - self.register_entity_file('test_ent.entity') - - def handler(self, message): - pass - - def stop(self): - pass - - -class SimpleSkill6(_TestSkill): - """ Test skill for padatious intent """ - - def initialize(self): - self.register_intent('test.intent', self.handler) - self.register_entity_file('test_ent.entity') - - def handler(self, message): - pass - - -class SameIntentNameSkill(_TestSkill): - """Test skill for duplicate intent namesr.""" - - def initialize(self): - intent = IntentBuilder('TheName').require('Keyword') - intent2 = IntentBuilder('TheName').require('Keyword') - self.register_intent(intent, self.handler) - self.register_intent(intent2, self.handler) - - def handler(self, message): - pass - - -class SameAnonymousIntentDecoratorsSkill(_TestSkill): - """Test skill for duplicate anonymous intent handlers.""" - - @intent_handler(IntentBuilder('').require('Keyword')) - @intent_handler(IntentBuilder('').require('OtherKeyword')) - def handler(self, message): - pass diff --git a/test/integrationtests/test_skill_api.py b/test/integrationtests/test_skill_api.py deleted file mode 100644 index fba8878ae11..00000000000 --- a/test/integrationtests/test_skill_api.py +++ /dev/null @@ -1,153 +0,0 @@ -from unittest import TestCase, mock - -# TODO - move test to ovos-workshop -from ovos_workshop.skills.ovos import OVOSSkill -from ovos_bus_client.message import Message -from ovos_workshop.decorators import skill_api_method -from ovos_workshop.skills.api import SkillApi - - -class Skill(OVOSSkill): - """Test skill with registered API methods.""" - def __init__(self, *args, **kwargs): - self.registered_methods = {} - super().__init__(*args, **kwargs) - - def add_event(self, event_type, func, **kwargs): - """Mock handler of add_event, simply storing type and method. - - Used in testing to verify the wrapped methods - """ - self.registered_methods[event_type] = func - - @skill_api_method - def test_method(self): - """Documentation.""" - return True - - @skill_api_method - def test_method2(self, arg): - """Documentation.""" - return 'TestResult' - - -def load_test_skill(): - """Helper for setting up the test skill. - - Returns: - (MycroftSkill): created test skill - """ - bus = mock.Mock() - test_skill = Skill(skill_id = 'test_skill', bus=bus) - return test_skill - - -def create_skill_api_from_skill(skill): - """Helper creating an api from a skill. - - Args: - skill (MycroftSkill): Skill to create api from. - - Returns: - (SkillApi): API for the skill. - """ - SkillApi.connect_bus(skill.bus) - return SkillApi(skill.public_api) - - -class testSkillMethod(TestCase): - """Tests for the MycroftSkill class API setup.""" - def test_public_api_event(self): - """Test that public api event handler is created.""" - test_skill = load_test_skill() - self.assertTrue( - 'test_skill.public_api' in test_skill.registered_methods - ) - - def test_public_api(self): - """Test that the public_api structure matches the decorators.""" - test_skill = load_test_skill() - # Check that methods has been added - self.assertTrue('test_method' in test_skill.public_api) - self.assertTrue('test_method2' in test_skill.public_api) - # Test docstring - self.assertEqual(test_skill.public_api['test_method']['help'], - 'Documentation.') - # Test type - self.assertEqual(test_skill.public_api['test_method']['type'], - '{}.{}'.format(test_skill.skill_id, 'test_method')) - - def test_public_api_method(self): - """Verify message from wrapped api method.""" - test_skill = load_test_skill() - api_method = test_skill.registered_methods['test_skill.test_method'] - - # Call method - call_msg = Message('test_skill.test_method', - data={'args': [], 'kwargs': {}}) - api_method(call_msg) - # Check response sent on the bus is the same as the method's return - # value - response = test_skill.bus.emit.call_args[0][0] - self.assertEqual(response.data['result'], test_skill.test_method()) - - def test_public_api_request(self): - """Test public api request handling. - - Ensures that a request for the skill's available public api returns - expected content. - """ - test_skill = load_test_skill() - sent_message = None - - def capture_sent_message(message): - """Capture sent message.""" - nonlocal sent_message - sent_message = message - - test_skill.bus.emit.side_effect = capture_sent_message - get_api_method = test_skill.registered_methods['test_skill.public_api'] - request_api_msg = Message('test_skill.public_api') - - # Ensure that the sent public api contains the correct items - get_api_method(request_api_msg) - public_api = sent_message.data - self.assertTrue('test_method' in public_api) - self.assertTrue('test_method2' in public_api) - self.assertEqual(len(public_api), 2) - - -class TestApiObject(TestCase): - """Tests for the generated SkillApi objects.""" - def test_create_api_object(self): - """Check that expected methods are available.""" - test_skill = load_test_skill() - test_api = create_skill_api_from_skill(test_skill) - - hasattr(test_api, 'test_method') - hasattr(test_api, 'test_method2') - - def test_call_api_method(self): - """Ensure that calling the methods works as expected.""" - test_skill = load_test_skill() - test_api = create_skill_api_from_skill(test_skill) - - expected_response = 'all is good' - sent_message = None - - def capture_sent_message(message, timeout=3): - """Capture sent message and return expected response message.""" - nonlocal sent_message - sent_message = message - return Message('', data={'result': expected_response}) - - test_api.bus.wait_for_response.side_effect = capture_sent_message - - response = test_api.test_method('hello', person='you') - - # Verify response - self.assertEqual(response, expected_response) - # Verify sent message - self.assertEqual(sent_message.msg_type, 'test_skill.test_method') - self.assertEqual(sent_message.data['args'], ('hello',)) - self.assertEqual(sent_message.data['kwargs'], {'person': 'you'}) diff --git a/test/integrationtests/test_skill_loader.py b/test/integrationtests/test_skill_loader.py deleted file mode 100644 index 7f81420f268..00000000000 --- a/test/integrationtests/test_skill_loader.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright 2019 Mycroft AI Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -"""Unit tests for the SkillLoader class.""" -import json -import unittest -from pathlib import Path -from unittest.mock import Mock - -from ovos_utils import classproperty -from ovos_utils.messagebus import FakeBus -from ovos_utils.process_utils import RuntimeRequirements -from ovos_workshop.skill_launcher import SkillLoader -# TODO - move test to ovos-workshop -from ovos_workshop.skills.mycroft_skill import MycroftSkill - -ONE_MINUTE = 60 - - -class OfflineSkill(MycroftSkill): - @classproperty - def runtime_requirements(self): - return RuntimeRequirements(internet_before_load=False, - network_before_load=False, - requires_internet=False, - requires_network=False, - no_internet_fallback=True, - no_network_fallback=True) - - -class LANSkill(MycroftSkill): - @classproperty - def runtime_requirements(self): - scans_on_init = True - return RuntimeRequirements(internet_before_load=False, - network_before_load=scans_on_init, - requires_internet=False, - requires_network=True, - no_internet_fallback=True, - no_network_fallback=False) - - -class TestSkillNetwork(unittest.TestCase): - - def test_class_property(self): - self.assertEqual(OfflineSkill.runtime_requirements, - RuntimeRequirements(internet_before_load=False, - network_before_load=False, - requires_internet=False, - requires_network=False, - no_internet_fallback=True, - no_network_fallback=True) - ) - self.assertEqual(LANSkill.runtime_requirements, - RuntimeRequirements(internet_before_load=False, - network_before_load=True, - requires_internet=False, - requires_network=True, - no_internet_fallback=True, - no_network_fallback=False) - ) - self.assertEqual(MycroftSkill.runtime_requirements, - RuntimeRequirements() - ) - - -msgs = [] -bus = FakeBus() -bus.msgs = [] - - -def _handle(msg): - global bus - bus.msgs.append(json.loads(msg)) - - -bus.on("message", _handle) - - -class TestSkillLoader(unittest.TestCase): - skill_directory = Path('/tmp/test_skill') - skill_directory.mkdir(exist_ok=True) - for file_name in ('__init__.py', 'bar.py', '.foobar', 'bar.pyc'): - skill_directory.joinpath(file_name).touch() - - def test_skill_reload(self): - """Test reloading a skill that was modified.""" - bus.msgs = [] - loader = SkillLoader(bus, str(self.skill_directory)) - loader.instance = Mock() - loader.loaded = True - loader.load_attempted = False - loader.last_loaded = 10 - loader.instance.reload_skill = True - loader.instance.name = "MySkill" - loader.skill_id = 'test_skill' - - # Mock to return a known (Mock) skill instance - real_create_skill_instance = loader._create_skill_instance - - def _update_skill_instance(*args, **kwargs): - loader.instance = Mock() - loader.loaded = True - loader.load_attempted = True - loader.last_loaded = 100 - loader.skill_id = 'test_skill' - loader.instance.name = "MySkill" - return True - - loader._create_skill_instance = _update_skill_instance - - loader.reload() - - self.assertTrue(loader.load_attempted) - self.assertTrue(loader.loaded) - - self.assertListEqual( - ['mycroft.skills.shutdown', 'mycroft.skills.loaded'], - [m["type"] for m in bus.msgs] - ) - loader._create_skill_instance = real_create_skill_instance - - def test_skill_load(self): - loader = SkillLoader(bus, str(self.skill_directory)) - bus.msgs = [] - loader.instance = None - loader.loaded = False - loader.last_loaded = 0 - - # Mock to return a known (Mock) skill instance - real_create_skill_instance = loader._create_skill_instance - - def _update_skill_instance(*args, **kwargs): - loader.instance = Mock() - loader.loaded = True - loader.last_loaded = 100 - loader.skill_id = 'test_skill' - loader.instance.name = "MySkill" - return True - - loader._create_skill_instance = _update_skill_instance - - loader.load() - - self.assertTrue(loader.load_attempted) - self.assertTrue(loader.loaded) - - self.assertListEqual( - ['mycroft.skills.loaded'], - [m["type"] for m in bus.msgs] - ) - loader._create_skill_instance = real_create_skill_instance - - def test_skill_load_blacklisted(self): - """Skill should not be loaded if it is blacklisted""" - loader = SkillLoader(bus, str(self.skill_directory)) - loader.instance = Mock() - loader.loaded = False - loader.last_loaded = 0 - loader.skill_id = 'test_skill' - loader.name = "MySkill" - bus.msgs = [] - - config = dict(loader.config) - config['skills']['blacklisted_skills'] = ['test_skill'] - loader.config = config - self.assertEqual(loader.config['skills']['blacklisted_skills'], - ['test_skill']) - loader.skill_id = 'test_skill' - - loader.load() - - self.assertTrue(loader.load_attempted) - self.assertFalse(loader.loaded) - - self.assertListEqual( - ['mycroft.skills.loading_failure'], - [m["type"] for m in bus.msgs] - ) - - loader.config['skills']['blacklisted_skills'].remove('test_skill') From e910701ae01ff5bcca54e0b909a4532cb30f4d80 Mon Sep 17 00:00:00 2001 From: JarbasAl Date: Tue, 15 Oct 2024 17:16:56 +0000 Subject: [PATCH 4/5] Increment Version to 0.2.1a1 --- ovos_core/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ovos_core/version.py b/ovos_core/version.py index 107dc6c2e70..b502d471ae9 100644 --- a/ovos_core/version.py +++ b/ovos_core/version.py @@ -1,8 +1,8 @@ # START_VERSION_BLOCK VERSION_MAJOR = 0 VERSION_MINOR = 2 -VERSION_BUILD = 0 -VERSION_ALPHA = 0 +VERSION_BUILD = 1 +VERSION_ALPHA = 1 # END_VERSION_BLOCK # for compat with old imports From df9b7ac1e49cc8e66a46297f1b4dbddc63144696 Mon Sep 17 00:00:00 2001 From: JarbasAl Date: Tue, 15 Oct 2024 17:17:52 +0000 Subject: [PATCH 5/5] Update Changelog --- CHANGELOG.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69e57364299..86a7c74e845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,12 @@ # Changelog -## [0.2.0a1](https://github.com/OpenVoiceOS/ovos-core/tree/0.2.0a1) (2024-10-14) +## [0.2.1a1](https://github.com/OpenVoiceOS/ovos-core/tree/0.2.1a1) (2024-10-15) -[Full Changelog](https://github.com/OpenVoiceOS/ovos-core/compare/0.1.4a1...0.2.0a1) - -**Closed issues:** - -- Standalone skill not working anymore [\#556](https://github.com/OpenVoiceOS/ovos-core/issues/556) - -**Merged pull requests:** - -- feat/pipeline\_plugins\_opm [\#527](https://github.com/OpenVoiceOS/ovos-core/pull/527) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.1.4a1](https://github.com/OpenVoiceOS/ovos-core/tree/0.1.4a1) (2024-10-14) - -[Full Changelog](https://github.com/OpenVoiceOS/ovos-core/compare/0.1.3...0.1.4a1) +[Full Changelog](https://github.com/OpenVoiceOS/ovos-core/compare/0.2.0...0.2.1a1) **Merged pull requests:** -- fix:usage without skills installed [\#555](https://github.com/OpenVoiceOS/ovos-core/pull/555) ([JarbasAl](https://github.com/JarbasAl)) +- requirements:latest stable packages [\#563](https://github.com/OpenVoiceOS/ovos-core/pull/563) ([JarbasAl](https://github.com/JarbasAl))