From ce466619e47b60a4dd447a74c62d2e1805f46c6d Mon Sep 17 00:00:00 2001 From: Nimisha Asthagiri Date: Tue, 27 Oct 2015 21:27:13 -0400 Subject: [PATCH] Add SampleCourseFactory and ToyCourseFactory --- .../xmodule/modulestore/tests/django_utils.py | 93 --------------- .../xmodule/modulestore/tests/factories.py | 107 ++++++++++++++++++ .../courseware/tests/test_module_render.py | 29 ++--- lms/lib/xblock/test/test_mixin.py | 4 +- 4 files changed, 125 insertions(+), 108 deletions(-) diff --git a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py index 9d80d29bd1..c0feac00fa 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/django_utils.py @@ -2,9 +2,7 @@ """ Modulestore configuration for test cases. """ -import datetime import functools -import pytz from uuid import uuid4 from mock import patch @@ -22,7 +20,6 @@ from xmodule.contentstore.django import _CONTENTSTORE from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore, clear_existing_modulestores from xmodule.modulestore.tests.mongo_connection import MONGO_PORT_NUM, MONGO_HOST -from xmodule.modulestore.tests.sample_courses import default_block_info_tree, TOY_BLOCK_INFO_TREE from xmodule.modulestore.tests.factories import XMODULE_FACTORY_LOCK @@ -445,93 +442,3 @@ class ModuleStoreTestCase(TestCase): self.store.update_item(course, user_id) updated_course = self.store.get_course(course.id) return updated_course - - def create_sample_course(self, org, course, run, block_info_tree=None, course_fields=None): - """ - create a course in the default modulestore from the collection of BlockInfo - records defining the course tree - Returns: - course_loc: the CourseKey for the created course - """ - if block_info_tree is None: - block_info_tree = default_block_info_tree - - with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None): - course = self.store.create_course(org, course, run, self.user.id, fields=course_fields) - self.course_loc = course.location # pylint: disable=attribute-defined-outside-init - - def create_sub_tree(parent_loc, block_info): - """Recursively creates a sub_tree on this parent_loc with this block.""" - block = self.store.create_child( - self.user.id, - # TODO remove version_agnostic() when we impl the single transaction - parent_loc.version_agnostic(), - block_info.category, block_id=block_info.block_id, - fields=block_info.fields, - ) - for tree in block_info.sub_tree: - create_sub_tree(block.location, tree) - setattr(self, block_info.block_id, block.location.version_agnostic()) - - for tree in block_info_tree: - create_sub_tree(self.course_loc, tree) - - # remove version_agnostic when bulk write works - self.store.publish(self.course_loc.version_agnostic(), self.user.id) - return self.course_loc.course_key.version_agnostic() - - def create_toy_course(self, org='edX', course='toy', run='2012_Fall'): - """ - Create an equivalent to the toy xml course - """ - with self.store.bulk_operations(self.store.make_course_key(org, course, run), emit_signals=False): - self.toy_loc = self.create_sample_course( # pylint: disable=attribute-defined-outside-init - org, course, run, TOY_BLOCK_INFO_TREE, - { - "textbooks": [["Textbook", "https://s3.amazonaws.com/edx-textbooks/guttag_computation_v3/"]], - "wiki_slug": "toy", - "display_name": "Toy Course", - "graded": True, - "discussion_topics": {"General": {"id": "i4x-edX-toy-course-2012_Fall"}}, - "graceperiod": datetime.timedelta(days=2, seconds=21599), - "start": datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc), - "xml_attributes": {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]}, - "pdf_textbooks": [ - { - "tab_title": "Sample Multi Chapter Textbook", - "id": "MyTextbook", - "chapters": [ - {"url": "/static/Chapter1.pdf", "title": "Chapter 1"}, - {"url": "/static/Chapter2.pdf", "title": "Chapter 2"} - ] - } - ], - "course_image": "just_a_test.jpg", - } - ) - with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, self.toy_loc): - self.store.create_item( - self.user.id, self.toy_loc, "about", block_id="short_description", - fields={"data": "A course about toys."} - ) - self.store.create_item( - self.user.id, self.toy_loc, "about", block_id="effort", - fields={"data": "6 hours"} - ) - self.store.create_item( - self.user.id, self.toy_loc, "about", block_id="end_date", - fields={"data": "TBD"} - ) - self.store.create_item( - self.user.id, self.toy_loc, "course_info", "handouts", - fields={"data": "Sample"} - ) - self.store.create_item( - self.user.id, self.toy_loc, "static_tab", "resources", - fields={"display_name": "Resources"}, - ) - self.store.create_item( - self.user.id, self.toy_loc, "static_tab", "syllabus", - fields={"display_name": "Syllabus"}, - ) - return self.toy_loc diff --git a/common/lib/xmodule/xmodule/modulestore/tests/factories.py b/common/lib/xmodule/xmodule/modulestore/tests/factories.py index 24f76048c2..85fac57f03 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/factories.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/factories.py @@ -2,8 +2,10 @@ Factories for use in tests of XBlocks. """ +import datetime import functools import pymongo.message +import pytz import threading import traceback from collections import defaultdict @@ -20,6 +22,7 @@ from opaque_keys.edx.locations import Location from opaque_keys.edx.keys import UsageKey from xblock.core import XBlock from xmodule.modulestore import prefer_xmodules, ModuleStoreEnum +from xmodule.modulestore.tests.sample_courses import default_block_info_tree, TOY_BLOCK_INFO_TREE from xmodule.tabs import CourseTab from xmodule.x_module import DEPRECATION_VSCOMPAT_EVENT @@ -134,6 +137,110 @@ class CourseFactory(XModuleFactory): return new_course +class SampleCourseFactory(CourseFactory): + """ + Factory for sample courses using block_info_tree definitions. + """ + # pylint: disable=unused-argument + @classmethod + def _create(cls, target_class, **kwargs): + """ + Create and return a new sample course. See CourseFactory for customization kwargs. + """ + block_info_tree = kwargs.pop('block_info_tree', default_block_info_tree) + store = kwargs.get('modulestore') + user_id = kwargs.get('user_id', ModuleStoreEnum.UserID.test) + with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None): + course = super(SampleCourseFactory, cls)._create(target_class, **kwargs) + + def create_sub_tree(parent_loc, block_info): + """Recursively creates a sub_tree on this parent_loc with this block.""" + block = store.create_child( + user_id, + parent_loc, + block_info.category, + block_id=block_info.block_id, + fields=block_info.fields, + ) + for tree in block_info.sub_tree: + create_sub_tree(block.location, tree) + + for tree in block_info_tree: + create_sub_tree(course.location, tree) + + store.publish(course.location, user_id) + return course + + +class ToyCourseFactory(SampleCourseFactory): + """ + Factory for sample course that is equivalent to the toy xml course. + """ + org = 'edX' + course = 'toy' + run = '2012_Fall' + display_name = 'Toy Course' + + # pylint: disable=unused-argument + @classmethod + def _create(cls, target_class, **kwargs): + """ + Create and return a new toy course instance. See SampleCourseFactory for customization kwargs. + """ + store = kwargs.get('modulestore') + user_id = kwargs.get('user_id', ModuleStoreEnum.UserID.test) + toy_course = super(ToyCourseFactory, cls)._create( + target_class, + block_info_tree=TOY_BLOCK_INFO_TREE, + textbooks=[["Textbook", "path/to/a/text_book"]], + wiki_slug="toy", + graded=True, + discussion_topics={"General": {"id": "i4x-edX-toy-course-2012_Fall"}}, + graceperiod=datetime.timedelta(days=2, seconds=21599), + start=datetime.datetime(2015, 07, 17, 12, tzinfo=pytz.utc), + xml_attributes={"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]}, + pdf_textbooks=[ + { + "tab_title": "Sample Multi Chapter Textbook", + "id": "MyTextbook", + "chapters": [ + {"url": "/static/Chapter1.pdf", "title": "Chapter 1"}, + {"url": "/static/Chapter2.pdf", "title": "Chapter 2"} + ] + } + ], + course_image="just_a_test.jpg", + **kwargs + ) + with store.bulk_operations(toy_course.id, emit_signals=False): + with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, toy_course.id): + store.create_item( + user_id, toy_course.id, "about", block_id="short_description", + fields={"data": "A course about toys."} + ) + store.create_item( + user_id, toy_course.id, "about", block_id="effort", + fields={"data": "6 hours"} + ) + store.create_item( + user_id, toy_course.id, "about", block_id="end_date", + fields={"data": "TBD"} + ) + store.create_item( + user_id, toy_course.id, "course_info", "handouts", + fields={"data": "Sample"} + ) + store.create_item( + user_id, toy_course.id, "static_tab", "resources", + fields={"display_name": "Resources"}, + ) + store.create_item( + user_id, toy_course.id, "static_tab", "syllabus", + fields={"display_name": "Syllabus"}, + ) + return toy_course + + class LibraryFactory(XModuleFactory): """ Factory for creating a content library diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 4afe1e86a3..cb673a0e68 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -47,7 +47,7 @@ from xmodule.lti_module import LTIDescriptor from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase -from xmodule.modulestore.tests.factories import ItemFactory, CourseFactory, check_mongo_calls +from xmodule.modulestore.tests.factories import ItemFactory, CourseFactory, ToyCourseFactory, check_mongo_calls from xmodule.x_module import XModuleDescriptor, XModule, STUDENT_VIEW, CombinedSystem from openedx.core.djangoapps.credit.models import CreditCourse @@ -127,7 +127,7 @@ class ModuleRenderTestCase(ModuleStoreTestCase, LoginEnrollmentTestCase): """ super(ModuleRenderTestCase, self).setUp() - self.course_key = self.create_toy_course() + self.course_key = ToyCourseFactory.create().id self.toy_course = modulestore().get_course(self.course_key) self.mock_user = UserFactory() self.mock_user.id = 1 @@ -403,7 +403,7 @@ class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase): def setUp(self): super(TestHandleXBlockCallback, self).setUp() - self.course_key = self.create_toy_course() + self.course_key = ToyCourseFactory.create().id self.location = self.course_key.make_usage_key('chapter', 'Overview') self.toy_course = modulestore().get_course(self.course_key) self.mock_user = UserFactory.create() @@ -602,8 +602,11 @@ class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase): @ddt.ddt class TestTOC(ModuleStoreTestCase): """Check the Table of Contents for a course""" - def setup_modulestore(self, default_ms, num_finds, num_sends): - self.course_key = self.create_toy_course() + def setup_request_and_course(self, num_finds, num_sends): + """ + Sets up the toy course in the modulestore and the request object. + """ + self.course_key = ToyCourseFactory.create().id # pylint: disable=attribute-defined-outside-init self.chapter = 'Overview' chapter_url = '%s/%s/%s' % ('/courses', self.course_key, self.chapter) factory = RequestFactory() @@ -612,9 +615,9 @@ class TestTOC(ModuleStoreTestCase): self.modulestore = self.store._get_modulestore_for_courselike(self.course_key) # pylint: disable=protected-access, attribute-defined-outside-init with self.modulestore.bulk_operations(self.course_key): with check_mongo_calls(num_finds, num_sends): - self.toy_course = self.store.get_course(self.toy_loc, depth=2) + self.toy_course = self.store.get_course(self.course_key, depth=2) # pylint: disable=attribute-defined-outside-init self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( - self.toy_loc, self.request.user, self.toy_course, depth=2 + self.course_key, self.request.user, self.toy_course, depth=2 ) # Mongo makes 3 queries to load the course to depth 2: @@ -632,7 +635,7 @@ class TestTOC(ModuleStoreTestCase): @ddt.unpack def test_toc_toy_from_chapter(self, default_ms, setup_finds, setup_sends, toc_finds): with self.store.default_store(default_ms): - self.setup_modulestore(default_ms, setup_finds, setup_sends) + self.setup_request_and_course(setup_finds, setup_sends) expected = ([{'active': True, 'sections': [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, @@ -672,7 +675,7 @@ class TestTOC(ModuleStoreTestCase): @ddt.unpack def test_toc_toy_from_section(self, default_ms, setup_finds, setup_sends, toc_finds): with self.store.default_store(default_ms): - self.setup_modulestore(default_ms, setup_finds, setup_sends) + self.setup_request_and_course(setup_finds, setup_sends) section = 'Welcome' expected = ([{'active': True, 'sections': [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, @@ -707,7 +710,7 @@ class TestProctoringRendering(ModuleStoreTestCase): Set up the initial mongo datastores """ super(TestProctoringRendering, self).setUp() - self.course_key = self.create_toy_course() + self.course_key = ToyCourseFactory.create().id self.chapter = 'Overview' chapter_url = '%s/%s/%s' % ('/courses', self.course_key, self.chapter) factory = RequestFactory() @@ -715,9 +718,9 @@ class TestProctoringRendering(ModuleStoreTestCase): self.request.user = UserFactory() self.modulestore = self.store._get_modulestore_for_courselike(self.course_key) # pylint: disable=protected-access, attribute-defined-outside-init with self.modulestore.bulk_operations(self.course_key): - self.toy_course = self.store.get_course(self.toy_loc, depth=2) + self.toy_course = self.store.get_course(self.course_key, depth=2) self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( - self.toy_loc, self.request.user, self.toy_course, depth=2 + self.course_key, self.request.user, self.toy_course, depth=2 ) @ddt.data( @@ -1475,7 +1478,7 @@ class TestAnonymousStudentId(ModuleStoreTestCase, LoginEnrollmentTestCase): def setUp(self): super(TestAnonymousStudentId, self).setUp(create_user=False) self.user = UserFactory() - self.course_key = self.create_toy_course() + self.course_key = ToyCourseFactory.create().id self.course = modulestore().get_course(self.course_key) @patch('courseware.module_render.has_access', Mock(return_value=True)) diff --git a/lms/lib/xblock/test/test_mixin.py b/lms/lib/xblock/test/test_mixin.py index 9a5cbc327b..cebf7d7a8e 100644 --- a/lms/lib/xblock/test/test_mixin.py +++ b/lms/lib/xblock/test/test_mixin.py @@ -5,7 +5,7 @@ import ddt from xblock.validation import ValidationMessage from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore.tests.factories import CourseFactory, ToyCourseFactory, ItemFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DATA_MIXED_TOY_MODULESTORE from xmodule.partitions.partitions import Group, UserPartition @@ -169,7 +169,7 @@ class XBlockGetParentTest(LmsXBlockMixinTestCase): if modulestore_type == 'xml': course_key = self.store.make_course_key('edX', 'toy', '2012_Fall') else: - course_key = self.create_toy_course('edX', 'toy', '2012_Fall_copy') + course_key = ToyCourseFactory.create(run='2012_Fall_copy').id course = self.store.get_course(course_key) self.assertIsNone(course.get_parent())