diff --git a/cms/djangoapps/contentstore/features/transcripts.feature b/cms/djangoapps/contentstore/features/transcripts.feature index 00684ffd67..d69d12a49f 100644 --- a/cms/djangoapps/contentstore/features/transcripts.feature +++ b/cms/djangoapps/contentstore/features/transcripts.feature @@ -1,4 +1,4 @@ -@shard_3 +@shard_3 @requires_stub_youtube Feature: CMS Transcripts As a course author, I want to be able to create video components diff --git a/cms/djangoapps/contentstore/features/video.feature b/cms/djangoapps/contentstore/features/video.feature index 6179a33eb1..f81c4bb712 100644 --- a/cms/djangoapps/contentstore/features/video.feature +++ b/cms/djangoapps/contentstore/features/video.feature @@ -1,4 +1,4 @@ -@shard_3 +@shard_3 @requires_stub_youtube Feature: CMS Video Component As a course author, I want to be able to view my created videos in Studio diff --git a/cms/djangoapps/contentstore/features/video.py b/cms/djangoapps/contentstore/features/video.py index fd7a352162..180e2e3164 100644 --- a/cms/djangoapps/contentstore/features/video.py +++ b/cms/djangoapps/contentstore/features/video.py @@ -1,8 +1,6 @@ # pylint: disable=C0111 from lettuce import world, step -from nose.tools import assert_less -from xmodule.modulestore import Location from contentstore.utils import get_modulestore from selenium.webdriver.common.keys import Keys @@ -22,7 +20,6 @@ SELECTORS = { # We should wait 300 ms for event handler invocation + 200ms for safety. DELAY = 0.5 - @step('youtube stub server (.*) YouTube API') def configure_youtube_api(_step, action): action=action.strip() @@ -33,7 +30,6 @@ def configure_youtube_api(_step, action): else: raise ValueError('Parameter `action` should be one of "proxies" or "blocks".') - @step('I have created a Video component$') def i_created_a_video_component(_step): diff --git a/cms/djangoapps/contentstore/features/video_editor.feature b/cms/djangoapps/contentstore/features/video_editor.feature index ba028412de..ec0eca51b5 100644 --- a/cms/djangoapps/contentstore/features/video_editor.feature +++ b/cms/djangoapps/contentstore/features/video_editor.feature @@ -1,4 +1,4 @@ -@shard_1 +@shard_1 @requires_stub_youtube Feature: CMS Video Component Editor As a course author, I want to be able to create video components diff --git a/cms/djangoapps/contentstore/features/video_handout.feature b/cms/djangoapps/contentstore/features/video_handout.feature index 980ae0ed6f..dba4c71f26 100644 --- a/cms/djangoapps/contentstore/features/video_handout.feature +++ b/cms/djangoapps/contentstore/features/video_handout.feature @@ -1,4 +1,4 @@ -@shard_3 +@shard_3 @requires_stub_youtube Feature: CMS Video Component Handout As a course author, I want to be able to create video handout diff --git a/common/djangoapps/terrain/__init__.py b/common/djangoapps/terrain/__init__.py index 2740ff326b..ee3c154101 100644 --- a/common/djangoapps/terrain/__init__.py +++ b/common/djangoapps/terrain/__init__.py @@ -1,7 +1,8 @@ # Use this as your lettuce terrain file so that the common steps # across all lms apps can be put in terrain/common # See https://groups.google.com/forum/?fromgroups=#!msg/lettuce-users/5VyU9B4HcX8/USgbGIJdS5QJ -from terrain.browser import * -from terrain.steps import * -from terrain.factories import * -from terrain.start_stubs import * + +from terrain.browser import * # pylint: disable=W0401 +from terrain.steps import * # pylint: disable=W0401 +from terrain.factories import * # pylint: disable=W0401 +from terrain.setup_prereqs import * # pylint: disable=W0401 diff --git a/common/djangoapps/terrain/setup_prereqs.py b/common/djangoapps/terrain/setup_prereqs.py new file mode 100644 index 0000000000..65efdb745a --- /dev/null +++ b/common/djangoapps/terrain/setup_prereqs.py @@ -0,0 +1,129 @@ +""" +Set up the prequisites for acceptance tests. + +This includes initialization and teardown for stub and video HTTP services +and checking for external URLs that need to be accessible and responding. + +""" +from lettuce import before, after, world +from django.conf import settings +from terrain.stubs.youtube import StubYouTubeService +from terrain.stubs.xqueue import StubXQueueService +from terrain.stubs.lti import StubLtiService +from terrain.stubs.video_source import VideoSourceHttpService + +import re +import requests + + +SERVICES = { + "youtube": {"port": settings.YOUTUBE_PORT, "class": StubYouTubeService}, + "xqueue": {"port": settings.XQUEUE_PORT, "class": StubXQueueService}, + "lti": {"port": settings.LTI_PORT, "class": StubLtiService}, +} + +YOUTUBE_API_URLS = { + 'main': 'https://www.youtube.com/', + 'player': 'http://www.youtube.com/iframe_api', + 'metadata': 'http://gdata.youtube.com/feeds/api/videos/', + # For transcripts, you need to check an actual video, so we will + # just specify our default video and see if that one is available. + 'transcript': 'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM', +} + + +@before.all # pylint: disable=E1101 +def start_video_server(): + """ + Serve the HTML5 Video Sources from a local port + """ + video_source_dir = '{}/data/video'.format(settings.TEST_ROOT) + video_server = VideoSourceHttpService(port_num=settings.VIDEO_SOURCE_PORT) + video_server.config['root_dir'] = video_source_dir + setattr(world, 'video_source', video_server) + + +@after.all # pylint: disable=E1101 +def stop_video_server(_total): + """ + Stop the HTML5 Video Source server after all tests have executed + """ + video_server = getattr(world, 'video_source', None) + if video_server: + video_server.shutdown() + + +@before.each_scenario +def process_requires_tags(scenario): + """ + Process the scenario tags to make sure that any + requirements are met prior to that scenario + being executed. + + Scenario tags must be named with this convention: + @requires_stub_bar, where 'bar' is the name of the stub service to start + + if 'bar' is 'youtube' + if 'youtube' is not available Then + DON'T start youtube stub server + ALSO DON'T start any other stub server BECAUSE we will SKIP this Scenario so no need to start any stub + else + start the stub server + + """ + tag_re = re.compile('requires_stub_(?P[^_]+)') + for tag in scenario.tags: + requires = tag_re.match(tag) + + if requires: + if requires.group('server') == 'youtube': + if not is_youtube_available(scenario, YOUTUBE_API_URLS): + return + + start_stub(requires.group('server')) + + +def start_stub(name): + """ + Start the required stub service running on a local port. + Since these services can be reconfigured on the fly, + we start them on a scenario basis when needed and + stop them at the end of the scenario. + """ + service = SERVICES.get(name, None) + if service: + fake_server = service['class'](port_num=service['port']) + setattr(world, name, fake_server) + + +def is_youtube_available(scenario, urls): + """ + Check if the required youtube urls are available. + If they are not, then skip the scenario. + """ + for name, url in urls.iteritems(): + response = requests.get(url, allow_redirects=False) + status = response.status_code + if status != 200: + print "ERROR: YouTube {0} service not available. Status code: {1}".format( + name, status) + + # This is a hackish way to skip a test in lettuce as there is + # no proper way to skip a test conditionally + scenario.steps = [] + + # No need to check all the URLs + return False + + return True + + +@after.each_scenario +def stop_stubs(_scenario): + """ + Shut down any stub services that were started up for the scenario. + """ + for name in SERVICES.keys(): + stub_server = getattr(world, name, None) + if stub_server is not None: + stub_server.shutdown() diff --git a/common/djangoapps/terrain/start_stubs.py b/common/djangoapps/terrain/start_stubs.py deleted file mode 100644 index f1ce715bcd..0000000000 --- a/common/djangoapps/terrain/start_stubs.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Initialize and teardown stub and video HTTP services for use in acceptance tests. -""" -from lettuce import before, after, world -from django.conf import settings -from terrain.stubs.youtube import StubYouTubeService -from terrain.stubs.xqueue import StubXQueueService -from terrain.stubs.lti import StubLtiService -from terrain.stubs.video_source import VideoSourceHttpService - - -SERVICES = { - "youtube": {"port": settings.YOUTUBE_PORT, "class": StubYouTubeService}, - "xqueue": {"port": settings.XQUEUE_PORT, "class": StubXQueueService}, - "lti": {"port": settings.LTI_PORT, "class": StubLtiService}, -} - - -@before.all # pylint: disable=E1101 -def start_video_server(): - """ - Serve the HTML5 Video Sources from a local port - """ - video_source_dir = '{}/data/video'.format(settings.TEST_ROOT) - video_server = VideoSourceHttpService(port_num=settings.VIDEO_SOURCE_PORT) - video_server.config['root_dir'] = video_source_dir - setattr(world, 'video_source', video_server) - - -@after.all # pylint: disable=E1101 -def stop_video_server(_total): - """ - Stop the HTML5 Video Source server after all tests have executed - """ - video_server = getattr(world, 'video_source', None) - if video_server: - video_server.shutdown() - - -@before.each_scenario -def start_stubs(_scenario): - """ - Start each stub service running on a local port. - Since these services can be reconfigured on the fly, - stop and restart them on a scenario basis. - """ - for name, service in SERVICES.iteritems(): - fake_server = service['class'](port_num=service['port']) - setattr(world, name, fake_server) - - -@after.each_scenario -def stop_stubs(_scenario): - """ - Shut down each stub service. - """ - for name in SERVICES.keys(): - stub_server = getattr(world, name, None) - if stub_server is not None: - stub_server.shutdown() diff --git a/lms/djangoapps/courseware/features/lti.feature b/lms/djangoapps/courseware/features/lti.feature index e26647de32..0570502d81 100644 --- a/lms/djangoapps/courseware/features/lti.feature +++ b/lms/djangoapps/courseware/features/lti.feature @@ -1,4 +1,4 @@ -@shard_1 +@shard_1 @requires_stub_lti Feature: LMS.LTI component As a student, I want to view LTI component in LMS. diff --git a/lms/djangoapps/courseware/features/problems.feature b/lms/djangoapps/courseware/features/problems.feature index 3b08be7b34..2985fc2432 100644 --- a/lms/djangoapps/courseware/features/problems.feature +++ b/lms/djangoapps/courseware/features/problems.feature @@ -1,4 +1,4 @@ -@shard_1 +@shard_1 @requires_stub_xqueue Feature: LMS.Answer problems As a student in an edX course In order to test my understanding of the material diff --git a/lms/djangoapps/courseware/features/video.feature b/lms/djangoapps/courseware/features/video.feature index 6204a590e4..9e655e5a17 100644 --- a/lms/djangoapps/courseware/features/video.feature +++ b/lms/djangoapps/courseware/features/video.feature @@ -1,4 +1,4 @@ -@shard_2 +@shard_2 @requires_stub_youtube Feature: LMS.Video component As a student, I want to view course videos in LMS diff --git a/lms/djangoapps/courseware/features/video.py b/lms/djangoapps/courseware/features/video.py index 217a4106b2..44d0766857 100644 --- a/lms/djangoapps/courseware/features/video.py +++ b/lms/djangoapps/courseware/features/video.py @@ -14,6 +14,7 @@ from cache_toolbox.core import del_cached_content from xmodule.contentstore.content import StaticContent from xmodule.contentstore.django import contentstore + TEST_ROOT = settings.COMMON_TEST_DATA_ROOT LANGUAGES = settings.ALL_LANGUAGES VIDEO_SOURCE_PORT = settings.VIDEO_SOURCE_PORT @@ -55,6 +56,7 @@ VIDEO_MENUS = { coursenum = 'test_course' + @before.each_scenario def setUp(scenario): world.video_sequences = {}