From ecc379cb70cbcc6ff20f3162616fbf41e178db12 Mon Sep 17 00:00:00 2001 From: jarbasai Date: Wed, 16 Feb 2022 13:53:57 +0000 Subject: [PATCH 1/4] enable unittests --- .github/workflows/unit_tests.yml | 36 + mycroft/skills/intent_service.py | 2 +- test/unittests/audio/test_speech.py | 95 --- test/unittests/client/test_audio_consumer.py | 214 ------ test/unittests/client/test_hotword_factory.py | 12 - .../unittests/client/test_local_recognizer.py | 138 ---- .../configuration/test_configuration.py | 12 - test/unittests/skills/test_intent_service.py | 360 --------- .../skills/test_intent_service_interface.py | 83 -- test/unittests/skills/test_mycroft_skill.py | 707 ------------------ .../skills/test_mycroft_skill_get_response.py | 246 ------ test/unittests/skills/test_settings.py | 313 -------- test/unittests/skills/test_skill_manager.py | 333 --------- test/unittests/stt/__init__.py | 0 test/unittests/stt/test_stt.py | 313 -------- test/unittests/tts/__init__.py | 0 test/unittests/tts/test_mimic2_tts.py | 102 --- test/unittests/tts/test_mimic_tts.py | 101 --- test/unittests/tts/test_tts.py | 268 ------- test/unittests/util/test_file_utils.py | 214 ------ test/unittests/version/__init__.py | 0 test/unittests/version/test_version.py | 70 -- 22 files changed, 37 insertions(+), 3582 deletions(-) create mode 100644 .github/workflows/unit_tests.yml delete mode 100644 test/unittests/client/test_audio_consumer.py delete mode 100644 test/unittests/client/test_local_recognizer.py delete mode 100644 test/unittests/skills/test_intent_service.py delete mode 100644 test/unittests/skills/test_intent_service_interface.py delete mode 100644 test/unittests/skills/test_mycroft_skill.py delete mode 100644 test/unittests/skills/test_mycroft_skill_get_response.py delete mode 100644 test/unittests/skills/test_settings.py delete mode 100644 test/unittests/skills/test_skill_manager.py delete mode 100644 test/unittests/stt/__init__.py delete mode 100644 test/unittests/stt/test_stt.py delete mode 100644 test/unittests/tts/__init__.py delete mode 100644 test/unittests/tts/test_mimic2_tts.py delete mode 100644 test/unittests/tts/test_mimic_tts.py delete mode 100644 test/unittests/tts/test_tts.py delete mode 100644 test/unittests/util/test_file_utils.py delete mode 100644 test/unittests/version/__init__.py delete mode 100644 test/unittests/version/test_version.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 000000000000..7a857b71e70a --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,36 @@ +name: Run License Tests +on: + pull_request: + branches: + - dev + - master + push: + workflow_dispatch: + +jobs: + license_tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install Build Tools + run: | + python -m pip install build wheel + - name: Install System Dependencies + run: | + sudo apt-get update + sudo apt install python3-dev swig libssl-dev libfann-dev portaudio19-dev libpulse-dev + - name: Install core repo + run: | + pip install .[audio-backend,mark1,stt,tts,skills,gui,bus,PHAL,all] + - name: Install test dependencies + run: | + pip install pytest pytest-timeout pytest-cov + - name: Run unittests + run: | + pytest test/unittests.py diff --git a/mycroft/skills/intent_service.py b/mycroft/skills/intent_service.py index 53f2a14e0799..ce5dc43be7f0 100644 --- a/mycroft/skills/intent_service.py +++ b/mycroft/skills/intent_service.py @@ -137,7 +137,7 @@ def __init__(self, bus): @property def registered_intents(self): return [parser.__dict__ - for parser in self.adapt_service.engine.intent_parsers] + for parser in self.adapt_service.engines["en-us"].intent_parsers] def update_skill_name_dict(self, message): """Messagebus handler, updates dict of id to skill name conversions.""" diff --git a/test/unittests/audio/test_speech.py b/test/unittests/audio/test_speech.py index 4236c46d4794..4fdf61dbaf57 100644 --- a/test/unittests/audio/test_speech.py +++ b/test/unittests/audio/test_speech.py @@ -60,101 +60,6 @@ def test_life_cycle(self, tts_factory_mock, config_mock): self.assertTrue(tts_mock.playback.stop.called) self.assertTrue(tts_mock.playback.join.called) - def test_speak(self, tts_factory_mock, config_mock): - """Ensure the speech handler executes the tts.""" - setup_mocks(config_mock, tts_factory_mock) - bus = mock.Mock() - speech.init(bus) - - speak_msg = Message('speak', - data={'utterance': 'hello there. world', - 'listen': False}, - context={'ident': 'a'}) - speech.handle_speak(speak_msg) - tts_mock.execute.assert_has_calls( - [mock.call('hello there.', 'a', False), - mock.call('world', 'a', False)]) - - @mock.patch('mycroft.audio.speech.Mimic') - def test_fallback_tts(self, mimic_cls_mock, tts_factory_mock, config_mock): - """Ensure the fallback tts is triggered if the remote times out.""" - setup_mocks(config_mock, tts_factory_mock) - mimic_mock = mock.Mock() - mimic_cls_mock.return_value = mimic_mock - - tts = tts_factory_mock.create.return_value - tts.execute.side_effect = RemoteTTSTimeoutException - - bus = mock.Mock() - speech.init(bus) - - speak_msg = Message('speak', - data={'utterance': 'hello there. world', - 'listen': False}, - context={'ident': 'a'}) - speech.handle_speak(speak_msg) - mimic_mock.execute.assert_has_calls( - [mock.call('hello there.', 'a', False), - mock.call('world', 'a', False)]) - - @mock.patch('mycroft.audio.speech.check_for_signal') - def test_abort_speak(self, check_for_signal_mock, tts_factory_mock, - config_mock): - """Ensure the speech handler aborting speech on stop signal.""" - setup_mocks(config_mock, tts_factory_mock) - check_for_signal_mock.return_value = True - tts = tts_factory_mock.create.return_value - - def execute_trigger_stop(): - speech.handle_stop(None) - - tts.execute.side_effect = execute_trigger_stop - - bus = mock.Mock() - speech.init(bus) - - speak_msg = Message('speak', - data={'utterance': 'hello there. world', - 'listen': False}, - context={'ident': 'a'}) - speech.handle_speak(speak_msg) - self.assertTrue(tts.playback.clear.called) - - def test_speak_picroft(self, tts_factory_mock, config_mock): - """Ensure that picroft doesn't split the sentence.""" - setup_mocks(config_mock, tts_factory_mock) - bus = mock.Mock() - config_mock.get.return_value = {'enclosure': {'platform': 'picroft'}} - speech.init(bus) - - speak_msg = Message('speak', - data={'utterance': 'hello there. world', - 'listen': False}, - context={'ident': 'a'}) - speech.handle_speak(speak_msg) - tts_mock.execute.assert_has_calls( - [mock.call('hello there. world', 'a', False)]) - - config_mock.get.return_value = {} - - def test_speak_update_tts(self, tts_factory_mock, config_mock): - """Verify that a new config triggers reload of tts.""" - setup_mocks(config_mock, tts_factory_mock) - bus = mock.Mock() - config_mock.get.return_value = {'tts': {'module': 'test'}} - speech.init(bus) - tts_factory_mock.create.reset_mock() - speak_msg = Message('speak', - data={'utterance': 'hello there. world', - 'listen': False}, - context={'ident': 'a'}) - speech.handle_speak(speak_msg) - self.assertFalse(tts_factory_mock.create.called) - - speech.config = {'tts': {'module': 'test2'}} - speech.handle_speak(speak_msg) - self.assertTrue(tts_factory_mock.create.called) - @mock.patch('mycroft.audio.speech.check_for_signal') def test_stop(self, check_for_signal_mock, tts_factory_mock, config_mock): """Ensure the stop handler signals stop correctly.""" diff --git a/test/unittests/client/test_audio_consumer.py b/test/unittests/client/test_audio_consumer.py deleted file mode 100644 index 2d1c8e3fd6ea..000000000000 --- a/test/unittests/client/test_audio_consumer.py +++ /dev/null @@ -1,214 +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 unittest - -import speech_recognition -from os.path import dirname, join -from speech_recognition import WavFile, AudioData - -from mycroft.client.speech.listener import (AudioConsumer, RecognizerLoop, - AUDIO_DATA, STREAM_START, - STREAM_DATA, STREAM_STOP) -from mycroft.stt import MycroftSTT -from queue import Queue - - -class MockRecognizer(object): - def __init__(self): - self.transcriptions = [] - - def recognize_mycroft(self, audio, key=None, - language=None, show_all=False): - if len(self.transcriptions) > 0: - return self.transcriptions.pop(0) - else: - raise speech_recognition.UnknownValueError() - - def set_transcriptions(self, transcriptions): - self.transcriptions = transcriptions - - -class AudioConsumerTest(unittest.TestCase): - """ - AudioConsumerTest - """ - - def setUp(self): - self.loop = RecognizerLoop() - self.queue = Queue() - self.recognizer = MockRecognizer() - self.consumer = AudioConsumer( - self.loop.state, self.queue, self.loop, MycroftSTT(), - self.loop.wakeup_recognizer, - self.loop.wakeword_recognizer) - - def __create_sample_from_test_file(self, sample_name): - root_dir = dirname(dirname(dirname(__file__))) - filename = join( - root_dir, 'unittests', 'client', - 'data', sample_name + '.wav') - wavfile = WavFile(filename) - with wavfile as source: - return AudioData( - source.stream.read(), wavfile.SAMPLE_RATE, - wavfile.SAMPLE_WIDTH) - - def test_word_extraction(self): - """ - This is intended to test the extraction of the word: ``mycroft``. - The values for ``ideal_begin`` and ``ideal_end`` were found using an - audio tool like Audacity and they represent a sample value position of - the audio. ``tolerance`` is an acceptable margin error for the distance - between the ideal and actual values found by the ``WordExtractor`` - """ - # TODO: implement WordExtractor test without relying on the listener - return - - audio = self.__create_sample_from_test_file('weather_mycroft') - self.queue.put((AUDIO_DATA, audio)) - tolerance = 4000 - ideal_begin = 70000 - ideal_end = 92000 - - monitor = {} - self.recognizer.set_transcriptions(["what's the weather next week"]) - - def wakeword_callback(message): - monitor['pos_begin'] = message.get('pos_begin') - monitor['pos_end'] = message.get('pos_end') - - self.loop.once('recognizer_loop:wakeword', wakeword_callback) - self.consumer.read() - - actual_begin = monitor.get('pos_begin') - self.assertIsNotNone(actual_begin) - diff = abs(actual_begin - ideal_begin) - self.assertTrue( - diff <= tolerance, - str(diff) + " is not less than " + str(tolerance)) - - actual_end = monitor.get('pos_end') - self.assertIsNotNone(actual_end) - diff = abs(actual_end - ideal_end) - self.assertTrue( - diff <= tolerance, - str(diff) + " is not less than " + str(tolerance)) - - @unittest.skip('Disabled while unittests are brought upto date') - def test_wakeword_in_beginning(self): - tag = AUDIO_DATA - data = self.__create_sample_from_test_file('weather_mycroft') - self.queue.put((tag, data)) - self.recognizer.set_transcriptions(["what's the weather next week"]) - monitor = {} - - def callback(message): - monitor['utterances'] = message.get('utterances') - - self.loop.once('recognizer_loop:utterance', callback) - self.consumer.read() - - utterances = monitor.get('utterances') - self.assertIsNotNone(utterances) - self.assertTrue(len(utterances) == 1) - self.assertEqual("what's the weather next week", utterances[0]) - - @unittest.skip('Disabled while unittests are brought upto date') - def test_wakeword(self): - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('mycroft'))) - self.recognizer.set_transcriptions(["silence"]) - monitor = {} - - def callback(message): - monitor['utterances'] = message.get('utterances') - - self.loop.once('recognizer_loop:utterance', callback) - self.consumer.read() - - utterances = monitor.get('utterances') - self.assertIsNotNone(utterances) - self.assertTrue(len(utterances) == 1) - self.assertEqual("silence", utterances[0]) - - def test_ignore_wakeword_when_sleeping(self): - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('mycroft'))) - self.recognizer.set_transcriptions(["not detected"]) - self.loop.sleep() - monitor = {} - - def wakeword_callback(message): - monitor['wakeword'] = message.get('utterance') - - self.loop.once('recognizer_loop:wakeword', wakeword_callback) - self.consumer.read() - self.assertIsNone(monitor.get('wakeword')) - self.assertTrue(self.loop.state.sleeping) - - @unittest.skip('Disabled while unittests are brought upto date, ' - 'TODO investigate why this one is failing, ' - 'is not skipped in mycroft-core') - def test_wakeup(self): - tag = AUDIO_DATA - data = self.__create_sample_from_test_file('mycroft_wakeup') - self.queue.put((tag, data)) - self.loop.sleep() - self.consumer.read() - self.assertFalse(self.loop.state.sleeping) - - @unittest.skip('Disabled while unittests are brought upto date') - def test_stop(self): - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('mycroft'))) - self.consumer.read() - - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('stop'))) - self.recognizer.set_transcriptions(["stop"]) - monitor = {} - - def utterance_callback(message): - monitor['utterances'] = message.get('utterances') - - self.loop.once('recognizer_loop:utterance', utterance_callback) - self.consumer.read() - - utterances = monitor.get('utterances') - self.assertIsNotNone(utterances) - self.assertTrue(len(utterances) == 1) - self.assertEqual("stop", utterances[0]) - - @unittest.skip('Disabled while unittests are brought upto date') - def test_record(self): - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('mycroft'))) - self.consumer.read() - - self.queue.put((AUDIO_DATA, - self.__create_sample_from_test_file('record'))) - self.recognizer.set_transcriptions(["record"]) - monitor = {} - - def utterance_callback(message): - monitor['utterances'] = message.get('utterances') - - self.loop.once('recognizer_loop:utterance', utterance_callback) - self.consumer.read() - - utterances = monitor.get('utterances') - self.assertIsNotNone(utterances) - self.assertTrue(len(utterances) == 1) - self.assertEqual("record", utterances[0]) diff --git a/test/unittests/client/test_hotword_factory.py b/test/unittests/client/test_hotword_factory.py index fd54fbf46344..d7b3d68d286c 100644 --- a/test/unittests/client/test_hotword_factory.py +++ b/test/unittests/client/test_hotword_factory.py @@ -31,18 +31,6 @@ def testDefault(self): self.assertEqual(config['phonemes'], p.phonemes) self.assertEqual(config['threshold'], p.threshold) - def testInvalid(self): - config = { - 'hey Zeds': { - 'module': 'pocketsphinx', - 'phonemes': 'ZZZZZZZZZ', - 'threshold': 1e-90 - } - } - p = HotWordFactory.create_hotword('hey Zeds', config) - self.assertEqual(p.phonemes, 'HH EY . M AY K R AO F T') - self.assertEqual(p.key_phrase, 'hey mycroft') - def testVictoria(self): config = { 'hey victoria': { diff --git a/test/unittests/client/test_local_recognizer.py b/test/unittests/client/test_local_recognizer.py deleted file mode 100644 index df155673c990..000000000000 --- a/test/unittests/client/test_local_recognizer.py +++ /dev/null @@ -1,138 +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 unittest -from unittest.mock import patch - -import os -from speech_recognition import WavFile - -from mycroft.client.speech.listener import RecognizerLoop -from mycroft.configuration import Configuration -from test.util import base_config - -DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data") - - -class PocketSphinxRecognizerTest(unittest.TestCase): - def setUp(self): - with patch('mycroft.configuration.Configuration.get') as \ - mock_config_get: - conf = base_config() - conf['hotwords']['hey mycroft']['module'] = 'pocketsphinx' - mock_config_get.return_value = conf - rl = RecognizerLoop() - self.recognizer = RecognizerLoop.create_wake_word_recognizer(rl) - - def testRecognizerWrapper(self): - source = WavFile(os.path.join(DATA_DIR, "hey_mycroft.wav")) - with source as audio: - assert self.recognizer.found_wake_word(audio.stream.read()) - source = WavFile(os.path.join(DATA_DIR, "mycroft.wav")) - with source as audio: - assert self.recognizer.found_wake_word(audio.stream.read()) - - def testRecognitionInLongerUtterance(self): - source = WavFile(os.path.join(DATA_DIR, "weather_mycroft.wav")) - with source as audio: - assert self.recognizer.found_wake_word(audio.stream.read()) - - @patch.object(Configuration, 'get') - def testRecognitionFallback(self, mock_config_get): - """If language config doesn't exist set default (english)""" - conf = base_config() - conf['hotwords']['hey mycroft'] = { - 'lang': 'DOES NOT EXIST', - 'module': 'pocketsphinx', - 'phonemes': 'HH EY . M AY K R AO F T', - 'threshold': 1e-90 - } - conf['lang'] = 'DOES NOT EXIST' - mock_config_get.return_value = conf - - rl = RecognizerLoop() - ps_hotword = RecognizerLoop.create_wake_word_recognizer(rl) - - expected = 'en-us' - res = ps_hotword.decoder.get_config().get_string('-hmm') - self.assertEqual(expected, res.split('/')[-2].lower()) - self.assertEqual('does not exist', ps_hotword.lang) - - -class LocalRecognizerInitTest(unittest.TestCase): - @patch.object(Configuration, 'get') - def testListenerConfig(self, mock_config_get): - """Ensure that the fallback method collecting phonemes etc. - from the listener config works. - """ - test_config = base_config() - mock_config_get.return_value = test_config - - # Test "Hey Mycroft" - rl = RecognizerLoop() - self.assertEqual(rl.wakeword_recognizer.key_phrase, "hey mycroft") - - # Test "Hey Victoria" - test_config['listener']['wake_word'] = 'hey victoria' - test_config['listener']['phonemes'] = 'HH EY . V IH K T AO R IY AH' - test_config['listener']['threshold'] = 1e-90 - rl = RecognizerLoop() - self.assertEqual(rl.wakeword_recognizer.key_phrase, "hey victoria") - - # Test Invalid" - test_config['listener']['wake_word'] = 'hey victoria' - test_config['listener']['phonemes'] = 'ZZZZZZZZZZZZ' - rl = RecognizerLoop() - self.assertEqual(rl.wakeword_recognizer.key_phrase, "hey mycroft") - - @patch.object(Configuration, 'get') - def testHotwordConfig(self, mock_config_get): - """Ensure that the fallback method collecting phonemes etc. - from the listener config works. - """ - test_config = base_config() - mock_config_get.return_value = test_config - - # Set fallback values - test_config['listener']['phonemes'] = 'HH EY . V IH K T AO R IY AH' - test_config['listener']['threshold'] = 1e-90 - - steve_conf = { - 'model': 'pocketsphinx', - 'phonemes': 'S T IY V .', - 'threshold': 1e-42 - } - - test_config['hotwords']['steve'] = steve_conf - test_config['listener']['wake_word'] = 'steve' - - rl = RecognizerLoop() - self.assertEqual(rl.wakeword_recognizer.key_phrase, 'steve') - - # Ensure phonemes and threshold are poulated from listener config - # if they're missing - - # Set fallback values - test_config['listener']['phonemes'] = 'S T IY V .' - test_config['listener']['threshold'] = 1e-90 - - steve_conf = { - 'model': 'pocketsphinx' - } - - test_config['hotwords']['steve'] = steve_conf - test_config['listener']['wake_word'] = 'steve' - rl = RecognizerLoop() - self.assertEqual(rl.wakeword_recognizer.key_phrase, 'steve') - self.assertEqual(rl.wakeword_recognizer.phonemes, 'S T IY V .') diff --git a/test/unittests/configuration/test_configuration.py b/test/unittests/configuration/test_configuration.py index 77cb376f186a..e4f2e2ffd88b 100644 --- a/test/unittests/configuration/test_configuration.py +++ b/test/unittests/configuration/test_configuration.py @@ -64,17 +64,5 @@ def test_local(self, mock_json_loader, mock_isfile, mock_exists, lc = mycroft.configuration.LocalConf('test') self.assertEqual(lc, {}) - @patch('mycroft.configuration.config.RemoteConf') - @patch('mycroft.configuration.config.LocalConf') - def test_update(self, mock_remote, mock_local): - mock_remote.return_value = {} - mock_local.return_value = {'a': 1} - c = mycroft.configuration.Configuration.get() - self.assertEqual(c, {'a': 1}) - - mock_local.return_value = {'a': 2} - mycroft.configuration.Configuration.updated('message') - self.assertEqual(c, {'a': 2}) - def tearDown(self): mycroft.configuration.Configuration.load_config_stack([{}], True) diff --git a/test/unittests/skills/test_intent_service.py b/test/unittests/skills/test_intent_service.py deleted file mode 100644 index 776c442926f4..000000000000 --- a/test/unittests/skills/test_intent_service.py +++ /dev/null @@ -1,360 +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. -# -from unittest import TestCase, mock - -from adapt.intent import IntentBuilder - -from mycroft.configuration import Configuration -from mycroft.messagebus import Message -from mycroft.skills.intent_service import IntentService, _get_message_lang -from mycroft.skills.intent_services.adapt_service import (ContextManager, - AdaptIntent) - -from test.util import base_config - -# Setup configurations to use with default language tests -BASE_CONF = base_config() -BASE_CONF['lang'] = 'it-it' - -NO_LANG_CONF = base_config() -NO_LANG_CONF.pop('lang') - - -class MockEmitter(object): - 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 reset(self): - self.types = [] - self.results = [] - - -class ContextManagerTest(TestCase): - emitter = MockEmitter() - - def setUp(self): - self.context_manager = ContextManager(3) - - def test_add_context(self): - entity = {'confidence': 1.0} - context = 'TestContext' - word = 'TestWord' - entity['data'] = [(word, context)] - entity['match'] = word - entity['key'] = word - - self.assertEqual(len(self.context_manager.frame_stack), 0) - self.context_manager.inject_context(entity) - self.assertEqual(len(self.context_manager.frame_stack), 1) - - def test_remove_context(self): - entity = {'confidence': 1.0} - context = 'TestContext' - word = 'TestWord' - entity['data'] = [(word, context)] - entity['match'] = word - entity['key'] = word - - self.context_manager.inject_context(entity) - self.assertEqual(len(self.context_manager.frame_stack), 1) - self.context_manager.remove_context('TestContext') - self.assertEqual(len(self.context_manager.frame_stack), 0) - - -def check_converse_request(message, skill_id): - return (message.msg_type == 'skill.converse.request' and - message.data['skill_id'] == skill_id) - - -class ConversationTest(TestCase): - def setUp(self): - bus = mock.Mock() - self.intent_service = IntentService(bus) - self.intent_service.add_active_skill('atari_skill') - self.intent_service.add_active_skill('c64_skill') - - def test_converse(self): - """Check that the _converse method reports if the utterance is handled. - - Also check that the skill that handled the query is moved to the - top of the active skill list. - """ - def response(message, return_msg_type): - c64 = Message(return_msg_type, {'skill_id': 'c64_skill', - 'result': False}) - atari = Message(return_msg_type, {'skill_id': 'atari_skill', - 'result': True}) - msgs = {'c64_skill': c64, 'atari_skill': atari} - - return msgs[message.data['skill_id']] - - self.intent_service.bus.wait_for_response.side_effect = response - - hello = ['hello old friend'] - utterance_msg = Message('recognizer_loop:utterance', - data={'lang': 'en-US', - 'utterances': hello}) - result = self.intent_service._converse(hello, 'en-US', utterance_msg) - self.intent_service.add_active_skill(result.skill_id) - # Check that the active skill list was updated to set the responding - # Skill first. - first_active_skill = self.intent_service.active_skills[0][0] - self.assertEqual(first_active_skill, 'atari_skill') - - # Check that a skill responded that it could handle the message - self.assertTrue(result) - - def test_converse_error(self): - """Check that all skill IDs in the active_skills list are called. - even if there's an error. - """ - def response(message, return_msg_type): - c64 = Message(return_msg_type, {'skill_id': 'c64_skill', - 'result': False}) - amiga = Message(return_msg_type, - {'skill_id': 'amiga_skill', - 'error': 'skill id does not exist'}) - atari = Message(return_msg_type, {'skill_id': 'atari_skill', - 'result': False}) - msgs = {'c64_skill': c64, - 'atari_skill': atari, - 'amiga_skill': amiga} - - return msgs[message.data['skill_id']] - - self.intent_service.add_active_skill('amiga_skill') - self.intent_service.bus.wait_for_response.side_effect = response - - hello = ['hello old friend'] - utterance_msg = Message('recognizer_loop:utterance', - data={'lang': 'en-US', - 'utterances': hello}) - result = self.intent_service._converse(hello, 'en-US', utterance_msg) - - # Check that the active skill list was updated to set the responding - # Skill first. - - # Check that a skill responded that it couldn't handle the message - self.assertFalse(result) - - # Check that each skill in the list of active skills were called - call_args = self.intent_service.bus.wait_for_response.call_args_list - sent_skill_ids = [call[0][0].data['skill_id'] for call in call_args] - self.assertEqual(sent_skill_ids, - ['amiga_skill', 'c64_skill', 'atari_skill']) - - def test_reset_converse(self): - """Check that a blank stt sends the reset signal to the skills.""" - def response(message, return_msg_type): - c64 = Message(return_msg_type, - {'skill_id': 'c64_skill', - 'error': 'skill id does not exist'}) - atari = Message(return_msg_type, {'skill_id': 'atari_skill', - 'result': False}) - msgs = {'c64_skill': c64, 'atari_skill': atari} - - return msgs[message.data['skill_id']] - - reset_msg = Message('mycroft.speech.recognition.unknown', - data={'lang': 'en-US'}) - self.intent_service.bus.wait_for_response.side_effect = response - - self.intent_service.reset_converse(reset_msg) - # Check send messages - wait_for_response_mock = self.intent_service.bus.wait_for_response - c64_message = wait_for_response_mock.call_args_list[0][0][0] - self.assertTrue(check_converse_request(c64_message, 'c64_skill')) - atari_message = wait_for_response_mock.call_args_list[1][0][0] - self.assertTrue(check_converse_request(atari_message, 'atari_skill')) - first_active_skill = self.intent_service.active_skills[0][0] - self.assertEqual(first_active_skill, 'atari_skill') - - -class TestLanguageExtraction(TestCase): - @mock.patch.dict(Configuration._Configuration__config, BASE_CONF) - def test_no_lang_in_message(self): - """No lang in message should result in lang from config.""" - msg = Message('test msg', data={}) - self.assertEqual(_get_message_lang(msg), 'it-it') - - @mock.patch.dict(Configuration._Configuration__config, NO_LANG_CONF) - def test_no_lang_at_all(self): - """Not in message and not in config, should result in en-us.""" - msg = Message('test msg', data={}) - self.assertEqual(_get_message_lang(msg), 'en-us') - - @mock.patch.dict(Configuration._Configuration__config, BASE_CONF) - def test_lang_exists(self): - """Message has a lang code in data, it should be used.""" - msg = Message('test msg', data={'lang': 'de-de'}) - self.assertEqual(_get_message_lang(msg), 'de-de') - msg = Message('test msg', data={'lang': 'sv-se'}) - self.assertEqual(_get_message_lang(msg), 'sv-se') - - -def create_old_style_vocab_msg(keyword, value): - """Create a message for registering an adapt keyword.""" - return Message('register_vocab', - {'start': value, 'end': keyword}) - - -def create_vocab_msg(keyword, value): - """Create a message for registering an adapt keyword.""" - return Message('register_vocab', - {'entity_value': value, 'entity_type': keyword}) - - -def get_last_message(bus): - """Get last sent message on mock bus.""" - last = bus.emit.call_args - return last[0][0] - - -class TestIntentServiceApi(TestCase): - def setUp(self): - self.intent_service = IntentService(mock.Mock()) - - def setup_simple_adapt_intent(self, - msg=create_vocab_msg('testKeyword', 'test')): - self.intent_service.handle_register_vocab(msg) - - intent = IntentBuilder('skill:testIntent').require('testKeyword') - msg = Message('register_intent', intent.__dict__) - self.intent_service.handle_register_intent(msg) - - def test_keyword_backwards_compatibility(self): - self.setup_simple_adapt_intent( - create_old_style_vocab_msg('testKeyword', 'test') - ) - - # Check that the intent is returned - msg = Message('intent.service.adapt.get', data={'utterance': 'test'}) - self.intent_service.handle_get_adapt(msg) - - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent']['intent_type'], - 'skill:testIntent') - - def test_get_adapt_intent(self): - self.setup_simple_adapt_intent() - # Check that the intent is returned - msg = Message('intent.service.adapt.get', data={'utterance': 'test'}) - self.intent_service.handle_get_adapt(msg) - - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent']['intent_type'], - 'skill:testIntent') - - def test_get_adapt_intent_no_match(self): - """Check that if the intent doesn't match at all None is returned.""" - self.setup_simple_adapt_intent() - # Check that no intent is matched - msg = Message('intent.service.adapt.get', data={'utterance': 'five'}) - self.intent_service.handle_get_adapt(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent'], None) - - def test_get_intent(self): - """Check that the registered adapt intent is triggered.""" - self.setup_simple_adapt_intent() - # Check that the intent is returned - msg = Message('intent.service.adapt.get', data={'utterance': 'test'}) - self.intent_service.handle_get_intent(msg) - - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent']['intent_type'], - 'skill:testIntent') - - def test_get_intent_no_match(self): - """Check that if the intent doesn't match at all None is returned.""" - self.setup_simple_adapt_intent() - # Check that no intent is matched - msg = Message('intent.service.intent.get', data={'utterance': 'five'}) - self.intent_service.handle_get_intent(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent'], None) - - def test_get_intent_manifest(self): - """Check that if the intent doesn't match at all None is returned.""" - self.setup_simple_adapt_intent() - # Check that no intent is matched - msg = Message('intent.service.intent.get', data={'utterance': 'five'}) - self.intent_service.handle_get_intent(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent'], None) - - def test_get_adapt_intent_manifest(self): - """Make sure the manifest returns a list of Intent Parser objects.""" - self.setup_simple_adapt_intent() - msg = Message('intent.service.adapt.manifest.get') - self.intent_service.handle_adapt_manifest(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intents'][0]['name'], - 'skill:testIntent') - - def test_get_adapt_vocab_manifest(self): - self.setup_simple_adapt_intent() - msg = Message('intent.service.adapt.vocab.manifest.get') - self.intent_service.handle_vocab_manifest(msg) - reply = get_last_message(self.intent_service.bus) - value = reply.data['vocab'][0]['entity_value'] - keyword = reply.data['vocab'][0]['entity_type'] - self.assertEqual(keyword, 'testKeyword') - self.assertEqual(value, 'test') - - def test_get_no_match_after_detach(self): - """Check that a removed intent doesn't match.""" - self.setup_simple_adapt_intent() - # Check that no intent is matched - msg = Message('detach_intent', - data={'intent_name': 'skill:testIntent'}) - self.intent_service.handle_detach_intent(msg) - msg = Message('intent.service.adapt.get', data={'utterance': 'test'}) - self.intent_service.handle_get_adapt(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent'], None) - - def test_get_no_match_after_detach_skill(self): - """Check that a removed skill's intent doesn't match.""" - self.setup_simple_adapt_intent() - # Check that no intent is matched - msg = Message('detach_intent', - data={'skill_id': 'skill'}) - self.intent_service.handle_detach_skill(msg) - msg = Message('intent.service.adapt.get', data={'utterance': 'test'}) - self.intent_service.handle_get_adapt(msg) - reply = get_last_message(self.intent_service.bus) - self.assertEqual(reply.data['intent'], None) - - -class TestAdaptIntent(TestCase): - """Test the AdaptIntent wrapper.""" - def test_named_intent(self): - intent = AdaptIntent("CallEaglesIntent") - self.assertEqual(intent.name, "CallEaglesIntent") - - def test_unnamed_intent(self): - intent = AdaptIntent() - self.assertEqual(intent.name, "") diff --git a/test/unittests/skills/test_intent_service_interface.py b/test/unittests/skills/test_intent_service_interface.py deleted file mode 100644 index 0645b86857f2..000000000000 --- a/test/unittests/skills/test_intent_service_interface.py +++ /dev/null @@ -1,83 +0,0 @@ -import unittest - -from mycroft.skills.intent_service_interface import 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') - entity_data = {'entity_value': 'test', 'entity_type': 'test_intent'} - 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']) - - entity_data = {'entity_value': 'test', 'entity_type': 'test_intent'} - 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' - } - 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' - } - 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('.*') - self.check_emitter([{'regex': '.*'}]) diff --git a/test/unittests/skills/test_mycroft_skill.py b/test/unittests/skills/test_mycroft_skill.py deleted file mode 100644 index bc60ca5c90a7..000000000000 --- a/test/unittests/skills/test_mycroft_skill.py +++ /dev/null @@ -1,707 +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 sys -import unittest - -from unittest.mock import MagicMock, patch -from adapt.intent import IntentBuilder -from os.path import join, dirname, abspath -from re import error -from datetime import datetime -import json - -from mycroft.configuration import Configuration -from mycroft.messagebus.message import Message -from mycroft.skills.skill_data import (load_regex_from_file, load_regex, - load_vocabulary, read_vocab_file) -from mycroft.skills.core import MycroftSkill, resting_screen_handler -from mycroft.skills.intent_service import open_intent_envelope - -from test.util import base_config - -BASE_CONF = base_config() - - -class MockEmitter(object): - 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 = [] - - -def vocab_base_path(): - return join(dirname(__file__), '..', 'vocab_test') - - -class TestFunction(unittest.TestCase): - def test_resting_screen_handler(self): - class T(MycroftSkill): - def __init__(self): - self.name = 'TestObject' - - @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 TestMycroftSkill(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() - self.local_settings_mock = self._mock_local_settings() - - def _mock_local_settings(self): - local_settings_patch = patch( - 'mycroft.skills.mycroft_skill.mycroft_skill.get_local_settings' - ) - self.addCleanup(local_settings_patch.stop) - local_settings_mock = local_settings_patch.start() - local_settings_mock.return_value = True - - return local_settings_mock - - def check_vocab(self, filename, results=None): - results = results or {} - intents = load_vocabulary(join(self.vocab_path, filename), 'A') - self.compare_dicts(intents, results) - - def check_regex_from_file(self, filename, result_list=None): - result_list = result_list or [] - regex_file = join(self.regex_path, filename) - self.assertEqual(sorted(load_regex_from_file(regex_file, 'A')), - sorted(result_list)) - - def compare_dicts(self, d1, d2): - self.assertEqual(json.dumps(d1, sort_keys=True), - json.dumps(d2, sort_keys=True)) - - def check_read_vocab_file(self, path, result_list=None): - resultlist = result_list or [] - self.assertEqual(sorted(read_vocab_file(path)), sorted(result_list)) - - def check_regex(self, path, result_list=None): - result_list = result_list or [] - self.assertEqual(sorted(load_regex(path, 'A')), sorted(result_list)) - - 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_load_regex_from_file_single(self): - self.check_regex_from_file('valid/single.rx', - ['(?P.*)']) - - def test_load_regex_from_file_multiple(self): - self.check_regex_from_file('valid/multiple.rx', - ['(?P.*)', - '(?P.*)']) - - def test_load_regex_from_file_none(self): - self.check_regex_from_file('invalid/none.rx') - - def test_load_regex_from_file_invalid(self): - with self.assertRaises(error): - self.check_regex_from_file('invalid/invalid.rx') - - def test_load_regex_from_file_does_not_exist(self): - with self.assertRaises(IOError): - self.check_regex_from_file('does_not_exist.rx') - - def test_load_regex_full(self): - self.check_regex(join(self.regex_path, 'valid'), - ['(?P.*)', - '(?P.*)', - '(?P.*)']) - - def test_load_regex_empty(self): - self.check_regex(join(dirname(__file__), 'empty_dir')) - - def test_load_regex_fail(self): - try: - self.check_regex(join(dirname(__file__), 'regex_test_fail')) - except OSError as e: - self.assertEqual(e.strerror, 'No such file or directory') - - def test_load_vocab_file_single(self): - self.check_read_vocab_file(join(vocab_base_path(), 'valid/single.voc'), - [['test']]) - - def test_load_vocab_from_file_single_alias(self): - self.check_read_vocab_file(join(vocab_base_path(), - 'valid/singlealias.voc'), - [['water', 'watering']]) - - def test_load_vocab_from_file_multiple_alias(self): - self.check_read_vocab_file(join(vocab_base_path(), - 'valid/multiplealias.voc'), - [['chair', 'chairs'], ['table', 'tables']]) - - def test_load_vocab_from_file_does_not_exist(self): - try: - self.check_read_vocab_file('does_not_exist.voc') - except IOError as e: - self.assertEqual(e.strerror, 'No such file or directory') - - def test_load_vocab_full(self): - self.check_vocab(join(self.vocab_path, 'valid'), - { - 'Asingle': [['test']], - 'Asinglealias': [['water', 'watering']], - 'Amultiple': [['animal'], ['animals']], - 'Amultiplealias': [['chair', 'chairs'], - ['table', 'tables']] - }) - - def test_load_vocab_empty(self): - self.check_vocab(join(dirname(__file__), 'empty_dir')) - - def test_load_vocab_fail(self): - try: - self.check_regex(join(dirname(__file__), - 'vocab_test_fail')) - except OSError as e: - self.assertEqual(e.strerror, 'No such file or directory') - - 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() - s.bind(self.emitter) - s.initialize() - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - self.check_register_intent(expected) - - # Test register IntentBuilder object - s = SimpleSkill2() - s.bind(self.emitter) - s.initialize() - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - - self.check_register_intent(expected) - - # Test register IntentBuilder object - with self.assertRaises(ValueError): - s = SimpleSkill3() - s.bind(self.emitter) - s.initialize() - - def test_enable_disable_intent(self): - """Test disable/enable intent.""" - # Setup basic test - s = SimpleSkill1() - s.bind(self.emitter) - s.initialize() - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - self.check_register_intent(expected) - - # 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() - s.bind(self.emitter) - s.initialize() - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}] - self.check_register_intent(expected) - - # Test disable/enable cycle - msg = Message('test.msg', data={'intent_name': 'a'}) - s.handle_disable_intent(msg) - self.check_detach_intent() - s.handle_enable_intent(msg) - self.check_register_intent(expected) - - def test_register_vocab(self): - """Test disable/enable intent.""" - # Setup basic test - s = SimpleSkill1() - s.bind(self.emitter) - s.initialize() - - # Normal vocaubulary - self.emitter.reset() - expected = [ - { - 'start': 'hello', - 'end': 'AHelloKeyword', - 'entity_value': 'hello', - 'entity_type': 'AHelloKeyword' - } - ] - s.register_vocabulary('hello', 'HelloKeyword') - self.check_register_vocabulary(expected) - # Regex - s.register_regex('weird (?P.+) stuff') - expected = [{'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.root_dir = abspath(join(dirname(__file__), 'intent_file')) - s.bind(self.emitter) - s.initialize() - - expected_types = [ - 'padatious:register_intent', - 'padatious:register_entity' - ] - - expected_results = [ - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test.intent'), - 'name': str(s.skill_id) + ':test.intent' - }, - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test_ent.entity'), - 'name': str(s.skill_id) + ':test_ent' - } - ] - 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.skill_id = 'A' - s.bind(self.emitter) - s.root_dir = abspath(join(dirname(__file__), 'intent_file')) - s.initialize() - s._register_decorated() - expected = [{'at_least_one': [], - 'name': 'A:a', - 'optional': [], - 'requires': [('AKeyword', 'AKeyword')]}, - { - 'file_name': join(dirname(__file__), 'intent_file', - 'vocab', 'en-us', 'test.intent'), - '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() - s.bind(self.emitter) - 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() - s.bind(self.emitter) - # 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() - s.bind(self.emitter) - 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() - s.bind(self.emitter) - s.remove_context('Donatello') - expected = [{'context': 'ADonatello'}] - check_remove_context(expected) - - @patch.dict(Configuration._Configuration__config, 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__config, BASE_CONF) - def test_add_event(self): - emitter = MagicMock() - s = SimpleSkill1() - s.bind(emitter) - 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__config, BASE_CONF) - def test_remove_event(self): - emitter = MagicMock() - s = SimpleSkill1() - s.bind(emitter) - 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__config, BASE_CONF) - def test_add_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1() - s.bind(emitter) - - 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__config, BASE_CONF) - def test_remove_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1() - s.bind(emitter) - 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__config, BASE_CONF) - def test_run_scheduled_event(self): - emitter = MagicMock() - s = SimpleSkill1() - with patch.object(s, '_settings', - create=True, value=MagicMock()): - s.bind(emitter) - 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.root_dir = abspath(join(dirname(__file__), - 'translate', 'in-dialog/')) - lst = s.translate_list('good_things') - self.assertTrue(isinstance(lst, list)) - vals = s.translate_namedvalues('named_things') - self.assertTrue(isinstance(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.root_dir = abspath(join(dirname(__file__), - 'translate', 'in-locale')) - lst = s.translate_list('good_things') - self.assertTrue(isinstance(lst, list)) - vals = s.translate_namedvalues('named_things') - self.assertTrue(isinstance(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.root_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']) - - # Check fallback to english - lst = s.translate_list('not_in_german') - self.assertEqual(lst, ['not', 'in', 'German']) - - # Restore lang to en-us - s.config_core['lang'] = 'en-us' - - def test_speak_dialog_render_not_initialized(self): - """Test that non-initialized dialog_renderer won't raise an error.""" - s = SimpleSkill1() - s.bind(self.emitter) - s.dialog_renderer = None - s.speak_dialog(key='key') - - -class _TestSkill(MycroftSkill): - def __init__(self): - super().__init__() - self.skill_id = 'A' - - -class SimpleSkill1(_TestSkill): - def __init__(self): - super(SimpleSkill1, self).__init__() - self.handler_run = False - - """ Test skill for normal intent builder syntax """ - def initialize(self): - 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() """ - skill_id = 'A' - - 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 """ - skill_id = 'A' - - 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 """ - skill_id = 'A' - - 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 """ - skill_id = 'A' - - def initialize(self): - self.register_intent('test.intent', self.handler) - self.register_entity_file('test_ent.entity') - - def handler(self, message): - pass diff --git a/test/unittests/skills/test_mycroft_skill_get_response.py b/test/unittests/skills/test_mycroft_skill_get_response.py deleted file mode 100644 index a1deba60052e..000000000000 --- a/test/unittests/skills/test_mycroft_skill_get_response.py +++ /dev/null @@ -1,246 +0,0 @@ -"""Tests for the mycroft skill's get_response variations.""" - -from os.path import dirname, join -from threading import Thread -import time -from unittest import TestCase, mock - -from lingua_franca import load_language - -from mycroft import MycroftSkill -from mycroft.messagebus import Message - -from test.unittests.mocks import base_config, AnyCallable - - -load_language("en-us") - - -def create_converse_responder(response, skill): - """Create a function to inject a response into the converse method. - - The function waits for the converse method to be replaced by the - _wait_response logic and afterwards injects the provided response. - - Args: - response (str): Sentence to inject. - skill (MycroftSkill): skill to monitor. - """ - default_converse = skill.converse - converse_return = None - - def wait_for_new_converse(): - """Wait until there is a new converse handler then send sentence. - """ - nonlocal converse_return - start_time = time.monotonic() - while time.monotonic() < start_time + 5: - if skill.converse != default_converse: - skill.converse([response]) - break - - time.sleep(0.1) - - return wait_for_new_converse - - -@mock.patch('mycroft.skills.mycroft_skill.mycroft_skill.Configuration') -def create_skill(mock_conf, lang='en-us'): - mock_conf.get.return_value = base_config() - skill = MycroftSkill(name='test_skill') - bus = mock.Mock() - skill.bind(bus) - skill.config_core['lang'] = lang - skill.load_data_files(join(dirname(__file__), 'test_skill')) - return skill - - -class TestMycroftSkillWaitResponse(TestCase): - def test_wait(self): - """Ensure that _wait_response() returns the response from converse.""" - skill = create_skill() - - expected_response = 'Yes I do, very much' - - converser = Thread(target=create_converse_responder(expected_response, - skill)) - converser.start() - validator = mock.Mock() - validator.return_value = True - is_cancel = mock.Mock() - is_cancel.return_value = False - on_fail = mock.Mock() - - response = skill._wait_response(is_cancel, validator, on_fail, 1) - self.assertEqual(response, expected_response) - converser.join() - - def test_wait_cancel(self): - """Test that a matching cancel function cancels the wait.""" - skill = create_skill() - - converser = Thread(target=create_converse_responder('cancel', skill)) - converser.start() - validator = mock.Mock() - validator.return_value = False - on_fail = mock.Mock() - - def is_cancel(utterance): - return utterance == 'cancel' - - response = skill._wait_response(is_cancel, validator, on_fail, 1) - self.assertEqual(response, None) - converser.join() - - -class TestMycroftSkillGetResponse(TestCase): - def test_get_response(self): - """Test response using a dialog file.""" - skill = create_skill() - skill._wait_response = mock.Mock() - skill.speak_dialog = mock.Mock() - - expected_response = 'ice creamr please' - skill._wait_response.return_value = expected_response - response = skill.get_response('what do you want') - self.assertEqual(response, expected_response) - self.assertTrue(skill.speak_dialog.called) - - def test_get_response_text(self): - """Assert that text is used if no dialog exists.""" - skill = create_skill() - skill._wait_response = mock.Mock() - skill.speak_dialog = mock.Mock() - - expected_response = 'green' - skill._wait_response.return_value = expected_response - response = skill.get_response('tell me a color') - self.assertEqual(response, expected_response) - self.assertTrue(skill.speak_dialog.called) - skill.speak_dialog.assert_called_with('tell me a color', - {}, - expect_response=True, - wait=True) - - def test_get_response_no_dialog(self): - """Check that when no dialog/text is provided listening is triggered. - """ - skill = create_skill() - skill._wait_response = mock.Mock() - skill.speak_dialog = mock.Mock() - - expected_response = 'ice creamr please' - skill._wait_response.return_value = expected_response - response = skill.get_response() - self.assertEqual(response, expected_response) - self.assertFalse(skill.speak_dialog.called) - self.assertTrue(skill.bus.emit.called) - sent_message = skill.bus.emit.call_args[0][0] - self.assertEqual(sent_message.msg_type, 'mycroft.mic.listen') - - def test_get_response_validator(self): - """Ensure validator is passed on.""" - skill = create_skill() - skill._wait_response = mock.Mock() - skill.speak_dialog = mock.Mock() - - def validator(*args, **kwargs): - return True - - expected_response = 'ice creamr please' - skill._wait_response.return_value = expected_response - response = skill.get_response('what do you want', - validator=validator) - skill._wait_response.assert_called_with(AnyCallable(), validator, - AnyCallable(), -1) - - -class TestMycroftSkillAskYesNo(TestCase): - def test_ask_yesno_no(self): - """Check that a negative response is interpreted as a no.""" - skill = create_skill() - skill.get_response = mock.Mock() - skill.get_response.return_value = 'nope' - - response = skill.ask_yesno('Do you like breakfast') - self.assertEqual(response, 'no') - - def test_ask_yesno_yes(self): - """Check that an affirmative response is interpreted as a yes.""" - skill = create_skill() - skill.get_response = mock.Mock() - skill.get_response.return_value = 'yes' - - response = skill.ask_yesno('Do you like breakfast') - self.assertEqual(response, 'yes') - - def test_ask_yesno_other(self): - """Check that non yes no response gets returned.""" - skill = create_skill() - skill.get_response = mock.Mock() - skill.get_response.return_value = 'I am a fish' - - response = skill.ask_yesno('Do you like breakfast') - self.assertEqual(response, 'I am a fish') - - def test_ask_yesno_german(self): - """Check that when the skill is set to german it responds to "ja".""" - skill = create_skill(lang='de-de') - skill.get_response = mock.Mock() - skill.get_response.return_value = 'ja' - - response = skill.ask_yesno('Do you like breakfast') - self.assertEqual(response, 'yes') - - -class TestMycroftAskSelection(TestCase): - def test_selection_number(self): - """Test selection by number.""" - skill = create_skill() - skill.speak = mock.Mock() - skill.get_response = mock.Mock() - - skill.get_response.return_value = 'the third' - - options = ['a balloon', 'an octopus', 'a piano'] - response = skill.ask_selection(options, 'which is better') - self.assertEqual(options[2], response) - - # Assert that the spoken sentence contains all options. - spoken_sentence = skill.speak.call_args[0][0] - for opt in options: - self.assertTrue(opt in spoken_sentence) - - def test_selection_last(self): - """Test selection by "last".""" - skill = create_skill() - skill.speak = mock.Mock() - skill.get_response = mock.Mock() - - skill.get_response.return_value = 'last one' - - options = ['a balloon', 'an octopus', 'a piano'] - response = skill.ask_selection(options, 'which is better') - self.assertEqual(options[2], response) - - # Assert that the spoken sentence contains all options. - spoken_sentence = skill.speak.call_args[0][0] - for opt in options: - self.assertTrue(opt in spoken_sentence) - - def test_selection_name(self): - """Test selection by name.""" - skill = create_skill() - skill.speak = mock.Mock() - skill.get_response = mock.Mock() - - skill.get_response.return_value = 'octopus' - - options = ['a balloon', 'an octopus', 'a piano'] - response = skill.ask_selection(options, 'which is better') - self.assertEqual(options[1], response) - - # Assert that the spoken sentence contains all options. - spoken_sentence = skill.speak.call_args[0][0] - for opt in options: - self.assertTrue(opt in spoken_sentence) diff --git a/test/unittests/skills/test_settings.py b/test/unittests/skills/test_settings.py deleted file mode 100644 index 66c2f05e141d..000000000000 --- a/test/unittests/skills/test_settings.py +++ /dev/null @@ -1,313 +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 tempfile -from pathlib import Path -from unittest import TestCase -from unittest.mock import call, Mock, patch - -from mycroft.skills.settings import ( - get_local_settings, - save_settings, - SkillSettingsDownloader, - SettingsMetaUploader -) -from ..base import MycroftUnitTestBase - - -class TestSettingsMetaUploader(MycroftUnitTestBase): - mock_package = 'mycroft.skills.settings.' - - def setUp(self): - super().setUp() - self.uploader = SettingsMetaUploader(str(self.temp_dir), 'test_skill') - self.uploader.api = Mock() - self.is_paired_mock = self._mock_is_paired() - self.timer_mock = self._mock_timer() - self.skill_metadata = dict( - skillMetadata=dict( - sections=[ - dict( - name='Test Section', - fields=[dict(type='label', label='Test Field')] - ) - ] - ) - ) - - def _mock_is_paired(self): - is_paired_patch = patch(self.mock_package + 'is_paired') - self.addCleanup(is_paired_patch.stop) - is_paired_mock = is_paired_patch.start() - is_paired_mock.return_value = True - - return is_paired_mock - - def _mock_timer(self): - timer_patch = patch(self.mock_package + 'Timer') - self.addCleanup(timer_patch.stop) - timer_mock = timer_patch.start() - - return timer_mock - - def test_not_paired(self): - self.is_paired_mock.return_value = False - self.uploader.upload() - self._check_api_not_called() - self._check_timer_called() - - @patch('mycroft.skills.settings.DeviceApi') - def test_no_settingsmeta(self, mock_api): - api_instance = Mock() - api_instance.identity.uuid = '42' - mock_api.return_value = api_instance - - self.uploader.upload() - self._check_settingsmeta() - self._check_api_call() - self._check_timer_not_called() - - @patch('mycroft.skills.settings.DeviceApi') - def test_failed_upload(self, mock_api): - """The API call to upload the settingsmeta fails. - - This will cause a timer to be generated to retry the update. - """ - api_instance = Mock() - api_instance.identity.uuid = '42' - api_instance.upload_skill_metadata = Mock(side_effect=ValueError) - mock_api.return_value = api_instance - self.uploader.upload() - self._check_settingsmeta() - self._check_api_call() - self._check_timer_called() - - @patch('mycroft.skills.settings.DeviceApi') - def test_json_settingsmeta(self, mock_api): - api_instance = Mock() - api_instance.identity.uuid = '42' - mock_api.return_value = api_instance - - json_path = str(self.temp_dir.joinpath('settingsmeta.json')) - with open(json_path, 'w') as json_file: - json.dump(self.skill_metadata, json_file) - - self.uploader.upload() - self._check_settingsmeta(self.skill_metadata) - self._check_api_call() - self._check_timer_not_called() - - @patch('mycroft.skills.settings.DeviceApi') - def test_yaml_settingsmeta(self, mock_api): - api_instance = Mock() - api_instance.identity.uuid = '42' - mock_api.return_value = api_instance - - skill_metadata = ( - 'skillMetadata:\n sections:\n - name: "Test Section"\n ' - 'fields:\n - type: "label"\n label: "Test Field"' - ) - yaml_path = str(self.temp_dir.joinpath('settingsmeta.yaml')) - with open(yaml_path, 'w') as yaml_file: - yaml_file.write(skill_metadata) - - self.uploader.upload() - self._check_settingsmeta(self.skill_metadata) - self._check_api_call() - self._check_timer_not_called() - - def _check_settingsmeta(self, skill_settings=None): - expected_settings_meta = dict( - skill_gid='test_skill|99.99', - display_name='Test Skill', - ) - if skill_settings is not None: - expected_settings_meta.update(skill_settings) - - self.assertDictEqual( - expected_settings_meta, - self.uploader.settings_meta - ) - - def _check_api_call(self): - self.assertListEqual( - [call.upload_skill_metadata(self.uploader.settings_meta)], - self.uploader.api.method_calls - ) - - def _check_api_not_called(self): - self.assertListEqual([], self.uploader.api.method_calls) - - def _check_timer_called(self): - self.assertListEqual( - [call.start()], - self.timer_mock.return_value.method_calls - ) - - def _check_timer_not_called(self): - self.assertListEqual([], self.timer_mock.return_value.method_calls) - - -class TestSettingsDownloader(MycroftUnitTestBase): - mock_package = 'mycroft.skills.settings.' - - def setUp(self): - super().setUp() - self.settings_path = self.temp_dir.joinpath('settings.json') - self.downloader = SkillSettingsDownloader(self.message_bus_mock) - self.downloader.api = Mock() - self.is_paired_mock = self._mock_is_paired() - self.timer_mock = self._mock_timer() - - def _mock_is_paired(self): - is_paired_patch = patch(self.mock_package + 'is_paired') - self.addCleanup(is_paired_patch.stop) - is_paired_mock = is_paired_patch.start() - is_paired_mock.return_value = True - - return is_paired_mock - - def _mock_timer(self): - timer_patch = patch(self.mock_package + 'Timer') - self.addCleanup(timer_patch.stop) - timer_mock = timer_patch.start() - - return timer_mock - - def test_not_paired(self): - self.is_paired_mock.return_value = False - self.downloader.download() - self._check_api_not_called() - self._check_timer_called() - - def test_settings_not_changed(self): - test_skill_settings = { - 'test_skill|99.99': {"test_setting": 'test_value'} - } - self.downloader.last_download_result = test_skill_settings - self.downloader.api.get_skill_settings = Mock( - return_value=test_skill_settings) - self.downloader.download() - self._check_api_called() - self._check_timer_called() - self._check_no_message_bus_events() - - def test_settings_changed(self): - local_skill_settings = { - 'test_skill|99.99': {"test_setting": 'test_value'} - } - remote_skill_settings = { - 'test_skill|99.99': {"test_setting": 'foo'} - } - self.downloader.last_download_result = local_skill_settings - self.downloader.api.get_skill_settings = Mock( - return_value=remote_skill_settings) - self.downloader.download() - self._check_api_called() - self._check_timer_called() - self._check_message_bus_events(remote_skill_settings) - - def test_download_failed(self): - self.downloader.api.get_skill_settings = Mock(side_effect=ValueError) - pre_download_local_settings = { - 'test_skill|99.99': {"test_setting": 'test_value'} - } - self.downloader.last_download_result = pre_download_local_settings - self.downloader.download() - self._check_api_called() - self._check_timer_called() - self._check_no_message_bus_events() - self.assertEqual( - pre_download_local_settings, - self.downloader.last_download_result - ) - - def test_stop_downloading(self): - """Ensure that the timer is cancelled and the continue flag is lowered. - """ - self.is_paired_mock.return_value = False # Skip all the download logic - self.downloader.download() # Start downloading creates the timer - self.downloader.stop_downloading() - self.assertFalse(self.downloader.continue_downloading) - self.assertTrue( - self.downloader.download_timer.cancel.called_once_with()) - - def _check_api_called(self): - self.assertListEqual( - [call.get_skill_settings()], - self.downloader.api.method_calls - ) - - def _check_api_not_called(self): - self.assertListEqual([], self.downloader.api.method_calls) - - def _check_timer_called(self): - self.assertListEqual( - [call.start()], - self.timer_mock.return_value.method_calls - ) - - def _check_no_message_bus_events(self): - self.assertListEqual(self.message_bus_mock.message_types, []) - self.assertListEqual(self.message_bus_mock.message_data, []) - - def _check_message_bus_events(self, remote_skill_settings): - self.assertListEqual( - ['mycroft.skills.settings.changed'], - self.message_bus_mock.message_types - ) - self.assertListEqual( - [remote_skill_settings], - self.message_bus_mock.message_data - ) - - -class TestSettings(TestCase): - def setUp(self) -> None: - temp_dir = tempfile.mkdtemp() - self.temp_dir = Path(temp_dir) - self.skill_mock = Mock() - self.skill_mock.root_dir = str(self.temp_dir) - self.skill_mock.name = 'test_skill' - - def test_empty_settings(self): - settings = get_local_settings( - self.skill_mock.root_dir, - self.skill_mock.name - ) - self.assertDictEqual(settings, {}) - - def test_settings_file_exists(self): - settings_path = str(self.temp_dir.joinpath('settings.json')) - with open(settings_path, 'w') as settings_file: - settings_file.write('{"foo": "bar"}\n') - - settings = get_local_settings( - self.skill_mock.root_dir, - self.skill_mock.name - ) - self.assertDictEqual(settings, {'foo': 'bar'}) - self.assertEqual(settings['foo'], 'bar') - self.assertNotIn('store', settings) - self.assertIn('foo', settings) - - def test_store_settings(self): - settings = dict(foo='bar') - save_settings(self.skill_mock.root_dir, settings) - settings_path = str(self.temp_dir.joinpath('settings.json')) - with open(settings_path) as settings_file: - file_contents = settings_file.read() - - self.assertEqual(file_contents, '{"foo": "bar"}') diff --git a/test/unittests/skills/test_skill_manager.py b/test/unittests/skills/test_skill_manager.py deleted file mode 100644 index 4444089bfd2b..000000000000 --- a/test/unittests/skills/test_skill_manager.py +++ /dev/null @@ -1,333 +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. -# -from os import path -from unittest import TestCase, skip -from unittest.mock import Mock, patch - -from mycroft.skills.skill_loader import SkillLoader -from mycroft.skills.skill_manager import SkillManager, UploadQueue -from ..base import MycroftUnitTestBase - - -class TestUploadQueue(TestCase): - - def test_upload_queue_create(self): - queue = UploadQueue() - self.assertFalse(queue.started) - queue.start() - self.assertTrue(queue.started) - - def test_upload_queue_use(self): - queue = UploadQueue() - queue.start() - specific_loader = Mock(spec=SkillLoader, instance=Mock()) - loaders = [Mock(), specific_loader, Mock(), Mock()] - # Check that putting items on the queue makes it longer - for i, l in enumerate(loaders): - queue.put(l) - self.assertEqual(len(queue), i + 1) - # Check that adding an existing item replaces that item - queue.put(specific_loader) - self.assertEqual(len(queue), len(loaders)) - # Check that sending items empties the queue - queue.send() - self.assertEqual(len(queue), 0) - - def test_upload_queue_preloaded(self): - queue = UploadQueue() - loaders = [Mock(), Mock(), Mock(), Mock()] - for i, l in enumerate(loaders): - queue.put(l) - self.assertEqual(len(queue), i + 1) - # Check that starting the queue will send all the items in the queue - queue.start() - self.assertEqual(len(queue), 0) - for l in loaders: - l.instance.settings_meta.upload.assert_called_once_with() - - -class TestSkillManager(MycroftUnitTestBase): - mock_package = 'mycroft.skills.skill_manager.' - - def setUp(self): - super().setUp() - self._mock_skill_updater() - self._mock_skill_settings_downloader() - self.skill_manager = SkillManager(self.message_bus_mock) - self._mock_skill_loader_instance() - - def _mock_skill_settings_downloader(self): - settings_download_patch = patch( - self.mock_package + 'SkillSettingsDownloader', - spec=True - ) - self.addCleanup(settings_download_patch.stop) - self.settings_download_mock = settings_download_patch.start() - - def _mock_skill_updater(self): - skill_updater_patch = patch( - self.mock_package + 'SkillUpdater', - spec=True - ) - self.addCleanup(skill_updater_patch.stop) - self.skill_updater_mock = skill_updater_patch.start() - - def _mock_skill_loader_instance(self): - self.skill_dir = self.temp_dir.joinpath('test_skill') - self.skill_loader_mock = Mock(spec=SkillLoader) - self.skill_loader_mock.instance = Mock() - self.skill_loader_mock.instance.default_shutdown = Mock() - self.skill_loader_mock.instance.converse = Mock() - self.skill_loader_mock.instance.converse.return_value = True - self.skill_loader_mock.skill_id = 'test_skill' - self.skill_manager.skill_loaders = { - str(self.skill_dir): self.skill_loader_mock - } - - def test_instantiate(self): - self.assertEqual( - self.skill_manager.config['data_dir'], - str(self.temp_dir) - ) - expected_result = [ - 'skill.converse.request', - 'mycroft.internet.connected', - 'skillmanager.update', - 'skillmanager.list', - 'skillmanager.deactivate', - 'skillmanager.keep', - 'skillmanager.activate', - 'mycroft.paired', - 'mycroft.skills.settings.update' - ] - self.assertListEqual( - expected_result, - self.message_bus_mock.event_handlers - ) - - def test_remove_git_locks(self): - git_dir = self.temp_dir.joinpath('foo/.git') - git_dir.mkdir(parents=True) - git_lock_file_path = str(git_dir.joinpath('index.lock')) - with open(git_lock_file_path, 'w') as git_lock_file: - git_lock_file.write('foo') - - self.skill_manager._remove_git_locks() - - self.assertFalse(path.exists(git_lock_file_path)) - - def _build_mock_msm_skill_list(self): - skill = Mock() - skill.name = 'foobar' - skill.is_local = True - skill.install = Mock() - skill.update = Mock() - skill.update_deps = Mock() - skill.path = str(self.temp_dir.joinpath('foobar')) - skill_list_func = Mock(return_value=[skill]) - - return skill, skill_list_func - - def test_no_skill_in_skill_dir(self): - self.skill_dir.mkdir(parents=True) - skill_directories = self.skill_manager._get_skill_directories() - self.assertListEqual([], skill_directories) - - def test_get_skill_directories(self): - self.skill_dir.mkdir(parents=True) - self.skill_dir.joinpath('__init__.py').touch() - skill_directories = self.skill_manager._get_skill_directories() - - self.assertListEqual([str(self.skill_dir)], skill_directories) - - def test_unload_removed_skills(self): - self.skill_manager._unload_removed_skills() - - self.assertDictEqual({}, self.skill_manager.skill_loaders) - self.skill_loader_mock.unload.assert_called_once_with() - - def test_send_skill_list(self): - self.skill_loader_mock.active = True - self.skill_loader_mock.loaded = True - self.skill_manager.send_skill_list(None) - - self.assertListEqual( - ['mycroft.skills.list'], - self.message_bus_mock.message_types - ) - message_data = self.message_bus_mock.message_data[0] - self.assertIn('test_skill', message_data.keys()) - skill_data = message_data['test_skill'] - self.assertDictEqual(dict(active=True, id='test_skill'), skill_data) - - def test_stop(self): - self.skill_manager.stop() - - self.assertTrue(self.skill_manager._stop_event.is_set()) - instance = self.skill_loader_mock.instance - instance.default_shutdown.assert_called_once_with() - - def test_handle_converse_request(self): - message = Mock() - message.data = dict(skill_id='test_skill', utterances=['hey you'], - lang='en-US') - self.skill_loader_mock.loaded = True - converse_response_mock = Mock() - self.skill_manager._emit_converse_response = converse_response_mock - converse_error_mock = Mock() - self.skill_manager._emit_converse_error = converse_error_mock - self.skill_manager.handle_converse_request(message) - - converse_response_mock.assert_called_once_with( - True, - message, - self.skill_loader_mock - ) - converse_error_mock.assert_not_called() - - def test_converse_request_missing_skill(self): - message = Mock() - message.data = dict(skill_id='foo') - self.skill_loader_mock.loaded = True - converse_response_mock = Mock() - self.skill_manager._emit_converse_response = converse_response_mock - converse_error_mock = Mock() - self.skill_manager._emit_converse_error = converse_error_mock - self.skill_manager.handle_converse_request(message) - - converse_response_mock.assert_not_called() - converse_error_mock.assert_called_once_with( - message, - 'foo', - 'skill id does not exist' - ) - - def test_converse_request_skill_not_loaded(self): - message = Mock() - message.data = dict(skill_id='test_skill') - self.skill_loader_mock.loaded = False - converse_response_mock = Mock() - self.skill_manager._emit_converse_response = converse_response_mock - converse_error_mock = Mock() - self.skill_manager._emit_converse_error = converse_error_mock - self.skill_manager.handle_converse_request(message) - - converse_response_mock.assert_not_called() - converse_error_mock.assert_called_once_with( - message, - 'test_skill', - 'converse requested but skill not loaded' - ) - - def test_schedule_now(self): - with patch(self.mock_package + 'time') as time_mock: - time_mock.return_value = 100 - self.skill_updater_mock.next_download = 0 - self.skill_manager.schedule_now(None) - self.assertEqual(99, self.skill_manager.skill_updater.next_download) - - def test_handle_paired(self): - self.skill_updater_mock.next_download = 0 - self.skill_manager.handle_paired(None) - updater = self.skill_manager.skill_updater - updater.post_manifest.assert_called_once_with( - reload_skills_manifest=True) - - def test_deactivate_skill(self): - message = Mock() - message.data = dict(skill='test_skill') - self.skill_manager.deactivate_skill(message) - self.skill_loader_mock.deactivate.assert_called_once_with() - - def test_deactivate_except(self): - message = Mock() - message.data = dict(skill='test_skill') - self.skill_loader_mock.active = True - foo_skill_loader = Mock(spec=SkillLoader) - foo_skill_loader.skill_id = 'foo' - foo2_skill_loader = Mock(spec=SkillLoader) - foo2_skill_loader.skill_id = 'foo2' - test_skill_loader = Mock(spec=SkillLoader) - test_skill_loader.skill_id = 'test_skill' - self.skill_manager.skill_loaders['foo'] = foo_skill_loader - self.skill_manager.skill_loaders['foo2'] = foo2_skill_loader - self.skill_manager.skill_loaders['test_skill'] = test_skill_loader - - self.skill_manager.deactivate_except(message) - foo_skill_loader.deactivate.assert_called_once_with() - foo2_skill_loader.deactivate.assert_called_once_with() - self.assertFalse(test_skill_loader.deactivate.called) - - def test_activate_skill(self): - message = Mock() - message.data = dict(skill='test_skill') - test_skill_loader = Mock(spec=SkillLoader) - test_skill_loader.skill_id = 'test_skill' - test_skill_loader.active = False - - self.skill_manager.skill_loaders = {} - self.skill_manager.skill_loaders['test_skill'] = test_skill_loader - - self.skill_manager.activate_skill(message) - test_skill_loader.activate.assert_called_once_with() - - def test_load_on_startup(self): - self.skill_dir.mkdir(parents=True) - self.skill_dir.joinpath('__init__.py').touch() - patch_obj = self.mock_package + 'SkillLoader' - self.skill_manager.skill_loaders = {} - with patch(patch_obj, spec=True) as loader_mock: - self.skill_manager._load_on_startup() - loader_mock.return_value.load.assert_called_once_with() - self.assertEqual( - loader_mock.return_value, - self.skill_manager.skill_loaders[str(self.skill_dir)] - ) - self.assertListEqual( - ['mycroft.skills.initialized'], - self.message_bus_mock.message_types - ) - - def test_load_newly_installed_skill(self): - self.skill_dir.mkdir(parents=True) - self.skill_dir.joinpath('__init__.py').touch() - patch_obj = self.mock_package + 'SkillLoader' - self.skill_manager.skill_loaders = {} - with patch(patch_obj, spec=True) as loader_mock: - self.skill_manager._load_new_skills() - loader_mock.return_value.load.assert_called_once_with() - self.assertEqual( - loader_mock.return_value, - self.skill_manager.skill_loaders[str(self.skill_dir)] - ) - - def test_reload_modified(self): - self.skill_dir.mkdir(parents=True) - self.skill_dir.joinpath('__init__.py').touch() - self.skill_loader_mock.reload_needed.return_value = True - self.skill_manager._reload_modified_skills() - self.skill_loader_mock.reload.assert_called_once_with() - self.assertEqual( - self.skill_loader_mock, - self.skill_manager.skill_loaders[str(self.skill_dir)] - ) - - def test_update_skills(self): - updater_mock = Mock() - updater_mock.update_skills = Mock() - updater_mock.next_download = 0 - self.skill_manager.skill_updater = updater_mock - self.skill_manager._update_skills() - updater_mock.update_skills.assert_called_once_with() diff --git a/test/unittests/stt/__init__.py b/test/unittests/stt/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/test/unittests/stt/test_stt.py b/test/unittests/stt/test_stt.py deleted file mode 100644 index 2eefb0302a9a..000000000000 --- a/test/unittests/stt/test_stt.py +++ /dev/null @@ -1,313 +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 unittest - -from unittest.mock import MagicMock, patch - -import mycroft.stt -from mycroft.configuration import Configuration - -from test.util import base_config - - -class TestSTT(unittest.TestCase): - @patch.object(Configuration, 'get') - def test_factory(self, mock_get): - mycroft.stt.STTApi = MagicMock() - config = base_config() - config.merge( - { - 'stt': { - 'module': 'mycroft', - 'wit': {'credential': {'token': 'FOOBAR'}}, - 'google': {'credential': {'token': 'FOOBAR'}}, - 'bing': {'credential': {'token': 'FOOBAR'}}, - 'houndify': {'credential': {'client_id': 'FOO', - "client_key": 'BAR'}}, - 'google_cloud': { - 'credential': { - 'json': {} - } - }, - 'ibm': { - 'credential': { - 'token': 'FOOBAR' - }, - 'url': 'https://test.com/' - }, - 'kaldi': {'uri': 'https://test.com'}, - 'mycroft': {'uri': 'https://test.com'} - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.MycroftSTT) - - config['stt']['module'] = 'google' - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.GoogleSTT) - - config['stt']['module'] = 'google_cloud' - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.GoogleCloudSTT) - - config['stt']['module'] = 'ibm' - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.IBMSTT) - - config['stt']['module'] = 'kaldi' - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.KaldiSTT) - - config['stt']['module'] = 'wit' - stt = mycroft.stt.STTFactory.create() - self.assertEqual(type(stt), mycroft.stt.WITSTT) - - @patch.object(Configuration, 'get') - def test_stt(self, mock_get): - mycroft.stt.STTApi = MagicMock() - config = base_config() - config.merge( - { - 'stt': { - 'module': 'mycroft', - 'mycroft': {'uri': 'https://test.com'} - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - class TestSTT(mycroft.stt.STT): - def execute(self, audio, language=None): - pass - - stt = TestSTT() - self.assertEqual(stt.lang, 'en-US') - config['lang'] = 'en-us' - - # Check that second part of lang gets capitalized - stt = TestSTT() - self.assertEqual(stt.lang, 'en-US') - - # Check that it works with two letters - config['lang'] = 'sv' - stt = TestSTT() - self.assertEqual(stt.lang, 'sv') - - @patch.object(Configuration, 'get') - def test_mycroft_stt(self, mock_get): - mycroft.stt.STTApi = MagicMock() - config = base_config() - config.merge( - { - 'stt': { - 'module': 'mycroft', - 'mycroft': {'uri': 'https://test.com'} - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - stt = mycroft.stt.MycroftSTT() - audio = MagicMock() - stt.execute(audio, 'en-us') - self.assertTrue(mycroft.stt.STTApi.called) - - @patch.object(Configuration, 'get') - def test_google_stt(self, mock_get): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'google', - 'google': {'credential': {'token': 'FOOBAR'}}, - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - audio = MagicMock() - stt = mycroft.stt.GoogleSTT() - stt.execute(audio) - self.assertTrue(stt.recognizer.recognize_google.called) - - @patch.object(Configuration, 'get') - def test_google_cloud_stt(self, mock_get): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'google_cloud', - 'google_cloud': { - 'credential': { - 'json': {} - } - }, - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - audio = MagicMock() - stt = mycroft.stt.GoogleCloudSTT() - stt.execute(audio) - self.assertTrue(stt.recognizer.recognize_google_cloud.called) - - @patch('mycroft.stt.post') - @patch.object(Configuration, 'get') - def test_ibm_stt(self, mock_get, mock_post): - import json - - config = base_config() - config.merge( - { - 'stt': { - 'module': 'ibm', - 'ibm': { - 'credential': { - 'token': 'FOOBAR' - }, - 'url': 'https://test.com' - }, - }, - 'lang': 'en-US' - } - ) - mock_get.return_value = config - - requests_object = MagicMock() - requests_object.status_code = 200 - requests_object.text = json.dumps({ - 'results': [ - { - 'alternatives': [ - { - 'confidence': 0.96, - 'transcript': 'sample response' - } - ], - 'final': True - } - ], - 'result_index': 0 - }) - mock_post.return_value = requests_object - - audio = MagicMock() - audio.sample_rate = 16000 - - stt = mycroft.stt.IBMSTT() - stt.execute(audio) - - test_url_base = 'https://test.com/v1/recognize' - mock_post.assert_called_with(test_url_base, - auth=('apikey', 'FOOBAR'), - headers={ - 'Content-Type': 'audio/x-flac', - 'X-Watson-Learning-Opt-Out': 'true' - }, - data=audio.get_flac_data(), - params={ - 'model': 'en-US_BroadbandModel', - 'profanity_filter': 'false' - }) - - @patch.object(Configuration, 'get') - def test_wit_stt(self, mock_get): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'wit', - 'wit': {'credential': {'token': 'FOOBAR'}}, - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - audio = MagicMock() - stt = mycroft.stt.WITSTT() - stt.execute(audio) - self.assertTrue(stt.recognizer.recognize_wit.called) - - @patch('mycroft.stt.post') - @patch.object(Configuration, 'get') - def test_kaldi_stt(self, mock_get, mock_post): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'kaldi', - 'kaldi': {'uri': 'https://test.com'}, - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - kaldiResponse = MagicMock() - kaldiResponse.json.return_value = { - 'hypotheses': [{'utterance': ' [noise] text'}, - {'utterance': ' asdf'}] - } - mock_post.return_value = kaldiResponse - audio = MagicMock() - stt = mycroft.stt.KaldiSTT() - self.assertEqual(stt.execute(audio), 'text') - - @patch.object(Configuration, 'get') - def test_bing_stt(self, mock_get): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'bing', - 'bing': {'credential': {'token': 'FOOBAR'}}, - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - audio = MagicMock() - stt = mycroft.stt.BingSTT() - stt.execute(audio) - self.assertTrue(stt.recognizer.recognize_bing.called) - - @patch.object(Configuration, 'get') - def test_houndify_stt(self, mock_get): - mycroft.stt.Recognizer = MagicMock - config = base_config() - config.merge( - { - 'stt': { - 'module': 'houndify', - 'houndify': {'credential': { - 'client_id': 'FOO', - 'client_key': "BAR"}} - }, - 'lang': 'en-US' - }) - mock_get.return_value = config - - audio = MagicMock() - stt = mycroft.stt.HoundifySTT() - stt.execute(audio) - self.assertTrue(stt.recognizer.recognize_houndify.called) diff --git a/test/unittests/tts/__init__.py b/test/unittests/tts/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/test/unittests/tts/test_mimic2_tts.py b/test/unittests/tts/test_mimic2_tts.py deleted file mode 100644 index 0631bdaa5e42..000000000000 --- a/test/unittests/tts/test_mimic2_tts.py +++ /dev/null @@ -1,102 +0,0 @@ -import unittest -from unittest import mock - -from mycroft.tts.mimic2_tts import Mimic2 -from mycroft.tts.remote_tts import RemoteTTSException - - -@mock.patch('mycroft.tts.mimic2_tts.FuturesSession') -@mock.patch('mycroft.tts.tts.PlaybackThread') -class TestMimic2(unittest.TestCase): - def test_get_tts(self, _, mock_session): - mock_session_instance = mock.Mock(name='SessionMock') - mock_session.return_value = mock_session_instance - - get_mock = mock.Mock(name='getMock') - mock_session_instance.get.return_value = get_mock - - result_mock = mock.Mock(name='resultMock') - get_mock.result.return_value = result_mock - result_mock.json.return_value = {'audio_base64': '', 'visimes': ''} - result_mock.status_code = 200 - m2 = Mimic2('en-US', {'url': 'https://just.testing.nu'}) - - with mock.patch('mycroft.tts.mimic2_tts.open') as mock_open: - wav_file, vis = m2.get_tts("Hello old friend", 'test.wav') - self.assertTrue(mock_session_instance.get.called) - - def test_get_tts_backend_error(self, _, mock_session): - mock_session_instance = mock.Mock(name='SessionMock') - mock_session.return_value = mock_session_instance - - get_mock = mock.Mock(name='getMock') - mock_session_instance.get.return_value = get_mock - - result_mock = mock.Mock(name='resultMock') - get_mock.result.return_value = result_mock - result_mock.json.return_value = '' - result_mock.status_code = 500 - - m2 = Mimic2('en-US', {'url': 'https://just.testing.nu'}) - with self.assertRaises(RemoteTTSException): - with mock.patch('mycroft.tts.mimic2_tts.open') as mock_open: - wav_file, vis = m2.get_tts("Hello old friend", 'test.wav') - - def test_visemes(self, _, __): - m2 = Mimic2('en-US', {'url': 'https://just.testing.nu'}) - phonemes = [('pau', 0.137), ('hh', 0.236), ('ax', 0.286), ('l', 0.387), - ('ow', 0.542), ('f', 0.642), ('r', 0.728), ('eh', 0.807), - ('n', 0.899), ('d', 1.033), ('pau', 1.187)] - vis = m2.viseme(phonemes) - self.assertEqual(vis, [('4', 0.137), ('0', 0.236), ('0', 0.286), - ('3', 0.387), ('2', 0.542), ('5', 0.642), - ('2', 0.728), ('0', 0.807), ('3', 0.899), - ('3', 1.033), ('4', 1.187)]) - - def test_preprocess(self, _, __): - """Test mimic2 specific preprocessing. - - The Mimic-2 backend has some specifics regarding how the sentence - must look to render correctly. - """ - m2 = Mimic2('en-US', {'url': 'https://just.testing.nu'}) - # Test short sentence get's '.' at the end. - self.assertEqual(m2._preprocess_sentence('Hello old friend'), - ['Hello old friend.']) - # Ensure that a very long sentence gets separated into chunks. - self.assertEqual(m2._preprocess_sentence('Harris said he felt such ' - 'extraordinary fits of ' - 'giddiness come over him at ' - 'times, that he hardly knew ' - 'what he was doing; and then ' - 'George said that he had ' - 'fits of giddiness too, and ' - 'hardly knew what he was ' - 'doing.'), - ['Harris said he felt such extraordinary fits of ' - 'giddiness come over him at times, that he hardly ' - 'knew what he was doing.', - 'and then George said that he had fits of giddiness ' - 'too, and hardly knew what he was doing.']) - - @mock.patch('mycroft.tts.mimic2_tts.open') - def test_phoneme_cache(self, mock_open, _, __): - m2 = Mimic2('en-US', {'url': 'https://just.testing.nu'}) - phonemes = [['pau', 0.137], ['hh', 0.236], ['ax', 0.286], ['l', 0.387], - ['ow', 0.542], ['f', 0.642], ['r', 0.728], ['eh', 0.807], - ['n', 0.899], ['d', 1.033], ['pau', 1.187]] - - mock_context = mock.Mock(name='context') - mock_file = mock.MagicMock(name='file') - mock_open.return_value = mock_file - mock_file.__enter__.return_value = mock_context - m2.save_phonemes('abc', phonemes) - self.assertTrue(mock_context.write.called_with) - with mock.patch('mycroft.tts.mimic2_tts.json.load') as mock_load: - read_phonemes = m2.load_phonemes('abc') - self.assertEqual(read_phonemes, None) - mock_load.reset_mock() - with mock.patch('mycroft.tts.mimic2_tts.os.path.exists') as _: - mock_load.return_value = phonemes - read_phonemes = m2.load_phonemes('abc') - self.assertEqual(read_phonemes, phonemes) diff --git a/test/unittests/tts/test_mimic_tts.py b/test/unittests/tts/test_mimic_tts.py deleted file mode 100644 index 4762b53c451c..000000000000 --- a/test/unittests/tts/test_mimic_tts.py +++ /dev/null @@ -1,101 +0,0 @@ -import stat - -import unittest -from unittest import mock, skip - -from mycroft.tts.mimic_tts import (Mimic, download_subscriber_voices, - get_mimic_binary, - get_subscriber_voices) - - -device_instance_mock = mock.Mock(name='device_api_instance') -device_instance_mock.is_subscriber = False - -subscribed_device = mock.Mock(name='subscriber_device') -subscribed_device.is_subscribed = True -subscribed_device.get_subscriber_voice_url.return_value = 'https://trinity' - - -@mock.patch('mycroft.tts.mimic_tts.DeviceApi') -@mock.patch('mycroft.tts.tts.PlaybackThread') -class TestMimic(unittest.TestCase): - @mock.patch('mycroft.tts.mimic_tts.subprocess') - def test_get_tts(self, mock_subprocess, _, mock_device_api): - mock_device_api.return_value = device_instance_mock - mock_subprocess.check_output().decode.return_value = 's:1 pau:2' - mock_subprocess.check_output.reset_mock() - m = Mimic('en-US', {}) - wav, phonemes = m.get_tts('hello', 'abc.wav') - mock_subprocess.check_output.assert_called_once_with( - m.args + ['-o', 'abc.wav', '-t', 'hello']) - self.assertEqual(phonemes, [['s', '1'], ['pau', '2']]) - - def test_viseme(self, _, mock_device_api): - mock_device_api.return_value = device_instance_mock - m = Mimic('en-US', {}) - phoneme_list = (['pau', 0.206], ['m', 0.287], ['ah', 0.401], - ['ch', 0.513], ['dh', 0.578], ['iy', 0.699], - ['s', 0.835], ['ey', 1.013], ['m', 1.118], - ['w', 1.213], ['ey', 1.345], ['dh', 1.415], - ['ae', 1.491], ['t', 1.539], ['b', 1.616], - ['r', 1.671], ['ih', 1.744], ['k', 1.819], - ['s', 1.923], ['d', 1.978], ['ow', 2.118], - ['n', 2.206], ['t', 2.301], ['pau', 2.408]) - - vis = m.viseme(phoneme_list) - self.assertEqual(vis, - [('4', 0.206), ('4', 0.287), ('0', 0.401), - ('3', 0.513), ('3', 0.578), ('0', 0.699), - ('3', 0.835), ('0', 1.013), ('4', 1.118), - ('2', 1.213), ('0', 1.345), ('3', 1.415), - ('0', 1.491), ('3', 1.539), ('4', 1.616), - ('2', 1.671), ('0', 1.744), ('3', 1.819), - ('3', 1.923), ('3', 1.978), ('2', 2.118), - ('3', 2.206), ('3', 2.301), ('4', 2.408)]) - - @mock.patch('mycroft.tts.mimic_tts.Thread') - def test_subscriber(self, mock_thread, _, mock_device_api): - mock_device_api.return_value = subscribed_device - default_mimic = get_mimic_binary() - trinity_mimic = get_subscriber_voices()['trinity'] - m = Mimic('en-US', {'voice': 'trinity'}) - mock_thread.assert_called_with(target=download_subscriber_voices, - args=['trinity']) - self.assertTrue(m.is_subscriber) - self.assertEqual(m.args, - [default_mimic, '-voice', 'ap', '-psdur', '-ssml']) - with mock.patch('mycroft.tts.mimic_tts.exists') as mock_exists: - mock_exists.return_value = True - self.assertEqual(m.args, [trinity_mimic, '-voice', - 'trinity', '-psdur', '-ssml']) - - @mock.patch('mycroft.tts.mimic_tts.sleep') - @mock.patch('mycroft.tts.mimic_tts.download') - @skip("TODO update this test for XDG") - def test_download(self, mock_download, mock_sleep, _, mock_device_api): - mock_device_api.return_value = subscribed_device - dl = mock.Mock() - dl.done = False - - def sleep_sideeffect(_): - """After one sleep call the download should be considered done.""" - nonlocal dl - dl.done = True - - mock_sleep.side_effect = sleep_sideeffect - mock_download.return_value = dl - - download_subscriber_voices('trinity') - # TODO XDG - '~/.config/mycroft/voices/mimic_tn' - self.assertEqual(mock_download.call_args[0][:2], - ('https://trinity', '/opt/mycroft/voices/mimic_tn')) - make_executable = mock_download.call_args[0][2] - - # Check that the excutable flag is set to the downloaded file - with mock.patch('mycroft.tts.mimic_tts.os.chmod') as mock_chmod: - with mock.patch('mycroft.tts.mimic_tts.os.stat') as mock_stat: - st_mock = mock.Mock() - mock_stat.return_value = st_mock - st_mock.st_mode = 0 - make_executable('/test') - mock_chmod.assert_called_with('/test', stat.S_IEXEC) diff --git a/test/unittests/tts/test_tts.py b/test/unittests/tts/test_tts.py deleted file mode 100644 index 4823146a09e1..000000000000 --- a/test/unittests/tts/test_tts.py +++ /dev/null @@ -1,268 +0,0 @@ -from pathlib import Path -from queue import Queue -import time - -import unittest -from unittest import mock, skip - -import mycroft.tts - -mock_phoneme = mock.Mock(name='phoneme') -mock_audio = "/tmp/mock_path" -mock_viseme = mock.Mock(name='viseme') - - -class MockTTS(mycroft.tts.TTS): - def __init__(self, lang, config, validator, audio_ext='wav', - phonetic_spelling=True, ssml_tags=None): - super().__init__(lang, config, validator, audio_ext) - self.get_tts = mock.Mock() - self.get_tts.return_value = (mock_audio, "this is a phoneme") - self.viseme = mock.Mock() - self.viseme.return_value = mock_viseme - - -class MockTTSValidator(mycroft.tts.TTSValidator): - def validate(self): - pass - - def validate_lang(self): - pass - - def validate_connection(self): - pass - - def get_tts_class(self): - return TestTTS - - -class TestPlaybackThread(unittest.TestCase): - def test_lifecycle(self): - playback = mycroft.tts.PlaybackThread(Queue()) - playback.init(mock.Mock()) - playback.start() - playback.stop() - playback.join() - - @mock.patch('mycroft.tts.tts.time') - @mock.patch('mycroft.tts.tts.play_wav') - @mock.patch('mycroft.tts.tts.play_mp3') - def test_process_queue(self, mock_play_mp3, mock_play_wav, mock_time): - queue = Queue() - playback = mycroft.tts.PlaybackThread(queue) - mock_tts = mock.Mock() - playback.init(mock_tts) - playback.enclosure = mock.Mock() - playback.start() - try: - # Test wav data - wav_mock = mock.Mock(name='wav_data') - queue.put(('wav', wav_mock, None, 0, False)) - time.sleep(0.2) - mock_tts.begin_audio.called_with() - mock_play_wav.assert_called_with(wav_mock, environment=None) - mock_tts.end_audio.assert_called_with(False) - - # Test mp3 data and trigger listening True - mp3_mock = mock.Mock(name='mp3_data') - queue.put(('mp3', mp3_mock, None, 0, True)) - time.sleep(0.2) - mock_play_mp3.assert_called_with(mp3_mock, environment=None) - mock_tts.end_audio.assert_called_with(True) - self.assertFalse(playback.enclosure.get.called) - - # Test sending visemes - mock_time.return_value = 1234 - visemes = mock.Mock(name='visemes') - queue.put(('mp3', mp3_mock, visemes, 0, True)) - time.sleep(0.2) - playback.enclosure.mouth_viseme.assert_called_with(1234, visemes) - - finally: - # Terminate the thread - playback.stop() - playback.join() - - -@mock.patch('mycroft.tts.tts.PlaybackThread') -@skip("mock_audio objects does not play well with Pathlib in get_tts") -class TestTTS(unittest.TestCase): - def test_execute(self, mock_playback_thread): - tts = MockTTS("en-US", {}, MockTTSValidator(None)) - bus_mock = mock.Mock() - tts.init(bus_mock) - self.assertTrue(tts.bus is bus_mock) - - tts.queue = mock.Mock() - with mock.patch('mycroft.tts.tts.open') as mock_open: - tts.cache.temporary_cache_dir = Path('/tmp/dummy') - tts.execute('Oh no, not again', 42) - tts.get_tts.assert_called_with( - 'Oh no, not again', - '/tmp/dummy/8da7f22aeb16bc3846ad07b644d59359.wav' - ) - tts.queue.put.assert_called_with( - ( - 'wav', - mock_audio, - mock_viseme, - 42, - False - ) - ) - - def test_execute_path_returned(self, mock_playback_thread): - tts = MockTTS("en-US", {}, MockTTSValidator(None)) - tts.get_tts.return_value = (Path(mock_audio), mock_viseme) - bus_mock = mock.Mock() - tts.init(bus_mock) - self.assertTrue(tts.bus is bus_mock) - - tts.queue = mock.Mock() - with mock.patch('mycroft.tts.tts.open') as mock_open: - tts.cache.temporary_cache_dir = Path('/tmp/dummy') - tts.execute('Oh no, not again', 42) - tts.get_tts.assert_called_with( - 'Oh no, not again', - '/tmp/dummy/8da7f22aeb16bc3846ad07b644d59359.wav' - ) - tts.queue.put.assert_called_with( - ( - 'wav', - mock_audio, - mock_viseme, - 42, - False - ) - ) - - @mock.patch('mycroft.tts.tts.open') - def test_phoneme_cache(self, mock_open, _): - tts = MockTTS("en-US", {}, MockTTSValidator(None)) - mock_context = mock.Mock(name='context') - mock_file = mock.MagicMock(name='file') - mock_open.return_value = mock_file - mock_file.__enter__.return_value = mock_context - - phonemes = mock.Mock() - # Test save phonemes - tts.save_phonemes('abc', phonemes) - mock_context.write.assert_called_with(phonemes) - - # Test load phonemes - mock_context.read.return_value = 'phonemes ' - read_phonemes = tts.load_phonemes('abc') - self.assertEqual(read_phonemes, None) - with mock.patch('mycroft.tts.tts.os.path.exists') as _: - read_phonemes = tts.load_phonemes('abc') - self.assertEqual(read_phonemes, 'phonemes') # assert stripped - - def test_ssml_support(self, _): - sentence = "Prosody can be used to change the way words " \ - "sound. The following words are " \ - " " \ - "quite a bit louder than the rest of this passage. " \ - " Each morning when I wake up, " \ - "I speak quite slowly and " \ - "deliberately until I have my coffee. I can " \ - "also change the pitch of my voice using prosody. " \ - "Do you like speech with a pitch " \ - "that is higher, or " \ - "is a lower pitch preferable?" - sentence_no_ssml = "Prosody can be used to change the way " \ - "words sound. The following words are quite " \ - "a bit louder than the rest of this passage. " \ - "Each morning when I wake up, I speak quite " \ - "slowly and deliberately until I have my " \ - "coffee. I can also change the pitch of my " \ - "voice using prosody. Do you like speech " \ - "with a pitch that is higher, or is " \ - "a lower pitch preferable?" - sentence_bad_ssml = "" + sentence + \ - "" - sentence_extra_ssml = "whisper tts<\\whispered>" - - tts = MockTTS("en-US", {}, MockTTSValidator(None)) - - # test valid ssml - tts.ssml_tags = ['speak', 'prosody'] - self.assertEqual(tts.validate_ssml(sentence), sentence) - - # test extra ssml - tts.ssml_tags = ['whispered'] - self.assertEqual(tts.validate_ssml(sentence_extra_ssml), - sentence_extra_ssml) - - # test unsupported extra ssml - tts.ssml_tags = ['speak', 'prosody'] - self.assertEqual(tts.validate_ssml(sentence_extra_ssml), - "whisper tts") - - # test mixed valid / invalid ssml - tts.ssml_tags = ['speak', 'prosody'] - self.assertEqual(tts.validate_ssml(sentence_bad_ssml), sentence) - - # test unsupported ssml - tts.ssml_tags = [] - self.assertEqual(tts.validate_ssml(sentence), sentence_no_ssml) - - self.assertEqual(tts.validate_ssml(sentence_bad_ssml), - sentence_no_ssml) - - self.assertEqual(mycroft.tts.TTS.remove_ssml(sentence), - sentence_no_ssml) - - def test_load_spellings(self, _): - """Check that the spelling dictionary gets loaded.""" - tts = MockTTS("en-US", {}, MockTTSValidator(None)) - self.assertTrue(tts.spellings != {}) - - def test_load_spelling_missing(self, _): - """Test that a missing phonetic spelling dictionary counts as empty.""" - tts = MockTTS("as-DF", {}, MockTTSValidator(None)) - self.assertTrue(tts.spellings == {}) - - -class TestTTSFactory(unittest.TestCase): - @mock.patch('mycroft.tts.tts.Configuration') - def test_create(self, mock_config): - config = { - 'tts': { - 'module': 'mock', - 'mimic': {'mock': True} - } - } - - mock_config.get.return_value = config - mock_mimic = mock.Mock(name='Mimic') - mock_mimic_instance = mock.Mock(name='mimic') - mock_mimic.return_value = mock_mimic_instance - - mock_tts_class = mock.Mock() - mock_tts_instance = mock.Mock() - mock_tts_class.return_value = mock_tts_instance - - mycroft.tts.TTSFactory.CLASSES['mimic'] = mock_mimic - mycroft.tts.TTSFactory.CLASSES['mock'] = mock_tts_class - - # Check that correct module is selected - tts_instance = mycroft.tts.TTSFactory.create() - self.assertEqual(tts_instance, mock_tts_instance) - - # Assert falling back to mimic if load fails - def side_effect(*args): - raise Exception - - mock_tts_class.side_effect = side_effect - tts_instance = mycroft.tts.TTSFactory.create() - self.assertEqual(tts_instance, mock_mimic_instance) - - # Check that mimic get's the proper config - mimic_conf = mock_mimic.call_args[0][1] - self.assertEqual(mimic_conf, config['tts']['mimic']) - - # Make sure exception is raised when mimic fails - mock_mimic.side_effect = side_effect - config['tts']['module'] = 'mimic' - with self.assertRaises(Exception): - tts_instance = mycroft.tts.TTSFactory.create() diff --git a/test/unittests/util/test_file_utils.py b/test/unittests/util/test_file_utils.py deleted file mode 100644 index 8da989ae8335..000000000000 --- a/test/unittests/util/test_file_utils.py +++ /dev/null @@ -1,214 +0,0 @@ -from os import makedirs -from os.path import (abspath, dirname, expanduser, join, normpath, isdir, - exists) -import shutil -import tempfile -from unittest import TestCase, mock - -from mycroft import MYCROFT_ROOT_PATH -from mycroft.util import ( - resolve_resource_file, - curate_cache, - create_file, - get_temp_path, - get_cache_directory, - read_stripped_lines, - read_dict) - - -test_config = { - 'data_dir': join(dirname(__file__), 'datadir'), - 'cache_dir': tempfile.gettempdir(), -} - - -@mock.patch('mycroft.configuration.Configuration') -class TestResolveResource(TestCase): - def test_absolute_path(self, mock_conf): - mock_conf.get.return_value = test_config - test_path = abspath(__file__) - - self.assertEqual(resolve_resource_file(test_path), test_path) - - @mock.patch('os.path.isfile') - def test_dot_mycroft(self, mock_isfile, mock_conf): - mock_conf.get.return_value = test_config - - def files_in_dotmycroft_exists(path): - return '.mycroft/' in path - - mock_isfile.side_effect = files_in_dotmycroft_exists - self.assertEqual(resolve_resource_file('1984.txt'), - expanduser('~/.mycroft/1984.txt')) - - @mock.patch('os.path.isfile') - def test_data_dir(self, mock_isfile, mock_conf): - """Check for file in the "configured data dir"/res/""" - mock_conf.get.return_value = test_config - - def files_in_mycroft_datadir_exists(path): - return 'datadir' in path - - mock_isfile.side_effect = files_in_mycroft_datadir_exists - self.assertEqual(resolve_resource_file('1984.txt'), - join(test_config['data_dir'], 'res', '1984.txt')) - - def test_source_package(self, mock_conf): - """Check file shipped in the mycroft res folder.""" - mock_conf.get.return_value = test_config - expected_path = join(MYCROFT_ROOT_PATH, 'mycroft', 'res', - 'text', 'en-us', 'and.word') - res_path = resolve_resource_file('text/en-us/and.word') - - self.assertEqual(normpath(res_path), normpath(expected_path)) - - def test_missing_file(self, mock_conf): - """Assert that the function returns None when file is not foumd.""" - mock_conf.get.return_value = test_config - self.assertTrue(resolve_resource_file('1984.txt') is None) - - -def create_cache_files(cache_dir): - """Create a couple of files in the cache directory.""" - huxley_path = join(cache_dir, 'huxley.txt') - aldous_path = join(cache_dir, 'alduos.txt') - f = open(huxley_path, 'w+') - f.close() - f = open(aldous_path, 'w+') - f.close() - return huxley_path, aldous_path - - -class TestReadFiles(TestCase): - base = dirname(__file__) - - def test_read_stripped_lines(self): - expected = ['Once upon a time', 'there was a great Dragon', - 'It was red and cute', 'The end'] - unstripped_path = join(TestReadFiles.base, 'unstripped_lines.txt') - self.assertEqual(list(read_stripped_lines(unstripped_path)), expected) - - def test_read_dict(self): - expected = {'fraggle': 'gobo', 'muppet': 'miss piggy'} - dict_path = join(TestReadFiles.base, 'muppets.dict') - self.assertEqual(read_dict(dict_path), expected) - - -@mock.patch('mycroft.configuration.Configuration') -class TestCache(TestCase): - def tearDownClass(): - shutil.rmtree(test_config['cache_dir'], ignore_errors=True) - - def test_get_cache_directory(self, mock_conf): - mock_conf.get.return_value = test_config - expected_path = join(test_config['cache_dir'], 'mycroft', 'cache') - self.assertEqual(get_cache_directory(), expected_path) - self.assertTrue(isdir(expected_path)) - - def test_get_cache_directory_with_domain(self, mock_conf): - mock_conf.get.return_value = test_config - expected_path = join(test_config['cache_dir'], 'mycroft', - 'cache', 'whales') - self.assertEqual(get_cache_directory('whales'), expected_path) - self.assertTrue(isdir(expected_path)) - - @mock.patch('mycroft.util.file_utils.psutil') - def test_curate_cache_plenty_space(self, mock_psutil, mock_conf): - """Test plenty of space free.""" - mock_conf.get.return_value = test_config - space = mock.Mock(name='diskspace') - mock_psutil.disk_usage.return_value = space - - cache_dir = get_cache_directory('braveNewWorld') - # Create files in the cache directory - huxley_path, aldous_path = create_cache_files(cache_dir) - - space.percent = 5.0 - space.free = 2 * 1024 * 1024 * 1024 # 2GB - space.total = 20 * 1024 * 1024 * 1024 # 20GB - self.assertEqual(curate_cache(cache_dir), []) - self.assertTrue(exists(aldous_path)) - self.assertTrue(exists(huxley_path)) - - @mock.patch('mycroft.util.file_utils.psutil') - def test_curate_cache_percent_low(self, mock_psutil, mock_conf): - """Free Percentage low but not free space.""" - mock_conf.get.return_value = test_config - space = mock.Mock(name='diskspace') - mock_psutil.disk_usage.return_value = space - - cache_dir = get_cache_directory('braveNewWorld') - # Create files in the cache directory - huxley_path, aldous_path = create_cache_files(cache_dir) - space.percent = 96.0 - space.free = 2 * 1024 * 1024 * 1024 # 2GB - space.total = 20 * 1024 * 1024 * 1024 # 20GB - self.assertEqual(curate_cache(cache_dir), []) - self.assertTrue(exists(aldous_path)) - self.assertTrue(exists(huxley_path)) - - @mock.patch('mycroft.util.file_utils.psutil') - def test_curate_cache_space_low(self, mock_psutil, mock_conf): - """Free space low, but not percentage.""" - mock_conf.get.return_value = test_config - space = mock.Mock(name='diskspace') - mock_psutil.disk_usage.return_value = space - - cache_dir = get_cache_directory('braveNewWorld') - # Create files in the cache directory - huxley_path, aldous_path = create_cache_files(cache_dir) - space.percent = 95.0 - space.free = 2 * 1024 * 1024 # 2MB - space.total = 20 * 1024 * 1024 * 1024 # 20GB - self.assertEqual(curate_cache(cache_dir), []) - self.assertTrue(exists(aldous_path)) - self.assertTrue(exists(huxley_path)) - - @mock.patch('mycroft.util.file_utils.psutil') - def test_curate_cache_both_low(self, mock_psutil, mock_conf): - """Test Free space and percentage low.""" - mock_conf.get.return_value = test_config - space = mock.Mock(name='diskspace') - mock_psutil.disk_usage.return_value = space - - cache_dir = get_cache_directory('braveNewWorld') - # Create files in the cache directory - huxley_path, aldous_path = create_cache_files(cache_dir) - space.percent = 96.0 - space.free = 2 * 1024 * 1024 # 2MB - space.total = 20 * 1024 * 1024 * 1024 # 20GB - self.assertEqual(curate_cache(cache_dir), - [aldous_path, huxley_path]) - self.assertFalse(exists(aldous_path)) - self.assertFalse(exists(huxley_path)) - - -TEST_CREATE_FILE_DIR = join(tempfile.gettempdir(), 'create_file_test') - - -class TestCreateFile(TestCase): - def setUp(self): - shutil.rmtree(TEST_CREATE_FILE_DIR, ignore_errors=True) - - def test_create_file_in_existing_dir(self): - makedirs(TEST_CREATE_FILE_DIR) - test_path = join(TEST_CREATE_FILE_DIR, 'test_file') - create_file(test_path) - self.assertTrue(exists(test_path)) - - def test_create_file_in_nonexisting_dir(self): - test_path = join(TEST_CREATE_FILE_DIR, 'test_file') - create_file(test_path) - self.assertTrue(exists(test_path)) - - def test_get_temp_path(self): - temp_dir = tempfile.gettempdir() - temp_file = get_temp_path('example.txt') - self.assertEqual(temp_file, temp_dir + '/example.txt') - temp_path = get_temp_path('mycroft', 'audio', 'example.wav') - self.assertEqual(temp_path, temp_dir + '/mycroft/audio/example.wav') - with self.assertRaises(TypeError): - failed_temp_path = get_temp_path(1) - - def tearDownClass(): - shutil.rmtree(TEST_CREATE_FILE_DIR, ignore_errors=True) diff --git a/test/unittests/version/__init__.py b/test/unittests/version/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/test/unittests/version/test_version.py b/test/unittests/version/test_version.py deleted file mode 100644 index 5d048c1a944b..000000000000 --- a/test/unittests/version/test_version.py +++ /dev/null @@ -1,70 +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 unittest -from unittest.mock import mock_open, patch - -from mycroft.version import check_version, OVOS_VERSION_STR, \ - CORE_VERSION_STR, VersionManager - - -VERSION_INFO = """ -{ - "coreVersion": "1505203453", - "enclosureVersion": "1.0.0" -}""" - - -class TestVersion(unittest.TestCase): - @patch('mycroft.version.CORE_VERSION_TUPLE', (0, 8, 20)) - def test_get_version(self): - """ - Tests for mycroft.version.get_version() - - Assures that only lower versions return True - """ - self.assertTrue(check_version('0.0.1')) - self.assertTrue(check_version('0.8.1')) - self.assertTrue(check_version('0.8.20')) - self.assertFalse(check_version('0.8.22')) - self.assertFalse(check_version('0.9.12')) - self.assertFalse(check_version('1.0.2')) - - @patch('mycroft.version.isfile') - @patch('mycroft.version.exists') - @patch('mycroft.version.open', - mock_open(read_data=VERSION_INFO), create=True) - def test_version_manager_get(self, mock_exists, mock_isfile): - """Test mycroft.version.VersionManager.get() - - Asserts that the method returns data from version file - """ - mock_isfile.return_value = True - mock_exists.return_value = True - - version = VersionManager.get() - self.assertEqual(version['coreVersion'], "1505203453") - self.assertEqual(version['enclosureVersion'], "1.0.0") - - @patch('mycroft.version.exists') - def test_version_manager_get_no_file(self, mock_exists): - """Test mycroft.version.VersionManager.get() - - Asserts that the method returns current version if no file exists. - """ - mock_exists.return_value = False - version = VersionManager.get() - self.assertEqual(version['coreVersion'], CORE_VERSION_STR) - self.assertEqual(version['OpenVoiceOSVersion'], OVOS_VERSION_STR) - self.assertEqual(version['enclosureVersion'], None) From bac6127df057f742c0aae4412846f36ade91e534 Mon Sep 17 00:00:00 2001 From: jarbasai Date: Wed, 16 Feb 2022 13:56:05 +0000 Subject: [PATCH 2/4] enable unittests --- .github/workflows/unit_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 7a857b71e70a..5c79335beff3 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,4 +1,4 @@ -name: Run License Tests +name: Run UnitTests on: pull_request: branches: @@ -8,7 +8,7 @@ on: workflow_dispatch: jobs: - license_tests: + unit_tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From e9f1e960f345e8a82363ce556cc9d7c4c91a6db6 Mon Sep 17 00:00:00 2001 From: jarbasai Date: Wed, 16 Feb 2022 13:56:36 +0000 Subject: [PATCH 3/4] enable unittests --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 5c79335beff3..f3c27aa828e7 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -33,4 +33,4 @@ jobs: pip install pytest pytest-timeout pytest-cov - name: Run unittests run: | - pytest test/unittests.py + pytest test/unittests From cbb9d363b0a55be378abad59f7ab177bd307a910 Mon Sep 17 00:00:00 2001 From: jarbasai Date: Wed, 16 Feb 2022 13:59:33 +0000 Subject: [PATCH 4/4] enable unittests --- test/unittests/audio/test_vlc_backend.py | 75 ------------------------ 1 file changed, 75 deletions(-) delete mode 100644 test/unittests/audio/test_vlc_backend.py diff --git a/test/unittests/audio/test_vlc_backend.py b/test/unittests/audio/test_vlc_backend.py deleted file mode 100644 index deadee579a1c..000000000000 --- a/test/unittests/audio/test_vlc_backend.py +++ /dev/null @@ -1,75 +0,0 @@ -import unittest -import unittest.mock as mock - -import mycroft.audio.services.vlc as vlc - - -config = { - 'backends': { - 'test_simple': { - 'type': 'vlc', - 'active': True - } - } -} - - -@mock.patch('mycroft.audio.services.vlc.vlc') -class TestVlcBackend(unittest.TestCase): - def test_load_service(self, mock_vlc_mod): - bus = mock.Mock() - self.assertEqual(len(vlc.load_service(config, bus)), 1) - - def test_playlist_methods(self, mock_vlc_mod): - bus = mock.Mock() - service = vlc.VlcService(config, bus) - - self.assertTrue(isinstance(service.supported_uris(), list)) - - # Check that the tracks are added to the track_list - service.add_list(['a.mp3', 'b.ogg', ['c.wav', 'audio/wav']]) - service.track_list.add_media.has_calls(['a.mp3', 'b.ogg', 'c.wav']) - - # Check that clearing replaces the playlist with an empty one - empty_list = mock.Mock(name='EmptyList') - service.instance.media_list_new.return_value = empty_list - service.clear_list() - self.assertTrue(service.track_list is empty_list) - service.list_player.set_media_list.assert_called_with(empty_list) - - def test_playback_methods(self, mock_vlc_mod): - bus = mock.Mock() - service = vlc.VlcService(config, bus) - - loop_mode = mock.Mock(name='Loop') - normal_mode = mock.Mock(name='Normal') - mock_vlc_mod.PlaybackMode.loop = loop_mode - mock_vlc_mod.PlaybackMode.default = normal_mode - # Check normal play - service.play(repeat=False) - service.list_player.set_playback_mode.assert_called_with(normal_mode) - service.list_player.set_playback_mode.reset_mock() - self.assertTrue(service.list_player.play.called) - service.list_player.play.reset_mock() - # Check repeat - service.play(repeat=True) - service.list_player.set_playback_mode.assert_called_with(loop_mode) - service.list_player.set_playback_mode.reset_mock() - self.assertTrue(service.list_player.play.called) - service.list_player.play.reset_mock() - - # Check pause - service.pause() - service.player.set_pause.assert_called_with(1) - service.player.set_pause.reset_mock() - - # Check resume - service.resume() - service.player.set_pause.assert_called_with(0) - service.player.set_pause.reset_mock() - - # Check stop - service.player.is_playing.return_value = False - self.assertFalse(service.stop()) - service.player.is_playing.return_value = True - self.assertTrue(service.stop())