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