1# -*- coding: utf-8 -*-
2
3import os
4
5from mutagen._compat import cBytesIO
6from mutagen.aiff import AIFF, AIFFInfo, delete, IFFFile, IFFChunk
7from mutagen.aiff import error as AIFFError
8
9from tests import TestCase, DATA_DIR, get_temp_copy
10
11
12class TAIFF(TestCase):
13    silence_1 = os.path.join(DATA_DIR, '11k-1ch-2s-silence.aif')
14    silence_2 = os.path.join(DATA_DIR, '48k-2ch-s16-silence.aif')
15    silence_3 = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif')
16    silence_4 = os.path.join(DATA_DIR, '8k-1ch-3.5s-silence.aif')
17    silence_5 = os.path.join(DATA_DIR, '8k-4ch-1s-silence.aif')
18
19    has_tags = os.path.join(DATA_DIR, 'with-id3.aif')
20    no_tags = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif')
21
22    def setUp(self):
23        self.filename_1 = get_temp_copy(self.has_tags)
24        self.filename_2 = get_temp_copy(self.no_tags)
25
26        self.aiff_tmp_id3 = AIFF(self.filename_1)
27        self.aiff_tmp_no_id3 = AIFF(self.filename_2)
28
29        self.aiff_1 = AIFF(self.silence_1)
30        self.aiff_2 = AIFF(self.silence_2)
31        self.aiff_3 = AIFF(self.silence_3)
32        self.aiff_4 = AIFF(self.silence_4)
33        self.aiff_5 = AIFF(self.silence_5)
34
35    def test_channels(self):
36        self.failUnlessEqual(self.aiff_1.info.channels, 1)
37        self.failUnlessEqual(self.aiff_2.info.channels, 2)
38        self.failUnlessEqual(self.aiff_3.info.channels, 1)
39        self.failUnlessEqual(self.aiff_4.info.channels, 1)
40        self.failUnlessEqual(self.aiff_5.info.channels, 4)
41
42    def test_length(self):
43        self.failUnlessEqual(self.aiff_1.info.length, 2)
44        self.failUnlessEqual(self.aiff_2.info.length, 0.1)
45        self.failUnlessEqual(self.aiff_3.info.length, 1)
46        self.failUnlessEqual(self.aiff_4.info.length, 3.5)
47        self.failUnlessEqual(self.aiff_5.info.length, 1)
48
49    def test_bitrate(self):
50        self.failUnlessEqual(self.aiff_1.info.bitrate, 176400)
51        self.failUnlessEqual(self.aiff_2.info.bitrate, 1536000)
52        self.failUnlessEqual(self.aiff_3.info.bitrate, 128000)
53        self.failUnlessEqual(self.aiff_4.info.bitrate, 128000)
54        self.failUnlessEqual(self.aiff_5.info.bitrate, 512000)
55
56    def test_sample_rate(self):
57        self.failUnlessEqual(self.aiff_1.info.sample_rate, 11025)
58        self.failUnlessEqual(self.aiff_2.info.sample_rate, 48000)
59        self.failUnlessEqual(self.aiff_3.info.sample_rate, 8000)
60        self.failUnlessEqual(self.aiff_4.info.sample_rate, 8000)
61        self.failUnlessEqual(self.aiff_5.info.sample_rate, 8000)
62
63    def test_sample_size(self):
64        self.failUnlessEqual(self.aiff_1.info.sample_size, 16)
65        self.failUnlessEqual(self.aiff_2.info.sample_size, 16)
66        self.failUnlessEqual(self.aiff_3.info.sample_size, 16)
67        self.failUnlessEqual(self.aiff_4.info.sample_size, 16)
68        self.failUnlessEqual(self.aiff_5.info.sample_size, 16)
69
70    def test_notaiff(self):
71        self.failUnlessRaises(
72            AIFFError, AIFF, os.path.join(DATA_DIR, 'empty.ofr'))
73
74    def test_pprint(self):
75        self.failUnless(self.aiff_1.pprint())
76        self.failUnless(self.aiff_tmp_id3.pprint())
77
78    def test_delete(self):
79        self.aiff_tmp_id3.delete()
80        self.failIf(self.aiff_tmp_id3.tags)
81        self.failUnless(AIFF(self.filename_1).tags is None)
82
83    def test_module_delete(self):
84        delete(self.filename_1)
85        self.failUnless(AIFF(self.filename_1).tags is None)
86
87    def test_module_double_delete(self):
88        delete(self.filename_1)
89        delete(self.filename_1)
90
91    def test_pprint_no_tags(self):
92        self.aiff_tmp_id3.tags = None
93        self.failUnless(self.aiff_tmp_id3.pprint())
94
95    def test_save_no_tags(self):
96        self.aiff_tmp_id3.tags = None
97        self.aiff_tmp_id3.save()
98        self.assertTrue(self.aiff_tmp_id3.tags is None)
99
100    def test_add_tags_already_there(self):
101        self.failUnless(self.aiff_tmp_id3.tags)
102        self.failUnlessRaises(Exception, self.aiff_tmp_id3.add_tags)
103
104    def test_mime(self):
105        self.failUnless("audio/aiff" in self.aiff_1.mime)
106        self.failUnless("audio/x-aiff" in self.aiff_1.mime)
107
108    def test_loaded_tags(self):
109        self.failUnless(self.aiff_tmp_id3["TIT2"] == "AIFF title")
110
111    def test_roundtrip(self):
112        self.failUnlessEqual(self.aiff_tmp_id3["TIT2"], ["AIFF title"])
113        self.aiff_tmp_id3.save()
114        new = AIFF(self.aiff_tmp_id3.filename)
115        self.failUnlessEqual(new["TIT2"], ["AIFF title"])
116
117    def test_save_tags(self):
118        from mutagen.id3 import TIT1
119        tags = self.aiff_tmp_id3.tags
120        tags.add(TIT1(encoding=3, text="foobar"))
121        tags.save()
122
123        new = AIFF(self.aiff_tmp_id3.filename)
124        self.failUnlessEqual(new["TIT1"], ["foobar"])
125
126    def test_save_with_ID3_chunk(self):
127        from mutagen.id3 import TIT1
128        self.aiff_tmp_id3["TIT1"] = TIT1(encoding=3, text="foobar")
129        self.aiff_tmp_id3.save()
130        self.failUnless(AIFF(self.filename_1)["TIT1"] == "foobar")
131        self.failUnless(self.aiff_tmp_id3["TIT2"] == "AIFF title")
132
133    def test_save_without_ID3_chunk(self):
134        from mutagen.id3 import TIT1
135        self.aiff_tmp_no_id3["TIT1"] = TIT1(encoding=3, text="foobar")
136        self.aiff_tmp_no_id3.save()
137        self.failUnless(AIFF(self.filename_2)["TIT1"] == "foobar")
138
139    def test_corrupt_tag(self):
140        with open(self.filename_1, "r+b") as h:
141            chunk = IFFFile(h)[u'ID3']
142            h.seek(chunk.data_offset)
143            h.seek(4, 1)
144            h.write(b"\xff\xff")
145        self.assertRaises(AIFFError, AIFF, self.filename_1)
146
147    def test_padding(self):
148        AIFF(self.filename_1).save()
149        self.assertEqual(AIFF(self.filename_1).tags._padding, 1002)
150        AIFF(self.filename_1).save()
151        self.assertEqual(AIFF(self.filename_1).tags._padding, 1002)
152
153        tags = AIFF(self.filename_1)
154        tags.save(padding=lambda x: 1)
155        self.assertEqual(AIFF(self.filename_1).tags._padding, 1)
156
157        tags = AIFF(self.filename_1)
158        tags.save(padding=lambda x: 100)
159        self.assertEqual(AIFF(self.filename_1).tags._padding, 100)
160
161        tags = AIFF(self.filename_1)
162        self.assertRaises(AIFFError, tags.save, padding=lambda x: -1)
163
164    def tearDown(self):
165        os.unlink(self.filename_1)
166        os.unlink(self.filename_2)
167
168
169class TAIFFInfo(TestCase):
170
171    def test_empty(self):
172        fileobj = cBytesIO(b"")
173        self.failUnlessRaises(AIFFError, AIFFInfo, fileobj)
174
175
176class TIFFFile(TestCase):
177    has_tags = os.path.join(DATA_DIR, 'with-id3.aif')
178    no_tags = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif')
179
180    def setUp(self):
181        self.file_1 = open(self.has_tags, 'rb')
182        self.iff_1 = IFFFile(self.file_1)
183        self.file_2 = open(self.no_tags, 'rb')
184        self.iff_2 = IFFFile(self.file_2)
185
186        self.tmp_1_name = get_temp_copy(self.has_tags)
187        self.file_1_tmp = open(self.tmp_1_name, 'rb+')
188        self.iff_1_tmp = IFFFile(self.file_1_tmp)
189
190        self.tmp_2_name = get_temp_copy(self.no_tags)
191        self.file_2_tmp = open(self.tmp_2_name, 'rb+')
192        self.iff_2_tmp = IFFFile(self.file_2_tmp)
193
194    def tearDown(self):
195        self.file_1.close()
196        self.file_2.close()
197        self.file_1_tmp.close()
198        self.file_2_tmp.close()
199        os.unlink(self.tmp_1_name)
200        os.unlink(self.tmp_2_name)
201
202    def test_has_chunks(self):
203        self.failUnless(u'FORM' in self.iff_1)
204        self.failUnless(u'COMM' in self.iff_1)
205        self.failUnless(u'SSND' in self.iff_1)
206        self.failUnless(u'ID3' in self.iff_1)
207
208        self.failUnless(u'FORM' in self.iff_2)
209        self.failUnless(u'COMM' in self.iff_2)
210        self.failUnless(u'SSND' in self.iff_2)
211
212    def test_is_chunks(self):
213        self.failUnless(isinstance(self.iff_1[u'FORM'], IFFChunk))
214        self.failUnless(isinstance(self.iff_1[u'COMM'], IFFChunk))
215        self.failUnless(isinstance(self.iff_1[u'SSND'], IFFChunk))
216        self.failUnless(isinstance(self.iff_1[u'ID3'], IFFChunk))
217
218    def test_chunk_size(self):
219        self.failUnlessEqual(self.iff_1[u'FORM'].size, 17096)
220        self.failUnlessEqual(self.iff_2[u'FORM'].size, 16054)
221
222    def test_chunk_data_size(self):
223        self.failUnlessEqual(self.iff_1[u'FORM'].data_size, 17088)
224        self.failUnlessEqual(self.iff_2[u'FORM'].data_size, 16046)
225
226    def test_FORM_chunk_resize(self):
227        self.iff_1_tmp[u'FORM'].resize(17000)
228        self.failUnlessEqual(
229            IFFFile(self.file_1_tmp)[u'FORM'].data_size, 17000)
230        self.iff_2_tmp[u'FORM'].resize(0)
231        self.failUnlessEqual(IFFFile(self.file_2_tmp)[u'FORM'].data_size, 0)
232
233    def test_child_chunk_resize(self):
234        self.iff_1_tmp[u'ID3'].resize(128)
235
236        id3 = self.iff_1_tmp[u'ID3']
237        id3.write(b"\xff" * 128)
238        self.assertEqual(id3.read(), b"\xff" * 128)
239
240        self.failUnlessEqual(IFFFile(self.file_1_tmp)[u'ID3'].data_size, 128)
241        self.failUnlessEqual(
242            IFFFile(self.file_1_tmp)[u'FORM'].data_size, 16182)
243
244    def test_chunk_delete(self):
245        del self.iff_1_tmp[u'ID3']
246        self.failIf(u'ID3' in self.iff_1_tmp)
247        self.failIf(u'ID3' in IFFFile(self.file_1_tmp))
248        self.failUnlessEqual(IFFFile(self.file_1_tmp)[u'FORM'].size, 16054)
249        del self.iff_2_tmp[u'SSND']
250        self.failIf(u'SSND' in self.iff_2_tmp)
251        self.failIf(u'SSND' in IFFFile(self.file_2_tmp))
252        self.failUnlessEqual(IFFFile(self.file_2_tmp)[u'FORM'].size, 38)
253
254    def test_insert_chunk(self):
255        self.iff_2_tmp.insert_chunk(u'ID3')
256
257        new_iff = IFFFile(self.file_2_tmp)
258        self.failUnless(u'ID3' in new_iff)
259        self.failUnless(isinstance(new_iff[u'ID3'], IFFChunk))
260        self.failUnlessEqual(new_iff[u'FORM'].size, 16062)
261        self.failUnlessEqual(new_iff[u'FORM'].data_size, 16054)
262        self.failUnlessEqual(new_iff[u'ID3'].size, 8)
263        self.failUnlessEqual(new_iff[u'ID3'].data_size, 0)
264