diff --git a/common/lib/xmodule/xmodule/randomize_module.py b/common/lib/xmodule/xmodule/randomize_module.py index b0d9b8de6d..8dfaed731d 100644 --- a/common/lib/xmodule/xmodule/randomize_module.py +++ b/common/lib/xmodule/xmodule/randomize_module.py @@ -1,14 +1,13 @@ import logging import random +from lxml import etree from xmodule.x_module import XModule, STUDENT_VIEW from xmodule.seq_module import SequenceDescriptor - -from lxml import etree - from xblock.fields import Scope, Integer from xblock.fragment import Fragment + log = logging.getLogger('edx.' + __name__) @@ -38,12 +37,11 @@ class RandomizeModule(RandomizeFields, XModule): grading interaction is a tangle between super and subclasses of descriptors and modules. """ + def __init__(self, *args, **kwargs): super(RandomizeModule, self).__init__(*args, **kwargs) - # NOTE: calling self.get_children() creates a circular reference-- - # it calls get_child_descriptors() internally, but that doesn't work until - # we've picked a choice + # NOTE: calling self.get_children() doesn't work until we've picked a choice num_choices = len(self.descriptor.get_children()) if self.choice > num_choices: @@ -58,15 +56,23 @@ class RandomizeModule(RandomizeFields, XModule): else: self.choice = random.randrange(0, num_choices) - if self.choice is not None: - self.child_descriptor = self.descriptor.get_children()[self.choice] - # Now get_children() should return a list with one element - log.debug("children of randomize module (should be only 1): %s", - self.get_children()) - self.child = self.get_children()[0] - else: - self.child_descriptor = None - self.child = None + if self.child is not None: + log.debug("children of randomize module (should be only 1): %s", self.child) + + @property + def child_descriptor(self): + """ Return descriptor of selected choice """ + if self.choice is None: + return None + return self.descriptor.get_children()[self.choice] + + @property + def child(self): + """ Return module instance of selected choice """ + child_descriptor = self.child_descriptor + if child_descriptor is None: + return None + return self.system.get_module(child_descriptor) def get_child_descriptors(self): """ @@ -95,7 +101,6 @@ class RandomizeDescriptor(RandomizeFields, SequenceDescriptor): filename_extension = "xml" def definition_to_xml(self, resource_fs): - xml_object = etree.Element('randomize') for child in self.get_children(): self.runtime.add_block_as_child_node(child, xml_object) diff --git a/common/lib/xmodule/xmodule/tests/test_randomize_module.py b/common/lib/xmodule/xmodule/tests/test_randomize_module.py index 81ba45b56c..228ba90997 100644 --- a/common/lib/xmodule/xmodule/tests/test_randomize_module.py +++ b/common/lib/xmodule/xmodule/tests/test_randomize_module.py @@ -1,45 +1,104 @@ import unittest +from datetime import datetime, timedelta + +from django.utils.timezone import UTC +from opaque_keys.edx.locator import BlockUsageLocator +from xblock.fields import ScopeIds +from xmodule.randomize_module import RandomizeModule from .test_course_module import DummySystem as DummyImportSystem +from xblock.field_data import DictFieldData + ORG = 'test_org' COURSE = 'test_course' START = '2013-01-01T01:00:00' +_TODAY = datetime.now(UTC()) +_LAST_WEEK = _TODAY - timedelta(days=7) +_NEXT_WEEK = _TODAY + timedelta(days=7) class RandomizeModuleTestCase(unittest.TestCase): """Make sure the randomize module works""" - @staticmethod - def get_dummy_course(start): + + def setUp(self): + """ + Initialize dummy testing course. + """ + super(RandomizeModuleTestCase, self).setUp() + self.system = DummyImportSystem(load_error_modules=True) + self.system.seed = None + self.course = self.get_dummy_course() + self.modulestore = self.system.modulestore + + def get_dummy_course(self, start=_TODAY): """Get a dummy course""" - system = DummyImportSystem(load_error_modules=True) - - def to_attrb(n, v): - return '' if v is None else '{0}="{1}"'.format(n, v).lower() - - start_xml = ''' + self.start_xml = ''' - + start="{start}"> + Two houses, ... Three houses, ... + + '''.format(org=ORG, course=COURSE, start=start) - return system.process_xml(start_xml) + return self.system.process_xml(self.start_xml) def test_import(self): """ Just make sure descriptor loads without error """ - descriptor = self.get_dummy_course(START) + self.get_dummy_course(START) - # TODO: add tests that create a module and check. Passing state is a good way to - # check that child access works... + def test_has_started(self): + """ + Test CourseDescriptor.has_started. + """ + self.course.start = _LAST_WEEK + self.assertTrue(self.course.has_started()) + self.course.start = _NEXT_WEEK + self.assertFalse(self.course.has_started()) + + def test_children(self): + """ Check course/randomize module works fine """ + + self.assertTrue(self.course.has_children) + self.assertEquals(len(self.course.get_children()), 2) + + def inner_get_module(descriptor): + """ + Override systems.get_module + This method will be called when any call is made to self.system.get_module + """ + if isinstance(descriptor, BlockUsageLocator): + location = descriptor + descriptor = self.modulestore.get_item(location, depth=None) + descriptor.xmodule_runtime = self.get_dummy_course() + descriptor.xmodule_runtime.descriptor_runtime = descriptor._runtime # pylint: disable=protected-access + descriptor.xmodule_runtime.get_module = inner_get_module + return descriptor + + self.system.get_module = inner_get_module + + # Get randomize_descriptor from the course & verify its children + randomize_descriptor = inner_get_module(self.course.id.make_usage_key('randomize', 'my_randomize')) + self.assertTrue(randomize_descriptor.has_children) + self.assertEquals(len(randomize_descriptor.get_children()), 2) + + # Call RandomizeModule which will select an element from the list of available items + randomize_module = RandomizeModule( + randomize_descriptor, + self.system, + DictFieldData({}), + ScopeIds(None, None, self.course.id, self.course.id) + ) + self.assertTrue(randomize_module.child.display_name in ['A', 'B']) + self.assertTrue(randomize_module.get_children, 1)