diff --git a/cms/djangoapps/contentstore/tests/utils.py b/cms/djangoapps/contentstore/tests/utils.py
index 2a4157f2b1..dc2ce98466 100644
--- a/cms/djangoapps/contentstore/tests/utils.py
+++ b/cms/djangoapps/contentstore/tests/utils.py
@@ -313,7 +313,12 @@ class CourseTestCase(ProceduralCourseTestMixin, ModuleStoreTestCase):
self.assertEqual(course1_item.data, course2_item.data)
# compare meta-data
- self.assertEqual(own_metadata(course1_item), own_metadata(course2_item))
+ course1_metadata = own_metadata(course1_item)
+ course2_metadata = own_metadata(course2_item)
+ # Omit edx_video_id as it can be different in case of extrnal video imports.
+ course1_metadata.pop('edx_video_id', None)
+ course2_metadata.pop('edx_video_id', None)
+ self.assertEqual(course1_metadata, course2_metadata)
# compare children
self.assertEqual(course1_item.has_children, course2_item.has_children)
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
index 1bdb08b138..2d02d378c4 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
@@ -6,6 +6,7 @@ Unit tests for the Mongo modulestore
# pylint: disable=bad-continuation
from nose.tools import assert_equals, assert_raises, \
assert_not_equals, assert_false, assert_true, assert_greater, assert_is_instance, assert_is_none
+from django.test import TestCase
# pylint: enable=E0611
from path import Path as path
import pymongo
@@ -15,7 +16,6 @@ from tempfile import mkdtemp
from uuid import uuid4
from datetime import datetime
from pytz import UTC
-import unittest
from mock import patch
from xblock.core import XBlock
@@ -68,7 +68,7 @@ class ReferenceTestXBlock(XModuleMixin):
reference_dict = ReferenceValueDict(scope=Scope.settings)
-class TestMongoModuleStoreBase(unittest.TestCase):
+class TestMongoModuleStoreBase(TestCase):
'''
Basic setup for all tests
'''
@@ -772,7 +772,7 @@ class TestMongoModuleStoreWithNoAssetCollection(TestMongoModuleStore):
self.assertRaises(ItemNotFoundError, lambda: self.draft_store.get_all_asset_metadata(course_key, 'asset')[:1])
-class TestMongoKeyValueStore(unittest.TestCase):
+class TestMongoKeyValueStore(TestCase):
"""
Tests for MongoKeyValueStore.
"""
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo_call_count.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo_call_count.py
index d5c071fb21..c89e8d7dc9 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/test_mongo_call_count.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mongo_call_count.py
@@ -153,9 +153,9 @@ class CountMongoCallsCourseTraversal(TestCase):
(MIXED_SPLIT_MODULESTORE_BUILDER, 0, False, True, 38),
(MIXED_SPLIT_MODULESTORE_BUILDER, 0, True, True, 38),
(MIXED_SPLIT_MODULESTORE_BUILDER, None, False, False, 4),
- (MIXED_SPLIT_MODULESTORE_BUILDER, None, True, False, 4),
- (MIXED_SPLIT_MODULESTORE_BUILDER, 0, False, False, 4),
- (MIXED_SPLIT_MODULESTORE_BUILDER, 0, True, False, 4)
+ (MIXED_SPLIT_MODULESTORE_BUILDER, None, True, False, 3),
+ (MIXED_SPLIT_MODULESTORE_BUILDER, 0, False, False, 3),
+ (MIXED_SPLIT_MODULESTORE_BUILDER, 0, True, False, 3)
)
@ddt.unpack
def test_number_mongo_calls(self, store_builder, depth, lazy, access_all_block_fields, num_mongo_calls):
diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py
index 1407885741..f6de590356 100644
--- a/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py
+++ b/common/lib/xmodule/xmodule/modulestore/tests/test_xml.py
@@ -3,7 +3,7 @@ Tests around our XML modulestore, including importing
well-formed and not-well-formed XML.
"""
import os.path
-import unittest
+from django.test import TestCase
from glob import glob
from mock import patch, Mock
@@ -28,7 +28,7 @@ def glob_tildes_at_end(path):
return no_tildes + with_tildes
-class TestXMLModuleStore(unittest.TestCase):
+class TestXMLModuleStore(TestCase):
"""
Test around the XML modulestore
"""
diff --git a/common/lib/xmodule/xmodule/tests/test_import.py b/common/lib/xmodule/xmodule/tests/test_import.py
index cc7b90de57..af73292bed 100644
--- a/common/lib/xmodule/xmodule/tests/test_import.py
+++ b/common/lib/xmodule/xmodule/tests/test_import.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
import datetime
-import ddt
-import unittest
+from tempfile import mkdtemp
-from fs.memoryfs import MemoryFS
+import ddt
+
+from django.test import TestCase
+
+from fs.osfs import OSFS
from lxml import etree
from mock import Mock, patch
@@ -33,7 +36,7 @@ COURSE = 'test_course'
class DummySystem(ImportSystem):
- @patch('xmodule.modulestore.xml.OSFS', lambda dir: MemoryFS())
+ @patch('xmodule.modulestore.xml.OSFS', lambda dir: OSFS(mkdtemp()))
def __init__(self, load_error_modules, library=False):
if library:
@@ -58,7 +61,7 @@ class DummySystem(ImportSystem):
raise Exception("Shouldn't be called")
-class BaseCourseTestCase(unittest.TestCase):
+class BaseCourseTestCase(TestCase):
'''Make sure module imports work properly, including for malformed inputs'''
@staticmethod
def get_system(load_error_modules=True, library=False):
@@ -208,7 +211,8 @@ class ImportTestCase(BaseCourseTestCase):
)
# Now export and check things
- descriptor.runtime.export_fs = MemoryFS()
+ file_system = OSFS(mkdtemp())
+ descriptor.runtime.export_fs = file_system.makedir(u'course', recreate=True)
node = etree.Element('unknown')
descriptor.add_xml_to_node(node)
@@ -594,7 +598,6 @@ class ImportTestCase(BaseCourseTestCase):
video = sections[i]
# Name should be 'video_{hash}'
print "video {0} url_name: {1}".format(i, video.url_name)
-
self.assertEqual(len(video.url_name), len('video_') + 12)
def test_poll_and_conditional_import(self):
diff --git a/common/lib/xmodule/xmodule/tests/test_video.py b/common/lib/xmodule/xmodule/tests/test_video.py
index c8e9f9ead4..e6b0766a7a 100644
--- a/common/lib/xmodule/xmodule/tests/test_video.py
+++ b/common/lib/xmodule/xmodule/tests/test_video.py
@@ -24,6 +24,7 @@ from mock import ANY, Mock, patch, MagicMock
import ddt
from django.conf import settings
+from django.test import TestCase
from django.test.utils import override_settings
from fs.osfs import OSFS
@@ -34,7 +35,7 @@ from xblock.fields import ScopeIds
from xmodule.tests import get_test_descriptor_system
from xmodule.validation import StudioValidationMessage
-from xmodule.video_module import VideoDescriptor, create_youtube_string, EXPORT_STATIC_DIR
+from xmodule.video_module import VideoDescriptor, create_youtube_string, EXPORT_IMPORT_STATIC_DIR
from xmodule.video_module.transcripts_utils import download_youtube_subs, save_to_store
from . import LogicTest
from .test_import import DummySystem
@@ -259,7 +260,7 @@ class TestCreateYouTubeUrl(VideoDescriptorTestBase):
@ddt.ddt
-class VideoDescriptorImportTestCase(unittest.TestCase):
+class VideoDescriptorImportTestCase(TestCase):
"""
Make sure that VideoDescriptor can import an old XML-based video correctly.
"""
@@ -628,30 +629,46 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
@patch('xmodule.video_module.video_module.edxval_api')
def test_import_val_data(self, mock_val_api):
- def mock_val_import(xml, edx_video_id, course_id):
+ """
+ Test that `from_xml` works method works as expected.
+ """
+ def mock_val_import(xml, edx_video_id, resource_fs, static_dir, external_transcripts, course_id):
"""Mock edxval.api.import_from_xml"""
self.assertEqual(xml.tag, 'video_asset')
self.assertEqual(dict(xml.items()), {'mock_attr': ''})
self.assertEqual(edx_video_id, 'test_edx_video_id')
+ self.assertEqual(static_dir, EXPORT_IMPORT_STATIC_DIR)
+ self.assertIsNotNone(resource_fs)
+ self.assertEqual(external_transcripts, {u'en': [u'subs_3_yD_cEKoCk.srt.sjson']})
self.assertEqual(course_id, 'test_course_id')
+ return edx_video_id
+ edx_video_id = 'test_edx_video_id'
mock_val_api.import_from_xml = Mock(wraps=mock_val_import)
module_system = DummySystem(load_error_modules=True)
+ # Create static directory in import file system and place transcript files inside it.
+ module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
+
# import new edx_video_id
xml_data = """
-
""".format(
- transcript_name=transcript_url.split('/')[-1],
language_code=language_code
)
parser = etree.XMLParser(remove_blank_text=True)
@@ -1620,7 +1626,7 @@ class VideoDescriptorTest(TestCase, VideoDescriptorTestBase):
self.assertXmlEqual(expected, actual)
# Verify transcript file is created.
- self.assertEqual([transcript_file_name], self.file_system.listdir(EXPORT_STATIC_DIR))
+ self.assertEqual([transcript_file_name], self.file_system.listdir(EXPORT_IMPORT_STATIC_DIR))
# Also verify the content of created transcript file.
expected_transcript_content = File(open(expected_transcript_path)).read()
@@ -1653,22 +1659,67 @@ class VideoDescriptorTest(TestCase, VideoDescriptorTestBase):
self.assertXmlEqual(expected, actual)
def test_import_val_data_internal(self):
+ """
+ Test that import val data internal works as expected.
+ """
create_profile('mobile')
module_system = DummySystem(load_error_modules=True)
+ edx_video_id = 'test_edx_video_id'
+ sub_id = '0CzPOIIdUsA'
+ external_transcript_name = 'The_Flash.srt'
+ external_transcript_language_code = 'ur'
+ val_transcript_language_code = 'ar'
+ val_transcript_provider = 'Cielo24'
+ external_transcripts = {
+ external_transcript_language_code: external_transcript_name
+ }
+
+ # Create static directory in import file system and place transcript files inside it.
+ module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
+
+ # Create VAL transcript.
+ create_file_in_fs(
+ TRANSCRIPT_FILE_SRT_DATA,
+ 'test_edx_video_id-ar.srt',
+ module_system.resources_fs,
+ EXPORT_IMPORT_STATIC_DIR
+ )
+
+ # Create self.sub and self.transcripts transcript.
+ create_file_in_fs(
+ TRANSCRIPT_FILE_SRT_DATA,
+ subs_filename(sub_id, self.descriptor.transcript_language),
+ module_system.resources_fs,
+ EXPORT_IMPORT_STATIC_DIR
+ )
+ create_file_in_fs(
+ TRANSCRIPT_FILE_SRT_DATA,
+ external_transcript_name,
+ module_system.resources_fs,
+ EXPORT_IMPORT_STATIC_DIR
+ )
+
xml_data = """
-
+
-
+
- """
+ """.format(
+ edx_video_id=edx_video_id,
+ sub_id=sub_id,
+ transcripts=json.dumps(external_transcripts),
+ val_transcript_language_code=val_transcript_language_code,
+ val_transcript_provider=val_transcript_provider
+ )
id_generator = Mock()
id_generator.target_course_id = "test_course_id"
video = self.descriptor.from_xml(xml_data, module_system, id_generator)
+
self.assertEqual(video.edx_video_id, 'test_edx_video_id')
video_data = get_video_info(video.edx_video_id)
self.assertEqual(video_data['client_video_id'], 'test_client_video_id')
@@ -1679,10 +1730,246 @@ class VideoDescriptorTest(TestCase, VideoDescriptorTestBase):
self.assertEqual(video_data['encoded_videos'][0]['url'], 'http://example.com/video')
self.assertEqual(video_data['encoded_videos'][0]['file_size'], 222)
self.assertEqual(video_data['encoded_videos'][0]['bitrate'], 333)
- # verify transcript data
- self.assertDictEqual(
- get_video_transcript(video.edx_video_id, 'ar'),
- self.get_video_transcript_data('test_edx_video_id')
+
+ # Verify that VAL transcript is imported.
+ self.assertDictContainsSubset(
+ self.get_video_transcript_data(
+ edx_video_id,
+ language_code=val_transcript_language_code,
+ provider=val_transcript_provider
+ ),
+ get_video_transcript(video.edx_video_id, val_transcript_language_code)
+ )
+
+ # Verify that transcript from sub field is imported.
+ self.assertDictContainsSubset(
+ self.get_video_transcript_data(
+ edx_video_id,
+ language_code=self.descriptor.transcript_language
+ ),
+ get_video_transcript(video.edx_video_id, self.descriptor.transcript_language)
+ )
+
+ # Verify that transcript from transcript field is imported.
+ self.assertDictContainsSubset(
+ self.get_video_transcript_data(
+ edx_video_id,
+ language_code=external_transcript_language_code
+ ),
+ get_video_transcript(video.edx_video_id, external_transcript_language_code)
+ )
+
+ def test_import_no_video_id(self):
+ """
+ Test that importing a video with no video id, creates a new external video.
+ """
+ xml_data = """"""
+ module_system = DummySystem(load_error_modules=True)
+ id_generator = Mock()
+
+ # Verify edx_video_id is empty before.
+ self.assertEqual(self.descriptor.edx_video_id, u'')
+
+ video = self.descriptor.from_xml(xml_data, module_system, id_generator)
+
+ # Verify edx_video_id is populated after the import.
+ self.assertNotEqual(video.edx_video_id, u'')
+
+ video_data = get_video_info(video.edx_video_id)
+ self.assertEqual(video_data['client_video_id'], 'External Video')
+ self.assertEqual(video_data['duration'], 0.0)
+ self.assertEqual(video_data['status'], 'external')
+
+ def test_import_val_transcript(self):
+ """
+ Test that importing a video with val transcript, creates a new transcript record.
+ """
+ edx_video_id = 'test_edx_video_id'
+ val_transcript_language_code = 'es'
+ val_transcript_provider = 'Cielo24'
+ xml_data = """
+
+
+
+
+
+
+
+ """.format(
+ edx_video_id=edx_video_id,
+ val_transcript_language_code=val_transcript_language_code,
+ val_transcript_provider=val_transcript_provider
+ )
+ module_system = DummySystem(load_error_modules=True)
+ id_generator = Mock()
+
+ # Create static directory in import file system and place transcript files inside it.
+ module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
+
+ # Create VAL transcript.
+ create_file_in_fs(
+ TRANSCRIPT_FILE_SRT_DATA,
+ 'test_edx_video_id-es.srt',
+ module_system.resources_fs,
+ EXPORT_IMPORT_STATIC_DIR
+ )
+
+ # Verify edx_video_id is empty before.
+ self.assertEqual(self.descriptor.edx_video_id, u'')
+
+ video = self.descriptor.from_xml(xml_data, module_system, id_generator)
+
+ # Verify edx_video_id is populated after the import.
+ self.assertNotEqual(video.edx_video_id, u'')
+
+ video_data = get_video_info(video.edx_video_id)
+ self.assertEqual(video_data['status'], 'imported')
+
+ # Verify that VAL transcript is imported.
+ self.assertDictContainsSubset(
+ self.get_video_transcript_data(
+ edx_video_id,
+ language_code=val_transcript_language_code,
+ provider=val_transcript_provider
+ ),
+ get_video_transcript(video.edx_video_id, val_transcript_language_code)
+ )
+
+ @ddt.data(
+ (
+ 'test_sub_id',
+ {'en': 'The_Flash.srt'},
+ '',
+ # VAL transcript takes priority
+ {
+ 'video_id': u'test_edx_video_id',
+ 'language_code': u'en',
+ 'file_format': 'srt',
+ 'provider': 'Cielo24'
+ }
+ ),
+ (
+ '',
+ {'en': 'The_Flash.srt'},
+ '',
+ # VAL transcript takes priority
+ {
+ 'video_id': u'test_edx_video_id',
+ 'language_code': u'en',
+ 'file_format': 'srt',
+ 'provider': 'Cielo24'
+ }
+ ),
+ (
+ 'test_sub_id',
+ {},
+ '',
+ # VAL transcript takes priority
+ {
+ 'video_id': u'test_edx_video_id',
+ 'language_code': u'en',
+ 'file_format': 'srt',
+ 'provider': 'Cielo24'
+ }
+ ),
+ (
+ 'test_sub_id',
+ {'en': 'The_Flash.srt'},
+ '',
+ # self.sub transcript takes priority
+ {
+ 'video_id': u'test_edx_video_id',
+ 'language_code': u'en',
+ 'file_format': 'sjson',
+ 'provider': 'Custom'
+ }
+ ),
+ (
+ '',
+ {'en': 'The_Flash.srt'},
+ '',
+ # self.transcripts would be saved.
+ {
+ 'video_id': u'test_edx_video_id',
+ 'language_code': u'en',
+ 'file_format': 'srt',
+ 'provider': 'Custom'
+ }
+ )
+ )
+ @ddt.unpack
+ def test_import_val_transcript_priority(self, sub_id, external_transcripts, val_transcripts, expected_transcript):
+ """
+ Test that importing a video with different type of transcripts for same language,
+ creates expected transcript record.
+ """
+ edx_video_id = 'test_edx_video_id'
+ language_code = 'en'
+
+ module_system = DummySystem(load_error_modules=True)
+ id_generator = Mock()
+
+ # Create static directory in import file system and place transcript files inside it.
+ module_system.resources_fs.makedirs(EXPORT_IMPORT_STATIC_DIR, recreate=True)
+
+ xml_data = "'
+
+ # Prepare VAL transcripts data.
+ if val_transcripts:
+ create_file_in_fs(
+ TRANSCRIPT_FILE_SRT_DATA,
+ '{edx_video_id}-{language_code}.srt'.format(
+ edx_video_id=edx_video_id,
+ language_code=language_code
+ ),
+ module_system.resources_fs,
+ EXPORT_IMPORT_STATIC_DIR
+ )
+ xml_data += val_transcripts
+
+ xml_data += ''
+
+ # Verify edx_video_id is empty before import.
+ self.assertEqual(self.descriptor.edx_video_id, u'')
+
+ video = self.descriptor.from_xml(xml_data, module_system, id_generator)
+
+ # Verify edx_video_id is not empty after import.
+ self.assertNotEqual(video.edx_video_id, u'')
+
+ video_data = get_video_info(video.edx_video_id)
+ self.assertEqual(video_data['status'], 'imported')
+
+ # Verify that correct transcripts are imported.
+ self.assertDictContainsSubset(
+ expected_transcript,
+ get_video_transcript(video.edx_video_id, language_code)
)
def test_import_val_data_invalid(self):