Commit 80f38b8f by Vasyl Nakvasiuk

python tests for videoalpha

parent 048be222
---
metadata:
display_name: Video Alpha 1
display_name: Video Alpha
version: 1
data: |
<videoalpha show_captions="true" sub="name_of_file" youtube="0.75:JMD_ifUUfsU,1.0:OEoXaMPEzfM,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY" >
......
# -*- coding: utf-8 -*-
# pylint: disable=W0232
"""Test for Xmodule functional logic."""
import json
import unittest
from lxml import etree
from xmodule.poll_module import PollDescriptor
from xmodule.conditional_module import ConditionalDescriptor
from xmodule.word_cloud_module import WordCloudDescriptor
from xmodule.videoalpha_module import VideoAlphaDescriptor
from xmodule.tests import test_system
class PostData:
"""Class which emulate postdata."""
......@@ -17,6 +16,7 @@ class PostData:
self.dict_data = dict_data
def getlist(self, key):
"""Get data by key from `self.dict_data`."""
return self.dict_data.get(key)
......@@ -27,9 +27,10 @@ class LogicTest(unittest.TestCase):
def setUp(self):
class EmptyClass:
"""Empty object."""
pass
self.system = None
self.system = test_system()
self.descriptor = EmptyClass()
self.xmodule_class = self.descriptor_class.module_class
......@@ -40,10 +41,12 @@ class LogicTest(unittest.TestCase):
)
def ajax_request(self, dispatch, get):
"""Call Xmodule.handle_ajax."""
return json.loads(self.xmodule.handle_ajax(dispatch, get))
class PollModuleTest(LogicTest):
"""Logic tests for Poll Xmodule."""
descriptor_class = PollDescriptor
raw_model_data = {
'poll_answers': {'Yes': 1, 'Dont_know': 0, 'No': 0},
......@@ -69,6 +72,7 @@ class PollModuleTest(LogicTest):
class ConditionalModuleTest(LogicTest):
"""Logic tests for Conditional Xmodule."""
descriptor_class = ConditionalDescriptor
def test_ajax_request(self):
......@@ -83,6 +87,7 @@ class ConditionalModuleTest(LogicTest):
class WordCloudModuleTest(LogicTest):
"""Logic tests for Word Cloud Xmodule."""
descriptor_class = WordCloudDescriptor
raw_model_data = {
'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2},
......@@ -91,8 +96,6 @@ class WordCloudModuleTest(LogicTest):
}
def test_bad_ajax_request(self):
# TODO: move top global test. Formalize all our Xmodule errors.
response = self.ajax_request('bad_dispatch', {})
self.assertDictEqual(response, {
'status': 'fail',
......@@ -118,34 +121,6 @@ class WordCloudModuleTest(LogicTest):
{'text': 'cat', 'size': 12, 'percent': 54.0}]
)
self.assertEqual(100.0, sum(i['percent'] for i in response['top_words']) )
class VideoAlphaModuleTest(LogicTest):
descriptor_class = VideoAlphaDescriptor
raw_model_data = {
'data': '<videoalpha />'
}
def test_get_timeframe_no_parameters(self):
xmltree = etree.fromstring('<videoalpha>test</videoalpha>')
output = self.xmodule._get_timeframe(xmltree)
self.assertEqual(output, ('', ''))
def test_get_timeframe_with_one_parameter(self):
xmltree = etree.fromstring(
'<videoalpha start_time="00:04:07">test</videoalpha>'
)
output = self.xmodule._get_timeframe(xmltree)
self.assertEqual(output, (247, ''))
def test_get_timeframe_with_two_parameters(self):
xmltree = etree.fromstring(
'''<videoalpha
start_time="00:04:07"
end_time="13:04:39"
>test</videoalpha>'''
)
output = self.xmodule._get_timeframe(xmltree)
self.assertEqual(output, (247, 47079))
self.assertEqual(
100.0,
sum(i['percent'] for i in response['top_words']))
# pylint: disable=W0223
"""VideoAlpha is ungraded Xmodule for support video content.
It's new improved video module, which support additional feature:
- Can play non-YouTube video sources via in-browser HTML5 video player.
- YouTube defaults to HTML5 mode from the start.
- Speed changes in both YouTube and non-YouTube videos happen via
in-browser HTML5 video method (when in HTML5 mode).
- Navigational subtitles can be disabled altogether via an attribute
in XML.
"""
import json
import logging
......@@ -21,6 +33,7 @@ log = logging.getLogger(__name__)
class VideoAlphaFields(object):
"""Fields for `VideoAlphaModule` and `VideoAlphaDescriptor`."""
data = String(help="XML data for the problem", scope=Scope.content)
position = Integer(help="Current position in the video", scope=Scope.user_state, default=0)
display_name = String(help="Display name for this module", scope=Scope.settings)
......@@ -68,7 +81,7 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
'ogv': self._get_source(xmltree, ['ogv']),
}
self.track = self._get_track(xmltree)
self.start_time, self.end_time = self._get_timeframe(xmltree)
self.start_time, self.end_time = self.get_timeframe(xmltree)
def _get_source(self, xmltree, exts=None):
"""Find the first valid source, which ends with one of `exts`."""
......@@ -77,7 +90,7 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
return self._get_first_external(xmltree, 'source', condition)
def _get_track(self, xmltree):
# find the first valid track
"""Find the first valid track."""
return self._get_first_external(xmltree, 'track')
def _get_first_external(self, xmltree, tag, condition=bool):
......@@ -93,39 +106,33 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
break
return result
def _get_timeframe(self, xmltree):
def get_timeframe(self, xmltree):
""" Converts 'start_time' and 'end_time' parameters in video tag to seconds.
If there are no parameters, returns empty string. """
def parse_time(s):
def parse_time(str_time):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if s is None:
if str_time is None:
return ''
else:
x = time.strptime(s, '%H:%M:%S')
obj_time = time.strptime(str_time, '%H:%M:%S')
return datetime.timedelta(
hours=x.tm_hour,
minutes=x.tm_min,
seconds=x.tm_sec
hours=obj_time.tm_hour,
minutes=obj_time.tm_min,
seconds=obj_time.tm_sec
).total_seconds()
return parse_time(xmltree.get('start_time')), parse_time(xmltree.get('end_time'))
def handle_ajax(self, dispatch, get):
"""Handle ajax calls to this video.
TODO (vshnayder): This is not being called right now, so the
position is not being saved.
"""
"""This is not being called right now and we raise 404 error."""
log.debug(u"GET {0}".format(get))
log.debug(u"DISPATCH {0}".format(dispatch))
if dispatch == 'goto_position':
self.position = int(float(get['position']))
log.info(u"NEW POSITION {0}".format(self.position))
return json.dumps({'success': True})
raise Http404()
def get_instance_state(self):
"""Return information about state (position)."""
return json.dumps({'position': self.position})
def get_html(self):
......@@ -143,7 +150,8 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
'sources': self.sources,
'track': self.track,
'display_name': self.display_name_with_default,
# TODO (cpennington): This won't work when we move to data that isn't on the filesystem
# This won't work when we move to data that
# isn't on the filesystem
'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': caption_asset_path,
'show_captions': self.show_captions,
......@@ -154,5 +162,6 @@ class VideoAlphaModule(VideoAlphaFields, XModule):
class VideoAlphaDescriptor(VideoAlphaFields, RawDescriptor):
"""Descriptor for `VideoAlphaModule`."""
module_class = VideoAlphaModule
template_dir_name = "videoalpha"
......@@ -25,8 +25,8 @@ class BaseTestXmodule(ModuleStoreTestCase):
"""Base class for testing Xmodules with mongo store.
This class prepares course and users for tests:
1. create test course
2. create, enrol and login users for this course
1. create test course;
2. create, enrol and login users for this course;
Any xmodule should overwrite only next parameters for test:
1. TEMPLATE_NAME
......
# -*- coding: utf-8 -*-
"""Video xmodule tests in mongo."""
from . import BaseTestXmodule
from .test_videoalpha_xml import SOURCE_XML
from django.conf import settings
class TestVideo(BaseTestXmodule):
"""Integration tests: web client + mongo."""
TEMPLATE_NAME = "i4x://edx/templates/videoalpha/Video_Alpha"
DATA = SOURCE_XML
MODEL_DATA = {
'data': DATA
}
def test_handle_ajax_dispatch(self):
responses = {
user.username: self.clients[user.username].post(
self.get_url('whatever'),
{},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
for user in self.users
}
self.assertEqual(
set([
response.status_code
for _, response in responses.items()
]).pop(),
404)
def test_videoalpha_constructor(self):
"""Make sure that all parameters extracted correclty from xml"""
# `get_html` return only context, cause we
# overwrite `system.render_template`
context = self.item_module.get_html()
expected_context = {
'data_dir': getattr(self, 'data_dir', None),
'caption_asset_path': '/c4x/MITx/999/asset/subs_',
'show_captions': self.item_module.show_captions,
'display_name': self.item_module.display_name_with_default,
'end': self.item_module.end_time,
'id': self.item_module.location.html_id(),
'sources': self.item_module.sources,
'start': self.item_module.start_time,
'sub': self.item_module.sub,
'track': self.item_module.track,
'youtube_streams': self.item_module.youtube_streams,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
}
self.assertDictEqual(context, expected_context)
# -*- coding: utf-8 -*-
"""Test for VideoAlpha Xmodule functional logic.
These tests data readed from xml, not from mongo.
We have a ModuleStoreTestCase class defined in
common/lib/xmodule/xmodule/modulestore/tests/django_utils.py.
You can search for usages of this in the cms and lms tests for examples.
You use this so that it will do things like point the modulestore
setting to mongo, flush the contentstore before and after, load the
templates, etc.
You can then use the CourseFactory and XModuleItemFactory as defined in
common/lib/xmodule/xmodule/modulestore/tests/factories.py to create the
course, section, subsection, unit, etc.
"""
import json
import unittest
from mock import Mock
from lxml import etree
from django.conf import settings
from xmodule.videoalpha_module import VideoAlphaDescriptor, VideoAlphaModule
from xmodule.modulestore import Location
from xmodule.tests import test_system
from xmodule.tests.test_logic import LogicTest
SOURCE_XML = """
<videoalpha show_captions="true"
youtube="0.75:jNCf2gIqpeE,1.0:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg"
data_dir=""
caption_asset_path=""
autoplay="true"
start_time="01:00:03" end_time="01:00:10"
>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.mp4"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.webm"/>
<source src=".../mit-3091x/M-3091X-FA12-L21-3_100.ogv"/>
</videoalpha>
"""
class VideoAlphaFactory(object):
"""A helper class to create videoalpha modules with various parameters
for testing.
"""
# tag that uses youtube videos
sample_problem_xml_youtube = SOURCE_XML
@staticmethod
def create():
"""Method return VideoAlpha Xmodule instance."""
location = Location(["i4x", "edX", "videoalpha", "default",
"SampleProblem1"])
model_data = {'data': VideoAlphaFactory.sample_problem_xml_youtube}
descriptor = Mock(weight="1")
system = test_system()
system.render_template = lambda template, context: context
VideoAlphaModule.location = location
module = VideoAlphaModule(system, descriptor, model_data)
return module
class VideoAlphaModuleTest(LogicTest):
"""Tests for logic of VideoAlpha Xmodule."""
descriptor_class = VideoAlphaDescriptor
raw_model_data = {
'data': '<videoalpha />'
}
def test_get_timeframe_no_parameters(self):
xmltree = etree.fromstring('<videoalpha>test</videoalpha>')
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, ('', ''))
def test_get_timeframe_with_one_parameter(self):
xmltree = etree.fromstring(
'<videoalpha start_time="00:04:07">test</videoalpha>'
)
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, (247, ''))
def test_get_timeframe_with_two_parameters(self):
xmltree = etree.fromstring(
'''<videoalpha
start_time="00:04:07"
end_time="13:04:39"
>test</videoalpha>'''
)
output = self.xmodule.get_timeframe(xmltree)
self.assertEqual(output, (247, 47079))
class VideoAlphaModuleUnitTest(unittest.TestCase):
"""Unit tests for VideoAlpha Xmodule."""
def test_videoalpha_constructor(self):
"""Make sure that all parameters extracted correclty from xml"""
module = VideoAlphaFactory.create()
# `get_html` return only context, cause we
# overwrite `system.render_template`
context = module.get_html()
expected_context = {
'caption_asset_path': '/static/subs/',
'sub': module.sub,
'data_dir': getattr(self, 'data_dir', None),
'display_name': module.display_name_with_default,
'end': module.end_time,
'start': module.start_time,
'id': module.location.html_id(),
'show_captions': module.show_captions,
'sources': module.sources,
'youtube_streams': module.youtube_streams,
'track': module.track,
'autoplay': settings.MITX_FEATURES.get('AUTOPLAY_VIDEOS', True)
}
self.assertDictEqual(context, expected_context)
self.assertDictEqual(
json.loads(module.get_instance_state()),
{'position': 0})
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