1# -*- coding: utf-8 -*- 2 3import operator 4 5from tests import TestCase 6 7from mutagen._compat import text_type, xrange, PY2, PY3, iteritems, izip, \ 8 integer_types 9from mutagen._constants import GENRES 10from mutagen.id3._tags import read_frames, save_frame, ID3Header 11from mutagen.id3._util import ID3SaveConfig, is_valid_frame_id, \ 12 ID3JunkFrameError 13from mutagen.id3 import APIC, CTOC, CHAP, TPE2, Frames, Frames_2_2, CRA, \ 14 AENC, PIC, LNK, LINK, SIGN, PRIV, GRID, ENCR, COMR, USER, UFID, GEOB, \ 15 POPM, EQU2, RVA2, COMM, SYLT, USLT, WXXX, TXXX, WCOM, TextFrame, \ 16 UrlFrame, NumericTextFrame, NumericPartTextFrame, TPE1, TIT2, \ 17 TimeStampTextFrame, TCON, ID3TimeStamp, Frame, RVRB, RBUF, CTOCFlags, \ 18 PairedTextFrame, BinaryFrame, ETCO, MLLT, SYTC, PCNT, PCST, POSS, OWNE, \ 19 SEEK, ASPI, PictureType, CRM, RVAD, RVA, ID3Tags 20 21_22 = ID3Header() 22_22.version = (2, 2, 0) 23 24_23 = ID3Header() 25_23.version = (2, 3, 0) 26 27_24 = ID3Header() 28_24.version = (2, 4, 0) 29 30 31class TVariousFrames(TestCase): 32 33 DATA = [ 34 ['TALB', b'\x00a/b', 'a/b', '', dict(encoding=0)], 35 ['TBPM', b'\x00120', '120', 120, dict(encoding=0)], 36 ['TCMP', b'\x001', '1', 1, dict(encoding=0)], 37 ['TCMP', b'\x000', '0', 0, dict(encoding=0)], 38 ['TCOM', b'\x00a/b', 'a/b', '', dict(encoding=0)], 39 ['TCON', b'\x00(21)Disco', '(21)Disco', '', dict(encoding=0)], 40 ['TCOP', b'\x001900 c', '1900 c', '', dict(encoding=0)], 41 ['TDAT', b'\x00a/b', 'a/b', '', dict(encoding=0)], 42 ['TDEN', b'\x001987', '1987', '', dict(encoding=0, year=[1987])], 43 [ 44 'TDOR', b'\x001987-12', '1987-12', '', 45 dict(encoding=0, year=[1987], month=[12]) 46 ], 47 ['TDRC', b'\x001987\x00', '1987', '', dict(encoding=0, year=[1987])], 48 [ 49 'TDRL', b'\x001987\x001988', '1987,1988', '', 50 dict(encoding=0, year=[1987, 1988]) 51 ], 52 ['TDTG', b'\x001987', '1987', '', dict(encoding=0, year=[1987])], 53 ['TDLY', b'\x001205', '1205', 1205, dict(encoding=0)], 54 ['TENC', b'\x00a b/c d', 'a b/c d', '', dict(encoding=0)], 55 ['TEXT', b'\x00a b\x00c d', ['a b', 'c d'], '', dict(encoding=0)], 56 ['TFLT', b'\x00MPG/3', 'MPG/3', '', dict(encoding=0)], 57 ['TIME', b'\x001205', '1205', '', dict(encoding=0)], 58 [ 59 'TIPL', b'\x02\x00a\x00\x00\x00b', [["a", "b"]], '', 60 dict(encoding=2) 61 ], 62 ['TIT1', b'\x00a/b', 'a/b', '', dict(encoding=0)], 63 # TIT2 checks misaligned terminator '\x00\x00' across crosses utf16 64 # chars 65 [ 66 'TIT2', b'\x01\xff\xfe\x38\x00\x00\x38', u'8\u3800', '', 67 dict(encoding=1) 68 ], 69 ['TIT3', b'\x00a/b', 'a/b', '', dict(encoding=0)], 70 ['TKEY', b'\x00A#m', 'A#m', '', dict(encoding=0)], 71 ['TLAN', b'\x006241', '6241', '', dict(encoding=0)], 72 ['TLEN', b'\x006241', '6241', 6241, dict(encoding=0)], 73 [ 74 'TMCL', b'\x02\x00a\x00\x00\x00b', [["a", "b"]], '', 75 dict(encoding=2) 76 ], 77 ['TMED', b'\x00med', 'med', '', dict(encoding=0)], 78 ['TMOO', b'\x00moo', 'moo', '', dict(encoding=0)], 79 ['TOAL', b'\x00alb', 'alb', '', dict(encoding=0)], 80 ['TOFN', b'\x0012 : bar', '12 : bar', '', dict(encoding=0)], 81 ['TOLY', b'\x00lyr', 'lyr', '', dict(encoding=0)], 82 ['TOPE', b'\x00own/lic', 'own/lic', '', dict(encoding=0)], 83 ['TORY', b'\x001923', '1923', 1923, dict(encoding=0)], 84 ['TOWN', b'\x00own/lic', 'own/lic', '', dict(encoding=0)], 85 ['TPE1', b'\x00ab', ['ab'], '', dict(encoding=0)], 86 [ 87 'TPE2', b'\x00ab\x00cd\x00ef', ['ab', 'cd', 'ef'], '', 88 dict(encoding=0) 89 ], 90 ['TPE3', b'\x00ab\x00cd', ['ab', 'cd'], '', dict(encoding=0)], 91 ['TPE4', b'\x00ab\x00', ['ab'], '', dict(encoding=0)], 92 ['TPOS', b'\x0008/32', '08/32', 8, dict(encoding=0)], 93 ['TPRO', b'\x00pro', 'pro', '', dict(encoding=0)], 94 ['TPUB', b'\x00pub', 'pub', '', dict(encoding=0)], 95 ['TRCK', b'\x004/9', '4/9', 4, dict(encoding=0)], 96 ['TRDA', b'\x00Sun Jun 12', 'Sun Jun 12', '', dict(encoding=0)], 97 ['TRSN', b'\x00ab/cd', 'ab/cd', '', dict(encoding=0)], 98 ['TRSO', b'\x00ab', 'ab', '', dict(encoding=0)], 99 ['TSIZ', b'\x0012345', '12345', 12345, dict(encoding=0)], 100 ['TSOA', b'\x00ab', 'ab', '', dict(encoding=0)], 101 ['TSOP', b'\x00ab', 'ab', '', dict(encoding=0)], 102 ['TSOT', b'\x00ab', 'ab', '', dict(encoding=0)], 103 ['TSO2', b'\x00ab', 'ab', '', dict(encoding=0)], 104 ['TSOC', b'\x00ab', 'ab', '', dict(encoding=0)], 105 ['TSRC', b'\x0012345', '12345', '', dict(encoding=0)], 106 ['TSSE', b'\x0012345', '12345', '', dict(encoding=0)], 107 ['TSST', b'\x0012345', '12345', '', dict(encoding=0)], 108 ['TYER', b'\x002004', '2004', 2004, dict(encoding=0)], 109 ['MVNM', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 110 ['MVIN', b'\x001/3\x00', '1/3', 1, dict(encoding=0)], 111 ['GRP1', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 112 [ 113 'TXXX', b'\x00usr\x00a/b\x00c', ['a/b', 'c'], '', 114 dict(encoding=0, desc='usr') 115 ], 116 ['WCOM', b'http://foo', 'http://foo', '', {}], 117 ['WCOP', b'http://bar', 'http://bar', '', {}], 118 ['WOAF', b'http://baz', 'http://baz', '', {}], 119 ['WOAR', b'http://bar', 'http://bar', '', {}], 120 ['WOAS', b'http://bar', 'http://bar', '', {}], 121 ['WORS', b'http://bar', 'http://bar', '', {}], 122 ['WPAY', b'http://bar', 'http://bar', '', {}], 123 ['WPUB', b'http://bar', 'http://bar', '', {}], 124 ['WXXX', b'\x00usr\x00http', 'http', '', dict(encoding=0, desc='usr')], 125 [ 126 'IPLS', b'\x00a\x00A\x00b\x00B\x00', [['a', 'A'], ['b', 'B']], '', 127 dict(encoding=0) 128 ], 129 ['MCDI', b'\x01\x02\x03\x04', b'\x01\x02\x03\x04', '', {}], 130 [ 131 'ETCO', b'\x01\x12\x00\x00\x7f\xff', [(18, 32767)], '', 132 dict(format=1) 133 ], 134 [ 135 'COMM', b'\x00ENUT\x00Com', 'Com', '', 136 dict(desc='T', lang='ENU', encoding=0) 137 ], 138 [ 139 'APIC', b'\x00-->\x00\x03cover\x00cover.jpg', b'cover.jpg', '', 140 dict(mime='-->', type=3, desc='cover', encoding=0) 141 ], 142 ['USER', b'\x00ENUCom', 'Com', '', dict(lang='ENU', encoding=0)], 143 [ 144 'RVA2', b'testdata\x00\x01\xfb\x8c\x10\x12\x23', 145 'Master volume: -2.2266 dB/0.1417', '', 146 dict(desc='testdata', channel=1, gain=-2.22656, peak=0.14169) 147 ], 148 [ 149 'RVA2', b'testdata\x00\x01\xfb\x8c\x24\x01\x22\x30\x00\x00', 150 'Master volume: -2.2266 dB/0.1417', '', 151 dict(desc='testdata', channel=1, gain=-2.22656, peak=0.14169) 152 ], 153 [ 154 'RVA2', b'testdata2\x00\x01\x04\x01\x00', 155 'Master volume: +2.0020 dB/0.0000', '', 156 dict(desc='testdata2', channel=1, gain=2.001953125, peak=0) 157 ], 158 ['PCNT', b'\x00\x00\x00\x11', 17, 17, dict(count=17)], 159 [ 160 'POPM', b'foo@bar.org\x00\xde\x00\x00\x00\x11', 222, 222, 161 dict(email="foo@bar.org", rating=222, count=17) 162 ], 163 [ 164 'POPM', b'foo@bar.org\x00\xde\x00', 222, 222, 165 dict(email="foo@bar.org", rating=222, count=0) 166 ], 167 # Issue #33 - POPM may have no playcount at all. 168 [ 169 'POPM', b'foo@bar.org\x00\xde', 222, 222, 170 dict(email="foo@bar.org", rating=222) 171 ], 172 ['UFID', b'own\x00data', b'data', '', dict(data=b'data', owner='own')], 173 ['UFID', b'own\x00\xdd', b'\xdd', '', dict(data=b'\xdd', owner='own')], 174 [ 175 'GEOB', b'\x00mime\x00name\x00desc\x00data', b'data', '', 176 dict(encoding=0, mime='mime', filename='name', desc='desc') 177 ], 178 [ 179 'USLT', b'\x00engsome lyrics\x00woo\nfun', 'woo\nfun', '', 180 dict(encoding=0, lang='eng', desc='some lyrics', text='woo\nfun') 181 ], 182 [ 183 'SYLT', (b'\x00eng\x02\x01some lyrics\x00foo\x00\x00\x00\x00\x01' 184 b'bar\x00\x00\x00\x00\x10'), 185 "[1ms]: foo\n[16ms]: bar", '', 186 dict(encoding=0, lang='eng', type=1, format=2, desc='some lyrics') 187 ], 188 ['POSS', b'\x01\x0f', 15, 15, dict(format=1, position=15)], 189 [ 190 'OWNE', b'\x00USD10.01\x0020041010CDBaby', 'CDBaby', 'CDBaby', 191 dict(encoding=0, price="USD10.01", date='20041010', 192 seller='CDBaby') 193 ], 194 [ 195 'PRIV', b'a@b.org\x00random data', b'random data', 'random data', 196 dict(owner='a@b.org', data=b'random data') 197 ], 198 [ 199 'PRIV', b'a@b.org\x00\xdd', b'\xdd', '\xdd', 200 dict(owner='a@b.org', data=b'\xdd') 201 ], 202 ['SIGN', b'\x92huh?', b'huh?', 'huh?', dict(group=0x92, sig=b'huh?')], 203 [ 204 'ENCR', b'a@b.org\x00\x92Data!', b'Data!', 'Data!', 205 dict(owner='a@b.org', method=0x92, data=b'Data!') 206 ], 207 [ 208 'SEEK', b'\x00\x12\x00\x56', 209 0x12 * 256 * 256 + 0x56, 0x12 * 256 * 256 + 0x56, 210 dict(offset=0x12 * 256 * 256 + 0x56) 211 ], 212 [ 213 'SYTC', b"\x01\x10obar", b'\x10obar', '', 214 dict(format=1, data=b'\x10obar') 215 ], 216 [ 217 'RBUF', b'\x00\x12\x00', 0x12 * 256, 0x12 * 256, 218 dict(size=0x12 * 256) 219 ], 220 [ 221 'RBUF', b'\x00\x12\x00\x01', 0x12 * 256, 0x12 * 256, 222 dict(size=0x12 * 256, info=1) 223 ], 224 [ 225 'RBUF', b'\x00\x12\x00\x01\x00\x00\x00\x23', 226 0x12 * 256, 0x12 * 256, 227 dict(size=0x12 * 256, info=1, offset=0x23) 228 ], 229 [ 230 'RVRB', b'\x12\x12\x23\x23\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11', 231 (0x12 * 256 + 0x12, 0x23 * 256 + 0x23), '', 232 dict(left=0x12 * 256 + 0x12, right=0x23 * 256 + 0x23) 233 ], 234 [ 235 'AENC', b'a@b.org\x00\x00\x12\x00\x23', 'a@b.org', 'a@b.org', 236 dict(owner='a@b.org', preview_start=0x12, preview_length=0x23) 237 ], 238 [ 239 'AENC', b'a@b.org\x00\x00\x12\x00\x23!', 'a@b.org', 'a@b.org', 240 dict(owner='a@b.org', preview_start=0x12, 241 preview_length=0x23, data=b'!') 242 ], 243 [ 244 'GRID', b'a@b.org\x00\x99', 'a@b.org', 0x99, 245 dict(owner='a@b.org', group=0x99) 246 ], 247 [ 248 'GRID', b'a@b.org\x00\x99data', 'a@b.org', 0x99, 249 dict(owner='a@b.org', group=0x99, data=b'data') 250 ], 251 [ 252 'COMR', 253 (b'\x00USD10.00\x0020051010ql@sc.net\x00\x09Joe\x00A song\x00' 254 b'x-image/fake\x00some data'), 255 COMR(encoding=0, price="USD10.00", valid_until="20051010", 256 contact="ql@sc.net", format=9, seller="Joe", desc="A song", 257 mime='x-image/fake', logo=b'some data'), '', 258 dict(encoding=0, price="USD10.00", valid_until="20051010", 259 contact="ql@sc.net", format=9, seller="Joe", desc="A song", 260 mime='x-image/fake', logo=b'some data') 261 ], 262 [ 263 'COMR', 264 b'\x00USD10.00\x0020051010ql@sc.net\x00\x09Joe\x00A song\x00', 265 COMR(encoding=0, price="USD10.00", valid_until="20051010", 266 contact="ql@sc.net", format=9, seller="Joe", desc="A song"), 267 '', 268 dict(encoding=0, price="USD10.00", valid_until="20051010", 269 contact="ql@sc.net", format=9, seller="Joe", desc="A song") 270 ], 271 [ 272 'MLLT', b'\x00\x01\x00\x00\x02\x00\x00\x03\x04\x08foobar', 273 b'foobar', '', 274 dict(frames=1, bytes=2, milliseconds=3, bits_for_bytes=4, 275 bits_for_milliseconds=8, data=b'foobar') 276 ], 277 [ 278 'EQU2', b'\x00Foobar\x00\x01\x01\x04\x00', [(128.5, 2.0)], '', 279 dict(method=0, desc="Foobar") 280 ], 281 [ 282 'ASPI', 283 b'\x00\x00\x00\x00\x00\x00\x00\x10\x00\x03\x08\x01\x02\x03', 284 [1, 2, 3], '', dict(S=0, L=16, N=3, b=8) 285 ], 286 [ 287 'ASPI', b'\x00\x00\x00\x00\x00\x00\x00\x10\x00\x03\x10' 288 b'\x00\x01\x00\x02\x00\x03', [1, 2, 3], '', 289 dict(S=0, L=16, N=3, b=16) 290 ], 291 [ 292 'LINK', b'TIT1http://www.example.org/TIT1.txt\x00', 293 ("TIT1", 'http://www.example.org/TIT1.txt', b''), '', 294 dict(frameid='TIT1', url='http://www.example.org/TIT1.txt', 295 data=b'') 296 ], 297 [ 298 'LINK', b'COMMhttp://www.example.org/COMM.txt\x00engfoo', 299 ("COMM", 'http://www.example.org/COMM.txt', b'engfoo'), '', 300 dict(frameid='COMM', url='http://www.example.org/COMM.txt', 301 data=b'engfoo') 302 ], 303 # iTunes podcast frames 304 ['TGID', b'\x00i', u'i', '', dict(encoding=0)], 305 ['TDES', b'\x00ii', u'ii', '', dict(encoding=0)], 306 ['TKWD', b'\x00ii', u'ii', '', dict(encoding=0)], 307 ['TCAT', b'\x00ii', u'ii', '', dict(encoding=0)], 308 ['WFED', b'http://zzz', 'http://zzz', '', {}], 309 ['PCST', b'\x00\x00\x00\x00', 0, 0, dict(value=0)], 310 311 # Chapter extension 312 ['CHAP', (b'foo\x00\x11\x11\x11\x11\x22\x22\x22\x22' 313 b'\x33\x33\x33\x33\x44\x44\x44\x44'), 314 CHAP(element_id=u'foo', start_time=286331153, end_time=572662306, 315 start_offset=858993459, end_offset=1145324612), '', dict()], 316 ['CTOC', b'foo\x00\x03\x01bla\x00', 317 CTOC(element_id=u'foo', 318 flags=CTOCFlags.ORDERED | CTOCFlags.TOP_LEVEL, 319 child_element_ids=[u'bla']), 320 '', dict()], 321 322 ['RVAD', b'\x03\x10\x00\x00\x00\x00', 323 RVAD(adjustments=[0, 0]), '', dict()], 324 ['RVAD', b'\x03\x08\x00\x01\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00', 325 RVAD(adjustments=[0, 1, 2, 3, -4, -5, 6, 7, 0, 0, 0, 0]), '', dict()], 326 327 # 2.2 tags 328 ['RVA', b'\x03\x10\x00\x00\x00\x00', 329 RVA(adjustments=[0, 0]), '', dict()], 330 ['UFI', b'own\x00data', b'data', '', dict(data=b'data', owner='own')], 331 [ 332 'SLT', (b'\x00eng\x02\x01some lyrics\x00foo\x00\x00\x00\x00\x01bar' 333 b'\x00\x00\x00\x00\x10'), 334 "[1ms]: foo\n[16ms]: bar", '', 335 dict(encoding=0, lang='eng', type=1, format=2, desc='some lyrics') 336 ], 337 ['TT1', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 338 ['TT2', b'\x00ab', 'ab', '', dict(encoding=0)], 339 ['TT3', b'\x00ab', 'ab', '', dict(encoding=0)], 340 ['TP1', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 341 ['TP2', b'\x00ab', 'ab', '', dict(encoding=0)], 342 ['TP3', b'\x00ab', 'ab', '', dict(encoding=0)], 343 ['TP4', b'\x00ab', 'ab', '', dict(encoding=0)], 344 ['TCM', b'\x00ab/cd', 'ab/cd', '', dict(encoding=0)], 345 ['TXT', b'\x00lyr', 'lyr', '', dict(encoding=0)], 346 ['TLA', b'\x00ENU', 'ENU', '', dict(encoding=0)], 347 ['TCO', b'\x00gen', 'gen', '', dict(encoding=0)], 348 ['TAL', b'\x00alb', 'alb', '', dict(encoding=0)], 349 ['TPA', b'\x001/9', '1/9', 1, dict(encoding=0)], 350 ['TRK', b'\x002/8', '2/8', 2, dict(encoding=0)], 351 ['TRC', b'\x00isrc', 'isrc', '', dict(encoding=0)], 352 ['TYE', b'\x001900', '1900', 1900, dict(encoding=0)], 353 ['TDA', b'\x002512', '2512', '', dict(encoding=0)], 354 ['TIM', b'\x001225', '1225', '', dict(encoding=0)], 355 ['TRD', b'\x00Jul 17', 'Jul 17', '', dict(encoding=0)], 356 ['TMT', b'\x00DIG/A', 'DIG/A', '', dict(encoding=0)], 357 ['TFT', b'\x00MPG/3', 'MPG/3', '', dict(encoding=0)], 358 ['TBP', b'\x00133', '133', 133, dict(encoding=0)], 359 ['TCP', b'\x001', '1', 1, dict(encoding=0)], 360 ['TCP', b'\x000', '0', 0, dict(encoding=0)], 361 ['TCR', b'\x00Me', 'Me', '', dict(encoding=0)], 362 ['TPB', b'\x00Him', 'Him', '', dict(encoding=0)], 363 ['TEN', b'\x00Lamer', 'Lamer', '', dict(encoding=0)], 364 ['TSS', b'\x00ab', 'ab', '', dict(encoding=0)], 365 ['TOF', b'\x00ab:cd', 'ab:cd', '', dict(encoding=0)], 366 ['TLE', b'\x0012', '12', 12, dict(encoding=0)], 367 ['TSI', b'\x0012', '12', 12, dict(encoding=0)], 368 ['TDY', b'\x0012', '12', 12, dict(encoding=0)], 369 ['TKE', b'\x00A#m', 'A#m', '', dict(encoding=0)], 370 ['TOT', b'\x00org', 'org', '', dict(encoding=0)], 371 ['TOA', b'\x00org', 'org', '', dict(encoding=0)], 372 ['TOL', b'\x00org', 'org', '', dict(encoding=0)], 373 ['TOR', b'\x001877', '1877', 1877, dict(encoding=0)], 374 ['TXX', b'\x00desc\x00val', 'val', '', dict(encoding=0, desc='desc')], 375 ['TSC', b'\x00ab', 'ab', '', dict(encoding=0)], 376 ['TSA', b'\x00ab', 'ab', '', dict(encoding=0)], 377 ['TS2', b'\x00ab', 'ab', '', dict(encoding=0)], 378 ['TST', b'\x00ab', 'ab', '', dict(encoding=0)], 379 ['TSP', b'\x00ab', 'ab', '', dict(encoding=0)], 380 ['MVN', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 381 ['MVI', b'\x001/3\x00', '1/3', 1, dict(encoding=0)], 382 ['GP1', b'\x00ab\x00', 'ab', '', dict(encoding=0)], 383 384 ['WAF', b'http://zzz', 'http://zzz', '', {}], 385 ['WAR', b'http://zzz', 'http://zzz', '', {}], 386 ['WAS', b'http://zzz', 'http://zzz', '', {}], 387 ['WCM', b'http://zzz', 'http://zzz', '', {}], 388 ['WCP', b'http://zzz', 'http://zzz', '', {}], 389 ['WPB', b'http://zzz', 'http://zzz', '', {}], 390 [ 391 'WXX', b'\x00desc\x00http', 'http', '', 392 dict(encoding=0, desc='desc') 393 ], 394 [ 395 'IPL', b'\x00a\x00A\x00b\x00B\x00', [['a', 'A'], ['b', 'B']], '', 396 dict(encoding=0) 397 ], 398 ['MCI', b'\x01\x02\x03\x04', b'\x01\x02\x03\x04', '', {}], 399 [ 400 'ETC', b'\x01\x12\x00\x00\x7f\xff', [(18, 32767)], '', 401 dict(format=1) 402 ], 403 [ 404 'COM', b'\x00ENUT\x00Com', 'Com', '', 405 dict(desc='T', lang='ENU', encoding=0) 406 ], 407 [ 408 'PIC', b'\x00-->\x03cover\x00cover.jpg', b'cover.jpg', '', 409 dict(mime='-->', type=3, desc='cover', encoding=0) 410 ], 411 [ 412 'POP', b'foo@bar.org\x00\xde\x00\x00\x00\x11', 222, 222, 413 dict(email="foo@bar.org", rating=222, count=17) 414 ], 415 ['CNT', b'\x00\x00\x00\x11', 17, 17, dict(count=17)], 416 [ 417 'GEO', b'\x00mime\x00name\x00desc\x00data', b'data', '', 418 dict(encoding=0, mime='mime', filename='name', desc='desc') 419 ], 420 [ 421 'ULT', b'\x00engsome lyrics\x00woo\nfun', 'woo\nfun', '', 422 dict(encoding=0, lang='eng', desc='some lyrics', text='woo\nfun')], 423 [ 424 'BUF', b'\x00\x12\x00', 0x12 * 256, 0x12 * 256, 425 dict(size=0x12 * 256) 426 ], 427 [ 428 'CRA', b'a@b.org\x00\x00\x12\x00\x23', 'a@b.org', 'a@b.org', 429 dict(owner='a@b.org', preview_start=0x12, preview_length=0x23) 430 ], 431 [ 432 'CRA', b'a@b.org\x00\x00\x12\x00\x23!', 'a@b.org', 'a@b.org', 433 dict(owner='a@b.org', preview_start=0x12, 434 preview_length=0x23, data=b'!') 435 ], 436 [ 437 'REV', b'\x12\x12\x23\x23\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11', 438 (0x12 * 256 + 0x12, 0x23 * 256 + 0x23), '', 439 dict(left=0x12 * 256 + 0x12, right=0x23 * 256 + 0x23) 440 ], 441 [ 442 'STC', b"\x01\x10obar", b'\x10obar', '', 443 dict(format=1, data=b'\x10obar') 444 ], 445 [ 446 'MLL', b'\x00\x01\x00\x00\x02\x00\x00\x03\x04\x08foobar', 447 b'foobar', '', 448 dict(frames=1, bytes=2, milliseconds=3, bits_for_bytes=4, 449 bits_for_milliseconds=8, data=b'foobar') 450 ], 451 [ 452 'LNK', b'TT1http://www.example.org/TIT1.txt\x00', 453 ("TT1", 'http://www.example.org/TIT1.txt', b''), '', 454 dict(frameid='TT1', url='http://www.example.org/TIT1.txt', 455 data=b'') 456 ], 457 [ 458 'CRM', b'foo@example.org\x00test\x00woo', b'woo', '', 459 dict(owner='foo@example.org', desc='test', data=b'woo') 460 ], 461 [ 462 'CRM', b'\x00\x00', b'', '', 463 dict(owner='', desc='', data=b'') 464 ], 465 ] 466 467 def _get_frame(self, id_): 468 return getattr(getattr(__import__('mutagen.id3'), "id3"), id_) 469 470 def test_all_tested(self): 471 check = dict.fromkeys(list(Frames.keys()) + list(Frames_2_2.keys())) 472 tested = [l[0] for l in self.DATA] 473 for t in tested: 474 check.pop(t, None) 475 self.assertEqual(list(check.keys()), []) 476 477 def test_tag_repr(self): 478 for frame_id, data, value, intval, info in self.DATA: 479 kind = self._get_frame(frame_id) 480 tag = kind._fromData(_23, 0, data) 481 self.assertTrue(isinstance(tag.__str__(), str)) 482 self.assertTrue(isinstance(tag.__repr__(), str)) 483 if PY2: 484 if hasattr(tag, "__unicode__"): 485 self.assertTrue(isinstance(tag.__unicode__(), unicode)) 486 else: 487 if hasattr(tag, "__bytes__"): 488 self.assertTrue(isinstance(tag.__bytes__(), bytes)) 489 490 def test_tag_write(self): 491 for frame_id, data, value, intval, info in self.DATA: 492 kind = self._get_frame(frame_id) 493 tag = kind._fromData(_24, 0, data) 494 towrite = tag._writeData() 495 tag2 = kind._fromData(_24, 0, towrite) 496 for spec in kind._framespec: 497 attr = spec.name 498 self.assertEquals(getattr(tag, attr), getattr(tag2, attr)) 499 for spec in kind._optionalspec: 500 attr = spec.name 501 other = object() 502 self.assertEquals( 503 getattr(tag, attr, other), getattr(tag2, attr, other)) 504 505 def test_tag_write_v23(self): 506 for frame_id, data, value, intval, info in self.DATA: 507 kind = self._get_frame(frame_id) 508 tag = kind._fromData(_24, 0, data) 509 config = ID3SaveConfig(3, "/") 510 towrite = tag._writeData(config) 511 tag2 = kind._fromData(_23, 0, towrite) 512 tag3 = kind._fromData(_23, 0, tag2._writeData(config)) 513 for spec in kind._framespec: 514 attr = spec.name 515 self.assertEquals(getattr(tag2, attr), getattr(tag3, attr)) 516 for spec in kind._optionalspec: 517 attr = spec.name 518 other = object() 519 self.assertEquals( 520 getattr(tag2, attr, other), getattr(tag3, attr, other)) 521 self.assertEqual(hasattr(tag, attr), hasattr(tag2, attr)) 522 523 def test_tag(self): 524 for frame_id, data, value, intval, info in self.DATA: 525 kind = self._get_frame(frame_id) 526 tag = kind._fromData(_23, 0, data) 527 self.failUnless(tag.HashKey) 528 self.failUnless(tag.pprint()) 529 self.assertEquals(value, tag) 530 if 'encoding' not in info: 531 self.assertRaises(AttributeError, getattr, tag, 'encoding') 532 for attr, value in iteritems(info): 533 t = tag 534 if not isinstance(value, list): 535 value = [value] 536 t = [t] 537 for value, t in izip(value, iter(t)): 538 if isinstance(value, float): 539 self.failUnlessAlmostEqual(value, getattr(t, attr), 5) 540 else: 541 self.assertEquals(value, getattr(t, attr)) 542 543 if isinstance(intval, integer_types): 544 self.assertEquals(intval, operator.pos(t)) 545 else: 546 self.assertRaises(TypeError, operator.pos, t) 547 548 549class TPCST(TestCase): 550 551 def test_default(self): 552 frame = PCST() 553 self.assertEqual(frame.value, 0) 554 555 556class TETCO(TestCase): 557 558 def test_default(self): 559 frame = ETCO() 560 self.assertEqual(frame.format, 1) 561 self.assertEqual(frame.events, []) 562 563 def test_default_mutable(self): 564 frame = ETCO() 565 frame.events.append(1) 566 self.assertEqual(ETCO().events, []) 567 568 569class TSYTC(TestCase): 570 571 def test_default(self): 572 frame = SYTC() 573 self.assertEqual(frame.format, 1) 574 self.assertEqual(frame.data, b"") 575 576 577class TCRA(TestCase): 578 579 def test_upgrade(self): 580 frame = CRA(owner="a", preview_start=1, preview_length=2, data=b"foo") 581 new = AENC(frame) 582 self.assertEqual(new.owner, "a") 583 self.assertEqual(new.preview_start, 1) 584 self.assertEqual(new.preview_length, 2) 585 self.assertEqual(new.data, b"foo") 586 587 588class TPIC(TestCase): 589 590 def test_default(self): 591 frame = PIC() 592 self.assertEqual(frame.encoding, 1) 593 self.assertEqual(frame.mime, u"JPG") 594 self.assertEqual(frame.type, PictureType.COVER_FRONT) 595 self.assertEqual(frame.desc, u"") 596 self.assertEqual(frame.data, b"") 597 598 def test_upgrade(self): 599 frame = PIC(encoding=0, mime="PNG", desc="bla", type=3, data=b"\x00") 600 new = APIC(frame) 601 self.assertEqual(new.encoding, 0) 602 self.assertEqual(new.mime, "PNG") 603 self.assertEqual(new.desc, "bla") 604 self.assertEqual(new.data, b"\x00") 605 606 frame = PIC(encoding=0, mime="foo", 607 desc="bla", type=3, data=b"\x00") 608 self.assertEqual(frame.mime, "foo") 609 new = APIC(frame) 610 self.assertEqual(new.mime, "foo") 611 612 613class TLNK(TestCase): 614 615 def test_default(self): 616 frame = LNK() 617 self.assertEqual(frame.frameid, u"XXX") 618 self.assertEqual(frame.url, u"") 619 620 def test_upgrade(self): 621 url = "http://foo.bar" 622 623 frame = LNK(frameid="PIC", url=url, data=b"\x00") 624 new = LINK(frame) 625 self.assertEqual(new.frameid, "APIC") 626 self.assertEqual(new.url, url) 627 self.assertEqual(new.data, b"\x00") 628 629 frame = LNK(frameid="XYZ") 630 new = LINK(frame) 631 self.assertEqual(new.frameid, "XYZ ") 632 633 634class TSIGN(TestCase): 635 636 def test_default(self): 637 frame = SIGN() 638 self.assertEqual(frame.group, 0x80) 639 self.assertEqual(frame.sig, b"") 640 641 def test_hash(self): 642 frame = SIGN(group=1, sig=b"foo") 643 self.assertEqual(frame.HashKey, "SIGN:1:foo") 644 645 def test_pprint(self): 646 frame = SIGN(group=1, sig=b"foo") 647 frame._pprint() 648 649 650class TCRM(TestCase): 651 652 def test_default(self): 653 frame = CRM() 654 self.assertEqual(frame.owner, u"") 655 self.assertEqual(frame.desc, u"") 656 self.assertEqual(frame.data, b"") 657 658 659class TPRIV(TestCase): 660 661 def test_default(self): 662 frame = PRIV() 663 self.assertEqual(frame.owner, u"") 664 self.assertEqual(frame.data, b"") 665 666 def test_hash(self): 667 frame = PRIV(owner="foo", data=b"foo") 668 self.assertEqual(frame.HashKey, "PRIV:foo:foo") 669 frame._pprint() 670 671 frame = PRIV(owner="foo", data=b"\x00\xff") 672 self.assertEqual(frame.HashKey, u"PRIV:foo:\x00\xff") 673 frame._pprint() 674 675 676class TGRID(TestCase): 677 678 def test_default(self): 679 frame = GRID() 680 self.assertEqual(frame.owner, u"") 681 self.assertEqual(frame.group, 0x80) 682 683 def test_hash(self): 684 frame = GRID(owner="foo", group=42) 685 self.assertEqual(frame.HashKey, "GRID:42") 686 frame._pprint() 687 688 689class TENCR(TestCase): 690 691 def test_default(self): 692 frame = ENCR() 693 self.assertEqual(frame.owner, u"") 694 self.assertEqual(frame.method, 0x80) 695 self.assertEqual(frame.data, b"") 696 697 def test_hash(self): 698 frame = ENCR(owner="foo", method=42, data=b"\xff") 699 self.assertEqual(frame.HashKey, "ENCR:foo") 700 frame._pprint() 701 702 703class TOWNE(TestCase): 704 705 def test_default(self): 706 frame = OWNE() 707 self.assertEqual(frame.encoding, 1) 708 self.assertEqual(frame.price, u"") 709 self.assertEqual(frame.date, u"19700101") 710 self.assertEqual(frame.seller, u"") 711 712 713class TCOMR(TestCase): 714 715 def test_default(self): 716 frame = COMR() 717 self.assertEqual(frame.encoding, 1) 718 self.assertEqual(frame.price, u"") 719 self.assertEqual(frame.valid_until, u"19700101") 720 self.assertEqual(frame.contact, u"") 721 self.assertEqual(frame.format, 0) 722 self.assertEqual(frame.seller, u"") 723 self.assertEqual(frame.desc, u"") 724 725 def test_hash(self): 726 frame = COMR( 727 encoding=0, price="p", valid_until="v" * 8, contact="c", 728 format=42, seller="s", desc="d", mime="m", logo=b"\xff") 729 self.assertEqual( 730 frame.HashKey, u"COMR:\x00p\x00vvvvvvvvc\x00*s\x00d\x00m\x00\xff") 731 frame._pprint() 732 733 734class TBinaryFrame(TestCase): 735 736 def test_default(self): 737 frame = BinaryFrame() 738 self.assertEqual(frame.data, b"") 739 740 741class TUSER(TestCase): 742 743 def test_default(self): 744 frame = USER() 745 self.assertEqual(frame.encoding, 1) 746 self.assertEqual(frame.lang, u"XXX") 747 self.assertEqual(frame.text, u"") 748 749 def test_hash(self): 750 frame = USER(encoding=0, lang="foo", text="bla") 751 self.assertEqual(frame.HashKey, "USER:foo") 752 frame._pprint() 753 754 self.assertEquals(USER(text="a").HashKey, USER(text="b").HashKey) 755 self.assertNotEquals( 756 USER(lang="abc").HashKey, USER(lang="def").HashKey) 757 758 759class TMLLT(TestCase): 760 761 def test_default(self): 762 frame = MLLT() 763 self.assertEqual(frame.frames, 0) 764 self.assertEqual(frame.bytes, 0) 765 self.assertEqual(frame.milliseconds, 0) 766 self.assertEqual(frame.bits_for_bytes, 0) 767 self.assertEqual(frame.bits_for_milliseconds, 0) 768 self.assertEqual(frame.data, b"") 769 770 771class TTIT2(TestCase): 772 773 def test_hash(self): 774 self.assertEquals(TIT2(text="a").HashKey, TIT2(text="b").HashKey) 775 776 777class TUFID(TestCase): 778 779 def test_default(self): 780 frame = UFID() 781 self.assertEqual(frame.owner, u"") 782 self.assertEqual(frame.data, b"") 783 784 def test_hash(self): 785 frame = UFID(owner="foo", data=b"\x42") 786 self.assertEqual(frame.HashKey, "UFID:foo") 787 frame._pprint() 788 789 self.assertEquals(UFID(data=b"1").HashKey, UFID(data=b"2").HashKey) 790 self.assertNotEquals(UFID(owner="a").HashKey, UFID(owner="b").HashKey) 791 792 793class TPairedTextFrame(TestCase): 794 795 def test_default(self): 796 frame = PairedTextFrame() 797 self.assertEqual(frame.encoding, 1) 798 self.assertEqual(frame.people, []) 799 800 801class TRVAD(TestCase): 802 803 def test_default(self): 804 frame = RVAD() 805 self.assertEqual(frame.adjustments, [0, 0]) 806 807 def test_hash(self): 808 frame = RVAD() 809 self.assertEqual(frame.HashKey, "RVAD") 810 811 def test_upgrade(self): 812 rva = RVA(adjustments=[1, 2]) 813 self.assertEqual(RVAD(rva).adjustments, [1, 2]) 814 815 816class TLINK(TestCase): 817 818 def test_read(self): 819 frame = LINK() 820 frame._readData(_24, b"XXX\x00Foo\x00") 821 # either we can read invalid frame ids or we fail properly, atm we read 822 # them. 823 self.assertEqual(frame.frameid, "XXX\x00") 824 825 def test_default(self): 826 frame = LINK() 827 self.assertEqual(frame.frameid, u"XXXX") 828 self.assertEqual(frame.url, u"") 829 830 def test_hash(self): 831 frame = LINK(frameid="TPE1", url="http://foo.bar", data=b"\x42") 832 self.assertEqual(frame.HashKey, "LINK:TPE1:http://foo.bar:B") 833 frame._pprint() 834 835 frame = LINK(frameid="TPE1", url="http://foo.bar") 836 self.assertEqual(frame.HashKey, "LINK:TPE1:http://foo.bar:") 837 838 839class TAENC(TestCase): 840 841 def test_default(self): 842 frame = AENC() 843 self.assertEqual(frame.owner, u"") 844 self.assertEqual(frame.preview_start, 0) 845 self.assertEqual(frame.preview_length, 0) 846 847 def test_hash(self): 848 frame = AENC( 849 owner="foo", preview_start=1, preview_length=2, data=b"\x42") 850 self.assertEqual(frame.HashKey, "AENC:foo") 851 frame._pprint() 852 853 854class TGEOB(TestCase): 855 856 def test_default(self): 857 frame = GEOB() 858 self.assertEqual(frame.encoding, 1) 859 self.assertEqual(frame.mime, u"") 860 self.assertEqual(frame.filename, u"") 861 self.assertEqual(frame.desc, u"") 862 self.assertEqual(frame.data, b"") 863 864 def test_hash(self): 865 frame = GEOB( 866 encoding=0, mtime="m", filename="f", desc="d", data=b"\x42") 867 self.assertEqual(frame.HashKey, "GEOB:d") 868 frame._pprint() 869 870 self.assertEquals(GEOB(data=b"1").HashKey, GEOB(data=b"2").HashKey) 871 self.assertNotEquals(GEOB(desc="a").HashKey, GEOB(desc="b").HashKey) 872 873 874class TPOPM(TestCase): 875 876 def test_default(self): 877 frame = POPM() 878 self.assertEqual(frame.email, u"") 879 self.assertEqual(frame.rating, 0) 880 self.assertFalse(hasattr(frame, "count")) 881 882 def test_hash(self): 883 frame = POPM(email="e", rating=42) 884 self.assertEqual(frame.HashKey, "POPM:e") 885 frame._pprint() 886 887 self.assertEquals(POPM(count=1).HashKey, POPM(count=2).HashKey) 888 self.assertNotEquals(POPM(email="a").HashKey, POPM(email="b").HashKey) 889 890 891class TEQU2(TestCase): 892 893 def test_default(self): 894 frame = EQU2() 895 self.assertEqual(frame.method, 0) 896 self.assertEqual(frame.desc, u"") 897 self.assertEqual(frame.adjustments, []) 898 899 def test_default_mutable(self): 900 frame = EQU2() 901 frame.adjustments.append(1) 902 self.assertEqual(EQU2(), []) 903 904 def test_hash(self): 905 frame = EQU2(method=42, desc="d", adjustments=[(0, 0)]) 906 self.assertEqual(frame.HashKey, "EQU2:d") 907 frame._pprint() 908 909 910class TSEEK(TestCase): 911 912 def test_default(self): 913 frame = SEEK() 914 self.assertEqual(frame.offset, 0) 915 916 917class TPOSS(TestCase): 918 919 def test_default(self): 920 frame = POSS() 921 self.assertEqual(frame.format, 1) 922 self.assertEqual(frame.position, 0) 923 924 925class TCOMM(TestCase): 926 927 def test_default(self): 928 frame = COMM() 929 self.assertEqual(frame.encoding, 1) 930 self.assertEqual(frame.lang, u"XXX") 931 self.assertEqual(frame.desc, u"") 932 self.assertEqual(frame.text, []) 933 934 def test_hash(self): 935 frame = COMM(encoding=0, lang="foo", desc="d") 936 self.assertEqual(frame.HashKey, "COMM:d:foo") 937 frame._pprint() 938 939 self.assertEquals(COMM(text="a").HashKey, COMM(text="b").HashKey) 940 self.assertNotEquals(COMM(desc="a").HashKey, COMM(desc="b").HashKey) 941 self.assertNotEquals( 942 COMM(lang="abc").HashKey, COMM(lang="def").HashKey) 943 944 def test_bad_unicodedecode(self): 945 # 7 bytes of "UTF16" data. 946 data = b'\x01\x00\x00\x00\xff\xfe\x00\xff\xfeh\x00' 947 self.assertRaises(ID3JunkFrameError, COMM._fromData, _24, 0x00, data) 948 949 950class TSYLT(TestCase): 951 952 def test_default(self): 953 frame = SYLT() 954 self.assertEqual(frame.encoding, 1) 955 self.assertEqual(frame.lang, u"XXX") 956 self.assertEqual(frame.format, 1) 957 self.assertEqual(frame.type, 0) 958 self.assertEqual(frame.desc, u"") 959 self.assertEqual(frame.text, u"") 960 961 def test_hash(self): 962 frame = SYLT(encoding=0, lang="foo", format=1, type=2, 963 desc="d", text=[("t", 0)]) 964 self.assertEqual(frame.HashKey, "SYLT:d:foo") 965 frame._pprint() 966 967 def test_bad_sylt(self): 968 self.assertRaises( 969 ID3JunkFrameError, SYLT._fromData, _24, 0x0, 970 b"\x00eng\x01description\x00foobar") 971 self.assertRaises( 972 ID3JunkFrameError, SYLT._fromData, _24, 0x0, 973 b"\x00eng\x01description\x00foobar\x00\xFF\xFF\xFF") 974 975 976class TRVRB(TestCase): 977 978 def test_default(self): 979 frame = RVRB() 980 self.assertEqual(frame.left, 0) 981 self.assertEqual(frame.right, 0) 982 self.assertEqual(frame.bounce_left, 0) 983 self.assertEqual(frame.bounce_right, 0) 984 self.assertEqual(frame.feedback_ltl, 0) 985 self.assertEqual(frame.feedback_ltr, 0) 986 self.assertEqual(frame.feedback_rtr, 0) 987 self.assertEqual(frame.feedback_rtl, 0) 988 self.assertEqual(frame.premix_ltr, 0) 989 self.assertEqual(frame.premix_rtl, 0) 990 991 def test_extradata(self): 992 self.assertEqual(RVRB()._readData(_24, b'L1R1BBFFFFPP#xyz'), b'#xyz') 993 994 995class TRBUF(TestCase): 996 997 def test_default(self): 998 frame = RBUF() 999 self.assertEqual(frame.size, 0) 1000 self.assertFalse(hasattr(frame, "info")) 1001 self.assertFalse(hasattr(frame, "offset")) 1002 1003 def test_extradata(self): 1004 self.assertEqual( 1005 RBUF()._readData( 1006 _24, b'\x00\x01\x00\x01\x00\x00\x00\x00#xyz'), b'#xyz') 1007 1008 1009class TUSLT(TestCase): 1010 1011 def test_default(self): 1012 frame = USLT() 1013 self.assertEqual(frame.encoding, 1) 1014 self.assertEqual(frame.lang, u"XXX") 1015 self.assertEqual(frame.desc, u"") 1016 self.assertEqual(frame.text, u"") 1017 1018 def test_hash(self): 1019 frame = USLT(encoding=0, lang="foo", desc="d", text="t") 1020 self.assertEqual(frame.HashKey, "USLT:d:foo") 1021 assert frame._pprint() == "d=foo=t" 1022 1023 1024class TWXXX(TestCase): 1025 1026 def test_default(self): 1027 frame = WXXX() 1028 self.assertEqual(frame.encoding, 1) 1029 self.assertEqual(frame.desc, u"") 1030 self.assertEqual(frame.url, u"") 1031 1032 def test_hash(self): 1033 self.assert_(isinstance(WXXX(url='durl'), WXXX)) 1034 1035 frame = WXXX(encoding=0, desc="d", url="u") 1036 self.assertEqual(frame.HashKey, "WXXX:d") 1037 frame._pprint() 1038 1039 self.assertEquals(WXXX(text="a").HashKey, WXXX(text="b").HashKey) 1040 self.assertNotEquals(WXXX(desc="a").HashKey, WXXX(desc="b").HashKey) 1041 1042 1043class TTXXX(TestCase): 1044 1045 def test_default(self): 1046 self.assertEqual(TXXX(), TXXX(desc=u"", encoding=1, text=[])) 1047 1048 def test_hash(self): 1049 frame = TXXX(encoding=0, desc="d", text=[]) 1050 self.assertEqual(frame.HashKey, "TXXX:d") 1051 frame._pprint() 1052 1053 self.assertEquals(TXXX(text="a").HashKey, TXXX(text="b").HashKey) 1054 self.assertNotEquals(TXXX(desc="a").HashKey, TXXX(desc="b").HashKey) 1055 1056 1057class TWCOM(TestCase): 1058 1059 def test_hash(self): 1060 frame = WCOM(url="u") 1061 self.assertEqual(frame.HashKey, "WCOM:u") 1062 frame._pprint() 1063 1064 1065class TUrlFrame(TestCase): 1066 1067 def test_default(self): 1068 self.assertEqual(UrlFrame(), UrlFrame(url=u"")) 1069 1070 def test_main(self): 1071 self.assertEqual(UrlFrame("url").url, "url") 1072 1073 1074class TNumericTextFrame(TestCase): 1075 1076 def test_default(self): 1077 self.assertEqual( 1078 NumericTextFrame(), NumericTextFrame(encoding=1, text=[])) 1079 1080 def test_main(self): 1081 self.assertEqual(NumericTextFrame(text='1').text, ["1"]) 1082 self.assertEqual(+NumericTextFrame(text='1'), 1) 1083 1084 1085class TNumericPartTextFrame(TestCase): 1086 1087 def test_default(self): 1088 self.assertEqual( 1089 NumericPartTextFrame(), 1090 NumericPartTextFrame(encoding=1, text=[])) 1091 1092 def test_main(self): 1093 self.assertEqual(NumericPartTextFrame(text='1/2').text, ["1/2"]) 1094 self.assertEqual(+NumericPartTextFrame(text='1/2'), 1) 1095 1096 1097class Tread_frames_load_frame(TestCase): 1098 1099 def test_detect_23_ints_in_24_frames(self): 1100 head = b'TIT1\x00\x00\x01\x00\x00\x00\x00' 1101 tail = b'TPE1\x00\x00\x00\x05\x00\x00\x00Yay!' 1102 1103 tagsgood = read_frames(_24, head + b'a' * 127 + tail, Frames)[0] 1104 tagsbad = read_frames(_24, head + b'a' * 255 + tail, Frames)[0] 1105 self.assertEquals(2, len(tagsgood)) 1106 self.assertEquals(2, len(tagsbad)) 1107 self.assertEquals('a' * 127, tagsgood[0]) 1108 self.assertEquals('a' * 255, tagsbad[0]) 1109 self.assertEquals('Yay!', tagsgood[1]) 1110 self.assertEquals('Yay!', tagsbad[1]) 1111 1112 tagsgood = read_frames(_24, head + b'a' * 127, Frames)[0] 1113 tagsbad = read_frames(_24, head + b'a' * 255, Frames)[0] 1114 self.assertEquals(1, len(tagsgood)) 1115 self.assertEquals(1, len(tagsbad)) 1116 self.assertEquals('a' * 127, tagsgood[0]) 1117 self.assertEquals('a' * 255, tagsbad[0]) 1118 1119 def test_zerolength_framedata(self): 1120 tail = b'\x00' * 6 1121 for head in b'WOAR TENC TCOP TOPE WXXX'.split(): 1122 data = head + tail 1123 self.assertEquals( 1124 0, len(list(read_frames(_24, data, Frames)[1]))) 1125 1126 def test_drops_truncated_frames(self): 1127 tail = b'\x00\x00\x00\x03\x00\x00' b'\x01\x02\x03' 1128 for head in b'RVA2 TXXX APIC'.split(): 1129 data = head + tail 1130 self.assertEquals( 1131 0, len(read_frames(_24, data, Frames)[1])) 1132 1133 def test_drops_nonalphanum_frames(self): 1134 tail = b'\x00\x00\x00\x03\x00\x00' b'\x01\x02\x03' 1135 for head in [b'\x06\xaf\xfe\x20', b'ABC\x00', b'A ']: 1136 data = head + tail 1137 self.assertEquals( 1138 0, len(read_frames(_24, data, Frames)[0])) 1139 1140 def test_frame_too_small(self): 1141 self.assertEquals([], read_frames(_24, b'012345678', Frames)[0]) 1142 self.assertEquals([], read_frames(_23, b'012345678', Frames)[0]) 1143 self.assertEquals([], read_frames(_22, b'01234', Frames_2_2)[0]) 1144 self.assertEquals( 1145 [], read_frames(_22, b'TT1' + b'\x00' * 3, Frames_2_2)[0]) 1146 1147 def test_unknown_22_frame(self): 1148 data = b'XYZ\x00\x00\x01\x00' 1149 self.assertEquals([data], read_frames(_22, data, {})[1]) 1150 1151 def test_22_uses_direct_ints(self): 1152 data = b'TT1\x00\x00\x83\x00' + (b'123456789abcdef' * 16) 1153 tag = read_frames(_22, data, Frames_2_2)[0][0] 1154 self.assertEquals(data[7:7 + 0x82].decode('latin1'), tag.text[0]) 1155 1156 def test_load_write(self): 1157 artists = [s.decode('utf8') for s in 1158 [b'\xc2\xb5', b'\xe6\x97\xa5\xe6\x9c\xac']] 1159 artist = TPE1(encoding=3, text=artists) 1160 config = ID3SaveConfig() 1161 tag = read_frames(_24, save_frame(artist, config=config), Frames)[0][0] 1162 self.assertEquals('TPE1', type(tag).__name__) 1163 self.assertEquals(artist.text, tag.text) 1164 1165 1166class TTPE2(TestCase): 1167 1168 def test_unsynch(self): 1169 header = ID3Header() 1170 header.version = (2, 4, 0) 1171 header._flags = 0x80 1172 badsync = b'\x00\xff\x00ab\x00' 1173 1174 self.assertEquals(TPE2._fromData(header, 0, badsync), [u"\xffab"]) 1175 1176 header._flags = 0x00 1177 self.assertEquals(TPE2._fromData(header, 0x02, badsync), [u"\xffab"]) 1178 1179 tag = TPE2._fromData(header, 0, badsync) 1180 self.assertEquals(tag, [u"\xff", u"ab"]) 1181 1182 1183class TTPE1(TestCase): 1184 1185 def test_badencoding(self): 1186 self.assertRaises( 1187 ID3JunkFrameError, TPE1._fromData, _24, 0, b"\x09ab") 1188 self.assertRaises(ValueError, TPE1, encoding=9, text="ab") 1189 1190 def test_badsync(self): 1191 frame = TPE1._fromData(_24, 0x02, b"\x00\xff\xfe") 1192 self.assertEqual(frame.text, [u'\xff\xfe']) 1193 1194 def test_noencrypt(self): 1195 self.assertRaises( 1196 NotImplementedError, TPE1._fromData, _24, 0x04, b"\x00") 1197 self.assertRaises( 1198 NotImplementedError, TPE1._fromData, _23, 0x40, b"\x00") 1199 1200 def test_badcompress(self): 1201 self.assertRaises( 1202 ID3JunkFrameError, TPE1._fromData, _24, 0x08, 1203 b"\x00\x00\x00\x00#") 1204 self.assertRaises( 1205 ID3JunkFrameError, TPE1._fromData, _23, 0x80, 1206 b"\x00\x00\x00\x00#") 1207 1208 def test_junkframe(self): 1209 self.assertRaises( 1210 ID3JunkFrameError, TPE1._fromData, _24, 0, b"") 1211 self.assertRaises( 1212 ID3JunkFrameError, TPE1._fromData, _24, 0, b'\x03A\xff\xfe') 1213 1214 def test_lengthone_utf16(self): 1215 tpe1 = TPE1._fromData(_24, 0, b'\x01\x00') 1216 self.assertEquals(u'', tpe1) 1217 tpe1 = TPE1._fromData(_24, 0, b'\x01\x00\x00\x00\x00') 1218 self.assertEquals([u'', u''], tpe1) 1219 1220 def test_utf16_wrongnullterm(self): 1221 # issue 169 1222 tpe1 = TPE1._fromData( 1223 _24, 0, b'\x01\xff\xfeH\x00e\x00l\x00l\x00o\x00\x00') 1224 self.assertEquals(tpe1, [u'Hello']) 1225 1226 tpe1 = TPE1._fromData( 1227 _24, 0, b'\x02\x00H\x00e\x00l\x00l\x00o\x00') 1228 self.assertEquals(tpe1, [u'Hello']) 1229 1230 def test_utf_16_missing_bom(self): 1231 tpe1 = TPE1._fromData( 1232 _24, 0, b'\x01H\x00e\x00l\x00l\x00o\x00\x00\x00') 1233 self.assertEquals(tpe1, [u'Hello']) 1234 1235 def test_utf_16_missing_bom_wrong_nullterm(self): 1236 tpe1 = TPE1._fromData( 1237 _24, 0, b'\x01H\x00e\x00l\x00l\x00o\x00\x00') 1238 self.assertEquals(tpe1, [u'Hello']) 1239 1240 tpe1 = TPE1._fromData( 1241 _24, 0, b'\x01f\x00o\x00o\x00\x00\x00b\x00a\x00r\x00\x00') 1242 self.assertEquals(tpe1, [u"foo", u"bar"]) 1243 1244 def test_zlib_bpi(self): 1245 tpe1 = TPE1(encoding=0, text="a" * (0xFFFF - 2)) 1246 data = save_frame(tpe1) 1247 datalen_size = data[4 + 4 + 2:4 + 4 + 2 + 4] 1248 self.failIf( 1249 max(datalen_size) >= b'\x80'[0], "data is not syncsafe: %r" % data) 1250 1251 def test_ql_0_12_missing_uncompressed_size(self): 1252 tag = TPE1._fromData( 1253 _24, 0x08, 1254 b'x\x9cc\xfc\xff\xaf\x84!\x83!\x93' 1255 b'\xa1\x98A\x01J&2\xe83\x940\xa4\x02\xd9%\x0c\x00\x87\xc6\x07#' 1256 ) 1257 self.assertEquals(tag.encoding, 1) 1258 self.assertEquals(tag, ['this is a/test']) 1259 1260 def test_zlib_latin1_missing_datalen(self): 1261 tag = TPE1._fromData( 1262 _24, 0x8, 1263 b'\x00\x00\x00\x0f' 1264 b'x\x9cc(\xc9\xc8,V\x00\xa2D\xfd\x92\xd4\xe2\x12\x00&\x7f\x05%' 1265 ) 1266 self.assertEquals(tag.encoding, 0) 1267 self.assertEquals(tag, ['this is a/test']) 1268 1269 1270class TTCON(TestCase): 1271 1272 def _g(self, s): 1273 return TCON(text=s).genres 1274 1275 def test_empty(self): 1276 self.assertEquals(self._g(""), []) 1277 1278 def test_num(self): 1279 for i in xrange(len(GENRES)): 1280 self.assertEquals(self._g("%02d" % i), [GENRES[i]]) 1281 1282 def test_parened_num(self): 1283 for i in xrange(len(GENRES)): 1284 self.assertEquals(self._g("(%02d)" % i), [GENRES[i]]) 1285 1286 def test_unknown(self): 1287 self.assertEquals(self._g("(255)"), ["Unknown"]) 1288 self.assertEquals(self._g("199"), ["Unknown"]) 1289 self.assertNotEqual(self._g("256"), ["Unknown"]) 1290 1291 def test_parened_multi(self): 1292 self.assertEquals(self._g("(00)(02)"), ["Blues", "Country"]) 1293 1294 def test_coverremix(self): 1295 self.assertEquals(self._g("CR"), ["Cover"]) 1296 self.assertEquals(self._g("(CR)"), ["Cover"]) 1297 self.assertEquals(self._g("RX"), ["Remix"]) 1298 self.assertEquals(self._g("(RX)"), ["Remix"]) 1299 1300 def test_parened_text(self): 1301 self.assertEquals( 1302 self._g("(00)(02)Real Folk Blues"), 1303 ["Blues", "Country", "Real Folk Blues"]) 1304 1305 def test_escape(self): 1306 self.assertEquals(self._g("(0)((A genre)"), ["Blues", "(A genre)"]) 1307 self.assertEquals(self._g("(10)((20)"), ["New Age", "(20)"]) 1308 1309 def test_nullsep(self): 1310 self.assertEquals(self._g("0\x00A genre"), ["Blues", "A genre"]) 1311 1312 def test_nullsep_empty(self): 1313 self.assertEquals(self._g("\x000\x00A genre"), ["Blues", "A genre"]) 1314 1315 def test_crazy(self): 1316 self.assertEquals( 1317 self._g("(20)(CR)\x0030\x00\x00Another\x00(51)Hooray"), 1318 ['Alternative', 'Cover', 'Fusion', 'Another', 1319 'Techno-Industrial', 'Hooray']) 1320 1321 def test_repeat(self): 1322 self.assertEquals(self._g("(20)Alternative"), ["Alternative"]) 1323 self.assertEquals( 1324 self._g("(20)\x00Alternative"), ["Alternative", "Alternative"]) 1325 1326 def test_set_genre(self): 1327 gen = TCON(encoding=0, text="") 1328 self.assertEquals(gen.genres, []) 1329 gen.genres = ["a genre", "another"] 1330 self.assertEquals(gen.genres, ["a genre", "another"]) 1331 1332 def test_set_string(self): 1333 gen = TCON(encoding=0, text="") 1334 gen.genres = "foo" 1335 self.assertEquals(gen.genres, ["foo"]) 1336 1337 def test_nodoubledecode(self): 1338 gen = TCON(encoding=1, text=u"(255)genre") 1339 gen.genres = gen.genres 1340 self.assertEquals(gen.genres, [u"Unknown", u"genre"]) 1341 1342 1343class TID3TimeStamp(TestCase): 1344 1345 def test_Y(self): 1346 s = ID3TimeStamp('1234') 1347 self.assertEquals(s.year, 1234) 1348 self.assertEquals(s.text, '1234') 1349 1350 def test_yM(self): 1351 s = ID3TimeStamp('1234-56') 1352 self.assertEquals(s.year, 1234) 1353 self.assertEquals(s.month, 56) 1354 self.assertEquals(s.text, '1234-56') 1355 1356 def test_ymD(self): 1357 s = ID3TimeStamp('1234-56-78') 1358 self.assertEquals(s.year, 1234) 1359 self.assertEquals(s.month, 56) 1360 self.assertEquals(s.day, 78) 1361 self.assertEquals(s.text, '1234-56-78') 1362 1363 def test_ymdH(self): 1364 s = ID3TimeStamp('1234-56-78T12') 1365 self.assertEquals(s.year, 1234) 1366 self.assertEquals(s.month, 56) 1367 self.assertEquals(s.day, 78) 1368 self.assertEquals(s.hour, 12) 1369 self.assertEquals(s.text, '1234-56-78 12') 1370 1371 def test_ymdhM(self): 1372 s = ID3TimeStamp('1234-56-78T12:34') 1373 self.assertEquals(s.year, 1234) 1374 self.assertEquals(s.month, 56) 1375 self.assertEquals(s.day, 78) 1376 self.assertEquals(s.hour, 12) 1377 self.assertEquals(s.minute, 34) 1378 self.assertEquals(s.text, '1234-56-78 12:34') 1379 1380 def test_ymdhmS(self): 1381 s = ID3TimeStamp('1234-56-78T12:34:56') 1382 self.assertEquals(s.year, 1234) 1383 self.assertEquals(s.month, 56) 1384 self.assertEquals(s.day, 78) 1385 self.assertEquals(s.hour, 12) 1386 self.assertEquals(s.minute, 34) 1387 self.assertEquals(s.second, 56) 1388 self.assertEquals(s.text, '1234-56-78 12:34:56') 1389 1390 def test_Ymdhms(self): 1391 s = ID3TimeStamp('1234-56-78T12:34:56') 1392 s.month = None 1393 self.assertEquals(s.text, '1234') 1394 1395 def test_alternate_reprs(self): 1396 s = ID3TimeStamp('1234-56.78 12:34:56') 1397 self.assertEquals(s.text, '1234-56-78 12:34:56') 1398 1399 def test_order(self): 1400 s = ID3TimeStamp('1234') 1401 t = ID3TimeStamp('1233-12') 1402 u = ID3TimeStamp('1234-01') 1403 1404 self.assert_(t < s < u) 1405 self.assert_(u > s > t) 1406 1407 def test_types(self): 1408 if PY3: 1409 self.assertRaises(TypeError, ID3TimeStamp, b"blah") 1410 self.assertEquals( 1411 text_type(ID3TimeStamp(u"2000-01-01")), u"2000-01-01") 1412 self.assertEquals( 1413 bytes(ID3TimeStamp(u"2000-01-01")), b"2000-01-01") 1414 1415 1416class TFrameTest(object): 1417 1418 FRAME = None 1419 1420 def test_has_doc(self): 1421 self.failUnless(self.FRAME.__doc__, "%s has no docstring" % self.FRAME) 1422 1423 def test_fake_zlib(self): 1424 header = ID3Header() 1425 header.version = (2, 4, 0) 1426 self.assertRaises(ID3JunkFrameError, self.FRAME._fromData, header, 1427 Frame.FLAG24_COMPRESS, b'\x03abcdefg') 1428 1429 def test_no_hash(self): 1430 self.failUnlessRaises( 1431 TypeError, {}.__setitem__, self.FRAME(), None) 1432 1433 def test_is_valid_frame_id(self): 1434 self.assertTrue(is_valid_frame_id(self.FRAME.__name__)) 1435 1436 def test_all_specs_have_default(self): 1437 for spec in self.FRAME._framespec: 1438 self.assertTrue( 1439 spec.default is not None, 1440 msg="%r:%r" % (self.FRAME, spec.name)) 1441 1442 @classmethod 1443 def create_frame_tests(cls): 1444 for kind in (list(Frames.values()) + list(Frames_2_2.values())): 1445 new_type = type(cls.__name__ + kind.__name__, 1446 (cls, TestCase), {"FRAME": kind}) 1447 assert new_type.__name__ not in globals() 1448 globals()[new_type.__name__] = new_type 1449 1450 1451TFrameTest.create_frame_tests() 1452 1453 1454class FrameIDValidate(TestCase): 1455 1456 def test_valid(self): 1457 self.failUnless(is_valid_frame_id("APIC")) 1458 self.failUnless(is_valid_frame_id("TPE2")) 1459 1460 def test_invalid(self): 1461 self.failIf(is_valid_frame_id("MP3e")) 1462 self.failIf(is_valid_frame_id("+ABC")) 1463 1464 1465class TTimeStampTextFrame(TestCase): 1466 1467 def test_default(self): 1468 self.assertEqual( 1469 TimeStampTextFrame(), TimeStampTextFrame(encoding=1, text=[])) 1470 1471 def test_compare_to_unicode(self): 1472 frame = TimeStampTextFrame(encoding=0, text=[u'1987', u'1988']) 1473 self.failUnlessEqual(frame, text_type(frame)) 1474 1475 1476class TTextFrame(TestCase): 1477 1478 def test_defaults(self): 1479 self.assertEqual(TextFrame(), TextFrame(encoding=1, text=[])) 1480 1481 def test_default_default_mutable(self): 1482 frame = TextFrame() 1483 frame.text.append("foo") 1484 self.assertEqual(TextFrame().text, []) 1485 1486 def test_main(self): 1487 self.assertEqual(TextFrame(text='text').text, ["text"]) 1488 self.assertEqual(TextFrame(text=['a', 'b']).text, ["a", "b"]) 1489 1490 def test_multi_value(self): 1491 frame = TextFrame( 1492 text=[u"foo", u"", u"", u"bar", u"", u""], encoding=0) 1493 config = ID3SaveConfig(3, None) 1494 data = frame._writeData(config) 1495 1496 frame = frame._fromData(_24, 0x0, data) 1497 self.assertEqual(frame.text, [u"foo", u"", u"", u"bar", u"", u""]) 1498 frame = frame._fromData(_23, 0x0, data) 1499 self.assertEqual(frame.text, [u"foo", u"", u"", u"bar"]) 1500 frame = frame._fromData(_22, 0x0, data) 1501 self.assertEqual(frame.text, [u"foo", u"", u"", u"bar"]) 1502 1503 def test_list_iface(self): 1504 frame = TextFrame() 1505 frame.append("a") 1506 frame.extend(["b", "c"]) 1507 self.assertEqual(frame.text, ["a", "b", "c"]) 1508 1509 def test_zlib_latin1(self): 1510 tag = TextFrame._fromData( 1511 _24, 0x9, b'\x00\x00\x00\x0f' 1512 b'x\x9cc(\xc9\xc8,V\x00\xa2D\xfd\x92\xd4\xe2\x12\x00&\x7f\x05%' 1513 ) 1514 self.assertEquals(tag.encoding, 0) 1515 self.assertEquals(tag, ['this is a/test']) 1516 1517 def test_datalen_but_not_compressed(self): 1518 tag = TextFrame._fromData(_24, 0x01, b'\x00\x00\x00\x06\x00A test') 1519 self.assertEquals(tag.encoding, 0) 1520 self.assertEquals(tag, ['A test']) 1521 1522 def test_utf8(self): 1523 tag = TextFrame._fromData(_23, 0x00, b'\x03this is a test') 1524 self.assertEquals(tag.encoding, 3) 1525 self.assertEquals(tag, 'this is a test') 1526 1527 def test_zlib_utf16(self): 1528 data = (b'\x00\x00\x00\x1fx\x9cc\xfc\xff\xaf\x84!\x83!\x93\xa1\x98A' 1529 b'\x01J&2\xe83\x940\xa4\x02\xd9%\x0c\x00\x87\xc6\x07#') 1530 tag = TextFrame._fromData(_23, 0x80, data) 1531 self.assertEquals(tag.encoding, 1) 1532 self.assertEquals(tag, ['this is a/test']) 1533 1534 tag = TextFrame._fromData(_24, 0x08, data) 1535 self.assertEquals(tag.encoding, 1) 1536 self.assertEquals(tag, ['this is a/test']) 1537 1538 1539class TRVA2(TestCase): 1540 1541 def test_default(self): 1542 frame = RVA2() 1543 self.assertEqual(frame.desc, u"") 1544 self.assertEqual(frame.channel, 1) 1545 self.assertEqual(frame.gain, 1) 1546 self.assertEqual(frame.peak, 1) 1547 1548 def test_basic(self): 1549 r = RVA2(gain=1, channel=1, peak=1) 1550 self.assertEqual(r, r) 1551 self.assertNotEqual(r, 42) 1552 1553 def test_hash_key(self): 1554 frame = RVA2(method=42, desc="d", channel=1, gain=1, peak=1) 1555 self.assertEqual(frame.HashKey, "RVA2:d") 1556 1557 self.assertEquals(RVA2(gain=1).HashKey, RVA2(gain=2).HashKey) 1558 self.assertNotEquals(RVA2(desc="a").HashKey, RVA2(desc="b").HashKey) 1559 1560 def test_pprint(self): 1561 frame = RVA2(method=42, desc="d", channel=1, gain=1, peak=1) 1562 frame._pprint() 1563 1564 def test_wacky_truncated(self): 1565 data = b'\x01{\xf0\x10\xff\xff\x00' 1566 self.assertRaises(ID3JunkFrameError, RVA2._fromData, _24, 0x00, data) 1567 1568 def test_bad_number_of_bits(self): 1569 data = b'\x00\x00\x01\xe6\xfc\x10{\xd7' 1570 self.assertRaises(ID3JunkFrameError, RVA2._fromData, _24, 0x00, data) 1571 1572 1573class TCTOC(TestCase): 1574 1575 def test_defaults(self): 1576 self.assertEqual(CTOC(), CTOC(element_id=u"", flags=0, 1577 child_element_ids=[], sub_frames=[])) 1578 1579 def test_hash(self): 1580 frame = CTOC(element_id=u"foo", flags=3, 1581 child_element_ids=[u"ch0"], 1582 sub_frames=[TPE2(encoding=3, text=[u"foo"])]) 1583 self.assertEqual(frame.HashKey, "CTOC:foo") 1584 1585 def test_pprint(self): 1586 frame = CTOC(element_id=u"foo", flags=3, 1587 child_element_ids=[u"ch0"], 1588 sub_frames=[TPE2(encoding=3, text=[u"foo"])]) 1589 self.assertEqual( 1590 frame.pprint(), 1591 "CTOC=foo flags=3 child_element_ids=ch0\n TPE2=foo") 1592 1593 def test_write(self): 1594 frame = CTOC(element_id=u"foo", flags=3, 1595 child_element_ids=[u"ch0"], 1596 sub_frames=[TPE2(encoding=3, text=[u"f", u"b"])]) 1597 config = ID3SaveConfig(3, "/") 1598 data = (b"foo\x00\x03\x01ch0\x00TPE2\x00\x00\x00\x0b\x00\x00\x01" 1599 b"\xff\xfef\x00/\x00b\x00\x00\x00") 1600 self.assertEqual(frame._writeData(config), data) 1601 1602 def test_eq(self): 1603 self.assertEqual(CTOC(), CTOC()) 1604 self.assertNotEqual(CTOC(), object()) 1605 1606 1607class TASPI(TestCase): 1608 1609 def test_default(self): 1610 frame = ASPI() 1611 self.assertEqual(frame.S, 0) 1612 self.assertEqual(frame.L, 0) 1613 self.assertEqual(frame.N, 0) 1614 self.assertEqual(frame.b, 0) 1615 self.assertEqual(frame.Fi, []) 1616 1617 def test_default_default_mutable(self): 1618 frame = ASPI() 1619 frame.Fi.append(1) 1620 self.assertEqual(ASPI().Fi, []) 1621 1622 1623class TCHAP(TestCase): 1624 1625 def test_default(self): 1626 frame = CHAP() 1627 self.assertEqual(frame.element_id, u"") 1628 self.assertEqual(frame.start_time, 0) 1629 self.assertEqual(frame.end_time, 0) 1630 self.assertEqual(frame.start_offset, 0xffffffff) 1631 self.assertEqual(frame.end_offset, 0xffffffff) 1632 self.assertEqual(frame.sub_frames, ID3Tags()) 1633 1634 def test_hash(self): 1635 frame = CHAP(element_id=u"foo", start_time=0, end_time=0, 1636 start_offset=0, end_offset=0, 1637 sub_frames=[TPE2(encoding=3, text=[u"foo"])]) 1638 self.assertEqual(frame.HashKey, "CHAP:foo") 1639 1640 def test_pprint(self): 1641 frame = CHAP(element_id=u"foo", start_time=0, end_time=0, 1642 start_offset=0, end_offset=0, 1643 sub_frames=[TPE2(encoding=3, text=[u"foo"])]) 1644 self.assertEqual( 1645 frame.pprint(), "CHAP=foo time=0..0 offset=0..0\n TPE2=foo") 1646 1647 def test_eq(self): 1648 self.assertEqual(CHAP(), CHAP()) 1649 self.assertNotEqual(CHAP(), object()) 1650 1651 1652class TPCNT(TestCase): 1653 1654 def test_default(self): 1655 frame = PCNT() 1656 self.assertEqual(frame.count, 0) 1657 1658 1659class TAPIC(TestCase): 1660 1661 def test_default(self): 1662 frame = APIC() 1663 self.assertEqual(frame.encoding, 1) 1664 self.assertEqual(frame.mime, u"") 1665 self.assertEqual(frame.type, 3) 1666 self.assertEqual(frame.desc, u"") 1667 self.assertEqual(frame.data, b"") 1668 1669 def test_hash(self): 1670 frame = APIC(encoding=0, mime=u"m", type=3, desc=u"d", data=b"\x42") 1671 self.assertEqual(frame.HashKey, "APIC:d") 1672 1673 def test_pprint(self): 1674 frame = APIC( 1675 encoding=0, mime=u"mime", type=3, desc=u"desc", data=b"\x42") 1676 self.assertEqual(frame._pprint(), u"cover front, desc (mime, 1 bytes)") 1677 1678 def test_multi(self): 1679 self.assertEquals(APIC(data=b"1").HashKey, APIC(data=b"2").HashKey) 1680 self.assertNotEquals(APIC(desc="a").HashKey, APIC(desc="b").HashKey) 1681 1682 def test_repr(self): 1683 frame = APIC(encoding=0, mime=u"m", type=3, desc=u"d", data=b"\x42") 1684 if PY2: 1685 expected = ( 1686 "APIC(encoding=<Encoding.LATIN1: 0>, mime=u'm', " 1687 "type=<PictureType.COVER_FRONT: 3>, desc=u'd', data='B')") 1688 else: 1689 expected = ( 1690 "APIC(encoding=<Encoding.LATIN1: 0>, mime='m', " 1691 "type=<PictureType.COVER_FRONT: 3>, desc='d', data=b'B')") 1692 1693 self.assertEqual(repr(frame), expected) 1694 new_frame = APIC() 1695 new_frame._readData(_24, frame._writeData()) 1696 self.assertEqual(repr(new_frame), expected) 1697