1# -*- coding: utf-8 -*- 2 3import os 4from tempfile import mkstemp 5import shutil 6import locale 7 8import mutagen 9from mutagen.id3 import ID3 10from mutagen._compat import PY2, PY3 11from mutagen._senf import fsnative as fsn 12 13from tests.test_tools import _TTools 14from tests import DATA_DIR 15 16 17class TMid3v2(_TTools): 18 19 TOOL_NAME = u"mid3v2" 20 21 def setUp(self): 22 super(TMid3v2, self).setUp() 23 original = os.path.join(DATA_DIR, fsn(u'silence-44-s.mp3')) 24 fd, self.filename = mkstemp(suffix=fsn(u'öäü.mp3')) 25 assert isinstance(self.filename, fsn) 26 os.close(fd) 27 shutil.copy(original, self.filename) 28 29 def tearDown(self): 30 super(TMid3v2, self).tearDown() 31 os.unlink(self.filename) 32 33 def test_no_tags(self): 34 f = ID3(self.filename) 35 f.delete() 36 res, out, err = self.call2(fsn(u"-l"), self.filename) 37 self.assertTrue("No ID3 header found" in out) 38 39 def test_list_genres(self): 40 for arg in [fsn(u"-L"), fsn(u"--list-genres")]: 41 res, out = self.call(arg) 42 self.failUnlessEqual(res, 0) 43 self.failUnless("Acid Punk" in out) 44 45 def test_list_frames(self): 46 for arg in [fsn(u"-f"), fsn(u"--list-frames")]: 47 res, out = self.call(arg) 48 self.failUnlessEqual(res, 0) 49 self.failUnless("--APIC" in out) 50 self.failUnless("--TIT2" in out) 51 52 def test_list(self): 53 f = ID3(self.filename) 54 album = f["TALB"].text[0] 55 for arg in [fsn(u"-l"), fsn(u"--list")]: 56 res, out = self.call(arg, self.filename) 57 self.assertFalse("b'" in out) 58 self.failUnlessEqual(res, 0) 59 self.failUnless("TALB=" + fsn(album) in out) 60 61 def test_list_raw(self): 62 f = ID3(self.filename) 63 res, out = self.call(fsn(u"--list-raw"), self.filename) 64 self.failUnlessEqual(res, 0) 65 self.failUnless(repr(f["TALB"]) in out) 66 67 def _test_text_frame(self, short, longer, frame): 68 new_value = fsn(u"TEST") 69 for arg in [short, longer]: 70 orig = ID3(self.filename) 71 frame_class = mutagen.id3.Frames[frame] 72 orig[frame] = frame_class(text=[u"BLAH"], encoding=3) 73 orig.save() 74 75 res, out = self.call(arg, new_value, self.filename) 76 self.failUnlessEqual(res, 0) 77 self.failIf(out) 78 self.failUnlessEqual(ID3(self.filename)[frame].text, [new_value]) 79 80 def test_artist(self): 81 self._test_text_frame(fsn(u"-a"), fsn(u"--artist"), "TPE1") 82 83 def test_album(self): 84 self._test_text_frame(fsn(u"-A"), fsn(u"--album"), "TALB") 85 86 def test_title(self): 87 self._test_text_frame(fsn(u"-t"), fsn(u"--song"), "TIT2") 88 89 def test_genre(self): 90 self._test_text_frame(fsn(u"-g"), fsn(u"--genre"), "TCON") 91 92 def test_convert(self): 93 res, out = self.call(fsn(u"--convert"), self.filename) 94 self.failUnlessEqual((res, out), (0, "")) 95 96 def test_artist_escape(self): 97 res, out = self.call( 98 fsn(u"-e"), fsn(u"-a"), fsn(u"foo\\nbar"), self.filename) 99 self.failUnlessEqual(res, 0) 100 self.failIf(out) 101 f = ID3(self.filename) 102 self.failUnlessEqual(f["TPE1"][0], "foo\nbar") 103 104 def test_txxx_escape(self): 105 res, out = self.call( 106 fsn(u"-e"), fsn(u"--TXXX"), 107 fsn(u"EscapeTest\\\\:\\\\:albumartist:Ex\\\\:ample"), 108 self.filename) 109 self.failUnlessEqual(res, 0) 110 self.failIf(out) 111 112 f = ID3(self.filename) 113 frame = f.getall("TXXX")[0] 114 self.failUnlessEqual(frame.desc, u"EscapeTest::albumartist") 115 self.failUnlessEqual(frame.text, [u"Ex:ample"]) 116 117 def test_txxx(self): 118 res, out = self.call(fsn(u"--TXXX"), fsn(u"A\\:B:C"), self.filename) 119 self.failUnlessEqual((res, out), (0, "")) 120 121 f = ID3(self.filename) 122 frame = f.getall("TXXX")[0] 123 self.failUnlessEqual(frame.desc, "A\\") 124 self.failUnlessEqual(frame.text, ["B:C"]) 125 126 def test_txxx_multiple(self): 127 res, out = self.call( 128 fsn(u"--TXXX"), fsn(u"A:B"), 129 fsn(u"--TXXX"), fsn(u"C:D"), 130 self.filename) 131 self.failUnlessEqual((res, out), (0, "")) 132 f = ID3(self.filename) 133 assert len(f.getall("TXXX")) == 2 134 135 def test_wcom(self): 136 res, out = self.call(fsn(u"--WCOM"), fsn(u"foo"), self.filename) 137 self.failUnlessEqual((res, out), (0, "")) 138 f = ID3(self.filename) 139 frames = f.getall("WCOM") 140 assert len(frames) == 1 141 assert frames[0].url == "foo" 142 143 def test_wcom_multiple(self): 144 res, out = self.call( 145 fsn(u"--WCOM"), fsn(u"foo"), 146 fsn(u"--WCOM"), fsn(u"bar"), 147 self.filename) 148 self.failUnlessEqual((res, out), (0, "")) 149 f = ID3(self.filename) 150 frames = f.getall("WCOM") 151 assert len(frames) == 1 152 assert frames[0].url == "bar" 153 154 def test_wxxx(self): 155 res, out = self.call(fsn(u"--WXXX"), fsn(u"foobar"), self.filename) 156 self.failUnlessEqual((res, out), (0, "")) 157 f = ID3(self.filename) 158 frames = f.getall("WXXX") 159 assert len(frames) == 1 160 assert frames[0].url == "foobar" 161 162 def test_wxxx_escape(self): 163 res, out = self.call( 164 fsn(u"-e"), fsn(u"--WXXX"), fsn(u"http\\://example.com/"), 165 self.filename) 166 self.failUnlessEqual((res, out), (0, "")) 167 f = ID3(self.filename) 168 frames = f.getall("WXXX") 169 assert frames[0].url == "http://example.com/" 170 171 def test_wxxx_multiple(self): 172 res, out = self.call( 173 fsn(u"--WXXX"), fsn(u"A:B"), 174 fsn(u"--WXXX"), fsn(u"C:D"), 175 self.filename) 176 self.failUnlessEqual((res, out), (0, "")) 177 f = ID3(self.filename) 178 frames = sorted(f.getall("WXXX"), key=lambda f: f.HashKey) 179 assert len(frames) == 2 180 assert frames[0].url == "B" 181 assert frames[0].desc == "A" 182 assert frames[1].url == "D" 183 assert frames[1].desc == "C" 184 185 def test_ufid(self): 186 res, out, err = self.call2( 187 fsn(u"--UFID"), fsn(u"foo:bar"), self.filename) 188 self.assertEqual((res, out, err), (0, "", "")) 189 190 f = ID3(self.filename) 191 frame = f.getall("UFID:foo")[0] 192 self.assertEqual(frame.owner, u"foo") 193 self.assertEqual(frame.data, b"bar") 194 195 def test_comm1(self): 196 res, out = self.call(fsn(u"--COMM"), fsn(u"A"), self.filename) 197 self.failUnlessEqual((res, out), (0, "")) 198 199 f = ID3(self.filename) 200 frame = f.getall("COMM:")[0] 201 self.failUnlessEqual(frame.desc, "") 202 self.failUnlessEqual(frame.text, ["A"]) 203 204 def test_comm2(self): 205 res, out = self.call(fsn(u"--COMM"), fsn(u"Y:B"), self.filename) 206 self.failUnlessEqual((res, out), (0, "")) 207 208 f = ID3(self.filename) 209 frame = f.getall("COMM:Y")[0] 210 self.failUnlessEqual(frame.desc, "Y") 211 self.failUnlessEqual(frame.text, ["B"]) 212 213 def test_comm2_escape(self): 214 res, out = self.call( 215 fsn(u"-e"), fsn(u"--COMM"), fsn(u"Y\\\\:B\\nG"), self.filename) 216 self.failUnlessEqual((res, out), (0, "")) 217 218 f = ID3(self.filename) 219 frame = f.getall("COMM:")[0] 220 self.failUnlessEqual(frame.desc, "") 221 self.failUnlessEqual(frame.text, ["Y:B\nG"]) 222 223 def test_comm3(self): 224 res, out = self.call( 225 fsn(u"--COMM"), fsn(u"Z:B:C:D:ger"), self.filename) 226 self.failUnlessEqual((res, out), (0, "")) 227 228 f = ID3(self.filename) 229 frame = f.getall("COMM:Z")[0] 230 self.failUnlessEqual(frame.desc, "Z") 231 self.failUnlessEqual(frame.text, ["B:C:D"]) 232 self.failUnlessEqual(frame.lang, "ger") 233 234 def test_USLT(self): 235 res, out = self.call(fsn(u"--USLT"), fsn(u"Y:foo"), self.filename) 236 assert (res, out) == (0, "") 237 238 f = ID3(self.filename) 239 frame = f.getall("USLT:Y")[0] 240 assert frame.desc == "Y" 241 assert frame.text == "foo" 242 assert frame.lang == "eng" 243 244 res, out = self.call(fsn(u"--USLT"), fsn(u"Z:bar:ger"), self.filename) 245 assert (res, out) == (0, "") 246 247 f = ID3(self.filename) 248 frame = f.getall("USLT:Z")[0] 249 assert frame.desc == "Z" 250 assert frame.text == "bar" 251 assert frame.lang == "ger" 252 253 res, out = self.call(fsn(u"--USLT"), fsn(u"X"), self.filename) 254 assert (res, out) == (0, "") 255 256 f = ID3(self.filename) 257 frame = f.getall("USLT:")[0] 258 assert frame.desc == "" 259 assert frame.text == "X" 260 assert frame.lang == "eng" 261 262 def test_apic(self): 263 image_path = os.path.join(DATA_DIR, "image.jpg") 264 image_path = os.path.relpath(image_path) 265 res, out, err = self.call2( 266 fsn(u"--APIC"), image_path + fsn(u":fooAPIC:3:image/jpeg"), 267 self.filename) 268 self.failUnlessEqual((res, out, err), (0, "", "")) 269 270 with open(image_path, "rb") as h: 271 data = h.read() 272 273 f = ID3(self.filename) 274 frame = f.getall("APIC:fooAPIC")[0] 275 self.assertEqual(frame.desc, u"fooAPIC") 276 self.assertEqual(frame.mime, "image/jpeg") 277 self.assertEqual(frame.data, data) 278 279 res, out = self.call(fsn(u"--list"), self.filename) 280 self.assertEqual(res, 0) 281 self.assertTrue("fooAPIC" in out) 282 283 def test_encoding_with_escape(self): 284 is_bytes = PY2 and os.name != "nt" 285 286 text = u'\xe4\xf6\xfc' 287 if is_bytes: 288 enc = locale.getpreferredencoding() 289 # don't fail in case getpreferredencoding doesn't give us a unicode 290 # encoding. 291 text = text.encode(enc, "replace") 292 res, out = self.call(fsn(u"-e"), fsn(u"-a"), text, self.filename) 293 self.failUnlessEqual((res, out), (0, "")) 294 f = ID3(self.filename) 295 if is_bytes: 296 text = text.decode(enc) 297 self.assertEqual(f.getall("TPE1")[0], text) 298 299 def test_invalid_encoding_escaped(self): 300 res, out, err = self.call2( 301 fsn(u"--TALB"), fsn(u'\\xff\\x81'), fsn(u'-e'), self.filename) 302 self.failIfEqual(res, 0) 303 self.failUnless("TALB" in err) 304 305 def test_invalid_encoding(self): 306 if os.name == "nt": 307 return 308 309 value = b"\xff\xff\x81" 310 self.assertRaises(ValueError, value.decode, "utf-8") 311 self.assertRaises(ValueError, value.decode, "cp1252") 312 enc = locale.getpreferredencoding() 313 314 # we need the decoding to fail for this test to work... 315 try: 316 value.decode(enc) 317 except ValueError: 318 pass 319 else: 320 return 321 322 if not PY2: 323 value = value.decode(enc, "surrogateescape") 324 res, out, err = self.call2("--TALB", value, self.filename) 325 self.failIfEqual(res, 0) 326 self.failUnless("TALB" in err) 327 328 def test_invalid_escape(self): 329 res, out, err = self.call2( 330 fsn(u"--TALB"), fsn(u'\\xaz'), fsn(u'-e'), self.filename) 331 self.failIfEqual(res, 0) 332 self.failUnless("TALB" in err) 333 334 res, out, err = self.call2( 335 fsn(u"--TALB"), fsn(u'\\'), fsn(u'-e'), self.filename) 336 self.failIfEqual(res, 0) 337 self.failUnless("TALB" in err) 338 339 def test_value_from_fsnative(self): 340 vffs = self.get_var("value_from_fsnative") 341 self.assertEqual(vffs(fsn(u"öäü\\n"), True), u"öäü\n") 342 self.assertEqual(vffs(fsn(u"öäü\\n"), False), u"öäü\\n") 343 344 if os.name != "nt" and PY3: 345 se = b"\xff".decode("utf-8", "surrogateescape") 346 self.assertRaises(ValueError, vffs, se, False) 347 348 def test_frame_from_fsnative(self): 349 fffs = self.get_var("frame_from_fsnative") 350 self.assertTrue(isinstance(fffs(fsn(u"abc")), str)) 351 self.assertEqual(fffs(fsn(u"abc")), "abc") 352 self.assertRaises(ValueError, fffs, fsn(u"öäü")) 353