mirror of
https://github.com/beetbox/beets.git
synced 2025-12-15 21:14:19 +01:00
modernize basic MediaFile tests
These tests were written when I knew almost nothing about Python and even less about unittest. The class-generating magic never worked with nose for a crazy reason I won't get into here. This has a bit more copypasta but the workings are more obvious and we no longer generate enormous numbers of independent tests. There should be a more representative number of dots in the test runner output now.
This commit is contained in:
parent
0a21d72a79
commit
f06c7dfbef
1 changed files with 200 additions and 164 deletions
|
|
@ -23,128 +23,7 @@ import _common
|
|||
from _common import unittest
|
||||
import beets.mediafile
|
||||
|
||||
|
||||
def MakeReadingTest(path, correct_dict, field):
|
||||
class ReadingTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.f = beets.mediafile.MediaFile(path)
|
||||
def runTest(self):
|
||||
got = getattr(self.f, field)
|
||||
correct = correct_dict[field]
|
||||
message = field + ' incorrect (expected ' + repr(correct) + \
|
||||
', got ' + repr(got) + ') when testing ' + \
|
||||
os.path.basename(path)
|
||||
if isinstance(correct, float):
|
||||
self.assertAlmostEqual(got, correct, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, correct, message)
|
||||
return ReadingTest
|
||||
|
||||
def MakeReadOnlyTest(path, field, value):
|
||||
class ReadOnlyTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.f = beets.mediafile.MediaFile(path)
|
||||
def runTest(self):
|
||||
got = getattr(self.f, field)
|
||||
fail_msg = field + ' incorrect (expected ' + \
|
||||
repr(value) + ', got ' + repr(got) + \
|
||||
') on ' + os.path.basename(path)
|
||||
if field == 'length':
|
||||
self.assertTrue(value-0.1 < got < value+0.1, fail_msg)
|
||||
else:
|
||||
self.assertEqual(got, value, fail_msg)
|
||||
return ReadOnlyTest
|
||||
|
||||
def MakeWritingTest(path, correct_dict, field, testsuffix='_test'):
|
||||
|
||||
class WritingTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# make a copy of the file we'll work on
|
||||
root, ext = os.path.splitext(path)
|
||||
self.tpath = root + testsuffix + ext
|
||||
shutil.copy(path, self.tpath)
|
||||
|
||||
# generate the new value we'll try storing
|
||||
if field == 'art':
|
||||
self.value = 'xxx'
|
||||
elif type(correct_dict[field]) is unicode:
|
||||
self.value = u'TestValue: ' + field
|
||||
elif type(correct_dict[field]) is int:
|
||||
self.value = correct_dict[field] + 42
|
||||
elif type(correct_dict[field]) is bool:
|
||||
self.value = not correct_dict[field]
|
||||
elif type(correct_dict[field]) is datetime.date:
|
||||
self.value = correct_dict[field] + datetime.timedelta(42)
|
||||
elif type(correct_dict[field]) is str:
|
||||
self.value = 'TestValue-' + str(field)
|
||||
elif type(correct_dict[field]) is float:
|
||||
self.value = 9.87
|
||||
else:
|
||||
raise ValueError('unknown field type ' + \
|
||||
str(type(correct_dict[field])))
|
||||
|
||||
def runTest(self):
|
||||
# write new tag
|
||||
a = beets.mediafile.MediaFile(self.tpath)
|
||||
setattr(a, field, self.value)
|
||||
a.save()
|
||||
|
||||
# verify ALL tags are correct with modification
|
||||
b = beets.mediafile.MediaFile(self.tpath)
|
||||
for readfield in correct_dict.keys():
|
||||
got = getattr(b, readfield)
|
||||
|
||||
# Make sure the modified field was changed correctly...
|
||||
if readfield == field:
|
||||
message = field + ' modified incorrectly (changed to ' + \
|
||||
repr(self.value) + ' but read ' + repr(got) + \
|
||||
') when testing ' + os.path.basename(path)
|
||||
if isinstance(self.value, float):
|
||||
self.assertAlmostEqual(got, self.value, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, self.value, message)
|
||||
|
||||
# ... and that no other field was changed.
|
||||
else:
|
||||
# MPEG-4: ReplayGain not implented.
|
||||
if 'm4a' in path and readfield.startswith('rg_'):
|
||||
continue
|
||||
|
||||
# The value should be what it was originally most of the
|
||||
# time.
|
||||
correct = correct_dict[readfield]
|
||||
|
||||
# The date field, however, is modified when its components
|
||||
# change.
|
||||
if readfield=='date' and field in ('year', 'month', 'day'):
|
||||
try:
|
||||
correct = datetime.date(
|
||||
self.value if field=='year' else correct.year,
|
||||
self.value if field=='month' else correct.month,
|
||||
self.value if field=='day' else correct.day
|
||||
)
|
||||
except ValueError:
|
||||
correct = datetime.date.min
|
||||
# And vice-versa.
|
||||
if field=='date' and readfield in ('year', 'month', 'day'):
|
||||
correct = getattr(self.value, readfield)
|
||||
|
||||
message = readfield + ' changed when it should not have' \
|
||||
' (expected ' + repr(correct) + ', got ' + \
|
||||
repr(got) + ') when modifying ' + field + \
|
||||
' in ' + os.path.basename(path)
|
||||
if isinstance(correct, float):
|
||||
self.assertAlmostEqual(got, correct, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, correct, message)
|
||||
|
||||
def tearDown(self):
|
||||
if os.path.exists(self.tpath):
|
||||
os.remove(self.tpath)
|
||||
|
||||
return WritingTest
|
||||
|
||||
correct_dicts = {
|
||||
CORRECT_DICTS = {
|
||||
|
||||
# All of the fields iTunes supports that we do also.
|
||||
'full': {
|
||||
|
|
@ -253,7 +132,7 @@ correct_dicts = {
|
|||
|
||||
}
|
||||
|
||||
read_only_correct_dicts = {
|
||||
READ_ONLY_CORRECT_DICTS = {
|
||||
'full.mp3': {
|
||||
'length': 1.0,
|
||||
'bitrate': 80000,
|
||||
|
|
@ -318,21 +197,7 @@ read_only_correct_dicts = {
|
|||
},
|
||||
}
|
||||
|
||||
def suite_for_file(path, correct_dict, writing=True):
|
||||
s = unittest.TestSuite()
|
||||
for field in correct_dict:
|
||||
if 'm4a' in path and field.startswith('rg_'):
|
||||
# MPEG-4 files: ReplayGain values not implemented.
|
||||
continue
|
||||
s.addTest(MakeReadingTest(path, correct_dict, field)())
|
||||
if writing and \
|
||||
not ( field == 'month' and correct_dict['year'] == 0
|
||||
or field == 'day' and correct_dict['month'] == 0):
|
||||
# ensure that we don't test fields that can't be modified
|
||||
s.addTest(MakeWritingTest(path, correct_dict, field)())
|
||||
return s
|
||||
|
||||
test_files = {
|
||||
TEST_FILES = {
|
||||
'm4a': ['full', 'partial', 'min'],
|
||||
'mp3': ['full', 'partial', 'min'],
|
||||
'flac': ['full', 'partial', 'min'],
|
||||
|
|
@ -342,39 +207,210 @@ test_files = {
|
|||
'mpc': ['full'],
|
||||
}
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
class AllFilesMixin(object):
|
||||
"""This is a dumb bit of copypasta but unittest has no supported
|
||||
method of generating tests at runtime.
|
||||
"""
|
||||
def test_m4a_full(self):
|
||||
self._run('full', 'm4a')
|
||||
|
||||
# General tests.
|
||||
for kind, tagsets in test_files.items():
|
||||
for tagset in tagsets:
|
||||
path = os.path.join(_common.RSRC, tagset + '.' + kind)
|
||||
correct_dict = correct_dicts[tagset]
|
||||
for test in suite_for_file(path, correct_dict):
|
||||
s.addTest(test)
|
||||
def test_m4a_partial(self):
|
||||
self._run('partial', 'm4a')
|
||||
|
||||
# Special test for missing ID3 tag.
|
||||
for test in suite_for_file(os.path.join(_common.RSRC, 'empty.mp3'),
|
||||
correct_dicts['empty'],
|
||||
writing=False):
|
||||
s.addTest(test)
|
||||
def test_m4a_min(self):
|
||||
self._run('min', 'm4a')
|
||||
|
||||
def test_mp3_full(self):
|
||||
self._run('full', 'mp3')
|
||||
|
||||
def test_mp3_partial(self):
|
||||
self._run('partial', 'mp3')
|
||||
|
||||
def test_mp3_min(self):
|
||||
self._run('min', 'mp3')
|
||||
|
||||
def test_flac_full(self):
|
||||
self._run('full', 'flac')
|
||||
|
||||
def test_flac_partial(self):
|
||||
self._run('partial', 'flac')
|
||||
|
||||
def test_flac_min(self):
|
||||
self._run('min', 'flac')
|
||||
|
||||
def test_ogg(self):
|
||||
self._run('full', 'ogg')
|
||||
|
||||
def test_ape(self):
|
||||
self._run('full', 'ape')
|
||||
|
||||
def test_wv(self):
|
||||
self._run('full', 'wv')
|
||||
|
||||
def test_mpc(self):
|
||||
self._run('full', 'mpc')
|
||||
|
||||
# Special test for advanced release date.
|
||||
for test in suite_for_file(os.path.join(_common.RSRC, 'date.mp3'),
|
||||
correct_dicts['date']):
|
||||
s.addTest(test)
|
||||
def test_date_mp3(self):
|
||||
self._run('date', 'mp3')
|
||||
|
||||
# Read-only attribute tests.
|
||||
for fname, correct_dict in read_only_correct_dicts.iteritems():
|
||||
path = os.path.join(_common.RSRC, fname)
|
||||
for field, value in correct_dict.iteritems():
|
||||
s.addTest(MakeReadOnlyTest(path, field, value)())
|
||||
class ReadingTest(unittest.TestCase, AllFilesMixin):
|
||||
def _read_field(self, mf, correct_dict, field):
|
||||
got = getattr(mf, field)
|
||||
correct = correct_dict[field]
|
||||
message = field + ' incorrect (expected ' + repr(correct) + \
|
||||
', got ' + repr(got) + ')'
|
||||
if isinstance(correct, float):
|
||||
self.assertAlmostEqual(got, correct, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, correct, message)
|
||||
|
||||
def _run(self, tagset, kind):
|
||||
correct_dict = CORRECT_DICTS[tagset]
|
||||
path = os.path.join(_common.RSRC, tagset + '.' + kind)
|
||||
f = beets.mediafile.MediaFile(path)
|
||||
for field in correct_dict:
|
||||
if 'm4a' in path and field.startswith('rg_'):
|
||||
# MPEG-4 files: ReplayGain values not implemented.
|
||||
continue
|
||||
self._read_field(f, correct_dict, field)
|
||||
|
||||
return s
|
||||
# Special test for missing ID3 tag.
|
||||
def test_empy_mp3(self):
|
||||
self._run('empty', 'mp3')
|
||||
|
||||
def test_nose_suite():
|
||||
for test in suite():
|
||||
yield test
|
||||
class WritingTest(unittest.TestCase, AllFilesMixin):
|
||||
def _write_field(self, tpath, field, value, correct_dict):
|
||||
# Write new tag.
|
||||
a = beets.mediafile.MediaFile(tpath)
|
||||
setattr(a, field, value)
|
||||
a.save()
|
||||
|
||||
# Verify ALL tags are correct with modification.
|
||||
b = beets.mediafile.MediaFile(tpath)
|
||||
for readfield in correct_dict.keys():
|
||||
got = getattr(b, readfield)
|
||||
|
||||
# Make sure the modified field was changed correctly...
|
||||
if readfield == field:
|
||||
message = field + ' modified incorrectly (changed to ' + \
|
||||
repr(value) + ' but read ' + repr(got) + ')'
|
||||
if isinstance(value, float):
|
||||
self.assertAlmostEqual(got, value, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, value, message)
|
||||
|
||||
# ... and that no other field was changed.
|
||||
else:
|
||||
# MPEG-4: ReplayGain not implented.
|
||||
if 'm4a' in tpath and readfield.startswith('rg_'):
|
||||
continue
|
||||
|
||||
# The value should be what it was originally most of the
|
||||
# time.
|
||||
correct = correct_dict[readfield]
|
||||
|
||||
# The date field, however, is modified when its components
|
||||
# change.
|
||||
if readfield=='date' and field in ('year', 'month', 'day'):
|
||||
try:
|
||||
correct = datetime.date(
|
||||
value if field=='year' else correct.year,
|
||||
value if field=='month' else correct.month,
|
||||
value if field=='day' else correct.day
|
||||
)
|
||||
except ValueError:
|
||||
correct = datetime.date.min
|
||||
# And vice-versa.
|
||||
if field=='date' and readfield in ('year', 'month', 'day'):
|
||||
correct = getattr(value, readfield)
|
||||
|
||||
message = readfield + ' changed when it should not have' \
|
||||
' (expected ' + repr(correct) + ', got ' + \
|
||||
repr(got) + ') when modifying ' + field
|
||||
if isinstance(correct, float):
|
||||
self.assertAlmostEqual(got, correct, msg=message)
|
||||
else:
|
||||
self.assertEqual(got, correct, message)
|
||||
|
||||
def _run(self, tagset, kind):
|
||||
correct_dict = CORRECT_DICTS[tagset]
|
||||
path = os.path.join(_common.RSRC, tagset + '.' + kind)
|
||||
|
||||
for field in correct_dict:
|
||||
if field == 'month' and correct_dict['year'] == 0 or \
|
||||
field == 'day' and correct_dict['month'] == 0:
|
||||
continue
|
||||
|
||||
# Generate the new value we'll try storing.
|
||||
if field == 'art':
|
||||
value = 'xxx'
|
||||
elif type(correct_dict[field]) is unicode:
|
||||
value = u'TestValue: ' + field
|
||||
elif type(correct_dict[field]) is int:
|
||||
value = correct_dict[field] + 42
|
||||
elif type(correct_dict[field]) is bool:
|
||||
value = not correct_dict[field]
|
||||
elif type(correct_dict[field]) is datetime.date:
|
||||
value = correct_dict[field] + datetime.timedelta(42)
|
||||
elif type(correct_dict[field]) is str:
|
||||
value = 'TestValue-' + str(field)
|
||||
elif type(correct_dict[field]) is float:
|
||||
value = 9.87
|
||||
else:
|
||||
raise ValueError('unknown field type ' + \
|
||||
str(type(correct_dict[field])))
|
||||
|
||||
# Make a copy of the file we'll work on.
|
||||
root, ext = os.path.splitext(path)
|
||||
tpath = root + '_test' + ext
|
||||
shutil.copy(path, tpath)
|
||||
|
||||
try:
|
||||
self._write_field(tpath, field, value, correct_dict)
|
||||
finally:
|
||||
os.remove(tpath)
|
||||
|
||||
class ReadOnlyTest(unittest.TestCase):
|
||||
def _read_field(self, mf, field, value):
|
||||
got = getattr(mf, field)
|
||||
fail_msg = field + ' incorrect (expected ' + \
|
||||
repr(value) + ', got ' + repr(got) + ')'
|
||||
if field == 'length':
|
||||
self.assertTrue(value-0.1 < got < value+0.1, fail_msg)
|
||||
else:
|
||||
self.assertEqual(got, value, fail_msg)
|
||||
|
||||
def _run(self, filename):
|
||||
path = os.path.join(_common.RSRC, filename)
|
||||
f = beets.mediafile.MediaFile(path)
|
||||
correct_dict = READ_ONLY_CORRECT_DICTS[filename]
|
||||
for field, value in correct_dict.items():
|
||||
self._read_field(f, field, value)
|
||||
|
||||
def test_mp3(self):
|
||||
self._run('full.mp3')
|
||||
|
||||
def test_m4a(self):
|
||||
self._run('full.m4a')
|
||||
|
||||
def test_flac(self):
|
||||
self._run('full.flac')
|
||||
|
||||
def test_ogg(self):
|
||||
self._run('full.ogg')
|
||||
|
||||
def test_ape(self):
|
||||
self._run('full.ape')
|
||||
|
||||
def test_wv(self):
|
||||
self._run('full.wv')
|
||||
|
||||
def test_mpc(self):
|
||||
self._run('full.mpc')
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='suite')
|
||||
|
|
|
|||
Loading…
Reference in a new issue