diff options
Diffstat (limited to 'test/test_video.py')
-rw-r--r-- | test/test_video.py | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/test/test_video.py b/test/test_video.py new file mode 100644 index 0000000..821066f --- /dev/null +++ b/test/test_video.py @@ -0,0 +1,258 @@ +import os, os.path +import tempfile +import threading +import unittest + +import mock + +from mvc import video +import base + +class GetMediaInfoTest(base.Test): + + def assertClose(self, output, expected): + diff = output - expected + self.assertTrue(diff ** 2 < 0.04, # abs(diff) < 0.2 + "%s != %s" % (output, expected)) + + def assertEqualOutput(self, filename, expected): + full_path = os.path.join(self.testdata_dir, filename) + try: + output = video.get_media_info(full_path) + except Exception, e: + raise AssertionError( + 'Error parsing %r\nException: %r\nOutput: %s' % ( + filename, e, video.get_ffmpeg_output(full_path))) + duration_output = output.pop('duration', None) + duration_expected = expected.pop('duration', None) + if duration_output is not None and duration_expected is not None: + self.assertClose(duration_output, duration_expected) + else: + # put them back in, let assertEqual handle the difference + output['duration'] = duration_output + expected['duration'] = duration_expected + self.assertEqual(output, expected) + + def test_mp3_0(self): + self.assertEqualOutput('mp3-0.mp3', + {'container': 'mp3', + 'audio_codec': 'mp3', + 'title': 'Invisible Walls', + 'artist': 'Revolution Void', + 'album': 'Increase The Dosage', + 'track': '1', + 'genre': 'Blues', + 'duration': 1.07}) + + def test_mp3_1(self): + self.assertEqualOutput('mp3-1.mp3', + {'container': 'mp3', + 'audio_codec': 'mp3', + 'title': 'Race Lieu', + 'artist': 'Ckz', + 'album': 'The Heart EP', + 'track': '2/5', + 'duration': 1.07}) + + def test_mp3_2(self): + self.assertEqualOutput('mp3-2.mp3', + {'container': 'mp3', + 'audio_codec': 'mp3', + 'artist': 'This American Life', + 'genre': 'Podcast', + 'title': '#426: Tough Room 2011', + 'duration': 1.09}) + + def test_theora(self): + self.assertEqualOutput('theora.ogv', + {'container': 'ogg', + 'video_codec': 'theora', + 'audio_codec': 'vorbis', + 'width': 400, + 'height': 304, + 'duration': 5.0}) + + def test_theora_with_ogg_extension(self): + self.assertEqualOutput('theora_with_ogg_extension.ogg', + {'container': 'ogg', + 'video_codec': 'theora', + 'width': 320, + 'height': 240, + 'duration': 0.1}) + + def test_webm_0(self): + self.assertEqualOutput('webm-0.webm', + {'container': ['matroska', 'webm'], + 'video_codec': 'vp8', + 'width': 1920, + 'height': 912, + 'duration': 0.43}) + + def test_mp4_0(self): + self.assertEqualOutput('mp4-0.mp4', + {'container': ['mov', + 'mp4', + 'm4a', + '3gp', + '3g2', + 'mj2', + 'isom', + 'mp41'], + 'video_codec': 'h264', + 'audio_codec': 'aac', + 'width': 640, + 'height': 480, + 'title': 'Africa: Cash for Climate Change?', + 'duration': 312.37}) + + def test_nuls(self): + self.assertEqualOutput('nuls.mp3', + {'container': 'mp3', + 'title': 'Invisible'}) + + @unittest.skip('inconsistent parsing of DRMed files') + def test_drm(self): + self.assertEqualOutput('drm.m4v', + {'container': ['mov', + 'mp4', + 'm4a', + '3gp', + '3g2', + 'mj2', + 'M4V', + 'M4V ', + 'mp42', + 'isom'], + 'video_codec': 'none', + 'audio_codec': 'aac', + 'has_drm': ['audio', 'video'], + 'width': 640, + 'height': 480, + 'title': 'Thinkers', + 'artist': 'The Most Extreme', + 'album': 'The Most Extreme', + 'track': '10', + 'genre': 'Nonfiction', + 'duration': 2668.8}) + + + +class GetThumbnailTest(base.Test): + + def setUp(self): + base.Test.setUp(self) + self.video_path = os.path.join(self.testdata_dir, + 'theora.ogv') + self.temp_path = tempfile.NamedTemporaryFile( + suffix='.png') + + def generate_thumbnail(self, width, height): + completion = mock.Mock() + with mock.patch('mvc.video.idle_add') as mock_idle_add: + with mock.patch('threading.Thread') as mock_thread: + video.get_thumbnail(self.video_path, width, height, + self.temp_path.name, completion, + skip=0) + # get_thumbnail() creates a thread to create the thumbnail. + # Run the function for that thread now. + mock_thread.call_args[1]['target']() + self.assertEquals(mock_idle_add.call_count, 1) + # At the end of the thread it uses add_idle() to call the + # completion function. Run that now. + mock_idle_add.call_args[0][0]() + # Now when we call get_thumbnail() it should return + # immediately with the thumbnail + path = completion.call_args[0][0] + self.assertNotEquals(path, None) + return video.VideoFile(path) + + def test_original_size(self): + thumbnail = self.generate_thumbnail(-1, -1) + self.assertEqual(thumbnail.width, 400) + self.assertEqual(thumbnail.height, 304) + + def test_height_resize(self): + thumbnail = self.generate_thumbnail(200, -1) + self.assertEqual(thumbnail.width, 200) + self.assertEqual(thumbnail.height, 152) + + def test_width_resize(self): + thumbnail = self.generate_thumbnail(-1, 152) + self.assertEqual(thumbnail.width, 200) + self.assertEqual(thumbnail.height, 152) + + def test_both_resize(self): + thumbnail = self.generate_thumbnail(100, 100) + self.assertEqual(thumbnail.width, 100) + self.assertEqual(thumbnail.height, 100) + +class VideoFileTest(base.Test): + + def setUp(self): + base.Test.setUp(self) + self.video_path = os.path.join(self.testdata_dir, + 'theora.ogv') + self.video = video.VideoFile(self.video_path) + self.video.thumbnails = {} + + def get_thumbnail_from_video(self, **kwargs): + """Run Video.get_thumbnail() + + This method uses mock to intercept the threading and idle_add calls and + just runs the code in the current thread + """ + completion = mock.Mock() + with mock.patch('mvc.video.idle_add') as mock_idle_add: + with mock.patch('threading.Thread') as mock_thread: + initial_rv = self.video.get_thumbnail(completion, **kwargs) + if initial_rv is not None: + # we already had a thumbnail and didn't have to do + # anything synchrously + return video.VideoFile(initial_rv) + # We don't already have a thumbnail, so get_thumbnail() + # created a thread to create it. Run the function for that + # thread. + mock_thread.call_args[1]['target']() + self.assertEquals(mock_idle_add.call_count, 1) + # At the end of the thread it uses add_idle() to call the + # completion function. Run that now. + mock_idle_add.call_args[0][0]() + # Now when we call get_thumbnail() it should return + # immediately with the thumbnail + path = self.video.get_thumbnail(completion, **kwargs) + self.assertNotEquals(path, None) + return video.VideoFile(path) + + def test_get_thumbnail_original_size(self): + thumbnail = self.get_thumbnail_from_video() + self.assertEqual(thumbnail.width, 400) + self.assertEqual(thumbnail.height, 304) + + def test_get_thumbnail_scaled_width(self): + thumbnail = self.get_thumbnail_from_video(width=200) + self.assertEqual(thumbnail.width, 200) + self.assertEqual(thumbnail.height, 152) + + def test_get_thumbnail_scaled_height(self): + thumbnail = self.get_thumbnail_from_video(height=152) + self.assertEqual(thumbnail.width, 200) + self.assertEqual(thumbnail.height, 152) + + def test_get_thumbnail_scaled_both(self): + thumbnail = self.get_thumbnail_from_video(width=100, height=100) + self.assertEqual(thumbnail.width, 100) + self.assertEqual(thumbnail.height, 100) + + def test_get_thumbnail_cache(self): + thumbnail = self.get_thumbnail_from_video() + thumbnail2 = self.get_thumbnail_from_video() + self.assertEqual(thumbnail.filename, + thumbnail2.filename) + + def test_get_thumbnail_audio(self): + audio_path = os.path.join(self.testdata_dir, 'mp3-0.mp3') + audio = video.VideoFile(audio_path) + def complete(): + pass + self.assertEqual(audio.get_thumbnail(complete), None) + self.assertEqual(audio.get_thumbnail(complete, 90, 70), None) |