From 9ec858772a57e4db35fd9ae9ea4ec00e89fb4cac Mon Sep 17 00:00:00 2001 From: Lucidiot Date: Mon, 23 Sep 2019 19:46:52 +0000 Subject: [PATCH] Unit tests --- .coveragerc | 4 ++ objtools/collections.py | 19 +++---- tests/test_collections.py | 113 ++++++++++++++++++++++++++++++++++++++ tests/test_registry.py | 63 +++++++++++++++++++++ 4 files changed, 187 insertions(+), 12 deletions(-) create mode 100644 .coveragerc create mode 100644 tests/test_collections.py create mode 100644 tests/test_registry.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..b14f680 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,4 @@ +[run] +include= + objtools/* + tests/* diff --git a/objtools/collections.py b/objtools/collections.py index acd1961..bd0a869 100644 --- a/objtools/collections.py +++ b/objtools/collections.py @@ -5,25 +5,23 @@ from typing import ( def namespacify(value: Any) -> Any: - if isinstance(value, (Namespace, MutableNamespace)): + if isinstance(value, Namespace): return value - elif isinstance(value, MutableMapping): - return MutableNamespace(value) - elif isinstance(value, Mapping): + if isinstance(value, Mapping): return Namespace(value) - elif isinstance(value, (str, ByteString)): + if isinstance(value, (str, ByteString)): # Do not treat strings and bytestrings as normal sequences return value - elif isinstance(value, Sequence): + if isinstance(value, Sequence): return list(map(namespacify, value)) - elif isinstance(value, Set): + if isinstance(value, Set): return set(map(namespacify, value)) - elif isinstance(value, Iterable): + if isinstance(value, Iterable): return map(namespacify, value) return value -class Namespace(Mapping): +class Namespace(MutableMapping): def __init__(self, *args: Iterable, **kwargs: Any): for iterable in (*args, kwargs): @@ -54,9 +52,6 @@ class Namespace(Mapping): def copy(self) -> 'Namespace': return self.__class__(self.__dict__) - -class MutableNamespace(Namespace, MutableMapping): - def __setitem__(self, name: Any, value: Any) -> None: setattr(self, name, value) diff --git a/tests/test_collections.py b/tests/test_collections.py new file mode 100644 index 0000000..15a8f65 --- /dev/null +++ b/tests/test_collections.py @@ -0,0 +1,113 @@ +from types import MappingProxyType +from typing import List, Set, Iterable, Iterator, Any +from unittest import TestCase +from objtools.collections import namespacify, Namespace + + +class TestNamespacify(TestCase): + + def test_namespace(self) -> None: + ns: Namespace = Namespace(a='b') + self.assertIs(ns, namespacify(ns)) + + def test_mapping(self) -> None: + ns: Namespace = namespacify(MappingProxyType({'a': 'b', 'c': 'd'})) + self.assertIsInstance(ns, Namespace) + self.assertEqual(dict(ns), {'a': 'b', 'c': 'd'}) + + def test_str(self) -> None: + ns: str = namespacify('something') + self.assertEqual(ns, 'something') + + def test_bytes(self) -> None: + ns: bytes = namespacify(b'something') + self.assertEqual(ns, b'something') + + def test_sequence(self) -> None: + ns: List[Namespace] = namespacify([{'a': 'b'}, {'c': 'd'}]) + self.assertIsInstance(ns, list) + self.assertTrue(all(isinstance(n, Namespace) for n in ns)) + self.assertListEqual(list(map(dict, ns)), [{'a': 'b'}, {'c': 'd'}]) + + def test_set(self) -> None: + ns: Set[int] = namespacify({1, 2, 3, 4, 5}) + self.assertSetEqual(ns, {1, 2, 3, 4, 5}) + + def test_iterable(self) -> None: + ns: Iterable[Namespace] = namespacify( + map(MappingProxyType, [{'a': 'b'}, {'c': 'd'}]) + ) + self.assertNotIsInstance(ns, list) + ns = list(ns) + self.assertTrue(all(isinstance(n, Namespace) for n in ns)) + self.assertListEqual(list(map(dict, ns)), [{'a': 'b'}, {'c': 'd'}]) + + def test_any(self) -> None: + self.assertEqual(namespacify(42), 42) + self.assertEqual(namespacify(42.0), 42.0) + self.assertEqual(namespacify(True), True) + self.assertEqual(namespacify(namespacify), namespacify) + + +class TestNamespace(TestCase): + + def test_init(self) -> None: + ns: Namespace = Namespace() + self.assertDictEqual(dict(ns), {}) + ns = Namespace({'b': 3}, {'a': 4}, a=1, b=2) + self.assertDictEqual(dict(ns), {'a': 1, 'b': 2}) + + def test_getitem(self) -> None: + ns: Namespace = Namespace(a=1) + self.assertEqual(ns['a'], 1) + with self.assertRaises(KeyError): + ns['b'] + + def test_getattr(self) -> None: + ns: Any = Namespace(a=1) + self.assertEqual(getattr(ns, 'a'), 1) + with self.assertRaises(AttributeError): + ns.b + + def test_iter(self) -> None: + ns: Namespace = Namespace(a=1, b=2) + iterator: Iterator = iter(ns) + self.assertEqual(next(iterator), 'a') + self.assertEqual(next(iterator), 'b') + + def test_len(self) -> None: + self.assertEqual(len(Namespace(a=1, b=2)), 2) + self.assertEqual(len(Namespace()), 0) + + def test_repr(self) -> None: + self.assertEqual(repr(Namespace()), 'Namespace({})') + self.assertEqual(repr(Namespace(a=1)), "Namespace({'a': 1})") + + def test_str(self) -> None: + self.assertEqual(str(Namespace()), '{}') + self.assertEqual(str(Namespace(a=1)), "{'a': 1}") + + def test_copy(self) -> None: + ns: Namespace = Namespace(a=1, b=2) + self.assertEqual(ns.copy(), ns) + self.assertIsNot(ns.copy(), ns) + + def test_set(self) -> None: + ns: Any = Namespace(a=4) + ns.a = 1 + ns['b'] = 2 + self.assertEqual(ns, Namespace(a=1, b=2)) + + def test_del(self) -> None: + ns: Any = Namespace(a=1, b=2) + del ns.a + self.assertEqual(ns, Namespace(b=2)) + del ns['b'] + self.assertEqual(ns, Namespace()) + + def test_del_not_found(self) -> None: + ns: Any = Namespace(a=1, b=2) + with self.assertRaises(AttributeError): + del ns.c + with self.assertRaises(KeyError): + del ns['c'] diff --git a/tests/test_registry.py b/tests/test_registry.py new file mode 100644 index 0000000..a93e96e --- /dev/null +++ b/tests/test_registry.py @@ -0,0 +1,63 @@ +from unittest import TestCase +from unittest.mock import patch +from objtools.registry import ClassRegistry + + +class TestRegistry(TestCase): + + def test_register(self) -> None: + registry = ClassRegistry() + registry.register('lol', str) + self.assertDictEqual(registry, {'lol': str}) + + def test_unregister(self) -> None: + registry = ClassRegistry({'lol': str}) + registry.unregister('lol') + self.assertDictEqual(registry, {}) + + def test_check_key(self) -> None: + registry = ClassRegistry() + with patch.object(registry, 'check_key') as mock: + mock.side_effect = KeyError('something') + with self.assertRaises(KeyError, msg='something'): + registry.register('lol', str) + + mock.side_effect = AssertionError('something') + with self.assertRaises(KeyError, msg='something'): + registry.register('lol', str) + + def test_check_value(self) -> None: + registry = ClassRegistry() + with patch.object(registry, 'check_value') as mock: + mock.side_effect = ValueError('something') + with self.assertRaises(ValueError, msg='something'): + registry.register('lol', str) + + mock.side_effect = AssertionError('something') + with self.assertRaises(ValueError, msg='something'): + registry.register('lol', str) + + def test_metaclass(self) -> None: + registry = ClassRegistry() + + class Thing(metaclass=registry.metaclass): # type: ignore + pass + + self.assertDictEqual(registry, {'Thing': Thing}) + + def test_metaclass_no_register(self) -> None: + registry = ClassRegistry() + + class Thing(metaclass=registry.metaclass, # type: ignore + register=False): + pass + + self.assertDictEqual(registry, {}) + + def test_metaclass_key(self) -> None: + registry = ClassRegistry() + + class Thing(metaclass=registry.metaclass, key='foo'): # type: ignore + pass + + self.assertDictEqual(registry, {'foo': Thing})