From 6563353bbf4b934d2be963d8d2b9a6e14f9df356 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 1 Nov 2014 00:36:02 -0400 Subject: bunch more tests --- t/__init__.py | 55 +++++++++++++ t/database_test.py | 56 ++++++++++++++ t/overrides_test.py | 187 +++++++++++++++++++++++++++++++++++++++++++++ t/simple_overrides_test.py | 93 ---------------------- 4 files changed, 298 insertions(+), 93 deletions(-) create mode 100644 t/database_test.py create mode 100644 t/overrides_test.py delete mode 100644 t/simple_overrides_test.py diff --git a/t/__init__.py b/t/__init__.py index e69de29..a714b9e 100644 --- a/t/__init__.py +++ b/t/__init__.py @@ -0,0 +1,55 @@ +import json +import mop + +CLASS_REGISTRY = {} + +class InMemoryDatabase(object): + def __init__(self): + self.store = {} + self.class_registry = {} + + def register_class(self, c): + self.class_registry[c.get_name()] = c + + def insert(self, name, obj): + data = self._get_repr(obj) + self.store[name] = json.dumps( + data, + separators=(',', ':'), + sort_keys=True + ) + + def lookup(self, name): + if name in self.store: + data = json.loads(self.store[name]) + if data["type"] == "plain": + return data["data"] + elif data["type"] == "object": + metaclass = self.class_registry[data["class"]] + return metaclass.create_instance(data["data"]) + else: + raise Exception("unknown object type") + else: + raise Exception("object not in database") + + def _get_repr(self, obj): + if type(obj) == type([]): + return { "type": "plain", "data": obj } + if type(obj) == type({}): + return { "type": "plain", "data": obj } + if type(obj) == type(""): + return { "type": "plain", "data": obj } + if type(obj) == type(0): + return { "type": "plain", "data": obj } + if type(obj) == type(True): + return { "type": "plain", "data": obj } + if type(obj) == type(None): + return { "type": "plain", "data": obj } + if hasattr(obj, 'isa') and obj.isa(mop.Object): + self.register_class(obj.metaclass) + return { + "type": "object", + "class": obj.metaclass.get_name(), + "data": obj.slots, + } + raise Exception("unknown object type") diff --git a/t/database_test.py b/t/database_test.py new file mode 100644 index 0000000..bd278c2 --- /dev/null +++ b/t/database_test.py @@ -0,0 +1,56 @@ +import unittest + +import mop + +from . import InMemoryDatabase + +class DatabaseTest(unittest.TestCase): + def test_database(self): + db = InMemoryDatabase() + + db.insert("foo", {"a": 1, "b": 2}) + assert db.lookup("foo") == {"a": 1, "b": 2} + assert db.store == {"foo": '{"data":{"a":1,"b":2},"type":"plain"}'} + + db.insert("foo", {"a": 3, "c": 5}) + assert db.lookup("foo") == {"a": 3, "c": 5} + assert db.store == {"foo": '{"data":{"a":3,"c":5},"type":"plain"}'} + + db.insert("bar", [1, 2, "b"]) + assert db.lookup("foo") == {"a": 3, "c": 5} + assert db.lookup("bar") == [1, 2, "b"] + assert db.store == { + "foo": '{"data":{"a":3,"c":5},"type":"plain"}', + "bar": '{"data":[1,2,"b"],"type":"plain"}', + } + + Point = mop.Class.new( + name="Point", + superclass=mop.Class.base_object_class(), + ) + Point.add_attribute(Point.attribute_class().new(name="x")) + Point.add_attribute(Point.attribute_class().new(name="y")) + Point.add_method(Point.method_class().new( + name="x", + body=lambda self: self.metaclass.get_all_attributes()["x"].get_value(self) + )) + Point.add_method(Point.method_class().new( + name="y", + body=lambda self: self.metaclass.get_all_attributes()["y"].get_value(self) + )) + Point.finalize() + + point = Point.new(x=10, y=23) + assert point.x() == 10 + assert point.y() == 23 + + db.insert("p", point) + point2 = db.lookup("p") + assert point2.x() == 10 + assert point2.y() == 23 + assert point is not point2 + assert db.store == { + "foo": '{"data":{"a":3,"c":5},"type":"plain"}', + "bar": '{"data":[1,2,"b"],"type":"plain"}', + "p": '{"class":"Point","data":{"x":10,"y":23},"type":"object"}', + } diff --git a/t/overrides_test.py b/t/overrides_test.py new file mode 100644 index 0000000..dd20097 --- /dev/null +++ b/t/overrides_test.py @@ -0,0 +1,187 @@ +import unittest + +import mop + +from . import InMemoryDatabase + +# in a real implementation, we'd add more functionality to the mop itself to +# allow for calling superclass methods, but that would be complicated enough to +# obscure the implementation and make it not as easy to follow (we would have +# to manage call stacks ourselves), and so we just do this instead for now +def call_method_at_class(c, method_name, invocant, *args, **kwargs): + return c.get_all_methods()[method_name].slots["body"]( + invocant, *args, **kwargs + ) + +class OverridesTest(unittest.TestCase): + def test_accessor_generation(self): + AccessorsMetaclass = mop.Class.new( + name="AccessorsMetaclass", + superclass=mop.Class, + ) + def add_attribute(self, attr): + name = attr.get_name() + call_method_at_class(mop.Class, "add_attribute", self, attr) + self.add_method(self.method_class().new( + name=name, + body=lambda self: self.metaclass.get_all_attributes()[name].get_value(self), + )) + AccessorsMetaclass.add_method(AccessorsMetaclass.metaclass.method_class().new( + name="add_attribute", + body=add_attribute, + )) + AccessorsMetaclass.finalize() + + Point = AccessorsMetaclass.new( + name="Point", + superclass=AccessorsMetaclass.base_object_class(), + ) + Point.add_attribute(Point.attribute_class().new(name="x", default=0)) + Point.add_attribute(Point.attribute_class().new(name="y", default=0)) + Point.finalize() + + point = Point.new(x=1, y=2) + assert point.x() == 1 + assert point.y() == 2 + + def test_trace_method_calls(self): + methods_called = [] + + TraceMethod = mop.Class.new( + name="TraceMethod", + superclass=mop.Method, + ) + + def execute(self, invocant, args, kwargs): + methods_called.append(self.get_name()) + return call_method_at_class(mop.Method, "execute", self, invocant, args, kwargs) + TraceMethod.add_method(TraceMethod.metaclass.method_class().new( + name="execute", + body=execute, + )) + TraceMethod.finalize() + + TraceClass = mop.Class.new( + name="TraceClass", + superclass=mop.Class, + ) + TraceClass.add_method(TraceClass.metaclass.method_class().new( + name="method_class", + body=lambda self: TraceMethod, + )) + TraceClass.finalize() + + Point = TraceClass.new( + name="Point", + superclass=TraceClass.base_object_class(), + ) + Point.add_attribute(Point.attribute_class().new(name="x", default=0)) + Point.add_attribute(Point.attribute_class().new(name="y", default=0)) + Point.add_method(Point.method_class().new( + name="x", + body=lambda self: self.metaclass.get_all_attributes()["x"].get_value(self) + )) + Point.add_method(Point.method_class().new( + name="y", + body=lambda self: self.metaclass.get_all_attributes()["y"].get_value(self) + )) + Point.finalize() + + point = Point.new(x=1, y=2) + assert methods_called == [] + assert point.x() == 1 + assert methods_called == ['x'] + assert point.y() == 2 + assert methods_called == ['x', 'y'] + + def test_db_backed_object(self): + DatabaseAttribute = mop.Class.new( + name="DatabaseAttribute", + superclass=mop.Attribute, + ) + DatabaseAttribute.add_attribute(DatabaseAttribute.attribute_class().new( + name="db", + )) + DatabaseAttribute.add_method(DatabaseAttribute.method_class().new( + name="db", + body=lambda self: self.metaclass.get_all_attributes()["db"].get_value(self) + )) + def get_value(self, instance): + key = str(instance.__hash__()) + ":" + self.get_name() + return self.db().lookup(key) + DatabaseAttribute.add_method(DatabaseAttribute.method_class().new( + name="get_value", + body=get_value, + )) + def set_value(self, instance, new_value): + key = str(instance.__hash__()) + ":" + self.get_name() + self.db().insert(key, new_value) + DatabaseAttribute.add_method(DatabaseAttribute.method_class().new( + name="set_value", + body=set_value, + )) + DatabaseAttribute.finalize() + + DatabaseBackedClass = mop.Class.new( + name="DatabaseBackedClass", + superclass=mop.Class, + ) + DatabaseBackedClass.add_attribute(DatabaseBackedClass.attribute_class().new( + name="db", + )) + DatabaseBackedClass.add_method(DatabaseBackedClass.method_class().new( + name="db", + body=lambda self: self.metaclass.get_all_attributes()["db"].get_value(self) + )) + def add_attribute(self, attr): + attr.metaclass.get_all_attributes()["db"].set_value(attr, self.db()) + call_method_at_class(mop.Class, "add_attribute", self, attr) + DatabaseBackedClass.add_method(DatabaseBackedClass.method_class().new( + name="add_attribute", + body=add_attribute, + )) + DatabaseBackedClass.add_method(DatabaseBackedClass.method_class().new( + name="attribute_class", + body=lambda self: DatabaseAttribute, + )) + DatabaseBackedClass.finalize() + + Point = DatabaseBackedClass.new( + name="Point", + superclass=DatabaseBackedClass.base_object_class(), + db=InMemoryDatabase(), + ) + Point.add_attribute(Point.attribute_class().new(name="x", default=0)) + Point.add_attribute(Point.attribute_class().new(name="y", default=0)) + Point.add_method(Point.method_class().new( + name="x", + body=lambda self: self.metaclass.get_all_attributes()["x"].get_value(self) + )) + Point.add_method(Point.method_class().new( + name="y", + body=lambda self: self.metaclass.get_all_attributes()["y"].get_value(self) + )) + Point.add_method(Point.method_class().new( + name="set_x", + body=lambda self, new_value: self.metaclass.get_all_attributes()["x"].set_value(self, new_value) + )) + Point.finalize() + + point = Point.new(x=3, y=7) + assert point.x() == 3 + assert point.y() == 7 + assert point.slots == {} + point.set_x(12) + assert point.x() == 12 + assert point.y() == 7 + assert point.slots == {} + assert len(Point.db().store) == 2 + + point2 = Point.new(x=123, y=-5) + assert point.x() == 12 + assert point.y() == 7 + assert point.slots == {} + assert point2.x() == 123 + assert point2.y() == -5 + assert point2.slots == {} + assert len(Point.db().store) == 4 diff --git a/t/simple_overrides_test.py b/t/simple_overrides_test.py deleted file mode 100644 index c38056a..0000000 --- a/t/simple_overrides_test.py +++ /dev/null @@ -1,93 +0,0 @@ -import unittest - -import mop - -# in a real implementation, we'd add more functionality to the mop itself to -# allow for calling superclass methods, but that would be complicated enough to -# obscure the implementation and make it not as easy to follow (we would have -# to manage call stacks ourselves), and so we just do this instead for now -def call_method_at_class(c, method_name, invocant, *args, **kwargs): - return c.get_all_methods()[method_name].slots["body"]( - invocant, *args, **kwargs - ) - -class SimpleOverridesTest(unittest.TestCase): - def test_accessor_generation(self): - AccessorsMetaclass = mop.Class.new( - name="AccessorsMetaclass", - superclass=mop.Class, - ) - def add_attribute(self, attr): - name = attr.get_name() - call_method_at_class(mop.Class, "add_attribute", self, attr) - self.add_method(self.method_class().new( - name=name, - body=lambda self: self.metaclass.get_all_attributes()[name].get_value(self), - )) - AccessorsMetaclass.add_method(AccessorsMetaclass.metaclass.method_class().new( - name="add_attribute", - body=add_attribute, - )) - AccessorsMetaclass.finalize() - - Point = AccessorsMetaclass.new( - name="Point", - superclass=AccessorsMetaclass.base_object_class(), - ) - Point.add_attribute(Point.attribute_class().new(name="x", default=0)) - Point.add_attribute(Point.attribute_class().new(name="y", default=0)) - Point.finalize() - - point = Point.new(x=1, y=2) - assert point.x() == 1 - assert point.y() == 2 - - def test_trace_method_calls(self): - methods_called = [] - - TraceMethod = mop.Class.new( - name="TraceMethod", - superclass=mop.Method, - ) - - def execute(self, invocant, args, kwargs): - methods_called.append(self.get_name()) - return call_method_at_class(mop.Method, "execute", self, invocant, args, kwargs) - TraceMethod.add_method(TraceMethod.metaclass.method_class().new( - name="execute", - body=execute, - )) - TraceMethod.finalize() - - TraceClass = mop.Class.new( - name="TraceClass", - superclass=mop.Class, - ) - TraceClass.add_method(TraceClass.metaclass.method_class().new( - name="method_class", - body=lambda self: TraceMethod, - )) - TraceClass.finalize() - - Point = TraceClass.new( - name="Point", - superclass=TraceClass.base_object_class(), - ) - Point.add_attribute(Point.attribute_class().new(name="x", default=0)) - Point.add_attribute(Point.attribute_class().new(name="y", default=0)) - Point.add_method(Point.method_class().new( - name="x", - body=lambda self: self.metaclass.get_all_attributes()["x"].get_value(self) - )) - Point.add_method(Point.method_class().new( - name="y", - body=lambda self: self.metaclass.get_all_attributes()["y"].get_value(self) - )) - Point.finalize() - - point = Point.new(x=1, y=2) - assert methods_called == [] - assert point.x() == 1 - assert methods_called == ['x'] - assert point.y() == 2 - assert methods_called == ['x', 'y'] -- cgit v1.2.3