aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2014-11-01 00:36:02 -0400
committerJesse Luehrs <doy@tozt.net>2014-11-01 00:36:02 -0400
commit6563353bbf4b934d2be963d8d2b9a6e14f9df356 (patch)
tree866d3a5643e62a1224ef7ea562a7c75673402f71
parent6954fa2b819feebf8c0e862a165f53aa5ecc3839 (diff)
downloadpython-mop-6563353bbf4b934d2be963d8d2b9a6e14f9df356.tar.gz
python-mop-6563353bbf4b934d2be963d8d2b9a6e14f9df356.zip
bunch more tests
-rw-r--r--t/__init__.py55
-rw-r--r--t/database_test.py56
-rw-r--r--t/overrides_test.py187
-rw-r--r--t/simple_overrides_test.py93
4 files changed, 298 insertions, 93 deletions
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']