From 3d2930bc86c674792cc1f3eca662bbffaf2b90b6 Mon Sep 17 00:00:00 2001 From: miro Date: Tue, 15 Oct 2024 18:47:11 +0100 Subject: [PATCH] port/tests_from_core --- test/unittests/dialog/__init__.py | 0 .../dialog/multiple_dialogs/one.dialog | 1 + .../dialog/multiple_dialogs/two.dialog | 1 + .../example-context.context.json | 5 + .../mustache_templates/example-context.dialog | 1 + .../mustache_templates/example-context.result | 1 + .../example-simple.context.json | 1 + .../mustache_templates/example-simple.dialog | 1 + .../mustache_templates/example-simple.result | 1 + .../example-comments.dialog | 2 + .../example-comments.result | 1 + .../example-multiple-context.context.json | 3 + .../example-multiple-context.dialog | 3 + .../example-multiple-context.result | 3 + .../example-multiple.context.json | 1 + .../example-multiple.dialog | 3 + .../example-multiple.result | 3 + test/unittests/dialog/test_dialog.py | 107 +++++++++++++++ test/unittests/test_event_container.py | 68 ++++++++++ test/unittests/test_event_scheduler.py | 123 ++++++++++++++++++ test/unittests/test_lock.py | 70 ++++++++++ 21 files changed, 399 insertions(+) create mode 100644 test/unittests/dialog/__init__.py create mode 100644 test/unittests/dialog/multiple_dialogs/one.dialog create mode 100644 test/unittests/dialog/multiple_dialogs/two.dialog create mode 100644 test/unittests/dialog/mustache_templates/example-context.context.json create mode 100644 test/unittests/dialog/mustache_templates/example-context.dialog create mode 100644 test/unittests/dialog/mustache_templates/example-context.result create mode 100644 test/unittests/dialog/mustache_templates/example-simple.context.json create mode 100644 test/unittests/dialog/mustache_templates/example-simple.dialog create mode 100644 test/unittests/dialog/mustache_templates/example-simple.result create mode 100644 test/unittests/dialog/mustache_templates_comments/example-comments.dialog create mode 100644 test/unittests/dialog/mustache_templates_comments/example-comments.result create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple-context.context.json create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple-context.dialog create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple-context.result create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple.context.json create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple.dialog create mode 100644 test/unittests/dialog/mustache_templates_multiple/example-multiple.result create mode 100644 test/unittests/dialog/test_dialog.py create mode 100644 test/unittests/test_event_container.py create mode 100644 test/unittests/test_event_scheduler.py create mode 100644 test/unittests/test_lock.py diff --git a/test/unittests/dialog/__init__.py b/test/unittests/dialog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/unittests/dialog/multiple_dialogs/one.dialog b/test/unittests/dialog/multiple_dialogs/one.dialog new file mode 100644 index 00000000..a2628c1e --- /dev/null +++ b/test/unittests/dialog/multiple_dialogs/one.dialog @@ -0,0 +1 @@ +ONE diff --git a/test/unittests/dialog/multiple_dialogs/two.dialog b/test/unittests/dialog/multiple_dialogs/two.dialog new file mode 100644 index 00000000..6333d309 --- /dev/null +++ b/test/unittests/dialog/multiple_dialogs/two.dialog @@ -0,0 +1 @@ +TWO diff --git a/test/unittests/dialog/mustache_templates/example-context.context.json b/test/unittests/dialog/mustache_templates/example-context.context.json new file mode 100644 index 00000000..091539d7 --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-context.context.json @@ -0,0 +1,5 @@ +{ + "name": "Chris", + "value": 10000, + "taxed_value": 6000 +} diff --git a/test/unittests/dialog/mustache_templates/example-context.dialog b/test/unittests/dialog/mustache_templates/example-context.dialog new file mode 100644 index 00000000..aecabb10 --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-context.dialog @@ -0,0 +1 @@ +Hello {{name}}, You have just won {{value}} dollars! Well, {{taxed_value}} dollars, after taxes. \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates/example-context.result b/test/unittests/dialog/mustache_templates/example-context.result new file mode 100644 index 00000000..0183799c --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-context.result @@ -0,0 +1 @@ +Hello Chris, You have just won 10000 dollars! Well, 6000 dollars, after taxes. \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates/example-simple.context.json b/test/unittests/dialog/mustache_templates/example-simple.context.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-simple.context.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates/example-simple.dialog b/test/unittests/dialog/mustache_templates/example-simple.dialog new file mode 100644 index 00000000..cd773cd1 --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-simple.dialog @@ -0,0 +1 @@ +Hello there! \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates/example-simple.result b/test/unittests/dialog/mustache_templates/example-simple.result new file mode 100644 index 00000000..cd773cd1 --- /dev/null +++ b/test/unittests/dialog/mustache_templates/example-simple.result @@ -0,0 +1 @@ +Hello there! \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates_comments/example-comments.dialog b/test/unittests/dialog/mustache_templates_comments/example-comments.dialog new file mode 100644 index 00000000..114efa33 --- /dev/null +++ b/test/unittests/dialog/mustache_templates_comments/example-comments.dialog @@ -0,0 +1,2 @@ +# This is a commented line +This is a line without comment diff --git a/test/unittests/dialog/mustache_templates_comments/example-comments.result b/test/unittests/dialog/mustache_templates_comments/example-comments.result new file mode 100644 index 00000000..30246649 --- /dev/null +++ b/test/unittests/dialog/mustache_templates_comments/example-comments.result @@ -0,0 +1 @@ +This is a line without comment diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.context.json b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.context.json new file mode 100644 index 00000000..1fd6fbf5 --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.context.json @@ -0,0 +1,3 @@ +{ + "name": "Sherlock" +} \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.dialog b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.dialog new file mode 100644 index 00000000..70d5611c --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.dialog @@ -0,0 +1,3 @@ +Hello there {{name}}! +Another possible outcome, {{name}} +Oh, {{name}} look at the capabilities \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.result b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.result new file mode 100644 index 00000000..ea3370bb --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple-context.result @@ -0,0 +1,3 @@ +Hello there Sherlock! +Another possible outcome, Sherlock +Oh, Sherlock look at the capabilities diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple.context.json b/test/unittests/dialog/mustache_templates_multiple/example-multiple.context.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple.context.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple.dialog b/test/unittests/dialog/mustache_templates_multiple/example-multiple.dialog new file mode 100644 index 00000000..2f058180 --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple.dialog @@ -0,0 +1,3 @@ +Hello there! +Another possible outcome +Oh look at the capabilities \ No newline at end of file diff --git a/test/unittests/dialog/mustache_templates_multiple/example-multiple.result b/test/unittests/dialog/mustache_templates_multiple/example-multiple.result new file mode 100644 index 00000000..267860fd --- /dev/null +++ b/test/unittests/dialog/mustache_templates_multiple/example-multiple.result @@ -0,0 +1,3 @@ +Hello there! +Another possible outcome +Oh look at the capabilities diff --git a/test/unittests/dialog/test_dialog.py b/test/unittests/dialog/test_dialog.py new file mode 100644 index 00000000..cd2150f2 --- /dev/null +++ b/test/unittests/dialog/test_dialog.py @@ -0,0 +1,107 @@ +# +# 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 pathlib +import json + + +from ovos_utils.dialog import MustacheDialogRenderer, load_dialogs, get_dialog + + +# TODO - move to ovos-workshop +class DialogTest(unittest.TestCase): + def setUp(self): + self.stache = MustacheDialogRenderer() + self.topdir = pathlib.Path(__file__).parent + + def test_general_dialog(self): + """ Test the loading and filling of valid simple mustache dialogs """ + template_path = self.topdir.joinpath('./mustache_templates') + for file in template_path.iterdir(): + if file.suffix == '.dialog': + self.stache.load_template_file(file.name, str(file.absolute())) + context = json.load( + file.with_suffix('.context.json').open( + 'r', encoding='utf-8')) + self.assertEqual( + self.stache.render(file.name, context), + file.with_suffix('.result').open('r', + encoding='utf-8').read()) + + def test_unknown_dialog(self): + """ Test for returned file name literals in case of unkown dialog """ + self.assertEqual( + self.stache.render("unknown.template"), "unknown template") + + def test_multiple_dialog(self): + """ + Test the loading and filling of valid mustache dialogs + where a dialog file contains multiple text versions + """ + template_path = self.topdir.joinpath('./mustache_templates_multiple') + for file in template_path.iterdir(): + if file.suffix == '.dialog': + self.stache.load_template_file(file.name, str(file.absolute())) + context = json.load( + file.with_suffix('.context.json').open( + 'r', encoding='utf-8')) + results = [ + line.strip() for line in file.with_suffix('.result').open( + 'r', encoding='utf-8') + ] + # Try all lines + for index, line in enumerate(results): + self.assertEqual( + self.stache.render( + file.name, index=index, context=context), + line.strip()) + # Test random index function + # (bad test because non-deterministic?) + self.assertIn( + self.stache.render(file.name, context=context), results) + + def test_comment_dialog(self): + """ + Test the loading and filling of valid mustache dialogs + where a dialog file contains multiple text versions + """ + template_path = self.topdir.joinpath('./mustache_templates_comments') + for f in template_path.iterdir(): + if f.suffix == '.dialog': + self.stache.load_template_file(f.name, str(f.absolute())) + results = [line.strip() + for line in f.with_suffix('.result').open('r')] + # Try all lines + for index, line in enumerate(results): + self.assertEqual(self.stache.render(f.name, index=index), + line.strip()) + + def test_dialog_loader(self): + template_path = self.topdir.joinpath('./multiple_dialogs') + renderer = load_dialogs(template_path) + self.assertEqual(renderer.render('one'), 'ONE') + self.assertEqual(renderer.render('two'), 'TWO') + + def test_dialog_loader_missing(self): + template_path = self.topdir.joinpath('./missing_dialogs') + renderer = load_dialogs(template_path) + self.assertEqual(renderer.render('test'), 'test') + + + + +if __name__ == "__main__": + unittest.main() diff --git a/test/unittests/test_event_container.py b/test/unittests/test_event_container.py new file mode 100644 index 00000000..afe8cb06 --- /dev/null +++ b/test/unittests/test_event_container.py @@ -0,0 +1,68 @@ +import unittest +from unittest import mock +from ovos_utils.events import EventContainer + + +def example_handler(message): + pass + + +class TestEventContainer(unittest.TestCase): + def test_init(self): + bus = mock.MagicMock() + + # Set bus via init + container = EventContainer(bus) + self.assertEqual(container.bus, bus) + + # Set bus using .set_bus + container = EventContainer(None) + self.assertIsNotNone(container.bus) + container.set_bus(bus) + self.assertEqual(container.bus, bus) + + def test_add(self): + bus = mock.MagicMock() + container = EventContainer(bus) + self.assertEqual(len(container.events), 0) + + # Test add normal event handler + container.add('test1', example_handler) + self.assertTrue(bus.on.called) + + # Test add single shot event handler + len_before = len(container.events) + container.add('test2', example_handler, once=True) + self.assertEqual(len_before + 1, len(container.events)) + self.assertTrue(bus.once.called) + + # Verify correct content in event container + self.assertTrue(('test1', example_handler) in container.events) + self.assertEqual(len(container.events), 2) + + def test_remove(self): + bus = mock.MagicMock() + container = EventContainer(bus) + self.assertEqual(len(container.events), 0) + + container.add('test1', example_handler) + container.add('test2', example_handler) + container.add('test3', example_handler) + self.assertEqual(len(container.events), 3) + + self.assertTrue(('test2', example_handler) in container.events) + container.remove('test2') + self.assertTrue(('test2', example_handler) not in container.events) + self.assertTrue(bus.remove_all_listeners.called) + + def test_clear(self): + bus = mock.MagicMock() + container = EventContainer(bus) + + container.add('test1', example_handler) + container.add('test2', example_handler) + container.add('test3', example_handler) + self.assertEqual(len(container.events), 3) + + container.clear() + self.assertEqual(len(container.events), 0) diff --git a/test/unittests/test_event_scheduler.py b/test/unittests/test_event_scheduler.py new file mode 100644 index 00000000..49b11d77 --- /dev/null +++ b/test/unittests/test_event_scheduler.py @@ -0,0 +1,123 @@ +""" + Test cases regarding the event scheduler. +""" + +import unittest +import time +from pyee import ExecutorEventEmitter + +from unittest.mock import MagicMock, patch +from ovos_utils.messagebus import FakeBus +from ovos_bus_client.util.scheduler import EventScheduler, EventSchedulerInterface + + +class TestEventScheduler(unittest.TestCase): + @patch('threading.Thread') + @patch('json.load') + @patch('json.dump') + @patch('builtins.open') + def test_create(self, mock_open, mock_json_dump, mock_load, mock_thread): + """ + Test creating and shutting down event_scheduler. + """ + mock_load.return_value = '' + mock_open.return_value = MagicMock() + emitter = MagicMock() + es = EventScheduler(emitter) + es.shutdown() + self.assertEqual(mock_json_dump.call_args[0][0], {}) + + @patch('threading.Thread') + @patch('json.load') + @patch('json.dump') + @patch('builtins.open') + def test_add_remove(self, mock_open, mock_json_dump, + mock_load, mock_thread): + """ + Test add an event and then remove it. + """ + # Thread start is mocked so will not actually run the thread loop + mock_load.return_value = '' + mock_open.return_value = MagicMock() + emitter = MagicMock() + es = EventScheduler(emitter) + + # 900000000000 should be in the future for a long time + es.schedule_event('test', 90000000000, None) + es.schedule_event('test-2', 90000000000, None) + + es.check_state() # run one cycle + self.assertTrue('test' in es.events) + self.assertTrue('test-2' in es.events) + + es.remove_event('test') + es.check_state() # run one cycle + self.assertTrue('test' not in es.events) + self.assertTrue('test-2' in es.events) + es.shutdown() + + @patch('threading.Thread') + @patch('json.load') + @patch('json.dump') + @patch('builtins.open') + def test_save(self, mock_open, mock_dump, mock_load, mock_thread): + """ + Test save functionality. + """ + mock_load.return_value = '' + mock_open.return_value = MagicMock() + emitter = MagicMock() + es = EventScheduler(emitter) + + # 900000000000 should be in the future for a long time + es.schedule_event('test', 900000000000, None) + es.schedule_event('test-repeat', 910000000000, 60) + es.check_state() + + es.shutdown() + + # Make sure the dump method wasn't called with test-repeat + self.assertEqual(mock_dump.call_args[0][0], + {'test': [(900000000000, None, {}, None)]}) + + @patch('threading.Thread') + @patch('json.load') + @patch('json.dump') + @patch('builtins.open') + def test_send_event(self, mock_open, mock_dump, mock_load, mock_thread): + """ + Test save functionality. + """ + mock_load.return_value = '' + mock_open.return_value = MagicMock() + emitter = MagicMock() + es = EventScheduler(emitter) + + # 0 should be in the future for a long time + es.schedule_event('test', time.time(), None) + + es.check_state() + self.assertEqual(emitter.emit.call_args[0][0].msg_type, 'test') + self.assertEqual(emitter.emit.call_args[0][0].data, {}) + es.shutdown() + + +class TestEventSchedulerInterface(unittest.TestCase): + def test_shutdown(self): + def f(message): + print('TEST FUNC') + + bus = ExecutorEventEmitter() + + es = EventSchedulerInterface('tester') + es.set_bus(FakeBus()) + es.set_id('id') + + # Schedule a repeating event + es.schedule_repeating_event(f, None, 10, name='f') + self.assertTrue(len(es.bus.ee._events['id:f']) == 1) + + es.shutdown() + # Check that the reference to the function has been removed from the + # bus emitter + self.assertTrue(len(bus._events['id:f']) == 0) diff --git a/test/unittests/test_lock.py b/test/unittests/test_lock.py new file mode 100644 index 00000000..2816ce1e --- /dev/null +++ b/test/unittests/test_lock.py @@ -0,0 +1,70 @@ +# 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 signal +import unittest +from shutil import rmtree + +from unittest.mock import patch +import os +from os.path import exists, isfile + +from ovos_utils.process_utils import PIDLock as Lock +from ovos_utils.file_utils import get_temp_path +from ovos_config.meta import get_xdg_base + + +class TestLock(unittest.TestCase): + def setUp(self): + if exists(get_temp_path(get_xdg_base())): + rmtree(get_temp_path(get_xdg_base())) + + def test_create_lock(self): + l1 = Lock('test') + self.assertTrue( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + + def test_delete_lock(self): + l1 = Lock('test') + self.assertTrue( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + l1.delete() + self.assertFalse( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + + @patch('os.kill') + def test_existing_lock(self, mock_kill): + """ Test that an existing lock will kill the old pid. """ + l1 = Lock('test') + self.assertTrue( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + l2 = Lock('test2') + self.assertFalse(mock_kill.called) + l2 = Lock('test') + self.assertTrue(mock_kill.called) + + def test_keyboard_interrupt(self): + l1 = Lock('test') + self.assertTrue( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + try: + os.kill(os.getpid(), signal.SIGINT) + except KeyboardInterrupt: + pass + self.assertFalse( + isfile(get_temp_path(get_xdg_base(), 'test.pid'))) + + +if __name__ == '__main__': + unittest.main()