Skip to content

Commit e45809b

Browse files
committed
feat: initial unit tests for dispatch module
Signed-off-by: Christopher Arndt <chris@chrisarndt.de>
1 parent 87808a8 commit e45809b

File tree

3 files changed

+83
-20
lines changed

3 files changed

+83
-20
lines changed

run_tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
export MICROPYPATH="$(pwd):${MICROPYPATH:-.frozen:$HOME/.micropython/lib:/usr/lib/micropython}"
44
micropython tests/test_client.py "$@" && \
55
micropython tests/test_server.py "$@"
6+
micropython tests/test_dispatch.py "$@"

tests/test_dispatch.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Unit tests for the uosc.dispatch module."""
2+
3+
import unittest
4+
5+
from uosc.dispatch import get_global_root, OSCAddressContainer, OSCMethod, TYPETAGS_ANY
6+
7+
8+
class TestCreateMessage(unittest.TestCase):
9+
def setUp(self):
10+
def _dummy(*args, **kw):
11+
pass
12+
13+
self.root = root = get_global_root()
14+
root.register_method(_dummy, "/ops/math/add", "ii")
15+
root.register_method(_dummy, "/ops/math/sum", TYPETAGS_ANY)
16+
root.register_method(_dummy, "/ops/string/add", "ii")
17+
root.register_method(_dummy, "/ops/array/add", "ii")
18+
root.register_method(_dummy, "/ops/math/sub", "ii")
19+
20+
def tearDown(self):
21+
self.root.clear()
22+
23+
def test_get_global_root(self):
24+
root = get_global_root()
25+
self.assertTrue(root is self.root)
26+
27+
def test_osc_address_space(self):
28+
self.assertTrue(isinstance(self.root, OSCAddressContainer))
29+
self.assertEqual(self.root.name, "/")
30+
31+
def test_match_address(self):
32+
results = self.root.match("/ops/math/add")
33+
self.assertTrue(isinstance(results, list))
34+
self.assertEqual(len(results), 1)
35+
self.assertTrue(isinstance(results[0], OSCMethod))
36+
self.assertEqual(results[0].name, "/ops/math/add")
37+
self.assertTrue(callable(results[0]))
38+
39+
def test_match_address_with_typetags(self):
40+
results = self.root.match("/ops/math/add", "ii")
41+
self.assertTrue(isinstance(results, list))
42+
self.assertEqual(len(results), 1)
43+
self.assertTrue(isinstance(results[0], OSCMethod))
44+
self.assertEqual(results[0].name, "/ops/math/add")
45+
self.assertTrue(callable(results[0]))
46+
self.assertEqual(results[0].typetags, "ii")
47+
results = self.root.match("/ops/math/add", "f")
48+
self.assertTrue(isinstance(results, list))
49+
self.assertEqual(len(results), 0)
50+
51+
52+
if __name__ == '__main__':
53+
unittest.main()

uosc/dispatch.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
* OSC Address Spaces and OSC Addresses
66
* OSC Message Dispatching and Pattern Matching
77
8-
See the ``_demo()`` function below for a usage example.
8+
See the unit tests in ``tests/ttest_dispatch.py`` for API usage examples.
99
10-
**Note:** Path-traversing wildcards (``//``) as specified by the OSC 1.1
10+
**Note:** Path-traversing wildcards (``//``) as envisioned by the OSC 1.1
1111
"specification" paper are **not** supported.
1212
1313
"""
@@ -46,6 +46,11 @@ def expand_curly_braces(s, offset=0):
4646

4747

4848
class OSCAddressContainer(dict):
49+
"""
50+
Branch node in the OSC Address Space tree containing OSC Methods or
51+
sub-branches.
52+
53+
"""
4954
def __init__(self, name, parent=None):
5055
super().__init__()
5156
self.name = name
@@ -139,6 +144,13 @@ def _check_branch(node, ptn, nodetype, brace_expansion=True):
139144

140145

141146
class OSCMethod:
147+
"""
148+
A leaf node in the OSC Address Space tree wrapping the callable for an OSC
149+
Method.
150+
151+
"""
152+
__slots__ = ("name", "callable_", "typetags", "parent")
153+
142154
def __init__(self, name, callable_, typetags=TYPETAGS_ANY, parent=None):
143155
self.name = name
144156
self.callable_ = callable_
@@ -155,28 +167,25 @@ def __repr__(self):
155167
_root = None
156168

157169

158-
def get_default_root():
159-
global _root
160-
if _root is None:
161-
_root = OSCAddressContainer("/")
162-
return _root
170+
def get_global_root():
171+
"""Return global OSC Address Space root OSCAdressContainer node instance.
163172
173+
The root node is created on demand, when this function is first called and
174+
the tree will initially be unpopulated, i.e. have no branches or leaves.
164175
165-
def _demo():
166-
def fn(*args):
167-
pass
176+
The global root node, as the name says, is a module global, so changes to
177+
the tree it is the root of, will be visible via all references to it
178+
retrieved via this function in the same program.
168179
169-
import sys
180+
To create a non-global OSC Adress Space tree, just create a new
181+
``OSCAddressContainer`` instance like so:
170182
171-
root = get_default_root()
172-
root.register_method(fn, "/ops/math/add", "ii")
173-
root.register_method(fn, "/ops/math/sum", TYPETAGS_ANY)
174-
root.register_method(fn, "/ops/string/add", "ii")
175-
root.register_method(fn, "/ops/array/add", "ii")
176-
root.register_method(fn, "/ops/math/sub", "ii")
183+
myroot = OSCAddressContainer(name="/")
177184
178-
print(root.match(*sys.argv[1:]))
185+
"""
186+
global _root
179187

188+
if _root is None:
189+
_root = OSCAddressContainer("/")
180190

181-
if __name__ == "__main__":
182-
_demo()
191+
return _root

0 commit comments

Comments
 (0)