beets/test/test_mediafile_edge.py
Frederik “Freso” S. Olesen 4b1f0cbf48 Happy 2015. ;)
See 7a410f636b

Command used:

    git grep -l 'Copyright 201'|xargs sed -i -E 's/Copyright 201./Copyright 2015/'`
2015-01-08 21:37:09 +01:00

347 lines
12 KiB
Python

# This file is part of beets.
# Copyright 2015, Adrian Sampson.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
"""Specific, edge-case tests for the MediaFile metadata layer.
"""
import os
import shutil
import _common
from _common import unittest
from helper import TestHelper
import beets.mediafile
_sc = beets.mediafile._safe_cast
class EdgeTest(unittest.TestCase):
def test_emptylist(self):
# Some files have an ID3 frame that has a list with no elements.
# This is very hard to produce, so this is just the first 8192
# bytes of a file found "in the wild".
emptylist = beets.mediafile.MediaFile(
os.path.join(_common.RSRC, 'emptylist.mp3')
)
genre = emptylist.genre
self.assertEqual(genre, None)
def test_release_time_with_space(self):
# Ensures that release times delimited by spaces are ignored.
# Amie Street produces such files.
space_time = beets.mediafile.MediaFile(
os.path.join(_common.RSRC, 'space_time.mp3')
)
self.assertEqual(space_time.year, 2009)
self.assertEqual(space_time.month, 9)
self.assertEqual(space_time.day, 4)
def test_release_time_with_t(self):
# Ensures that release times delimited by Ts are ignored.
# The iTunes Store produces such files.
t_time = beets.mediafile.MediaFile(
os.path.join(_common.RSRC, 't_time.m4a')
)
self.assertEqual(t_time.year, 1987)
self.assertEqual(t_time.month, 3)
self.assertEqual(t_time.day, 31)
def test_tempo_with_bpm(self):
# Some files have a string like "128 BPM" in the tempo field
# rather than just a number.
f = beets.mediafile.MediaFile(os.path.join(_common.RSRC, 'bpm.mp3'))
self.assertEqual(f.bpm, 128)
def test_discc_alternate_field(self):
# Different taggers use different vorbis comments to reflect
# the disc and disc count fields: ensure that the alternative
# style works.
f = beets.mediafile.MediaFile(os.path.join(_common.RSRC, 'discc.ogg'))
self.assertEqual(f.disc, 4)
self.assertEqual(f.disctotal, 5)
def test_old_ape_version_bitrate(self):
f = beets.mediafile.MediaFile(os.path.join(_common.RSRC, 'oldape.ape'))
self.assertEqual(f.bitrate, 0)
class InvalidValueToleranceTest(unittest.TestCase):
def test_safe_cast_string_to_int(self):
self.assertEqual(_sc(int, 'something'), 0)
def test_safe_cast_int_string_to_int(self):
self.assertEqual(_sc(int, '20'), 20)
def test_safe_cast_string_to_bool(self):
self.assertEqual(_sc(bool, 'whatever'), False)
def test_safe_cast_intstring_to_bool(self):
self.assertEqual(_sc(bool, '5'), True)
def test_safe_cast_string_to_float(self):
self.assertAlmostEqual(_sc(float, '1.234'), 1.234)
def test_safe_cast_int_to_float(self):
self.assertAlmostEqual(_sc(float, 2), 2.0)
def test_safe_cast_string_with_cruft_to_float(self):
self.assertAlmostEqual(_sc(float, '1.234stuff'), 1.234)
def test_safe_cast_negative_string_to_float(self):
self.assertAlmostEqual(_sc(float, '-1.234'), -1.234)
def test_safe_cast_special_chars_to_unicode(self):
us = _sc(unicode, 'caf\xc3\xa9')
self.assertTrue(isinstance(us, unicode))
self.assertTrue(us.startswith(u'caf'))
def test_safe_cast_float_with_no_numbers(self):
v = _sc(float, '+')
self.assertEqual(v, 0.0)
class SafetyTest(unittest.TestCase, TestHelper):
def setUp(self):
self.create_temp_dir()
def tearDown(self):
self.remove_temp_dir()
def _exccheck(self, fn, exc, data=''):
fn = os.path.join(self.temp_dir, fn)
with open(fn, 'w') as f:
f.write(data)
try:
self.assertRaises(exc, beets.mediafile.MediaFile, fn)
finally:
os.unlink(fn) # delete the temporary file
def test_corrupt_mp3_raises_unreadablefileerror(self):
# Make sure we catch Mutagen reading errors appropriately.
self._exccheck('corrupt.mp3', beets.mediafile.UnreadableFileError)
def test_corrupt_mp4_raises_unreadablefileerror(self):
self._exccheck('corrupt.m4a', beets.mediafile.UnreadableFileError)
def test_corrupt_flac_raises_unreadablefileerror(self):
self._exccheck('corrupt.flac', beets.mediafile.UnreadableFileError)
def test_corrupt_ogg_raises_unreadablefileerror(self):
self._exccheck('corrupt.ogg', beets.mediafile.UnreadableFileError)
def test_invalid_ogg_header_raises_unreadablefileerror(self):
self._exccheck('corrupt.ogg', beets.mediafile.UnreadableFileError,
'OggS\x01vorbis')
def test_corrupt_monkeys_raises_unreadablefileerror(self):
self._exccheck('corrupt.ape', beets.mediafile.UnreadableFileError)
def test_invalid_extension_raises_filetypeerror(self):
self._exccheck('something.unknown', beets.mediafile.FileTypeError)
def test_magic_xml_raises_unreadablefileerror(self):
self._exccheck('nothing.xml', beets.mediafile.UnreadableFileError,
"ftyp")
@unittest.skipIf(not hasattr(os, 'symlink'), 'platform lacks symlink')
def test_broken_symlink(self):
fn = os.path.join(_common.RSRC, 'brokenlink')
os.symlink('does_not_exist', fn)
try:
self.assertRaises(IOError,
beets.mediafile.MediaFile, fn)
finally:
os.unlink(fn)
class SideEffectsTest(unittest.TestCase):
def setUp(self):
self.empty = os.path.join(_common.RSRC, 'empty.mp3')
def test_opening_tagless_file_leaves_untouched(self):
old_mtime = os.stat(self.empty).st_mtime
beets.mediafile.MediaFile(self.empty)
new_mtime = os.stat(self.empty).st_mtime
self.assertEqual(old_mtime, new_mtime)
class EncodingTest(unittest.TestCase, TestHelper):
def setUp(self):
self.create_temp_dir()
src = os.path.join(_common.RSRC, 'full.m4a')
self.path = os.path.join(self.temp_dir, 'test.m4a')
shutil.copy(src, self.path)
self.mf = beets.mediafile.MediaFile(self.path)
def tearDown(self):
self.remove_temp_dir()
def test_unicode_label_in_m4a(self):
self.mf.label = u'foo\xe8bar'
self.mf.save()
new_mf = beets.mediafile.MediaFile(self.path)
self.assertEqual(new_mf.label, u'foo\xe8bar')
class ZeroLengthMediaFile(beets.mediafile.MediaFile):
@property
def length(self):
return 0.0
class MissingAudioDataTest(unittest.TestCase):
def setUp(self):
super(MissingAudioDataTest, self).setUp()
path = os.path.join(_common.RSRC, 'full.mp3')
self.mf = ZeroLengthMediaFile(path)
def test_bitrate_with_zero_length(self):
del self.mf.mgfile.info.bitrate # Not available directly.
self.assertEqual(self.mf.bitrate, 0)
class TypeTest(unittest.TestCase):
def setUp(self):
super(TypeTest, self).setUp()
path = os.path.join(_common.RSRC, 'full.mp3')
self.mf = beets.mediafile.MediaFile(path)
def test_year_integer_in_string(self):
self.mf.year = '2009'
self.assertEqual(self.mf.year, 2009)
def test_set_replaygain_gain_to_none(self):
self.mf.rg_track_gain = None
self.assertEqual(self.mf.rg_track_gain, 0.0)
def test_set_replaygain_peak_to_none(self):
self.mf.rg_track_peak = None
self.assertEqual(self.mf.rg_track_peak, 0.0)
def test_set_year_to_none(self):
self.mf.year = None
self.assertIsNone(self.mf.year)
def test_set_track_to_none(self):
self.mf.track = None
self.assertEqual(self.mf.track, 0)
def test_set_date_to_none(self):
self.mf.date = None
self.assertIsNone(self.mf.date)
self.assertIsNone(self.mf.year)
self.assertIsNone(self.mf.month)
self.assertIsNone(self.mf.day)
class SoundCheckTest(unittest.TestCase):
def test_round_trip(self):
data = beets.mediafile._sc_encode(1.0, 1.0)
gain, peak = beets.mediafile._sc_decode(data)
self.assertEqual(gain, 1.0)
self.assertEqual(peak, 1.0)
def test_decode_zero(self):
data = u' 80000000 80000000 00000000 00000000 00000000 00000000 ' \
u'00000000 00000000 00000000 00000000'
gain, peak = beets.mediafile._sc_decode(data)
self.assertEqual(gain, 0.0)
self.assertEqual(peak, 0.0)
def test_malformatted(self):
gain, peak = beets.mediafile._sc_decode(u'foo')
self.assertEqual(gain, 0.0)
self.assertEqual(peak, 0.0)
def test_special_characters(self):
gain, peak = beets.mediafile._sc_decode(u'caf\xe9')
self.assertEqual(gain, 0.0)
self.assertEqual(peak, 0.0)
class ID3v23Test(unittest.TestCase, TestHelper):
def _make_test(self, ext='mp3', id3v23=False):
self.create_temp_dir()
src = os.path.join(_common.RSRC, 'full.{0}'.format(ext))
self.path = os.path.join(self.temp_dir, 'test.{0}'.format(ext))
shutil.copy(src, self.path)
return beets.mediafile.MediaFile(self.path, id3v23=id3v23)
def _delete_test(self):
self.remove_temp_dir()
def test_v24_year_tag(self):
mf = self._make_test(id3v23=False)
try:
mf.year = 2013
mf.save()
frame = mf.mgfile['TDRC']
self.assertTrue('2013' in str(frame))
self.assertTrue('TYER' not in mf.mgfile)
finally:
self._delete_test()
def test_v23_year_tag(self):
mf = self._make_test(id3v23=True)
try:
mf.year = 2013
mf.save()
frame = mf.mgfile['TYER']
self.assertTrue('2013' in str(frame))
self.assertTrue('TDRC' not in mf.mgfile)
finally:
self._delete_test()
def test_v23_on_non_mp3_is_noop(self):
mf = self._make_test('m4a', id3v23=True)
try:
mf.year = 2013
mf.save()
finally:
self._delete_test()
def test_v24_image_encoding(self):
mf = self._make_test(id3v23=False)
try:
mf.images = [beets.mediafile.Image(b'test data')]
mf.save()
frame = mf.mgfile.tags.getall('APIC')[0]
self.assertEqual(frame.encoding, 3)
finally:
self._delete_test()
@unittest.skip("a bug, see #899")
def test_v23_image_encoding(self):
"""For compatibility with OS X/iTunes (and strict adherence to
the standard), ID3v2.3 tags need to use an inferior text
encoding: UTF-8 is not supported.
"""
mf = self._make_test(id3v23=True)
try:
mf.images = [beets.mediafile.Image(b'test data')]
mf.save()
frame = mf.mgfile.tags.getall('APIC')[0]
self.assertEqual(frame.encoding, 1)
finally:
self._delete_test()
def suite():
return unittest.TestLoader().loadTestsFromName(__name__)
if __name__ == '__main__':
unittest.main(defaultTest='suite')