Add tests for __init__.py.
authorBenjamin Braatz <bb@bbraatz.eu>
Sat, 20 Mar 2021 20:53:49 +0000 (21:53 +0100)
committerBenjamin Braatz <bb@bbraatz.eu>
Sat, 20 Mar 2021 20:53:49 +0000 (21:53 +0100)
controlpi/__init__.py

index cc40432c59aa41f2efc98f2cb0ff2018bc25f7b4..173af725604aefaedb2e4fbf6e46c72caf1d5f4e 100644 (file)
@@ -2,9 +2,13 @@
 
 The infrastructure consists of the message bus from module messagebus, the
 plugin registry from module pluginregistry and the abstract base plugin from
-the module baseplugin.
+module baseplugin.
 
-The package combines them in its run function.
+The package combines them in its run function, which is used by __main__.py
+to run a ControlPi system based on a configuration file indefinitely.
+
+The test function is a utility function to test plugins with minimal
+boilerplate code.
 """
 import asyncio
 import jsonschema  # type: ignore
@@ -15,6 +19,7 @@ from controlpi.baseplugin import BasePlugin, PluginConf, ConfException
 
 from typing import Dict, List, Coroutine, Any
 
+
 CONF_SCHEMA = {'type': 'object',
                'patternProperties': {'.*': {'type': 'object'}}}
 
@@ -58,7 +63,27 @@ async def run(conf: Dict[str, PluginConf]) -> None:
     Setup message bus, process given configuration, and run message bus and
     plugins concurrently and indefinitely.
 
-    TODO: doctests for run using util.py
+    This function is mainly used by __main__.py to run a ControlPi system
+    based on a configuration loaded from a configuration JSON file on disk.
+
+    >>> async def test_coroutine():
+    ...     conf = {"Example Init":
+    ...             {"plugin": "Init",
+    ...              "messages": [{"id": 42,
+    ...                            "content": "Test Message"},
+    ...                           {"id": 42.42,
+    ...                            "content": "Second Message"}]},
+    ...             "Example Log":
+    ...             {"plugin": "Log",
+    ...              "filter": [{"sender": {"const": "Example Init"}}]}}
+    ...     run_task = asyncio.create_task(run(conf))
+    ...     await asyncio.sleep(0.1)
+    ...     run_task.cancel()
+    >>> asyncio.run(test_coroutine())  # doctest: +NORMALIZE_WHITESPACE
+    Example Log: {'sender': 'Example Init',
+                  'id': 42, 'content': 'Test Message'}
+    Example Log: {'sender': 'Example Init',
+                  'id': 42.42, 'content': 'Second Message'}
     """
     message_bus = MessageBus()
     coroutines = _process_conf(message_bus, conf)
@@ -76,6 +101,8 @@ async def test(conf: Dict[str, PluginConf],
     plugins concurrently, send given messages on message bus and print all
     messages on message bus. Terminate when queue of message bus is empty.
 
+    This function allows to test single plugins or small plugin
+    configurations with minimal boilerplate code:
     >>> asyncio.run(test(
     ...     {"Example Init": {"plugin": "Init",
     ...                       "messages": [{"id": 42,
@@ -89,23 +116,37 @@ async def test(conf: Dict[str, PluginConf],
              'sends': [{'id': {'const': 42},
                         'content': {'const': 'Test Message'}},
                        {'id': {'const': 42.42},
-                        'content': {'const': 'Second Message'}},
-                       {'target': {'const': 'Example Init'},
-                        'command': {'const': 'execute'}}],
+                        'content': {'const': 'Second Message'}}],
              'receives': [{'target': {'const': 'Example Init'},
                            'command': {'const': 'execute'}}]}
-    test(): {'sender': 'Example Init', 'target': 'Example Init',
-             'command': 'execute'}
-    test(): {'sender': 'test()', 'target': 'Example Init',
-             'command': 'execute'}
     test(): {'sender': 'Example Init',
              'id': 42, 'content': 'Test Message'}
     test(): {'sender': 'Example Init',
              'id': 42.42, 'content': 'Second Message'}
+    test(): {'sender': 'test()', 'target': 'Example Init',
+             'command': 'execute'}
     test(): {'sender': 'Example Init',
              'id': 42, 'content': 'Test Message'}
     test(): {'sender': 'Example Init',
              'id': 42.42, 'content': 'Second Message'}
+
+    Similar functionality could be reached by using the Log and Init plugins
+    to print messages and send some messages on the bus, but these would
+    clutter the test configuration and code to stop the idefinitely running
+    bus would have to be added to each and every test.
+
+    Incorrect plugin configurations can also be tested by this:
+    >>> asyncio.run(test(
+    ...     {"Example Init": {"plugin": "Init"}}, []))
+    'messages' is a required property
+    <BLANKLINE>
+    Failed validating 'required' in schema:
+        {'properties': {'messages': {'type': 'array'}},
+         'required': ['messages']}
+    <BLANKLINE>
+    On instance:
+        {'plugin': 'Init'}
+    Configuration for 'Example Init' is not valid.
     """
     message_bus = MessageBus()