Shuvit game master repo. http://shuvit.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test.py 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # tests can be extended using other bigger files that are not going to be
  4. # checked into git, by placing them into the custom_samples folder
  5. #
  6. # see custom_samples/instructions.txt
  7. #
  8. from __future__ import unicode_literals
  9. import timeit
  10. import os
  11. import re
  12. from nose.tools import *
  13. from tinytag import TinyTagException, TinyTag, ID3, Ogg, Wave, Flac
  14. try:
  15. from collections import OrderedDict
  16. except ImportError:
  17. OrderedDict = dict # python 2.6 and 3.2 compat
  18. testfiles = OrderedDict([
  19. # MP3
  20. ('samples/vbri.mp3', {'channels': 2, 'samplerate': 44100, 'track_total': None, 'duration': 0.47020408163265304, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'}),
  21. ('samples/cbr.mp3', {'channels': 2, 'samplerate': 44100, 'track_total': None, 'duration': 0.49, 'album': 'I Can Walk On Water I Can Fly', 'year': '2007', 'title': 'I Can Walk On Water I Can Fly', 'artist': 'Basshunter', 'track': '01'}),
  22. # the output of the lame encoder was 185.4 bitrate, but this is good enough for now
  23. ('samples/vbr_xing_header.mp3', {'bitrate': 186.04383278145696, 'channels': 1, 'samplerate': 44100, 'duration': 3.944489795918367}),
  24. ('samples/id3v22-test.mp3', {'channels': 2, 'samplerate': 44100, 'track_total': '11', 'duration': 0.138, 'album': 'Hymns for the Exiled', 'year': '2004', 'title': 'cosmic american', 'artist': 'Anais Mitchell', 'track': '3'}),
  25. ('samples/silence-44-s-v1.mp3', {'channels': 2, 'samplerate': 44100, 'genre': 'Darkwave', 'track_total': None, 'duration': 3.7355102040816326, 'album': 'Quod Libet Test Data', 'year': '2004', 'title': 'Silence', 'artist': 'piman', 'track': '2'}),
  26. ('samples/id3v1-latin1.mp3', {'channels': 2, 'samplerate': 44100, 'genre': 'Rock', 'samplerate': None, 'album': 'The Young Americans', 'title': 'Play Dead', 'bitrate': 0.0, 'filesize': 256, 'audio_offset': 0, 'track': '12', 'artist': 'Björk', 'duration': 0, 'track_total': None, 'year': '1993'}),
  27. ('samples/UTF16.mp3', {'channels': None, 'samplerate': None, 'duration': 0, 'track_total': '11', 'track': '07', 'artist': 'The National', 'year': '2010', 'album': 'High Violet', 'title': 'Lemonworld'}),
  28. ('samples/utf-8-id3v2.mp3', {'channels': 2, 'samplerate': 44100, 'genre': 'Acustico', 'duration': 0, 'track_total': '21', 'track': '01', 'filesize': 2119, 'title': 'Gran día', 'artist': 'Paso a paso', 'album': 'S/T', 'bitrate': 0.0, 'year': None, 'audio_offset': 0, 'samplerate': None}),
  29. ('samples/empty_file.mp3', {'channels': None, 'samplerate': None, 'track_total': None, 'album': None, 'year': None, 'duration': 0.0, 'title': None, 'track': None, 'artist': None}),
  30. ('samples/silence-44khz-56k-mono-1s.mp3', {'channels': 1, 'samplerate': 44100, 'duration': 1.018, 'samplerate': 44100}),
  31. ('samples/silence-22khz-mono-1s.mp3', {'channels': 1, 'samplerate': 22050}),
  32. ('samples/id3v24-long-title.mp3', {'track': '1', 'audio_offset': 0, 'disc_total': '1', 'album': 'The Double EP: A Sea of Split Peas', 'filesize': 10000, 'duration': 0, 'channels': None, 'track_total': '12', 'genre': 'AlternRock', 'title': 'Out of the Woodwork', 'artist': 'Courtney Barnett', 'bitrate': 0.0, 'samplerate': None, 'year': None, 'disc': '1'}),
  33. ('samples/utf16be.mp3', {'title': '52-girls', 'filesize': 2048, 'track': '6', 'album': 'party mix', 'artist': 'The B52s', 'genre': '(17)', 'albumartist': None, 'disc': None}),
  34. ('samples/id3v22_image.mp3', {'title': 'Kids (MGMT Cover) ', 'filesize': 35924, 'album': 'winniecooper.net ', 'artist': 'The Kooks', 'year': '2008'}),
  35. # OGG
  36. ('samples/empty.ogg', {'track_total': None, 'duration': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False}),
  37. ('samples/multipagecomment.ogg', {'track_total': None, 'duration': 3.684716553287982, 'album': None, '_max_samplenum': 162496, 'year': None, 'title': None, 'artist': None, 'track': None, '_tags_parsed': False}),
  38. ('samples/multipage-setup.ogg', {'genre': 'JRock', 'track_total': None, 'duration': 4.128798185941043, 'album': 'Timeless', 'year': '2006', 'title': 'Burst', 'artist': 'UVERworld', 'track': '7', '_tags_parsed': False}),
  39. ('samples/test.ogg', {'track_total': None, 'duration': 1.0, 'album': 'the boss', 'year': '2006', 'title': 'the boss', 'artist': 'james brown', 'track': '1', '_tags_parsed': False}),
  40. # OPUS
  41. ('samples/test.opus', {'albumartist': 'Alstroemeria Records', 'samplerate': 48000, 'channels': 2, 'track': '1', 'disc': '1', 'title': 'Bad Apple!!', 'duration': 2.0, 'year': '2008.05.25', 'filesize': 10000, 'artist': 'nomico', 'album': 'Exserens - A selection of Alstroemeria Records'}),
  42. # WAV
  43. ('samples/test.wav', {'duration': 1.0}),
  44. ('samples/test3sMono.wav', {'duration': 3.0}),
  45. ('samples/test-tagged.wav', {'duration': 1.0}),
  46. ('samples/silence-22khz-mono-1s.wav', {'duration': 1.0}),
  47. # FLAC
  48. ('samples/flac1sMono.flac', {'genre': 'Avantgarde', 'track_total': None, 'album': 'alb', 'year': '2014', 'duration': 1.0, 'title': 'track', 'track': '23', 'artist': 'art', 'channels': 1}),
  49. ('samples/flac453sStereo.flac', {'channels': 2, 'track_total': None, 'album': None, 'year': None, 'duration': 453.51473922902494, 'title': None, 'track': None, 'artist': None}),
  50. ('samples/flac1.5sStereo.flac', {'channels': 2, 'track_total': None, 'album': 'alb', 'year': '2014', 'duration': 1.4995238095238095, 'title': 'track', 'track': '23', 'artist': 'art'}),
  51. ('samples/flac_application.flac', {'channels': 2, 'track_total': '11', 'album': 'Belle and Sebastian Write About Love', 'year': '2010-10-11', 'duration': 273.64, 'title': 'I Want the World to Stop', 'track': '4', 'artist': 'Belle and Sebastian'}),
  52. ('samples/no-tags.flac', {'channels': 2, 'track_total': None, 'album': None, 'year': None, 'duration': 3.684716553287982, 'title': None, 'track': None, 'artist': None}),
  53. ('samples/variable-block.flac', {'channels': 2, 'track_total': None, 'album': 'Appleseed Original Soundtrack', 'year': '2004', 'duration': 261.68, 'title': 'DIVE FOR YOU', 'track': '01', 'artist': 'Boom Boom Satellites'}),
  54. ('samples/106-invalid-streaminfo.flac', {}),
  55. ('samples/106-short-picture-block-size.flac', {}),
  56. # WMA
  57. ('samples/test2.wma', {'samplerate': 44100, 'album': 'The Colour and the Shape', 'title': 'Doll', 'bitrate': 64.04, 'filesize': 5800, 'audio_offset': 0, 'track': 1, 'artist': 'Foo Fighters', 'duration': 86.406, 'track_total': None, 'year': '1997', 'genre': 'Alternative'}),
  58. # M4A/MP4
  59. ('samples/test.m4a', {'samplerate': 44100, 'duration': 314.97, 'bitrate': 256.0, 'channels': 2, 'genre': 'Pop', 'year': '2011', 'title': 'Nothing', 'album': 'Only Our Hearts To Lose', 'track_total': 11, 'track': 11, 'artist': 'Marian', 'filesize': 61432}),
  60. ('samples/test2.m4a', {'bitrate': 256.0, 'track': 1, 'albumartist': "Millie Jackson - Get It Out 'cha System - 1978", 'duration': 167.78739229024944, 'filesize': 223365, 'channels': 2, 'year': '1978', 'artist': 'Millie Jackson', 'track_total': 9, 'disc_total': 1, 'genre': 'R&B/Soul', 'album': "Get It Out 'cha System", 'samplerate': 44100, 'disc': 1, 'title': 'Go Out and Get Some'}),
  61. ('samples/iso8859_with_image.m4a', {'artist': 'Major Lazer', 'filesize': 57017, 'title': 'Cold Water (feat. Justin Bieber & M�)', 'album': 'Cold Water (feat. Justin Bieber & M�) - Single', 'year': '2016', 'samplerate': 44100, 'duration': 188.545, 'genre': 'Electronic;Music', 'albumartist': 'Major Lazer', 'channels': 2}),
  62. ])
  63. testfolder = os.path.join(os.path.dirname(__file__))
  64. # load custom samples
  65. custom_samples_folder = os.path.join(testfolder, 'custom_samples')
  66. pattern_field_name_type = [
  67. ('sr(\d+)', 'samplerate', int),
  68. ('dn(\d+)', 'disc', str),
  69. ('dt(\d+)', 'disc_total', str),
  70. ('d(\d+.?\d*)', 'duration', float),
  71. ('b(\d+)', 'bitrate', int),
  72. ('c(\d)', 'channels', int),
  73. ]
  74. for filename in os.listdir(custom_samples_folder):
  75. expected_values = {}
  76. for pattern, fieldname, _type in pattern_field_name_type:
  77. match = re.findall(pattern, filename)
  78. if match:
  79. expected_values[fieldname] = _type(match[0])
  80. if expected_values:
  81. testfiles[os.path.join('custom_samples', filename)] = expected_values
  82. def get_info(testfile, expected):
  83. filename = os.path.join(testfolder, testfile)
  84. # print(filename)
  85. tag = TinyTag.get(filename)
  86. for key, expected_val in expected.items():
  87. result = getattr(tag, key)
  88. if key == 'duration':
  89. # allow duration to be off by 100 ms and a maximum of 1%
  90. if abs(result - expected_val) < 0.100:
  91. if expected_val and min(result, expected_val) / max(result, expected_val) > 0.99:
  92. continue
  93. fmt_string = 'field "%s": got %s (%s) expected %s (%s)!'
  94. fmt_values = (key, repr(result), type(result), repr(expected_val), type(expected_val))
  95. assert result == expected_val, fmt_string % fmt_values
  96. def test_generator():
  97. for testfile, expected in testfiles.items():
  98. yield get_info, testfile, expected
  99. @raises(LookupError)
  100. def test_unsupported_extension():
  101. bogus_file = os.path.join(testfolder, 'samples/there_is_no_such_ext.bogus')
  102. TinyTag.get(bogus_file)
  103. @raises(NotImplementedError)
  104. def test_unsubclassed_tinytag_duration():
  105. tag = TinyTag(None, 0)
  106. tag._determine_duration(None)
  107. @raises(NotImplementedError)
  108. def test_unsubclassed_tinytag_parse_tag():
  109. tag = TinyTag(None, 0)
  110. tag._parse_tag(None)
  111. def test_mp3_length_estimation():
  112. ID3.set_estimation_precision(0.7)
  113. tag = TinyTag.get(os.path.join(testfolder, 'samples/silence-44-s-v1.mp3'))
  114. print(tag.duration)
  115. assert 3.5 < tag.duration < 4.0
  116. @raises(TinyTagException)
  117. def test_unexpected_eof():
  118. tag = ID3.get(os.path.join(testfolder, 'samples/incomplete.mp3'))
  119. @raises(TinyTagException)
  120. def test_invalid_flac_file():
  121. tag = Flac.get(os.path.join(testfolder, 'samples/silence-44-s-v1.mp3'))
  122. @raises(TinyTagException)
  123. def test_invalid_mp3_file():
  124. tag = ID3.get(os.path.join(testfolder, 'samples/flac1.5sStereo.flac'))
  125. @raises(TinyTagException)
  126. def test_invalid_ogg_file():
  127. tag = Ogg.get(os.path.join(testfolder, 'samples/flac1.5sStereo.flac'))
  128. @raises(TinyTagException)
  129. def test_invalid_wave_file():
  130. tag = Wave.get(os.path.join(testfolder, 'samples/flac1.5sStereo.flac'))
  131. def test_mp3_image_loading():
  132. tag = TinyTag.get(os.path.join(testfolder, 'samples/cover_img.mp3'), image=True)
  133. image_data = tag.get_image()
  134. assert image_data is not None
  135. assert 140000 < len(image_data) < 150000, 'Image is %d bytes but should be around 145kb' % len(image_data)
  136. assert image_data.startswith(b'\xff\xd8\xff\xe0'), 'The image data must start with a jpeg header'
  137. def test_mp3_id3v22_image_loading():
  138. tag = TinyTag.get(os.path.join(testfolder, 'samples/id3v22_image.mp3'), image=True)
  139. image_data = tag.get_image()
  140. assert image_data is not None
  141. assert 18000 < len(image_data) < 19000, 'Image is %d bytes but should be around 18.1kb' % len(image_data)
  142. assert image_data.startswith(b'\xff\xd8\xff\xe0'), 'The image data must start with a jpeg header'
  143. def test_mp3_image_loading_without_description():
  144. tag = TinyTag.get(os.path.join(testfolder, 'samples/id3image_without_description.mp3'), image=True)
  145. image_data = tag.get_image()
  146. assert image_data is not None
  147. assert 28600 < len(image_data) < 28700, 'Image is %d bytes but should be around 28.6kb' % len(image_data)
  148. assert image_data.startswith(b'\xff\xd8\xff\xe0'), 'The image data must start with a jpeg header'
  149. def test_mp4_image_loading():
  150. tag = TinyTag.get(os.path.join(testfolder, 'samples/iso8859_with_image.m4a'), image=True)
  151. image_data = tag.get_image()
  152. assert image_data is not None
  153. assert 20000 < len(image_data) < 25000, 'Image is %d bytes but should be around 22kb' % len(image_data)
  154. def test_to_str():
  155. tag = TinyTag.get(os.path.join(testfolder, 'samples/empty.ogg'))
  156. assert str(tag) # since the dict is not ordered we cannot == 'somestring'
  157. assert repr(tag) # since the dict is not ordered we cannot == 'somestring'