Commit 437a3bf9 by Don Mitchell

Merge pull request #4542 from edx/split/perf_test

Split/perf test
parents 36d47dd0 02e21d8a
......@@ -8,8 +8,6 @@ from datetime import datetime
import dateutil.parser
from lazy import lazy
from opaque_keys.edx.locations import Location
from opaque_keys.edx.locator import UsageKey
from xmodule.seq_module import SequenceDescriptor, SequenceModule
from xmodule.graders import grader_from_conf
from xmodule.tabs import CourseTabList
......@@ -17,7 +15,6 @@ import json
from xblock.fields import Scope, List, String, Dict, Boolean, Integer
from .fields import Date
from opaque_keys.edx.locator import CourseLocator
from django.utils.timezone import UTC
log = logging.getLogger(__name__)
......
......@@ -568,10 +568,10 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
:param category: the xblock category
:param fields: the dictionary of {fieldname: value}
"""
result = collections.defaultdict(dict)
if fields is None:
return {}
return result
cls = self.mixologist.mix(XBlock.load_class(category, select=prefer_xmodules))
result = collections.defaultdict(dict)
for field_name, value in fields.iteritems():
field = getattr(cls, field_name)
result[field.scope][field_name] = value
......
......@@ -85,3 +85,6 @@ class InvalidBranchSetting(Exception):
super(InvalidBranchSetting, self).__init__()
self.expected_setting = expected_setting
self.actual_setting = actual_setting
def __unicode__(self, *args, **kwargs):
return u"Invalid branch: expected {} but got {}".format(self.expected_setting, self.actual_setting)
......@@ -95,7 +95,7 @@ def path_to_location(modulestore, usage_key):
category = path[path_index].block_type
if category == 'sequential' or category == 'videosequence':
section_desc = modulestore.get_item(path[path_index])
child_locs = [c.location for c in section_desc.get_children()]
child_locs = [c.location.version_agnostic() for c in section_desc.get_children()]
# positions are 1-indexed, and should be strings to be consistent with
# url parsing.
position_list.append(str(child_locs.index(path[path_index + 1]) + 1))
......
......@@ -53,7 +53,7 @@ class SplitMigrator(object):
new_run = source_course_key.run
new_course_key = CourseLocator(new_org, new_course, new_run, branch=ModuleStoreEnum.BranchName.published)
new_fields = self._get_json_fields_translate_references(original_course, new_course_key, None)
new_fields = self._get_fields_translate_references(original_course, new_course_key, None)
if fields:
new_fields.update(fields)
new_course = self.split_modulestore.create_course(
......@@ -92,7 +92,7 @@ class SplitMigrator(object):
course_version_locator,
module.location.block_type,
block_id=module.location.block_id,
fields=self._get_json_fields_translate_references(
fields=self._get_fields_translate_references(
module, course_version_locator, new_course.location.block_id
),
continue_version=True
......@@ -127,7 +127,7 @@ class SplitMigrator(object):
if field.is_set_on(split_module) and not module.fields[name].is_set_on(module):
field.delete_from(split_module)
for field, value in self._get_fields_translate_references(
module, new_draft_course_loc, published_course_usage_key.block_id
module, new_draft_course_loc, published_course_usage_key.block_id, field_names=False
).iteritems():
field.write_to(split_module, value)
......@@ -138,7 +138,7 @@ class SplitMigrator(object):
user_id, new_draft_course_loc,
new_locator.block_type,
block_id=new_locator.block_id,
fields=self._get_json_fields_translate_references(
fields=self._get_fields_translate_references(
module, new_draft_course_loc, published_course_usage_key.block_id
)
)
......@@ -173,43 +173,13 @@ class SplitMigrator(object):
new_parent.children.insert(new_parent_cursor, new_locator)
new_parent = self.split_modulestore.update_item(new_parent, user_id)
def _get_json_fields_translate_references(self, xblock, new_course_key, course_block_id):
"""
Return the json repr for explicitly set fields but convert all references to their Locators
"""
def get_translation(location):
"""
Convert the location
"""
return new_course_key.make_usage_key(
location.category,
location.block_id if location.category != 'course' else course_block_id
)
result = {}
for field_name, field in xblock.fields.iteritems():
if field.is_set_on(xblock):
field_value = getattr(xblock, field_name)
if isinstance(field, Reference) and field_value is not None:
result[field_name] = get_translation(field_value)
elif isinstance(field, ReferenceList):
result[field_name] = [
get_translation(ele) for ele in field_value
]
elif isinstance(field, ReferenceValueDict):
result[field_name] = {
key: get_translation(subvalue)
for key, subvalue in field_value.iteritems()
}
else:
result[field_name] = field.read_json(xblock)
return result
def _get_fields_translate_references(self, xblock, new_course_key, course_block_id):
def _get_fields_translate_references(self, xblock, new_course_key, course_block_id, field_names=True):
"""
Return a dictionary of field: value pairs for explicitly set fields
but convert all references to their BlockUsageLocators
Args:
field_names: if Truthy, the dictionary keys are the field names. If falsey, the keys are the
field objects.
"""
def get_translation(location):
"""
......@@ -223,19 +193,20 @@ class SplitMigrator(object):
result = {}
for field_name, field in xblock.fields.iteritems():
if field.is_set_on(xblock):
field_value = getattr(xblock, field_name)
field_value = field.read_from(xblock)
field_key = field_name if field_names else field
if isinstance(field, Reference) and field_value is not None:
result[field] = get_translation(field_value)
result[field_key] = get_translation(field_value)
elif isinstance(field, ReferenceList):
result[field] = [
result[field_key] = [
get_translation(ele) for ele in field_value
]
elif isinstance(field, ReferenceValueDict):
result[field] = {
result[field_key] = {
key: get_translation(subvalue)
for key, subvalue in field_value.iteritems()
}
else:
result[field] = field_value
result[field_key] = field_value
return result
......@@ -77,6 +77,8 @@ from .caching_descriptor_system import CachingDescriptorSystem
from xmodule.modulestore.split_mongo.mongo_connection import MongoConnection
from xmodule.error_module import ErrorDescriptor
from xmodule.modulestore.split_mongo import encode_key_for_mongo, decode_key_from_mongo
import types
from _collections import defaultdict
log = logging.getLogger(__name__)
......@@ -1006,7 +1008,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
raise DuplicateCourseError(locator, index)
partitioned_fields = self.partition_fields_by_scope(root_category, fields)
block_fields = partitioned_fields.setdefault(Scope.settings, {})
block_fields = partitioned_fields[Scope.settings]
if Scope.children in partitioned_fields:
block_fields.update(partitioned_fields[Scope.children])
definition_fields = self._serialize_fields(root_category, partitioned_fields.get(Scope.content, {}))
......@@ -1106,14 +1108,15 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
original_structure = self._lookup_course(descriptor.location)['structure']
index_entry = self._get_index_if_valid(descriptor.location, force)
definition_fields = descriptor.get_explicitly_set_fields_by_scope(Scope.content)
partitioned_fields = self.partition_xblock_fields_by_scope(descriptor)
definition_fields = partitioned_fields[Scope.content]
descriptor.definition_locator, is_updated = self.update_definition_from_data(
descriptor.definition_locator, definition_fields, user_id
)
original_entry = self._get_block_from_structure(original_structure, descriptor.location.block_id)
# check metadata
settings = descriptor.get_explicitly_set_fields_by_scope(Scope.settings)
settings = partitioned_fields[Scope.settings]
settings = self._serialize_fields(descriptor.category, settings)
if not is_updated:
is_updated = self._compare_settings(settings, original_entry['fields'])
......@@ -1235,7 +1238,8 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
def _persist_subdag(self, xblock, user_id, structure_blocks, new_id):
# persist the definition if persisted != passed
new_def_data = self._serialize_fields(xblock.category, xblock.get_explicitly_set_fields_by_scope(Scope.content))
partitioned_fields = self.partition_xblock_fields_by_scope(xblock)
new_def_data = self._serialize_fields(xblock.category, partitioned_fields[Scope.content])
is_updated = False
if xblock.definition_locator is None or isinstance(xblock.definition_locator.definition_id, LocalId):
xblock.definition_locator = self.create_definition_from_data(
......@@ -1270,7 +1274,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
children.append(child.block_id)
is_updated = is_updated or structure_blocks[encoded_block_id]['fields']['children'] != children
block_fields = xblock.get_explicitly_set_fields_by_scope(Scope.settings)
block_fields = partitioned_fields[Scope.settings]
block_fields = self._serialize_fields(xblock.category, block_fields)
if not is_new and not is_updated:
is_updated = self._compare_settings(block_fields, structure_blocks[encoded_block_id]['fields'])
......@@ -1697,6 +1701,18 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
index_entry['versions'][branch] = new_id
self.db_connection.update_course_index(index_entry)
def partition_xblock_fields_by_scope(self, xblock):
"""
Return a dictionary of scopes mapped to this xblock's explicitly set fields w/o any conversions
"""
# explicitly_set_fields_by_scope converts to json; so, avoiding it
# the existing partition_fields_by_scope works on a dict not an xblock
result = defaultdict(dict)
for field in xblock.fields.itervalues():
if field.is_set_on(xblock):
result[field.scope][field.name] = field.read_from(xblock)
return result
def _serialize_fields(self, category, fields):
"""
Convert any references to their serialized form.
......@@ -1708,6 +1724,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
assert isinstance(fields, dict)
xblock_class = XBlock.load_class(category, self.default_class)
xblock_class = self.mixologist.mix(xblock_class)
for field_name, value in fields.iteritems():
if value:
if isinstance(xblock_class.fields[field_name], Reference):
......@@ -1719,6 +1736,9 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
elif isinstance(xblock_class.fields[field_name], ReferenceValueDict):
for key, subvalue in value.iteritems():
value[key] = subvalue.block_id
# should this recurse down dicts and lists just in case they contain datetime?
elif not isinstance(value, datetime.datetime): # don't convert datetimes!
fields[field_name] = xblock_class.fields[field_name].to_json(value)
# I think these are obsolete conditions; so, I want to confirm that. Thus the warnings
if 'location' in fields:
......
......@@ -9,7 +9,7 @@ from xblock.core import XBlock
from xmodule.tabs import StaticTab
from decorator import contextmanager
from mock import Mock, patch
from nose.tools import assert_less_equal, assert_greater_equal
from nose.tools import assert_less_equal, assert_greater_equal, assert_equal
class Dummy(object):
......@@ -259,18 +259,49 @@ def check_mongo_calls(mongo_store, num_finds=0, num_sends=None):
of calls to send_message which is for insert, update, and remove (if you provide num_sends). At the
end of the with statement, it compares the counts to the num_finds and num_sends.
:param mongo_store: the MongoModulestore or subclass to watch
:param mongo_store: the MongoModulestore or subclass to watch or a SplitMongoModuleStore
:param num_finds: the exact number of find calls expected
:param num_sends: If none, don't instrument the send calls. If non-none, count and compare to
the given int value.
"""
with check_exact_number_of_calls(mongo_store.collection, mongo_store.collection.find, num_finds):
if num_sends:
with check_exact_number_of_calls(
mongo_store.database.connection,
mongo_store.database.connection._send_message, # pylint: disable=protected-access
num_sends,
):
if mongo_store.get_modulestore_type() == ModuleStoreEnum.Type.mongo:
with check_exact_number_of_calls(mongo_store.collection, mongo_store.collection.find, num_finds):
if num_sends is not None:
with check_exact_number_of_calls(
mongo_store.database.connection,
mongo_store.database.connection._send_message, # pylint: disable=protected-access
num_sends,
):
yield
else:
yield
else:
yield
elif mongo_store.get_modulestore_type() == ModuleStoreEnum.Type.split:
collections = [
mongo_store.db_connection.course_index,
mongo_store.db_connection.structures,
mongo_store.db_connection.definitions,
]
# could add else clause which raises exception or just rely on the below to suss that out
try:
find_wraps = []
wrap_patches = []
for collection in collections:
find_wrap = Mock(wraps=collection.find)
find_wraps.append(find_wrap)
wrap_patch = patch.object(collection, 'find', find_wrap)
wrap_patches.append(wrap_patch)
wrap_patch.start()
if num_sends is not None:
connection = mongo_store.db_connection.database.connection
with check_exact_number_of_calls(
connection,
connection._send_message, # pylint: disable=protected-access
num_sends,
):
yield
else:
yield
finally:
map(lambda wrap_patch: wrap_patch.stop(), wrap_patches)
call_count = sum([find_wrap.call_count for find_wrap in find_wraps])
assert_equal(call_count, num_finds)
# encoding: utf-8
"""
The data type and use of it for declaratively creating test courses.
"""
# used to create course subtrees in ModuleStoreTestCase.create_test_course
# adds to self properties w/ the given block_id which hold the UsageKey for easy retrieval.
# fields is a dictionary of keys and values. sub_tree is a collection of BlockInfo
from collections import namedtuple
import datetime
BlockInfo = namedtuple('BlockInfo', 'block_id, category, fields, sub_tree')
default_block_info_tree = [
BlockInfo(
'chapter_x', 'chapter', {}, [
BlockInfo(
'sequential_x1', 'sequential', {}, [
BlockInfo(
'vertical_x1a', 'vertical', {}, [
BlockInfo('problem_x1a_1', 'problem', {}, []),
BlockInfo('problem_x1a_2', 'problem', {}, []),
BlockInfo('problem_x1a_3', 'problem', {}, []),
BlockInfo('html_x1a_1', 'html', {}, []),
]
)
]
)
]
),
BlockInfo(
'chapter_y', 'chapter', {}, [
BlockInfo(
'sequential_y1', 'sequential', {}, [
BlockInfo(
'vertical_y1a', 'vertical', {}, [
BlockInfo('problem_y1a_1', 'problem', {}, []),
BlockInfo('problem_y1a_2', 'problem', {}, []),
BlockInfo('problem_y1a_3', 'problem', {}, []),
]
)
]
)
]
)
]
# equivalent to toy course in xml
TOY_BLOCK_INFO_TREE = [
BlockInfo(
'Overview', "chapter", {"display_name" : "Overview"}, [
BlockInfo(
"Toy_Videos", "videosequence", {
"xml_attributes": {"filename": ["", None]}, "display_name": "Toy Videos", "format": "Lecture Sequence"
}, [
BlockInfo(
"secret:toylab", "html", {
"data": "<b>Lab 2A: Superposition Experiment</b>\n\n<<<<<<< Updated upstream\n<p>Isn't the toy course great?</p>\n\n<p>Let's add some markup that uses non-ascii characters.\nFor example, we should be able to write words like encyclop&aelig;dia, or foreign words like fran&ccedil;ais.\nLooking beyond latin-1, we should handle math symbols: &pi;r&sup2 &le; &#8734.\nAnd it shouldn't matter if we use entities or numeric codes &mdash; &Omega; &ne; &pi; &equiv; &#937; &#8800; &#960;.\n</p>\n=======\n<p>Isn't the toy course great? — &le;</p>\n>>>>>>> Stashed changes\n",
"xml_attributes": { "filename" : [ "html/secret/toylab.xml", "html/secret/toylab.xml" ] },
"display_name" : "Toy lab"
}, []
),
BlockInfo(
"toyjumpto", "html", {
"data" : "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n",
"xml_attributes": { "filename" : [ "html/toyjumpto.xml", "html/toyjumpto.xml" ] }
}, []),
BlockInfo(
"toyhtml", "html", {
"data" : "<a href='/static/handouts/sample_handout.txt'>Sample</a>",
"xml_attributes" : { "filename" : [ "html/toyhtml.xml", "html/toyhtml.xml" ] }
}, []),
BlockInfo(
"nonportable", "html", {
"data": "<a href=\"/static/foo.jpg\">link</a>\n",
"xml_attributes" : { "filename" : [ "html/nonportable.xml", "html/nonportable.xml" ] }
}, []),
BlockInfo(
"nonportable_link", "html", {
"data": "<a href=\"/jump_to_id/nonportable_link\">link</a>\n\n",
"xml_attributes": {"filename": ["html/nonportable_link.xml", "html/nonportable_link.xml"]}
}, []),
BlockInfo(
"badlink", "html", {
"data": "<img src=\"/static//file.jpg\" />\n",
"xml_attributes" : { "filename" : [ "html/badlink.xml", "html/badlink.xml" ] }
}, []),
BlockInfo(
"with_styling", "html", {
"data": "<p style=\"font:italic bold 72px/30px Georgia, serif; color: red; \">Red text here</p>",
"xml_attributes": {"filename": ["html/with_styling.xml", "html/with_styling.xml"]}
}, []),
BlockInfo(
"just_img", "html", {
"data": "<img src=\"/static/foo_bar.jpg\" />",
"xml_attributes": {"filename": [ "html/just_img.xml", "html/just_img.xml" ] }
}, []),
BlockInfo(
"Video_Resources", "video", {
"youtube_id_1_0" : "1bK-WdDi6Qw", "display_name" : "Video Resources"
}, []),
]),
BlockInfo(
"Welcome", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Welcome"}, []
),
BlockInfo(
"video_123456789012", "video", {"data": "", "youtube_id_1_0": "p2Q6BrNhdh8", "display_name": "Test Video"}, []
),
BlockInfo(
"video_4f66f493ac8f", "video", {"youtube_id_1_0": "p2Q6BrNhdh8"}, []
)
]
),
BlockInfo(
"secret:magic", "chapter", {
"xml_attributes": {"filename": [ "chapter/secret/magic.xml", "chapter/secret/magic.xml"]}
}, [
BlockInfo(
"toyvideo", "video", {"youtube_id_1_0": "OEoXaMPEzfMA", "display_name": "toyvideo"}, []
)
]
),
BlockInfo(
"poll_test", "chapter", {}, [
BlockInfo(
"T1_changemind_poll_foo", "poll_question", {
"question": "<p>Have you changed your mind? ’</p>",
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
"xml_attributes": {"reset": "false", "filename": ["", None]},
"display_name": "Change your answer"
}, []) ]
),
BlockInfo(
"vertical_container", "chapter", {
"xml_attributes": {"filename": ["chapter/vertical_container.xml", "chapter/vertical_container.xml"]}
}, [
BlockInfo("vertical_sequential", "sequential", {}, [
BlockInfo("vertical_test", "vertical", {
"xml_attributes": {"filename": ["vertical/vertical_test.xml", "vertical_test"]}
}, [
BlockInfo(
"sample_video", "video", {
"youtube_id_1_25": "AKqURZnYqpk",
"youtube_id_0_75": "JMD_ifUUfsU",
"youtube_id_1_0": "OEoXaMPEzfM",
"display_name": "default",
"youtube_id_1_5": "DYpADpL7jAY"
}, []),
BlockInfo(
"separate_file_video", "video", {
"youtube_id_1_25": "AKqURZnYqpk",
"youtube_id_0_75": "JMD_ifUUfsU",
"youtube_id_1_0": "OEoXaMPEzfM",
"display_name": "default",
"youtube_id_1_5": "DYpADpL7jAY"
}, []),
BlockInfo(
"video_with_end_time", "video", {
"youtube_id_1_25": "AKqURZnYqpk",
"display_name": "default",
"youtube_id_1_0": "OEoXaMPEzfM",
"end_time": datetime.timedelta(seconds=10),
"youtube_id_1_5": "DYpADpL7jAY",
"youtube_id_0_75": "JMD_ifUUfsU"
}, []),
BlockInfo(
"T1_changemind_poll_foo_2", "poll_question", {
"question": "<p>Have you changed your mind?</p>",
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
"xml_attributes": {"reset": "false", "filename": [ "", None]},
"display_name": "Change your answer"
}, []),
]),
BlockInfo("unicode", "html", {
"data": "…", "xml_attributes": {"filename": ["", None]}
}, [])
]),
]
),
BlockInfo(
"handout_container", "chapter", {
"xml_attributes" : {"filename" : ["chapter/handout_container.xml", "chapter/handout_container.xml"]}
}, [
BlockInfo(
"html_7e5578f25f79", "html", {
"data": "<a href=\"/static/handouts/sample_handout.txt\"> handouts</a>",
"xml_attributes": {"filename": ["", None]}
}, []
),
]
)
]
from nose.tools import assert_equals, assert_raises, assert_true, assert_false # pylint: disable=E0611
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.search import path_to_location
from opaque_keys.edx.locations import SlashSeparatedCourseKey
def check_path_to_location(modulestore):
"""
Make sure that path_to_location works: should be passed a modulestore
with the toy and simple courses loaded.
"""
course_id = SlashSeparatedCourseKey("edX", "toy", "2012_Fall")
should_work = (
(course_id.make_usage_key('video', 'Welcome'),
(course_id, "Overview", "Welcome", None)),
(course_id.make_usage_key('chapter', 'Overview'),
(course_id, "Overview", None, None)),
)
for location, expected in should_work:
assert_equals(path_to_location(modulestore, location), expected)
not_found = (
course_id.make_usage_key('video', 'WelcomeX'),
course_id.make_usage_key('course', 'NotHome'),
)
for location in not_found:
with assert_raises(ItemNotFoundError):
path_to_location(modulestore, location)
from nose.tools import assert_equals, assert_true, assert_false # pylint: disable=E0611
def check_has_course_method(modulestore, locator, locator_key_fields):
......
......@@ -31,13 +31,11 @@ from xmodule.modulestore.xml_exporter import export_to_xml
from xmodule.modulestore.xml_importer import import_from_xml, perform_xlint
from xmodule.contentstore.mongo import MongoContentStore
from xmodule.modulestore.tests.test_modulestore import check_path_to_location
from nose.tools import assert_in
from xmodule.exceptions import NotFoundError
from git.test.lib.asserts import assert_not_none
from xmodule.x_module import XModuleMixin
from xmodule.modulestore.mongo.base import as_draft
from xmodule.modulestore.tests.factories import check_mongo_calls
log = logging.getLogger(__name__)
......@@ -245,11 +243,6 @@ class TestMongoModuleStore(unittest.TestCase):
self.draft_store._find_one(Location('edX', 'toy', '2012_Fall', 'video', 'Welcome')),
)
def test_path_to_location(self):
'''Make sure that path_to_location works'''
with check_mongo_calls(self.draft_store, 9):
check_path_to_location(self.draft_store)
def test_xlinter(self):
'''
Run through the xlinter, we know the 'toy' course has violations, but the
......
......@@ -1464,19 +1464,11 @@ class TestCourseCreation(SplitModuleTest):
original_locator = CourseLocator(org='guestx', course='contender', run="run", branch=BRANCH_NAME_DRAFT)
original = modulestore().get_course(original_locator)
original_index = modulestore().get_course_index_info(original_locator)
fields = {}
for field in original.fields.values():
value = getattr(original, field.name)
if not isinstance(value, datetime.datetime):
json_value = field.to_json(value)
else:
json_value = value
if field.scope == Scope.content and field.name != 'location':
fields[field.name] = json_value
elif field.scope == Scope.settings:
fields[field.name] = json_value
fields = {
'grading_policy': original.grading_policy,
'display_name': 'Derivative',
}
fields['grading_policy']['GRADE_CUTOFFS'] = {'A': .9, 'B': .8, 'C': .65}
fields['display_name'] = 'Derivative'
new_draft = modulestore().create_course(
'counter', 'leech', 'leech_run', 'leech_master', BRANCH_NAME_DRAFT,
versions_dict={BRANCH_NAME_DRAFT: original_index['versions'][BRANCH_NAME_DRAFT]},
......
......@@ -8,10 +8,8 @@ from glob import glob
from mock import patch
from xmodule.modulestore.xml import XMLModuleStore
from opaque_keys.edx.locations import Location
from xmodule.modulestore import ModuleStoreEnum
from .test_modulestore import check_path_to_location
from xmodule.tests import DATA_DIR
from opaque_keys.edx.locations import SlashSeparatedCourseKey
from xmodule.modulestore.tests.test_modulestore import check_has_course_method
......@@ -32,15 +30,6 @@ class TestXMLModuleStore(unittest.TestCase):
"""
Test around the XML modulestore
"""
def test_path_to_location(self):
"""Make sure that path_to_location works properly"""
print "Starting import"
modulestore = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple'])
print "finished import"
check_path_to_location(modulestore)
def test_xml_modulestore_type(self):
store = XMLModuleStore(DATA_DIR, course_dirs=['toy', 'simple'])
self.assertEqual(store.get_modulestore_type(), ModuleStoreEnum.Type.xml)
......
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