From 084e2468d3c625b19a89a82d8292e1569b16c4fc Mon Sep 17 00:00:00 2001 From: muhammad-ammar Date: Mon, 13 Mar 2017 19:20:43 +0500 Subject: [PATCH] add hls profile TNL-6541 --- .../migrations/0004_data__add_hls_profile.py | 30 +++++++ edxval/tests/constants.py | 30 ++++++- edxval/tests/test_api.py | 72 ++++++++++++++-- edxval/tests/test_serializers.py | 9 +- edxval/tests/test_views.py | 86 +++++++++++-------- setup.py | 2 +- 6 files changed, 181 insertions(+), 48 deletions(-) create mode 100644 edxval/migrations/0004_data__add_hls_profile.py diff --git a/edxval/migrations/0004_data__add_hls_profile.py b/edxval/migrations/0004_data__add_hls_profile.py new file mode 100644 index 00000000..e560f17b --- /dev/null +++ b/edxval/migrations/0004_data__add_hls_profile.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +HLS_PROFILE = 'hls' + + +def create_hls_profile(apps, schema_editor): + """ Create hls profile """ + Profile = apps.get_model("edxval", "Profile") + Profile.objects.get_or_create(profile_name=HLS_PROFILE) + + +def delete_hls_profile(apps, schema_editor): + """ Delete hls profile """ + Profile = apps.get_model("edxval", "Profile") + Profile.objects.filter(profile_name=HLS_PROFILE).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('edxval', '0003_coursevideo_is_hidden'), + ] + + operations = [ + migrations.RunPython(create_hls_profile, delete_hls_profile), + ] diff --git a/edxval/tests/constants.py b/edxval/tests/constants.py index 93a976a0..5b941e36 100644 --- a/edxval/tests/constants.py +++ b/edxval/tests/constants.py @@ -10,6 +10,7 @@ PROFILE_MOBILE = "mobile" PROFILE_DESKTOP = "desktop" PROFILE_YOUTUBE = "youtube" +PROFILE_HLS = 'hls' """ Encoded_videos for test_api, does not have profile. """ @@ -43,6 +44,11 @@ file_size=3333, bitrate=4444, ) +ENCODED_VIDEO_DICT_HLS = dict( + url='https://www.tmnt.com/tmnt101.m3u8', + file_size=100, + bitrate=0 +) """ Validators """ @@ -150,6 +156,12 @@ bitrate=4222, profile="desktop", ) +ENCODED_VIDEO_DICT_FISH_HLS = dict( + url='https://www.tmnt.com/tmnt101.m3u8', + file_size=100, + bitrate=100, + profile='hls', +) ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE = dict( url="https://www.fishfellow.com", file_size=1, @@ -162,6 +174,12 @@ bitrate=2, profile="desktop", ) +ENCODED_VIDEO_DICT_UPDATE_FISH_HLS = dict( + url="https://www.comics.com/flash/intro.m3u8", + file_size=200, + bitrate=200, + profile="hls", +) ENCODED_VIDEO_DICT_FISH_INVALID_PROFILE = dict( url="https://www.swordsplints.com", file_size=1234, @@ -176,6 +194,15 @@ subtitles=[SUBTITLE_DICT_SRT, SUBTITLE_DICT_SJSON], **VIDEO_DICT_FISH ) +COMPLETE_SET_FISH_WITH_HLS = dict( + encoded_videos=[ + ENCODED_VIDEO_DICT_FISH_MOBILE, + ENCODED_VIDEO_DICT_FISH_DESKTOP, + ENCODED_VIDEO_DICT_FISH_HLS, + ], + subtitles=[SUBTITLE_DICT_SRT, SUBTITLE_DICT_SJSON], + **VIDEO_DICT_FISH +) COMPLETE_SET_TWO_MOBILE_FISH = dict( encoded_videos=[ ENCODED_VIDEO_DICT_FISH_MOBILE, @@ -187,7 +214,8 @@ COMPLETE_SET_UPDATE_FISH = dict( encoded_videos=[ ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE, - ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP + ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP, + ENCODED_VIDEO_DICT_UPDATE_FISH_HLS, ], subtitles=[SUBTITLE_DICT_SRT], **VIDEO_DICT_FISH diff --git a/edxval/tests/test_api.py b/edxval/tests/test_api.py index cfc278d4..574b245e 100644 --- a/edxval/tests/test_api.py +++ b/edxval/tests/test_api.py @@ -98,7 +98,8 @@ def test_create_video(self): video_data = dict( encoded_videos=[ - constants.ENCODED_VIDEO_DICT_FISH_MOBILE + constants.ENCODED_VIDEO_DICT_FISH_MOBILE, + constants.ENCODED_VIDEO_DICT_FISH_HLS ], **constants.VIDEO_DICT_FISH ) @@ -198,12 +199,17 @@ def test_create_profile(self): """ api.create_profile(constants.PROFILE_DESKTOP) profiles = list(Profile.objects.all()) - self.assertEqual(len(profiles), 6) + profile_names = [unicode(profile) for profile in profiles] + self.assertEqual(len(profiles), 7) self.assertIn( constants.PROFILE_DESKTOP, - [unicode(profile) for profile in profiles], + profile_names + ) + self.assertIn( + constants.PROFILE_HLS, + profile_names ) - self.assertEqual(len(profiles), 6) + self.assertEqual(len(profiles), 7) def test_invalid_create_profile(self): """ @@ -240,6 +246,11 @@ def setUp(self): profile=Profile.objects.get(profile_name="desktop"), **constants.ENCODED_VIDEO_DICT_DESKTOP ) + EncodedVideo.objects.create( + video=video, + profile=Profile.objects.get(profile_name="hls"), + **constants.ENCODED_VIDEO_DICT_HLS + ) self.course_id = 'test-course' CourseVideo.objects.create(video=video, course_id=self.course_id) @@ -308,6 +319,13 @@ def setUp(self): profile=Profile.objects.get(profile_name="desktop"), **constants.ENCODED_VIDEO_DICT_DESKTOP ) + EncodedVideo.objects.create( + video=Video.objects.get( + edx_video_id=constants.VIDEO_DICT_FISH.get("edx_video_id") + ), + profile=Profile.objects.get(profile_name="hls"), + **constants.ENCODED_VIDEO_DICT_HLS + ) self.course_id = 'test-course' CourseVideo.objects.create(video=video, course_id=self.course_id) @@ -315,12 +333,13 @@ def test_get_urls_for_profiles(self): """ Tests when the profiles to the video are found """ - profiles = ["mobile", "desktop"] + profiles = ["mobile", "desktop", 'hls'] edx_video_id = constants.VIDEO_DICT_FISH['edx_video_id'] urls = api.get_urls_for_profiles(edx_video_id, profiles) - self.assertEqual(len(urls), 2) + self.assertEqual(len(urls), 3) self.assertEqual(urls["mobile"], u'http://www.meowmix.com') self.assertEqual(urls["desktop"], u'http://www.meowmagic.com') + self.assertEqual(urls["hls"], u'https://www.tmnt.com/tmnt101.m3u8') def test_get_urls_for_profiles_no_video(self): """ @@ -357,11 +376,12 @@ def setUp(self): """ Creates two courses for testing - Creates two videos with 2 encoded videos for the first course, and then - 2 videos with 1 encoded video for the second course. + Creates two videos for first course where first video has 3 encodings and second + video has 2 encoding and then 2 videos with 1 encoded video for the second course. """ mobile_profile = Profile.objects.create(profile_name=constants.PROFILE_MOBILE) desktop_profile = Profile.objects.create(profile_name=constants.PROFILE_DESKTOP) + hls_profile = Profile.objects.get(profile_name=constants.PROFILE_HLS) self.course_id = 'test-course' # 1st video @@ -376,6 +396,11 @@ def setUp(self): profile=desktop_profile, **constants.ENCODED_VIDEO_DICT_DESKTOP ) + EncodedVideo.objects.create( + video=video, + profile=hls_profile, + **constants.ENCODED_VIDEO_DICT_HLS + ) CourseVideo.objects.create(video=video, course_id=self.course_id) # 2nd video video = Video.objects.create(**constants.VIDEO_DICT_STAR) @@ -553,6 +578,24 @@ def test_get_video_for_course_profiles_repeated_profile(self): )) self.assertEqual(videos, expected_dict) + def test_get_video_for_course_profiles_hls(self): + """ + Tests get_video_info_for_course_and_profiles for hls profile + """ + videos = api.get_video_info_for_course_and_profiles( + self.course_id, + ['hls'] + ) + self.assertEqual( + videos, + self._create_video_dict( + constants.VIDEO_DICT_FISH, + { + constants.PROFILE_HLS: constants.ENCODED_VIDEO_DICT_HLS + } + ) + ) + class GetVideosForCourseTest(TestCase, SortedVideoTestMixin): """ @@ -814,6 +857,7 @@ class ExportTest(TestCase): def setUp(self): mobile_profile = Profile.objects.create(profile_name=constants.PROFILE_MOBILE) desktop_profile = Profile.objects.create(profile_name=constants.PROFILE_DESKTOP) + hls_profile = Profile.objects.get(profile_name=constants.PROFILE_HLS) Video.objects.create(**constants.VIDEO_DICT_STAR) video = Video.objects.create(**constants.VIDEO_DICT_FISH) EncodedVideo.objects.create( @@ -826,6 +870,11 @@ def setUp(self): profile=desktop_profile, **constants.ENCODED_VIDEO_DICT_DESKTOP ) + EncodedVideo.objects.create( + video=video, + profile=hls_profile, + **constants.ENCODED_VIDEO_DICT_HLS + ) def assert_xml_equal(self, left, right): """ @@ -861,6 +910,7 @@ def test_basic(self): + """) self.assert_xml_equal( @@ -935,7 +985,7 @@ def test_new_video_full(self): xml = self.make_import_xml( video_dict=constants.VIDEO_DICT_STAR, - encoded_video_dicts=[constants.ENCODED_VIDEO_DICT_STAR] + encoded_video_dicts=[constants.ENCODED_VIDEO_DICT_STAR, constants.ENCODED_VIDEO_DICT_FISH_HLS] ) api.import_from_xml(xml, constants.VIDEO_DICT_STAR["edx_video_id"], new_course_id) @@ -945,6 +995,10 @@ def test_new_video_full(self): video.encoded_videos.get(profile__profile_name=constants.PROFILE_MOBILE), constants.ENCODED_VIDEO_DICT_STAR ) + self.assert_encoded_video_matches_dict( + video.encoded_videos.get(profile__profile_name=constants.PROFILE_HLS), + constants.ENCODED_VIDEO_DICT_FISH_HLS + ) video.courses.get(course_id=new_course_id) def test_new_video_minimal(self): diff --git a/edxval/tests/test_serializers.py b/edxval/tests/test_serializers.py index d3e576c2..f4a739ec 100644 --- a/edxval/tests/test_serializers.py +++ b/edxval/tests/test_serializers.py @@ -116,9 +116,14 @@ def test_encoded_video_set_output(self): profile=Profile.objects.get(profile_name="mobile"), **constants.ENCODED_VIDEO_DICT_MOBILE ) + EncodedVideo.objects.create( + video=video, + profile=Profile.objects.get(profile_name="hls"), + **constants.ENCODED_VIDEO_DICT_HLS + ) result = VideoSerializer(video).data # pylint: disable=E1101 - # Check for 2 EncodedVideo entries - self.assertEqual(len(result.get("encoded_videos")), 2) + # Check for 3 EncodedVideo entries + self.assertEqual(len(result.get("encoded_videos")), 3) # Check for original Video data self.assertDictContainsSubset(constants.VIDEO_DICT_FISH, result) diff --git a/edxval/tests/test_views.py b/edxval/tests/test_views.py index 24115067..242b5d59 100644 --- a/edxval/tests/test_views.py +++ b/edxval/tests/test_views.py @@ -103,16 +103,17 @@ def test_update_one_encoded_video(self): constants.ENCODED_VIDEO_UPDATE_DICT_STAR.get("url") ) - def test_update_two_encoded_videos(self): + def test_update_three_encoded_videos(self): """ - Tests PUTting two encoded videos and then PUT back. + Tests PUTting three encoded videos and then PUT back. """ url = reverse('video-list') - response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json') + response = self.client.post(url, constants.COMPLETE_SET_FISH_WITH_HLS, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) + url = reverse( 'video-detail', - kwargs={"edx_video_id": constants.COMPLETE_SET_FISH.get("edx_video_id")} + kwargs={"edx_video_id": constants.COMPLETE_SET_FISH_WITH_HLS.get("edx_video_id")} ) response = self.client.patch( # pylint: disable=E1101 path=url, @@ -122,41 +123,36 @@ def test_update_two_encoded_videos(self): self.assertEqual(response.status_code, status.HTTP_200_OK) videos = Video.objects.all() self.assertEqual(len(videos), 1) - self.assertEqual(len(videos[0].encoded_videos.all()), 2) - first_url = videos[0].encoded_videos.all()[0].url - self.assertNotEqual( - constants.ENCODED_VIDEO_DICT_FISH_MOBILE, - constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url")) - self.assertEqual( - first_url, - constants.ENCODED_VIDEO_DICT_UPDATE_FISH_MOBILE.get("url") - ) - second_url = videos[0].encoded_videos.all()[1].url - self.assertNotEqual( - constants.ENCODED_VIDEO_DICT_FISH_DESKTOP, - constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url")) - self.assertEqual( - second_url, - constants.ENCODED_VIDEO_DICT_UPDATE_FISH_DESKTOP.get("url") - ) + self.assertEqual(len(videos[0].encoded_videos.all()), 3) + + for index, encoding in enumerate(videos[0].encoded_videos.all()): + before_update_encoding = constants.COMPLETE_SET_FISH_WITH_HLS['encoded_videos'][index] + after_update_encoding = constants.COMPLETE_SET_UPDATE_FISH['encoded_videos'][index] + + self.assertNotEqual( + before_update_encoding.get('url'), + after_update_encoding.get('url') + ) + self.assertEqual( + encoding.url, + after_update_encoding.get('url') + ) + response = self.client.put( path=url, - data=constants.COMPLETE_SET_FISH, + data=constants.COMPLETE_SET_FISH_WITH_HLS, format='json' ) self.assertEqual(response.status_code, status.HTTP_200_OK) videos = Video.objects.all() self.assertEqual(len(videos), 1) - first_url = videos[0].encoded_videos.all()[0].url - self.assertEqual( - first_url, - constants.ENCODED_VIDEO_DICT_FISH_MOBILE.get("url") - ) - second_url = videos[0].encoded_videos.all()[1].url - self.assertEqual( - second_url, - constants.ENCODED_VIDEO_DICT_FISH_DESKTOP.get("url") - ) + self.assertEqual(len(videos[0].encoded_videos.all()), 3) + + for index, encoding in enumerate(videos[0].encoded_videos.all()): + self.assertEqual( + encoding.url, + constants.COMPLETE_SET_FISH_WITH_HLS['encoded_videos'][index].get('url') + ) def test_update_one_of_two_encoded_videos(self): """ @@ -418,18 +414,18 @@ def tearDown(self): Video.objects.all().delete() # Tests for successful POST 201 requests. - def test_complete_set_two_encoded_video_post(self): + def test_complete_set_three_encoded_video_post(self): """ Tests POSTing Video and EncodedVideo pair """ # pylint: disable=R0801 url = reverse('video-list') response = self.client.post( - url, constants.COMPLETE_SET_FISH, format='json' + url, constants.COMPLETE_SET_FISH_WITH_HLS, format='json' ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) video = self.client.get("/edxval/videos/").data self.assertEqual(len(video), 1) - self.assertEqual(len(video[0].get("encoded_videos")), 2) + self.assertEqual(len(video[0].get("encoded_videos")), 3) self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_complete_set_with_extra_video_field(self): @@ -625,6 +621,26 @@ def test_lookup_youtube(self): self.assertEqual(len(response), 1) self.assertEqual(response[0]['edx_video_id'], video['edx_video_id']) + def test_post_with_hls(self): + """ + Test that hls is a valid profile. + """ + url = reverse('video-list') + + video_data = dict( + encoded_videos=[ + constants.ENCODED_VIDEO_DICT_FISH_HLS + ], + **constants.VIDEO_DICT_FISH + ) + response = self.client.post( + url, video_data, format='json' + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + videos = self.client.get(url).data + self.assertEqual(len(videos), 1) + self.assertIn('https://www.tmnt.com/tmnt101.m3u8', videos[0]['encoded_videos'][0]['url']) # Tests for POST queries to database diff --git a/setup.py b/setup.py index 4c2bb36e..719faccd 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ def load_requirements(*requirements_paths): setup( name='edxval', - version='0.0.12', + version='0.0.13', author='edX', url='http://github.com/edx/edx-val', description='edx-val',