Commit eb1ab3ea by Calen Pennington

Add tests of xmodule.model and xmodule.runtime, and fix some exposed bugs

parent 01411ae6
......@@ -133,8 +133,6 @@ class NamespaceDescriptor(object):
self._namespace = namespace
def __get__(self, instance, owner):
if owner is None:
return self
return self._namespace(instance)
......
......@@ -11,13 +11,13 @@ class KeyValueStore(object):
# data.
Key = namedtuple("Key", "scope, student_id, module_scope_id, field_name")
def get(key):
def get(self, key):
pass
def set(key, value):
def set(self, key, value):
pass
def delete(key):
def delete(self, key):
pass
......@@ -50,7 +50,7 @@ class DbModel(MutableMapping):
for namespace_name in self._module_cls.namespaces:
namespace = getattr(self._module_cls, namespace_name)
namespace_field = getattr(type(namespace), name, None)
if namespace_field is not None and isinstance(module_field, ModelType):
if namespace_field is not None and isinstance(namespace_field, ModelType):
return namespace_field
# Not in the class or in any of the namespaces, so name
......@@ -59,7 +59,6 @@ class DbModel(MutableMapping):
def _key(self, name):
field = self._getfield(name)
print name, field
module = field.scope.module
if module == ModuleScope.ALL:
......@@ -69,7 +68,7 @@ class DbModel(MutableMapping):
elif module == ModuleScope.DEFINITION:
module_id = self._usage.def_id
elif module == ModuleScope.TYPE:
module_id = self.module_type.__name__
module_id = self._module_cls.__name__
if field.scope.student:
student_id = self._student_id
......
from mock import patch
from unittest import TestCase
from nose.tools import assert_in, assert_equals, assert_raises
class ModelMetaclassTester(object):
from xmodule.model import *
def test_model_metaclass():
class ModelMetaclassTester(object):
__metaclass__ = ModelMetaclass
field_a = Int(scope=Scope.settings)
field_b = Int(scope=Scope.content)
def test_model_metaclass():
def __init__(self, model_data):
self._model_data = model_data
assert hasattr(ModelMetaclassTester, 'field_a')
assert hasattr(ModelMetaclassTester, 'field_b')
assert_in(ModelMetaclassTester.field_a, ModelMetaclassTester.fields)
assert_in(ModelMetaclassTester.field_b, ModelMetaclassTester.fields)
def test_parent_metaclass():
class HasChildren(object):
__metaclass__ = ParentModelMetaclass
has_children = True
class WithoutChildren(object):
__metaclass = ParentModelMetaclass
assert hasattr(HasChildren, 'children')
assert not hasattr(WithoutChildren, 'children')
assert isinstance(HasChildren.children, List)
assert_equals(Scope.settings, HasChildren.children.scope)
def test_field_access():
class FieldTester(object):
__metaclass__ = ModelMetaclass
field_a = Int(scope=Scope.settings)
field_b = Int(scope=Scope.content, default=10)
field_c = Int(scope=Scope.student_state, computed_default=lambda s: s.field_a + s.field_b)
def __init__(self, model_data):
self._model_data = model_data
field_tester = FieldTester({'field_a': 5, 'field_x': 15})
assert_equals(5, field_tester.field_a)
assert_equals(10, field_tester.field_b)
assert_equals(15, field_tester.field_c)
assert not hasattr(field_tester, 'field_x')
field_tester.field_a = 20
assert_equals(20, field_tester._model_data['field_a'])
assert_equals(10, field_tester.field_b)
assert_equals(30, field_tester.field_c)
del field_tester.field_a
assert_equals(None, field_tester.field_a)
assert hasattr(FieldTester, 'field_a')
class TestNamespace(Namespace):
field_x = List(scope=Scope.content)
field_y = String(scope=Scope.student_state, default="default_value")
@patch('xmodule.model.Namespace.load_classes', return_value=[('test', TestNamespace)])
def test_namespace_metaclass(mock_load_classes):
class TestClass(object):
__metaclass__ = NamespacesMetaclass
assert hasattr(TestClass, 'test')
assert hasattr(TestClass.test, 'field_x')
assert hasattr(TestClass.test, 'field_y')
assert_in(TestNamespace.field_x, TestClass.test.fields)
assert_in(TestNamespace.field_y, TestClass.test.fields)
assert isinstance(TestClass.test, Namespace)
@patch('xmodule.model.Namespace.load_classes', return_value=[('test', TestNamespace)])
def test_namespace_field_access(mock_load_classes):
class Metaclass(ModelMetaclass, NamespacesMetaclass):
pass
class FieldTester(object):
__metaclass__ = Metaclass
field_a = Int(scope=Scope.settings)
field_b = Int(scope=Scope.content, default=10)
field_c = Int(scope=Scope.student_state, computed_default=lambda s: s.field_a + s.field_b)
def __init__(self, model_data):
self._model_data = model_data
field_tester = FieldTester({
'field_a': 5,
'field_x': [1, 2, 3],
})
assert_equals(5, field_tester.field_a)
assert_equals(10, field_tester.field_b)
assert_equals(15, field_tester.field_c)
assert_equals([1, 2, 3], field_tester.test.field_x)
assert_equals('default_value', field_tester.test.field_y)
field_tester.test.field_x = ['a', 'b']
assert_equals(['a', 'b'], field_tester._model_data['field_x'])
del field_tester.test.field_x
assert_equals(None, field_tester.test.field_x)
assert_raises(AttributeError, getattr, field_tester.test, 'field_z')
assert_raises(AttributeError, delattr, field_tester.test, 'field_z')
# Namespaces are created on the fly, so setting a new attribute on one
# has no long-term effect
field_tester.test.field_z = 'foo'
assert_raises(AttributeError, getattr, field_tester.test, 'field_z')
assert 'field_z' not in field_tester._model_data
def test_field_serialization():
class CustomField(ModelType):
def from_json(self, value):
return value['value']
def to_json(self, value):
return {'value': value}
class FieldTester(object):
__metaclass__ = ModelMetaclass
field = CustomField()
def __init__(self, model_data):
self._model_data = model_data
field_tester = FieldTester({
'field': {'value': 4}
})
assert_equals(4, field_tester.field)
field_tester.field = 5
assert_equals({'value': 5}, field_tester._model_data['field'])
from nose.tools import assert_equals
from mock import patch
from xmodule.model import *
from xmodule.runtime import *
class Metaclass(NamespacesMetaclass, ParentModelMetaclass, ModelMetaclass):
pass
class TestNamespace(Namespace):
n_content = String(scope=Scope.content, default='nc')
n_settings = String(scope=Scope.settings, default='ns')
n_student_state = String(scope=Scope.student_state, default='nss')
n_student_preferences = String(scope=Scope.student_preferences, default='nsp')
n_student_info = String(scope=Scope.student_info, default='nsi')
n_by_type = String(scope=Scope(False, ModuleScope.TYPE), default='nbt')
n_for_all = String(scope=Scope(False, ModuleScope.ALL), default='nfa')
n_student_def = String(scope=Scope(True, ModuleScope.DEFINITION), default='nsd')
with patch('xmodule.model.Namespace.load_classes', return_value=[('test', TestNamespace)]):
class TestModel(object):
__metaclass__ = Metaclass
content = String(scope=Scope.content, default='c')
settings = String(scope=Scope.settings, default='s')
student_state = String(scope=Scope.student_state, default='ss')
student_preferences = String(scope=Scope.student_preferences, default='sp')
student_info = String(scope=Scope.student_info, default='si')
by_type = String(scope=Scope(False, ModuleScope.TYPE), default='bt')
for_all = String(scope=Scope(False, ModuleScope.ALL), default='fa')
student_def = String(scope=Scope(True, ModuleScope.DEFINITION), default='sd')
def __init__(self, model_data):
self._model_data = model_data
class DictKeyValueStore(KeyValueStore):
def __init__(self):
self.db = {}
def get(self, key):
return self.db[key]
def set(self, key, value):
self.db[key] = value
def delete(self, key):
del self.db[key]
Usage = namedtuple('Usage', 'id, def_id')
def test_empty():
tester = TestModel(DbModel(DictKeyValueStore(), TestModel, 's0', Usage('u0', 'd0')))
for collection in (tester, tester.test):
for field in collection.fields:
print "Getting %s from %r" % (field.name, collection)
assert_equals(field.default, getattr(collection, field.name))
new_value = 'new ' + field.name
print "Setting %s to %s on %r" % (field.name, new_value, collection)
setattr(collection, field.name, new_value)
print "Checking %s on %r" % (field.name, collection)
assert_equals(new_value, getattr(collection, field.name))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment