From 58a38ffd3a52a972a744af67c097f55c92408dde Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Wed, 26 Mar 2014 15:06:48 -0400 Subject: [PATCH] CC-5709: Airtime Analyzer * Added unit test coverage report stuff * Added more unit tests * 98% test coverage :-) * Fixed a few bugs and removed some dead code --- python_apps/airtime_analyzer/README.rst | 2 ++ .../airtime_analyzer/analyzer.py | 2 ++ .../airtime_analyzer/analyzer_pipeline.py | 4 --- .../airtime_analyzer/filemover_analyzer.py | 9 +++-- .../airtime_analyzer/metadata_analyzer.py | 6 ++-- python_apps/airtime_analyzer/setup.py | 2 ++ .../tests/analyzer_pipeline_tests.py | 19 +++++++++- .../tests/metadata_analyzer_tests.py | 35 +++++++++++++++++++ 8 files changed, 66 insertions(+), 13 deletions(-) diff --git a/python_apps/airtime_analyzer/README.rst b/python_apps/airtime_analyzer/README.rst index 0f6a12205..e6a5a08b9 100644 --- a/python_apps/airtime_analyzer/README.rst +++ b/python_apps/airtime_analyzer/README.rst @@ -68,4 +68,6 @@ a test, run: $ nosetests -s +To run the unit tests and generate a code coverage report, run: + $ nosetests --with-coverage --cover-package=airtime_analyzer diff --git a/python_apps/airtime_analyzer/airtime_analyzer/analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/analyzer.py index 7fb00d8db..b47ebe0ab 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/analyzer.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/analyzer.py @@ -5,6 +5,8 @@ class Analyzer: def analyze(filename, metadata): raise NotImplementedError +''' class AnalyzerError(Exception): def __init__(self): super.__init__(self) +''' diff --git a/python_apps/airtime_analyzer/airtime_analyzer/analyzer_pipeline.py b/python_apps/airtime_analyzer/airtime_analyzer/analyzer_pipeline.py index 3a267280f..2266fb9a2 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/analyzer_pipeline.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/analyzer_pipeline.py @@ -5,10 +5,6 @@ from filemover_analyzer import FileMoverAnalyzer class AnalyzerPipeline: - # Constructor - def __init__(self): - pass - # Take message dictionary and perform the necessary analysis. @staticmethod def run_analysis(queue, audio_file_path, import_directory, original_filename): diff --git a/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py index 0920bb8ec..ab12aad79 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/filemover_analyzer.py @@ -10,9 +10,6 @@ from analyzer import Analyzer class FileMoverAnalyzer(Analyzer): - def __init__(self): - pass - @staticmethod def analyze(audio_file_path, metadata): raise Exception("Use FileMoverAnalyzer.move() instead.") @@ -35,8 +32,8 @@ class FileMoverAnalyzer(Analyzer): final_file_path = import_directory if metadata.has_key("artist_name"): final_file_path += "/" + metadata["artist_name"] - if metadata.has_key("album"): - final_file_path += "/" + metadata["album"] + if metadata.has_key("album_title"): + final_file_path += "/" + metadata["album_title"] final_file_path += "/" + original_filename #Ensure any redundant slashes are stripped @@ -71,6 +68,8 @@ class FileMoverAnalyzer(Analyzer): return metadata def mkdir_p(path): + if path == "": + return try: os.makedirs(path) except OSError as exc: # Python >2.5 diff --git a/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py b/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py index 12a792a31..fa0a88807 100644 --- a/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py +++ b/python_apps/airtime_analyzer/airtime_analyzer/metadata_analyzer.py @@ -6,9 +6,6 @@ from analyzer import Analyzer class MetadataAnalyzer(Analyzer): - def __init__(self): - pass - @staticmethod def analyze(filename, metadata): if not isinstance(filename, unicode): @@ -33,6 +30,9 @@ class MetadataAnalyzer(Analyzer): #Use the python-magic module to get the MIME type. mime_magic = magic.Magic(mime=True) metadata["mime"] = mime_magic.from_file(filename) + + if isinstance(info, mutagen.mp3.MPEGInfo): + print "mode is: " + str(info.mode) #Try to get the number of channels if mutagen can... try: diff --git a/python_apps/airtime_analyzer/setup.py b/python_apps/airtime_analyzer/setup.py index 259696b8e..5691676f3 100644 --- a/python_apps/airtime_analyzer/setup.py +++ b/python_apps/airtime_analyzer/setup.py @@ -14,6 +14,8 @@ setup(name='airtime_analyzer', 'python-magic', 'pika', 'nose', + 'coverage', + 'mock', 'python-daemon', 'requests', ], diff --git a/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py b/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py index cc5dc96b5..ba7b6f202 100644 --- a/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py +++ b/python_apps/airtime_analyzer/tests/analyzer_pipeline_tests.py @@ -2,11 +2,12 @@ from nose.tools import * import os import shutil import multiprocessing +import Queue import datetime from airtime_analyzer.analyzer_pipeline import AnalyzerPipeline DEFAULT_AUDIO_FILE = u'tests/test_data/44100Hz-16bit-mono.mp3' -DEFAULT_IMPORT_DEST = u'Test Artist/44100Hz-16bit-mono.mp3' +DEFAULT_IMPORT_DEST = u'Test Artist/Test Album/44100Hz-16bit-mono.mp3' def setup(): pass @@ -32,3 +33,19 @@ def test_basic(): assert results["length"] == str(datetime.timedelta(seconds=results["length_seconds"])) assert os.path.exists(DEFAULT_IMPORT_DEST) +@raises(TypeError) +def test_wrong_type_queue_param(): + AnalyzerPipeline.run_analysis(Queue.Queue(), u'', u'', u'') + +@raises(TypeError) +def test_wrong_type_string_param2(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), '', u'', u'') + +@raises(TypeError) +def test_wrong_type_string_param3(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), u'', '', u'') + +@raises(TypeError) +def test_wrong_type_string_param4(): + AnalyzerPipeline.run_analysis(multiprocessing.queues.Queue(), u'', u'', '') + diff --git a/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py b/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py index 01a2c3d77..8baad0c54 100644 --- a/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py +++ b/python_apps/airtime_analyzer/tests/metadata_analyzer_tests.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import datetime +import mutagen +import mock from nose.tools import * from airtime_analyzer.metadata_analyzer import MetadataAnalyzer @@ -112,3 +114,36 @@ def test_mp3_utf8(): assert metadata['mime'] == 'audio/mpeg' assert metadata['track_total'] == u'10' # MP3s can have a track_total +# Make sure the parameter checking works +@raises(TypeError) +def test_move_wrong_string_param1(): + not_unicode = 'asdfasdf' + MetadataAnalyzer.analyze(not_unicode, dict()) + +@raises(TypeError) +def test_move_wrong_metadata_dict(): + not_a_dict = list() + MetadataAnalyzer.analyze(u'asdfasdf', not_a_dict) + +# Test an mp3 file where the number of channels is invalid or missing: +def test_mp3_bad_channels(): + filename = u'tests/test_data/44100Hz-16bit-mono.mp3' + ''' + It'd be a pain in the ass to construct a real MP3 with an invalid number + of channels by hand because that value is stored in every MP3 frame in the file + ''' + print "testing bad channels..." + audio_file = mutagen.File(filename, easy=True) + audio_file.info.mode = 1777 + with mock.patch('airtime_analyzer.metadata_analyzer.mutagen') as mock_mutagen: + mock_mutagen.File.return_value = audio_file + #mock_mutagen.side_effect = lambda *args, **kw: audio_file #File(*args, **kw) + + metadata = MetadataAnalyzer.analyze(filename, dict()) + check_default_metadata(metadata) + assert metadata['channels'] == 1 + assert metadata['bit_rate'] == 64000 + assert metadata['length_seconds'] == 3.90925 + assert metadata['mime'] == 'audio/mpeg' # Not unicode because MIMEs aren't. + assert metadata['track_total'] == u'10' # MP3s can have a track_total + #Mutagen doesn't extract comments from mp3s it seems