From 847c7ca190f39a956bb1b154058dcad0732c47ba Mon Sep 17 00:00:00 2001
From: David Trattnig <david.trattnig@o94.at>
Date: Tue, 13 Jul 2021 16:06:42 +0200
Subject: [PATCH] New test cases. #78

---
 tests/test.py                 | 225 -----------------------------
 tests/test_config.py          |  73 ++++++++++
 tests/test_engine_executor.py | 264 ++++++++++++++++++++++++++++++++++
 tests/test_logger.py          |  47 ++++++
 4 files changed, 384 insertions(+), 225 deletions(-)
 delete mode 100644 tests/test.py
 create mode 100644 tests/test_config.py
 create mode 100644 tests/test_engine_executor.py
 create mode 100644 tests/test_logger.py

diff --git a/tests/test.py b/tests/test.py
deleted file mode 100644
index 6ef286a9..00000000
--- a/tests/test.py
+++ /dev/null
@@ -1,225 +0,0 @@
-
-#
-# Aura Engine (https://gitlab.servus.at/aura/engine)
-#
-# Copyright (C) 2017-2020 - The Aura Engine Team.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-
-import os
-import unittest
-import validators
-import time
-
-from datetime import datetime
-
-from src.base.utils import SimpleUtil as SU
-from src.base.logger import AuraLogger
-from src.base.config import AuraConfig
-from src.control import EngineExecutor
-
-
-
-class TestLogger(unittest.TestCase):
-    """
-    Testing the Logger.
-    """
-    aura_logger = None
-
-    def setUp(self):
-        self.config = AuraConfig()
-        self.aura_logger = AuraLogger(self.config)
-
-    def test_logger(self):
-        self.assertTrue(self.aura_logger.logger.hasHandlers())
-
-
-class TestConfig(unittest.TestCase):
-    """
-    Testing the Configuration.
-    """
-    config = None
-
-    def setUp(self):
-        self.config = AuraConfig()
-
-
-    def test_config(self):
-        # Check if config is available
-        self.assertIsNotNone(self.config.ini_path)
-
-        # Check if "install_dir" is a valid directory (is evaluated at runtime)
-        self.assertTrue(os.path.isdir(self.config.get("install_dir")))
-
-        # Check API Urls
-        self.assertTrue(validators.url(self.config.get("api_steering_status")))
-        self.assertTrue(validators.url(self.config.get("api_steering_calendar")))
-        self.assertTrue(validators.url(self.config.get("api_tank_status")))
-        tank_playlist_url = self.config.get("api_tank_playlist").replace("${ID}", "1")
-        self.assertTrue(validators.url(tank_playlist_url))
-        self.assertTrue(validators.url(self.config.get("api_engine_status")))
-        self.assertTrue(validators.url(self.config.get("api_engine_store_playlog")))
-        self.assertTrue(validators.url(self.config.get("api_engine_store_clock")))
-        engine_health_url = self.config.get("api_engine_store_health").replace("${ENGINE_NUMBER}", "1")
-        self.assertTrue(validators.url(engine_health_url))
-
-        # Check if Liquidsoap "socket_dir" is set and a directory
-        self.assertTrue(os.path.isdir(self.config.get("socket_dir")))
-
-        # Check if database settings are set
-        self.assertIsNotNone(self.config.get("db_user"))
-        self.assertIsNotNone(self.config.get("db_pass"))
-        self.assertIsNotNone(self.config.get("db_name"))
-        self.assertIsNotNone(self.config.get("db_host"))
-
-
-
-class TestEngineExecutor(unittest.TestCase):
-    """
-    Testing the EngineExecutor.
-    """
-
-    def setUp(self):
-        None
-
-
-    def test_single_executor(self):
-        # Initialize state and executor params
-        global_state = ["none"]
-        due_time = SU.timestamp() + 2
-        def f(param):
-            global_state[0] = param
-
-        # Before the executor is done there should be the initial value
-        e = EngineExecutor("RANDOM_NAMESPACE", None, due_time, f, "hello world")
-        self.assertEqual("none", global_state[0])
-        self.assertNotEqual("hello world", global_state[0])
-
-        # After 3 seconds there should be the updated value
-        time.sleep(5)
-        self.assertEqual("hello world", global_state[0])
-
-
-
-    def test_two_executors(self):
-        # Initialize state and executor params
-        global_state = ["none"]
-        def f(param):
-            global_state[0] = param
-
-        # Before the executor 1 is done there should be the initial value
-        due_time1 = SU.timestamp() + 4
-        e1 = EngineExecutor("EXECUTOR_1", None, due_time1, f, "hello world from executor 1")
-        self.assertEqual("none", global_state[0])
-        self.assertNotEqual("hello world from executor 1", global_state[0])
-
-        # Before the executor 2 is done there should be still the initial value
-        due_time2 = SU.timestamp() + 2
-        e2 = EngineExecutor("EXECUTOR_2", None, due_time2, f, "hello world from executor 2")
-        self.assertEqual("none", global_state[0])
-        self.assertNotEqual("hello world from executor 2", global_state[0])
-
-        # After 1 second there still should be the initial value
-        time.sleep(1)
-        self.assertEqual("none", global_state[0])
-
-        # After 3 seconds max there should be the updated value from executor 2
-        time.sleep(2)
-        self.assertEqual("hello world from executor 2", global_state[0])
-
-        # After 5 seconds max there should be the updated value from executor 1
-        time.sleep(5)
-        self.assertEqual("hello world from executor 1", global_state[0])
-
-
-
-    def test_parent_child_executors_in_order(self):
-        # Initialize state and executor params
-        global_state = ["none"]
-        def f(param):
-            global_state[0] = param
-
-        # Before the the parent is done there should be the initial value
-        due_time1 = SU.timestamp() + 1
-        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
-        self.assertEqual("none", global_state[0])
-
-        # Before the the child is done there should be the initial value
-        due_time2 = SU.timestamp() + 3
-        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
-        self.assertEqual("none", global_state[0])
-
-        # After 0.5 seconds there still should be the initial value
-        time.sleep(0.5)
-        self.assertEqual("none", global_state[0])
-
-        # After 2 seconds max there should be the updated value from parent executor
-        time.sleep(2)
-        self.assertEqual("hello world from parent", global_state[0])
-
-        # After 4 seconds max there should be the updated value from child executor
-        time.sleep(4)
-        self.assertEqual("hello world from child", global_state[0])
-
-
-
-    def test_parent_child_executors_with_child_before(self):
-        # Initialize state and executor params
-        global_state = ["none", "never called by parent"]
-        def f(param):
-            global_state[0] = param
-            if param == "hello world from parent":
-                global_state[1] = param
-
-
-        # Before the the parent is done there should be the initial value
-        due_time1 = SU.timestamp() + 3
-        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
-        self.assertEqual("none", global_state[0])
-
-        # Before the the child is done there should be the initial value
-        due_time2 = SU.timestamp() + 1
-        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
-        self.assertEqual("none", global_state[0])
-
-        # After 0.5 seconds there still should be the initial value
-        time.sleep(0.5)
-        self.assertEqual("none", global_state[0])
-
-        # After 2 seconds max there isn't a setting from the child yet, because it's waiting for the parent
-        time.sleep(2)
-        self.assertNotEqual("hello world from child", global_state[0])
-
-        # But the parent didn't set anything either, because it's scheduled for later
-        self.assertNotEqual("hello world from parent", global_state[0])
-        self.assertEqual("none", global_state[0])
-
-        # Double check if it has ever been called by parent
-        self.assertEqual("never called by parent", global_state[1])
-
-        # After 4 seconds max there should be the updated value from parent & child
-        # Because the child is due before the parent, it is executed right away,
-        # hence overwriting the value just set by the parent
-        time.sleep(4)
-        self.assertNotEqual("hello world from parent", global_state[0])
-        self.assertEqual("hello world from child", global_state[0])
-
-        # But we do not just believe what we expect, but check if it really has ever been called by a parent:
-        self.assertEqual("hello world from parent", global_state[1])
-
-
-
-if __name__ == '__main__':
-    unittest.main()
\ No newline at end of file
diff --git a/tests/test_config.py b/tests/test_config.py
new file mode 100644
index 00000000..392ebc46
--- /dev/null
+++ b/tests/test_config.py
@@ -0,0 +1,73 @@
+
+#
+# Aura Engine (https://gitlab.servus.at/aura/engine)
+#
+# Copyright (C) 2017-now() - The Aura Engine Team.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import os
+import unittest
+import validators
+
+from src.base.utils import SimpleUtil as SU
+from src.base.config import AuraConfig
+
+
+
+
+class TestConfig(unittest.TestCase):
+    """
+    Testing the Configuration.
+    """
+    config = None
+
+    def setUp(self):
+        self.config = AuraConfig()
+
+
+    def test_config(self):
+        # Check if config is available
+        self.assertIsNotNone(self.config.ini_path)
+
+        # Check if "install_dir" is a valid directory (is evaluated at runtime)
+        self.assertTrue(os.path.isdir(self.config.get("install_dir")))
+
+        # Check API Urls
+        self.assertTrue(validators.url(self.config.get("api_steering_status")))
+        self.assertTrue(validators.url(self.config.get("api_steering_calendar")))
+        self.assertTrue(validators.url(self.config.get("api_tank_status")))
+        tank_playlist_url = self.config.get("api_tank_playlist").replace("${ID}", "1")
+        self.assertTrue(validators.url(tank_playlist_url))
+        self.assertTrue(validators.url(self.config.get("api_engine_status")))
+        self.assertTrue(validators.url(self.config.get("api_engine_store_playlog")))
+        self.assertTrue(validators.url(self.config.get("api_engine_store_clock")))
+        engine_health_url = self.config.get("api_engine_store_health").replace("${ENGINE_NUMBER}", "1")
+        self.assertTrue(validators.url(engine_health_url))
+
+        # Check if Liquidsoap "socket_dir" is set and a directory
+        self.assertTrue(os.path.isdir(self.config.get("socket_dir")))
+
+        # Check if database settings are set
+        self.assertIsNotNone(self.config.get("db_user"))
+        self.assertIsNotNone(self.config.get("db_pass"))
+        self.assertIsNotNone(self.config.get("db_name"))
+        self.assertIsNotNone(self.config.get("db_host"))
+
+
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/tests/test_engine_executor.py b/tests/test_engine_executor.py
new file mode 100644
index 00000000..9977dd5a
--- /dev/null
+++ b/tests/test_engine_executor.py
@@ -0,0 +1,264 @@
+
+#
+# Aura Engine (https://gitlab.servus.at/aura/engine)
+#
+# Copyright (C) 2017-now() - The Aura Engine Team.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from src.engine import Engine
+import unittest
+import time
+
+from src.base.utils import SimpleUtil as SU
+from src.control import EngineExecutor
+
+
+
+
+class TestEngineExecutor(unittest.TestCase):
+    """
+    Testing the EngineExecutor.
+    """
+
+    def setUp(self):
+        None
+
+
+
+    def test_single_executor(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none"]
+        due_time = SU.timestamp() + 0.3
+        def f(param):
+            global_state[0] = param
+
+        # Before the executor is done there should be the initial value
+        e = EngineExecutor("RANDOM_NAMESPACE", None, due_time, f, "hello world")
+        self.assertEqual("none", global_state[0])
+        self.assertNotEqual("hello world", global_state[0])
+
+        # After 0.5 seconds there should be the updated value
+        time.sleep(0.5)
+        self.assertEqual("hello world", global_state[0])
+
+
+
+    def test_two_executors(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none"]
+        def f(param):
+            global_state[0] = param
+
+        # Before the executor 1 is done there should be the initial value
+        due_time1 = SU.timestamp() + 1
+        e1 = EngineExecutor("EXECUTOR_1", None, due_time1, f, "hello world from executor 1")
+        self.assertEqual("none", global_state[0])
+        self.assertNotEqual("hello world from executor 1", global_state[0])
+
+        # Before the executor 2 is done there should be still the initial value
+        due_time2 = SU.timestamp() + 0.5
+        e2 = EngineExecutor("EXECUTOR_2", None, due_time2, f, "hello world from executor 2")
+        self.assertEqual("none", global_state[0])
+        self.assertNotEqual("hello world from executor 2", global_state[0])
+
+        # After 0.3 seconds there still should be the initial value
+        time.sleep(0.3)
+        self.assertEqual("none", global_state[0])
+
+        # After 0.6 seconds max there should be the updated value from executor 2
+        time.sleep(0.3)
+        self.assertEqual("hello world from executor 2", global_state[0])
+
+        # After 1.1 seconds max there should be the updated value from executor 1
+        time.sleep(0.5)
+        self.assertEqual("hello world from executor 1", global_state[0])
+
+
+
+    def test_parent_child_executors_in_order(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none"]
+        def f(param):
+            global_state[0] = param
+
+        # Before the the parent is done there should be the initial value
+        due_time1 = SU.timestamp() + 0.5
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
+        self.assertEqual("none", global_state[0])
+
+        # Before the the child is done there should be the initial value
+        due_time2 = SU.timestamp() + 1
+        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
+        self.assertEqual("none", global_state[0])
+
+        # After 0.3 seconds there still should be the initial value
+        time.sleep(0.3)
+        self.assertEqual("none", global_state[0])
+
+        # After 0.6 seconds max there should be the updated value from parent executor
+        time.sleep(0.3)
+        self.assertEqual("hello world from parent", global_state[0])
+
+        # After 1.2 seconds max there should be the updated value from child executor
+        time.sleep(1.2)
+        self.assertEqual("hello world from child", global_state[0])
+
+
+
+    def test_parent_child_executors_with_child_before(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none", "never called by parent"]
+        def f(param):
+            global_state[0] = param
+            if param == "hello world from parent":
+                global_state[1] = param
+
+
+        # Before the the parent is done there should be the initial value
+        due_time1 = SU.timestamp() + 0.5
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
+        self.assertEqual("none", global_state[0])
+
+        # Before the the child is done there should be the initial value
+        due_time2 = SU.timestamp() + 1.5
+        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
+        self.assertEqual("none", global_state[0])
+
+        # After 0.2 seconds there still should be the initial value
+        time.sleep(0.2)
+        self.assertEqual("none", global_state[0])
+
+        # After 0.4 seconds max there isn't a setting from the child yet, because it's waiting for the parent
+        time.sleep(0.2)
+        self.assertNotEqual("hello world from child", global_state[0])
+
+        # But the parent didn't set anything either, because it's scheduled for later
+        self.assertNotEqual("hello world from parent", global_state[0])
+        self.assertEqual("none", global_state[0])
+
+        # Double check if it has ever been called by parent
+        self.assertEqual("never called by parent", global_state[1])
+
+        # After 2.2 seconds max there should be the updated value from parent & child
+        # Because the child is due before the parent, it is executed right away,
+        # hence overwriting the value just set by the parent
+        time.sleep(2.2)
+        self.assertNotEqual("hello world from parent", global_state[0])
+        self.assertEqual("hello world from child", global_state[0])
+
+        # But we do not just believe what we expect, but check if it really has ever been called by a parent
+        self.assertEqual("hello world from parent", global_state[1])
+
+
+
+
+    def test_timer_store_replacement(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none"]
+        def f(param):
+            global_state[0] = param
+
+        # There should be a total of 0 timers
+        timers = EngineExecutor.command_history()
+        self.assertEqual(0, len(timers))
+
+        # Before the the parent is done there should be the initial value
+        due_time1 = SU.timestamp() + 0.5
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
+        self.assertEqual("none", global_state[0])
+
+        # Before the the child is done there should be the initial value
+        due_time2 = SU.timestamp() + 1.5
+        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
+        self.assertEqual("none", global_state[0])
+
+        # There should be a total of 2 timers
+        timers = EngineExecutor.command_history()
+        self.assertEqual(2, len(timers))
+
+        # Replacing the parent with a new instance
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from alternative parent")
+        self.assertEqual("none", global_state[0])
+
+        # Before the the child is done there should be the initial value
+        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from alternative child")
+        self.assertEqual("none", global_state[0])
+
+        # After 1 seconds max there should be the updated value from the alternative parent
+        time.sleep(1)
+        self.assertEqual("hello world from alternative parent", global_state[0])
+
+        # After 2 seconds max there should be the updated value from the alternative child
+        time.sleep(2)
+        self.assertEqual("hello world from alternative child", global_state[0])
+
+        # There should be a total of 2 timers, even though 4 got instantiated
+        timers = EngineExecutor.command_history()
+        self.assertEqual(2, len(timers))
+
+
+
+
+    def test_dead_parent_with_lively_child(self):
+        # Initialize state and executor params
+        EngineExecutor.timer_store = {}
+        global_state = ["none"]
+        def f(param):
+            global_state[0] = param
+
+        # Before the the parent is done there should be the initial value
+        due_time1 = SU.timestamp() + 0.5
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from parent")
+        self.assertEqual("none", global_state[0])
+
+        # Before the the child is done there should be the initial value
+        due_time2 = SU.timestamp() + 1.5
+        child = EngineExecutor("EXECUTOR_CHILD", parent, due_time2, f, "hello world from child")
+        self.assertEqual("none", global_state[0])
+
+        # Wait until the parent timer got executed
+        time.sleep(1)
+        self.assertEqual("hello world from parent", global_state[0])
+
+        # Parent dead - child alive
+        self.assertEqual(False, parent.is_alive())
+        self.assertEqual(True, child.is_alive())
+
+        # Replacing the parent & child with a new instance
+        parent = EngineExecutor("EXECUTOR_PARENT", None, due_time1, f, "hello world from late parent")
+        child = EngineExecutor("EXECUTOR_PARENT", None, due_time2, f, "hello world from alternative child")
+
+        # New parent = dead before finished initialization already, so actually never born
+        self.assertEqual(False, parent.is_alive())
+
+        # Even though the late parent would be executed by now, it wasn't, because the initial parent
+        # was finished at instatiation time already
+        self.assertEqual("hello world from parent", global_state[0])
+
+        # Finally there should be the updated value from the alternative child
+        time.sleep(2)
+        self.assertEqual("hello world from alternative child", global_state[0])
+
+
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
diff --git a/tests/test_logger.py b/tests/test_logger.py
new file mode 100644
index 00000000..dbbdcf6c
--- /dev/null
+++ b/tests/test_logger.py
@@ -0,0 +1,47 @@
+
+#
+# Aura Engine (https://gitlab.servus.at/aura/engine)
+#
+# Copyright (C) 2017-now() - The Aura Engine Team.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+import unittest
+
+from src.base.utils import SimpleUtil as SU
+from src.base.logger import AuraLogger
+from src.base.config import AuraConfig
+
+
+
+
+class TestLogger(unittest.TestCase):
+    """
+    Testing the Logger.
+    """
+    aura_logger = None
+
+    def setUp(self):
+        self.config = AuraConfig()
+        self.aura_logger = AuraLogger(self.config)
+
+    def test_logger(self):
+        self.assertTrue(self.aura_logger.logger.hasHandlers())
+
+
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file
-- 
GitLab