1# -*- coding: utf-8 -*- 2 3from tests import TestCase 4 5from mutagen._compat import PY3 6from mutagen.id3._specs import SpecError, Latin1TextListSpec, ID3FramesSpec, \ 7 ASPIIndexSpec, ByteSpec, EncodingSpec, StringSpec, BinaryDataSpec, \ 8 EncodedTextSpec, VolumePeakSpec, VolumeAdjustmentSpec, CTOCFlagsSpec, \ 9 Spec, SynchronizedTextSpec, TimeStampSpec, FrameIDSpec, RVASpec 10from mutagen.id3._frames import Frame 11from mutagen.id3._tags import ID3Header, ID3Tags, ID3SaveConfig 12from mutagen.id3 import TIT3, ASPI, CTOCFlags, ID3TimeStamp 13 14 15class TSynchronizedTextSpec(TestCase): 16 17 def test_write(self): 18 s = SynchronizedTextSpec('name') 19 f = Frame() 20 21 values = [(u"A", 100), (u"\xe4xy", 0), (u"", 42), (u"", 0)] 22 23 # utf-16 24 f.encoding = 1 25 self.assertEqual( 26 s.read(None, f, s.write(None, f, values)), (values, b"")) 27 data = s.write(None, f, [(u"A", 100)]) 28 self.assertEquals(data, b"\xff\xfeA\x00\x00\x00\x00\x00\x00d") 29 30 # utf-16be 31 f.encoding = 2 32 self.assertEqual( 33 s.read(None, f, s.write(None, f, values)), (values, b"")) 34 self.assertEquals( 35 s.write(None, f, [(u"A", 100)]), b"\x00A\x00\x00\x00\x00\x00d") 36 37 # utf-8 38 f.encoding = 3 39 self.assertEqual( 40 s.read(None, f, s.write(None, f, values)), (values, b"")) 41 self.assertEquals( 42 s.write(None, f, [(u"A", 100)]), b"A\x00\x00\x00\x00d") 43 44 45class TTimeStampSpec(TestCase): 46 47 def test_read(self): 48 s = TimeStampSpec('name') 49 f = Frame() 50 f.encoding = 0 51 header = ID3Header() 52 header.version = (2, 4, 0) 53 self.assertEquals( 54 (ID3TimeStamp('ab'), b'fg'), s.read(header, f, b'ab\x00fg')) 55 self.assertEquals( 56 (ID3TimeStamp('1234'), b''), s.read(header, f, b'1234\x00')) 57 58 def test_write(self): 59 s = TimeStampSpec('name') 60 f = Frame() 61 f.encoding = 0 62 self.assertEquals(b'1234\x00', s.write(None, f, ID3TimeStamp('1234'))) 63 self.assertRaises(AttributeError, s.write, None, f, None) 64 65 66class TEncodedTextSpec(TestCase): 67 68 def test_read(self): 69 s = EncodedTextSpec('name') 70 f = Frame() 71 f.encoding = 0 72 header = ID3Header() 73 header.version = (2, 4, 0) 74 self.assertEquals((u'abcd', b'fg'), s.read(header, f, b'abcd\x00fg')) 75 76 def test_write(self): 77 s = EncodedTextSpec('name') 78 f = Frame() 79 f.encoding = 0 80 self.assertEquals(b'abcdefg\x00', s.write(None, f, u'abcdefg')) 81 self.assertRaises(AttributeError, s.write, None, f, None) 82 83 84class TEncodingSpec(TestCase): 85 86 def test_read(self): 87 s = EncodingSpec('name') 88 self.assertEquals((3, b'abcdefg'), s.read(None, None, b'\x03abcdefg')) 89 self.assertRaises(SpecError, s.read, None, None, b'\x04abcdefg') 90 91 def test_write(self): 92 s = EncodingSpec('name') 93 self.assertEquals(b'\x00', s.write(None, None, 0)) 94 self.assertRaises(TypeError, s.write, None, None, b'abc') 95 self.assertRaises(TypeError, s.write, None, None, None) 96 97 def test_validate(self): 98 s = EncodingSpec('name') 99 self.assertRaises(TypeError, s.validate, None, None) 100 101 102class TASPIIndexSpec(TestCase): 103 104 def test_read(self): 105 frame = ASPI(b=16, N=2) 106 s = ASPIIndexSpec('name', []) 107 self.assertRaises(SpecError, s.read, None, frame, b'') 108 self.assertEqual( 109 s.read(None, frame, b'\x01\x00\x00\x01'), ([256, 1], b"")) 110 frame = ASPI(b=42) 111 self.assertRaises(SpecError, s.read, None, frame, b'') 112 113 114class TVolumeAdjustmentSpec(TestCase): 115 116 def test_validate(self): 117 s = VolumeAdjustmentSpec('gain', 0) 118 self.assertRaises(ValueError, s.validate, None, 65) 119 120 def test_read(self): 121 s = VolumeAdjustmentSpec('gain', 0) 122 self.assertEquals((0.0, b''), s.read(None, None, b'\x00\x00')) 123 self.assertEquals((2.0, b''), s.read(None, None, b'\x04\x00')) 124 self.assertEquals((-2.0, b''), s.read(None, None, b'\xfc\x00')) 125 126 def test_write(self): 127 s = VolumeAdjustmentSpec('gain', 0) 128 self.assertEquals(b'\x00\x00', s.write(None, None, 0.0)) 129 self.assertEquals(b'\x04\x00', s.write(None, None, 2.0)) 130 self.assertEquals(b'\xfc\x00', s.write(None, None, -2.0)) 131 132 133class TByteSpec(TestCase): 134 135 def test_validate(self): 136 s = ByteSpec('byte') 137 self.assertRaises(ValueError, s.validate, None, 1000) 138 139 def test_read(self): 140 s = ByteSpec('name') 141 self.assertEquals((97, b'bcdefg'), s.read(None, None, b'abcdefg')) 142 143 def test_write(self): 144 s = ByteSpec('name') 145 self.assertEquals(b'a', s.write(None, None, 97)) 146 self.assertRaises(TypeError, s.write, None, None, b'abc') 147 self.assertRaises(TypeError, s.write, None, None, None) 148 149 150class TVolumePeakSpec(TestCase): 151 152 def test_validate(self): 153 s = VolumePeakSpec('peak', 0) 154 self.assertRaises(ValueError, s.validate, None, 2) 155 156 157class TStringSpec(TestCase): 158 159 def test_validate(self): 160 s = StringSpec('byte', 3) 161 self.assertEqual(s.validate(None, "ABC"), "ABC") 162 self.assertEqual(s.validate(None, u"ABC"), u"ABC") 163 self.assertRaises(ValueError, s.validate, None, "abc2") 164 self.assertRaises(ValueError, s.validate, None, "ab") 165 self.assertRaises(TypeError, s.validate, None, None) 166 167 if PY3: 168 self.assertRaises(TypeError, s.validate, None, b"ABC") 169 self.assertRaises(ValueError, s.validate, None, u"\xf6\xe4\xfc") 170 171 def test_read(self): 172 s = StringSpec('name', 3) 173 self.assertEquals(('abc', b'defg'), s.read(None, None, b'abcdefg')) 174 self.assertRaises(SpecError, s.read, None, None, b'\xff') 175 176 def test_write(self): 177 s = StringSpec('name', 3) 178 self.assertEquals(b'abc', s.write(None, None, 'abcdefg')) 179 self.assertEquals(b'\x00\x00\x00', s.write(None, None, '\x00')) 180 self.assertEquals(b'a\x00\x00', s.write(None, None, 'a')) 181 182 183class TBinaryDataSpec(TestCase): 184 185 def test_validate(self): 186 s = BinaryDataSpec('name') 187 self.assertRaises(TypeError, s.validate, None, None) 188 self.assertEqual(s.validate(None, b"abc"), b"abc") 189 if PY3: 190 self.assertRaises(TypeError, s.validate, None, "abc") 191 else: 192 self.assertEqual(s.validate(None, u"abc"), b"abc") 193 self.assertRaises(ValueError, s.validate, None, u"\xf6\xe4\xfc") 194 195 def test_read(self): 196 s = BinaryDataSpec('name') 197 self.assertEquals((b'abcdefg', b''), s.read(None, None, b'abcdefg')) 198 199 def test_write(self): 200 s = BinaryDataSpec('name') 201 self.assertEquals(b'43', s.write(None, None, 43)) 202 self.assertEquals(b'abc', s.write(None, None, b'abc')) 203 204 205class TSpec(TestCase): 206 207 def test_no_hash(self): 208 self.failUnlessRaises( 209 TypeError, {}.__setitem__, Spec("foo", None), None) 210 211 212class TRVASpec(TestCase): 213 214 def test_read(self): 215 spec = RVASpec("name", False) 216 val, rest = spec.read( 217 None, None, 218 b"\x03\x10\xc7\xc7\xc7\xc7\x00\x00\x00\x00\x00\x00\x00\x00") 219 self.assertEqual(rest, b"") 220 self.assertEqual(val, [51143, 51143, 0, 0, 0, 0]) 221 222 def test_read_stereo_only(self): 223 spec = RVASpec("name", True) 224 val, rest = spec.read( 225 None, None, 226 b"\x03\x10\xc7\xc7\xc7\xc7\x00\x00\x00\x00\x00\x00\x00\x00") 227 self.assertEqual(rest, b"\x00\x00\x00\x00") 228 self.assertEqual(val, [51143, 51143, 0, 0]) 229 230 def test_write(self): 231 spec = RVASpec("name", False) 232 data = spec.write(None, None, [0, 1, 2, 3, -4, -5]) 233 self.assertEqual( 234 data, b"\x03\x10\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05") 235 236 def test_write_stereo_only(self): 237 spec = RVASpec("name", True) 238 self.assertRaises( 239 SpecError, spec.write, None, None, [0, 0, 0, 0, 0, 0]) 240 241 def test_validate(self): 242 spec = RVASpec("name", False) 243 self.assertRaises(ValueError, spec.validate, None, []) 244 self.assertEqual(spec.validate(None, [1, 2]), [1, 2]) 245 246 247class TFrameIDSpec(TestCase): 248 249 def test_read(self): 250 spec = FrameIDSpec("name", 3) 251 self.assertEqual(spec.read(None, None, b"FOOX"), (u"FOO", b"X")) 252 253 def test_validate(self): 254 spec = FrameIDSpec("name", 3) 255 self.assertRaises(ValueError, spec.validate, None, u"123") 256 self.assertRaises(ValueError, spec.validate, None, u"TXXX") 257 self.assertEqual(spec.validate(None, u"TXX"), u"TXX") 258 259 spec = FrameIDSpec("name", 4) 260 self.assertEqual(spec.validate(None, u"TXXX"), u"TXXX") 261 262 263class TCTOCFlagsSpec(TestCase): 264 265 def test_read(self): 266 spec = CTOCFlagsSpec("name") 267 v, r = spec.read(None, None, b"\x03") 268 self.assertEqual(r, b"") 269 self.assertEqual(v, 3) 270 self.assertTrue(isinstance(v, CTOCFlags)) 271 272 def test_write(self): 273 spec = CTOCFlagsSpec("name") 274 self.assertEqual(spec.write(None, None, CTOCFlags.ORDERED), b"\x01") 275 276 def test_validate(self): 277 spec = CTOCFlagsSpec("name") 278 self.assertEqual(spec.validate(None, 3), 3) 279 self.assertTrue(isinstance(spec.validate(None, 3), CTOCFlags)) 280 self.assertEqual(spec.validate(None, None), None) 281 282 283class TID3FramesSpec(TestCase): 284 285 def test_read_empty(self): 286 header = ID3Header() 287 header.version = (2, 4, 0) 288 spec = ID3FramesSpec("name") 289 290 value, data = spec.read(header, None, b"") 291 self.assertEqual(data, b"") 292 self.assertTrue(isinstance(value, ID3Tags)) 293 294 def test_read_tit3(self): 295 header = ID3Header() 296 header.version = (2, 4, 0) 297 spec = ID3FramesSpec("name") 298 299 value, data = spec.read(header, None, 300 b"TIT3" + b"\x00\x00\x00\x03" + b"\x00\x00" + b"\x03" + b"F\x00") 301 302 self.assertTrue(isinstance(value, ID3Tags)) 303 self.assertEqual(data, b"") 304 frames = value.getall("TIT3") 305 self.assertEqual(len(frames), 1) 306 self.assertEqual(frames[0].encoding, 3) 307 self.assertEqual(frames[0].text, [u"F"]) 308 309 def test_write_empty(self): 310 header = ID3Header() 311 header.version = (2, 4, 0) 312 spec = ID3FramesSpec("name") 313 config = ID3SaveConfig() 314 315 tags = ID3Tags() 316 self.assertEqual(spec.write(config, None, tags), b"") 317 318 def test_write_tit3(self): 319 spec = ID3FramesSpec("name") 320 config = ID3SaveConfig() 321 322 tags = ID3Tags() 323 tags.add(TIT3(encoding=3, text=[u"F", u"B"])) 324 self.assertEqual(spec.write(config, None, tags), 325 b"TIT3" + b"\x00\x00\x00\x05" + b"\x00\x00" + 326 b"\x03" + b"F\x00" + b"B\x00") 327 328 def test_write_tit3_v23(self): 329 spec = ID3FramesSpec("name") 330 config = ID3SaveConfig(3, "/") 331 332 tags = ID3Tags() 333 tags.add(TIT3(encoding=3, text=[u"F", u"B"])) 334 self.assertEqual(spec.write(config, None, tags), 335 b"TIT3" + b"\x00\x00\x00\x0B" + b"\x00\x00" + 336 b"\x01" + b"\xff\xfeF\x00/\x00B\x00\x00\x00") 337 338 def test_validate(self): 339 header = ID3Header() 340 header.version = (2, 4, 0) 341 spec = ID3FramesSpec("name") 342 343 self.assertRaises(TypeError, spec.validate, None, None) 344 self.assertTrue(isinstance(spec.validate(None, []), ID3Tags)) 345 346 v = spec.validate(None, [TIT3(encoding=3, text=[u"foo"])]) 347 self.assertEqual(v.getall("TIT3")[0].text, [u"foo"]) 348 349 350class TLatin1TextListSpec(TestCase): 351 352 def test_read(self): 353 spec = Latin1TextListSpec("name") 354 self.assertEqual(spec.read(None, None, b"\x00xxx"), ([], b"xxx")) 355 self.assertEqual( 356 spec.read(None, None, b"\x01foo\x00"), ([u"foo"], b"")) 357 self.assertEqual( 358 spec.read(None, None, b"\x01\x00"), ([u""], b"")) 359 self.assertEqual( 360 spec.read(None, None, b"\x02f\x00o\x00"), ([u"f", u"o"], b"")) 361 362 def test_write(self): 363 spec = Latin1TextListSpec("name") 364 self.assertEqual(spec.write(None, None, []), b"\x00") 365 self.assertEqual(spec.write(None, None, [u""]), b"\x01\x00") 366 367 def test_validate(self): 368 spec = Latin1TextListSpec("name") 369 self.assertRaises(TypeError, spec.validate, None, object()) 370 self.assertRaises(TypeError, spec.validate, None, None) 371 self.assertEqual(spec.validate(None, [u"foo"]), [u"foo"]) 372 self.assertEqual(spec.validate(None, []), []) 373