1 /*************************************************************************** 2 copyright : (C) 2009 by Lukas Lalinsky 3 email : lukas@oxygene.sk 4 ***************************************************************************/ 5 6 /*************************************************************************** 7 * This library is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU Lesser General Public License version * 9 * 2.1 as published by the Free Software Foundation. * 10 * * 11 * This library is distributed in the hope that it will be useful, but * 12 * WITHOUT ANY WARRANTY; without even the implied warranty of * 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 14 * Lesser General Public License for more details. * 15 * * 16 * You should have received a copy of the GNU Lesser General Public * 17 * License along with this library; if not, write to the Free Software * 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 19 * 02110-1301 USA * 20 * * 21 * Alternatively, this file is available under the Mozilla Public * 22 * License Version 1.1. You may obtain a copy of the License at * 23 * http://www.mozilla.org/MPL/ * 24 ***************************************************************************/ 25 26 #include <string> 27 #include <stdio.h> 28 #include <tag.h> 29 #include <tstringlist.h> 30 #include <tbytevectorlist.h> 31 #include <tpropertymap.h> 32 #include <flacfile.h> 33 #include <xiphcomment.h> 34 #include <id3v1tag.h> 35 #include <id3v2tag.h> 36 #include <cppunit/extensions/HelperMacros.h> 37 #include "utils.h" 38 39 using namespace std; 40 using namespace TagLib; 41 42 class TestFLAC : public CppUnit::TestFixture 43 { 44 CPPUNIT_TEST_SUITE(TestFLAC); 45 CPPUNIT_TEST(testSignature); 46 CPPUNIT_TEST(testMultipleCommentBlocks); 47 CPPUNIT_TEST(testReadPicture); 48 CPPUNIT_TEST(testAddPicture); 49 CPPUNIT_TEST(testReplacePicture); 50 CPPUNIT_TEST(testRemoveAllPictures); 51 CPPUNIT_TEST(testRepeatedSave1); 52 CPPUNIT_TEST(testRepeatedSave2); 53 CPPUNIT_TEST(testRepeatedSave3); 54 CPPUNIT_TEST(testSaveMultipleValues); 55 CPPUNIT_TEST(testDict); 56 CPPUNIT_TEST(testInvalid); 57 CPPUNIT_TEST(testAudioProperties); 58 CPPUNIT_TEST(testZeroSizedPadding1); 59 CPPUNIT_TEST(testZeroSizedPadding2); 60 CPPUNIT_TEST(testShrinkPadding); 61 CPPUNIT_TEST(testSaveID3v1); 62 CPPUNIT_TEST(testUpdateID3v2); 63 CPPUNIT_TEST(testEmptyID3v2); 64 CPPUNIT_TEST(testStripTags); 65 CPPUNIT_TEST(testRemoveXiphField); 66 CPPUNIT_TEST(testEmptySeekTable); 67 CPPUNIT_TEST_SUITE_END(); 68 69 public: 70 testSignature()71 void testSignature() 72 { 73 FLAC::File f(TEST_FILE_PATH_C("no-tags.flac")); 74 CPPUNIT_ASSERT_EQUAL(ByteVector("a1b141f766e9849ac3db1030a20a3c77"), f.audioProperties()->signature().toHex()); 75 } 76 testMultipleCommentBlocks()77 void testMultipleCommentBlocks() 78 { 79 ScopedFileCopy copy("multiple-vc", ".flac"); 80 string newname = copy.fileName(); 81 82 { 83 FLAC::File f(newname.c_str()); 84 CPPUNIT_ASSERT_EQUAL(String("Artist 1"), f.tag()->artist()); 85 f.tag()->setArtist("The Artist"); 86 f.save(); 87 } 88 { 89 FLAC::File f(newname.c_str()); 90 CPPUNIT_ASSERT_EQUAL(String("The Artist"), f.tag()->artist()); 91 CPPUNIT_ASSERT_EQUAL(69L, f.find("Artist")); 92 CPPUNIT_ASSERT_EQUAL(-1L, f.find("Artist", 70)); 93 } 94 } 95 testReadPicture()96 void testReadPicture() 97 { 98 ScopedFileCopy copy("silence-44-s", ".flac"); 99 string newname = copy.fileName(); 100 101 FLAC::File f(newname.c_str()); 102 List<FLAC::Picture *> lst = f.pictureList(); 103 CPPUNIT_ASSERT_EQUAL((unsigned int)1, lst.size()); 104 105 FLAC::Picture *pic = lst.front(); 106 CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); 107 CPPUNIT_ASSERT_EQUAL(1, pic->width()); 108 CPPUNIT_ASSERT_EQUAL(1, pic->height()); 109 CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); 110 CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); 111 CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); 112 CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); 113 CPPUNIT_ASSERT_EQUAL((unsigned int)150, pic->data().size()); 114 } 115 testAddPicture()116 void testAddPicture() 117 { 118 ScopedFileCopy copy("silence-44-s", ".flac"); 119 string newname = copy.fileName(); 120 121 { 122 FLAC::File f(newname.c_str()); 123 List<FLAC::Picture *> lst = f.pictureList(); 124 CPPUNIT_ASSERT_EQUAL((unsigned int)1, lst.size()); 125 126 FLAC::Picture *newpic = new FLAC::Picture(); 127 newpic->setType(FLAC::Picture::BackCover); 128 newpic->setWidth(5); 129 newpic->setHeight(6); 130 newpic->setColorDepth(16); 131 newpic->setNumColors(7); 132 newpic->setMimeType("image/jpeg"); 133 newpic->setDescription("new image"); 134 newpic->setData("JPEG data"); 135 f.addPicture(newpic); 136 f.save(); 137 } 138 { 139 FLAC::File f(newname.c_str()); 140 List<FLAC::Picture *> lst = f.pictureList(); 141 CPPUNIT_ASSERT_EQUAL((unsigned int)2, lst.size()); 142 143 FLAC::Picture *pic = lst[0]; 144 CPPUNIT_ASSERT_EQUAL(FLAC::Picture::FrontCover, pic->type()); 145 CPPUNIT_ASSERT_EQUAL(1, pic->width()); 146 CPPUNIT_ASSERT_EQUAL(1, pic->height()); 147 CPPUNIT_ASSERT_EQUAL(24, pic->colorDepth()); 148 CPPUNIT_ASSERT_EQUAL(0, pic->numColors()); 149 CPPUNIT_ASSERT_EQUAL(String("image/png"), pic->mimeType()); 150 CPPUNIT_ASSERT_EQUAL(String("A pixel."), pic->description()); 151 CPPUNIT_ASSERT_EQUAL((unsigned int)150, pic->data().size()); 152 153 pic = lst[1]; 154 CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); 155 CPPUNIT_ASSERT_EQUAL(5, pic->width()); 156 CPPUNIT_ASSERT_EQUAL(6, pic->height()); 157 CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); 158 CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); 159 CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); 160 CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); 161 CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); 162 } 163 } 164 testReplacePicture()165 void testReplacePicture() 166 { 167 ScopedFileCopy copy("silence-44-s", ".flac"); 168 string newname = copy.fileName(); 169 170 { 171 FLAC::File f(newname.c_str()); 172 List<FLAC::Picture *> lst = f.pictureList(); 173 CPPUNIT_ASSERT_EQUAL((unsigned int)1, lst.size()); 174 175 FLAC::Picture *newpic = new FLAC::Picture(); 176 newpic->setType(FLAC::Picture::BackCover); 177 newpic->setWidth(5); 178 newpic->setHeight(6); 179 newpic->setColorDepth(16); 180 newpic->setNumColors(7); 181 newpic->setMimeType("image/jpeg"); 182 newpic->setDescription("new image"); 183 newpic->setData("JPEG data"); 184 f.removePictures(); 185 f.addPicture(newpic); 186 f.save(); 187 } 188 { 189 FLAC::File f(newname.c_str()); 190 List<FLAC::Picture *> lst = f.pictureList(); 191 CPPUNIT_ASSERT_EQUAL((unsigned int)1, lst.size()); 192 193 FLAC::Picture *pic = lst[0]; 194 CPPUNIT_ASSERT_EQUAL(FLAC::Picture::BackCover, pic->type()); 195 CPPUNIT_ASSERT_EQUAL(5, pic->width()); 196 CPPUNIT_ASSERT_EQUAL(6, pic->height()); 197 CPPUNIT_ASSERT_EQUAL(16, pic->colorDepth()); 198 CPPUNIT_ASSERT_EQUAL(7, pic->numColors()); 199 CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType()); 200 CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description()); 201 CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data()); 202 } 203 } 204 testRemoveAllPictures()205 void testRemoveAllPictures() 206 { 207 ScopedFileCopy copy("silence-44-s", ".flac"); 208 string newname = copy.fileName(); 209 210 { 211 FLAC::File f(newname.c_str()); 212 List<FLAC::Picture *> lst = f.pictureList(); 213 CPPUNIT_ASSERT_EQUAL((unsigned int)1, lst.size()); 214 215 f.removePictures(); 216 f.save(); 217 } 218 { 219 FLAC::File f(newname.c_str()); 220 List<FLAC::Picture *> lst = f.pictureList(); 221 CPPUNIT_ASSERT_EQUAL((unsigned int)0, lst.size()); 222 } 223 } 224 testRepeatedSave1()225 void testRepeatedSave1() 226 { 227 ScopedFileCopy copy("silence-44-s", ".flac"); 228 string newname = copy.fileName(); 229 230 { 231 FLAC::File f(newname.c_str()); 232 CPPUNIT_ASSERT_EQUAL(String("Silence"), f.tag()->title()); 233 f.tag()->setTitle("NEW TITLE"); 234 f.save(); 235 CPPUNIT_ASSERT_EQUAL(String("NEW TITLE"), f.tag()->title()); 236 f.tag()->setTitle("NEW TITLE 2"); 237 f.save(); 238 CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); 239 } 240 { 241 FLAC::File f(newname.c_str()); 242 CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title()); 243 } 244 } 245 testRepeatedSave2()246 void testRepeatedSave2() 247 { 248 ScopedFileCopy copy("no-tags", ".flac"); 249 250 FLAC::File f(copy.fileName().c_str()); 251 f.ID3v2Tag(true)->setTitle("0123456789"); 252 f.save(); 253 CPPUNIT_ASSERT_EQUAL(5735L, f.length()); 254 f.save(); 255 CPPUNIT_ASSERT_EQUAL(5735L, f.length()); 256 CPPUNIT_ASSERT(f.find("fLaC") >= 0); 257 } 258 testRepeatedSave3()259 void testRepeatedSave3() 260 { 261 ScopedFileCopy copy("no-tags", ".flac"); 262 263 FLAC::File f(copy.fileName().c_str()); 264 f.xiphComment()->setTitle(longText(8 * 1024)); 265 f.save(); 266 CPPUNIT_ASSERT_EQUAL(12862L, f.length()); 267 f.save(); 268 CPPUNIT_ASSERT_EQUAL(12862L, f.length()); 269 } 270 testSaveMultipleValues()271 void testSaveMultipleValues() 272 { 273 ScopedFileCopy copy("silence-44-s", ".flac"); 274 string newname = copy.fileName(); 275 276 { 277 FLAC::File f(newname.c_str()); 278 f.xiphComment(true)->addField("ARTIST", "artist 1", true); 279 f.xiphComment(true)->addField("ARTIST", "artist 2", false); 280 f.save(); 281 } 282 { 283 FLAC::File f(newname.c_str()); 284 Ogg::FieldListMap m = f.xiphComment()->fieldListMap(); 285 CPPUNIT_ASSERT_EQUAL((unsigned int)2, m["ARTIST"].size()); 286 CPPUNIT_ASSERT_EQUAL(String("artist 1"), m["ARTIST"][0]); 287 CPPUNIT_ASSERT_EQUAL(String("artist 2"), m["ARTIST"][1]); 288 } 289 } 290 testDict()291 void testDict() 292 { 293 // test unicode & multiple values with dict interface 294 ScopedFileCopy copy("silence-44-s", ".flac"); 295 string newname = copy.fileName(); 296 297 { 298 FLAC::File f(newname.c_str()); 299 PropertyMap dict; 300 dict["ARTIST"].append("artøst 1"); 301 dict["ARTIST"].append("artöst 2"); 302 f.setProperties(dict); 303 f.save(); 304 } 305 { 306 FLAC::File f(newname.c_str()); 307 PropertyMap dict = f.properties(); 308 CPPUNIT_ASSERT_EQUAL((unsigned int)2, dict["ARTIST"].size()); 309 CPPUNIT_ASSERT_EQUAL(String("artøst 1"), dict["ARTIST"][0]); 310 CPPUNIT_ASSERT_EQUAL(String("artöst 2"), dict["ARTIST"][1]); 311 } 312 } 313 testInvalid()314 void testInvalid() 315 { 316 ScopedFileCopy copy("silence-44-s", ".flac"); 317 PropertyMap map; 318 map[L"H\x00c4\x00d6"] = String("bla"); 319 FLAC::File f(copy.fileName().c_str()); 320 PropertyMap invalid = f.setProperties(map); 321 CPPUNIT_ASSERT_EQUAL((unsigned int)1, invalid.size()); 322 CPPUNIT_ASSERT_EQUAL((unsigned int)0, f.properties().size()); 323 } 324 testAudioProperties()325 void testAudioProperties() 326 { 327 FLAC::File f(TEST_FILE_PATH_C("sinewave.flac")); 328 CPPUNIT_ASSERT(f.audioProperties()); 329 CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); 330 CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds()); 331 CPPUNIT_ASSERT_EQUAL(145, f.audioProperties()->bitrate()); 332 CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); 333 CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); 334 CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); 335 CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames()); 336 CPPUNIT_ASSERT_EQUAL( 337 ByteVector("\xcf\xe3\xd9\xda\xba\xde\xab\x2c\xbf\x2c\xa2\x35\x27\x4b\x7f\x76"), 338 f.audioProperties()->signature()); 339 } 340 testZeroSizedPadding1()341 void testZeroSizedPadding1() 342 { 343 ScopedFileCopy copy("zero-sized-padding", ".flac"); 344 345 FLAC::File f(copy.fileName().c_str()); 346 CPPUNIT_ASSERT(f.isValid()); 347 } 348 testZeroSizedPadding2()349 void testZeroSizedPadding2() 350 { 351 ScopedFileCopy copy("silence-44-s", ".flac"); 352 353 { 354 FLAC::File f(copy.fileName().c_str()); 355 f.xiphComment()->setTitle("ABC"); 356 f.save(); 357 } 358 { 359 FLAC::File f(copy.fileName().c_str()); 360 f.xiphComment()->setTitle(std::string(3067, 'X').c_str()); 361 f.save(); 362 } 363 { 364 FLAC::File f(copy.fileName().c_str()); 365 CPPUNIT_ASSERT(f.isValid()); 366 } 367 } 368 testShrinkPadding()369 void testShrinkPadding() 370 { 371 ScopedFileCopy copy("no-tags", ".flac"); 372 373 { 374 FLAC::File f(copy.fileName().c_str()); 375 f.xiphComment()->setTitle(longText(128 * 1024)); 376 f.save(); 377 CPPUNIT_ASSERT(f.length() > 128 * 1024); 378 } 379 { 380 FLAC::File f(copy.fileName().c_str()); 381 f.xiphComment()->setTitle("0123456789"); 382 f.save(); 383 CPPUNIT_ASSERT(f.length() < 8 * 1024); 384 } 385 } 386 testSaveID3v1()387 void testSaveID3v1() 388 { 389 ScopedFileCopy copy("no-tags", ".flac"); 390 391 ByteVector audioStream; 392 { 393 FLAC::File f(copy.fileName().c_str()); 394 CPPUNIT_ASSERT(!f.hasID3v1Tag()); 395 CPPUNIT_ASSERT_EQUAL((long)4692, f.length()); 396 397 f.seek(0x0100); 398 audioStream = f.readBlock(4436); 399 400 f.ID3v1Tag(true)->setTitle("01234 56789 ABCDE FGHIJ"); 401 f.save(); 402 CPPUNIT_ASSERT(f.hasID3v1Tag()); 403 CPPUNIT_ASSERT_EQUAL((long)4820, f.length()); 404 405 f.seek(0x0100); 406 CPPUNIT_ASSERT_EQUAL(audioStream, f.readBlock(4436)); 407 } 408 } 409 testUpdateID3v2()410 void testUpdateID3v2() 411 { 412 ScopedFileCopy copy("no-tags", ".flac"); 413 414 { 415 FLAC::File f(copy.fileName().c_str()); 416 f.ID3v2Tag(true)->setTitle("0123456789"); 417 f.save(); 418 } 419 { 420 FLAC::File f(copy.fileName().c_str()); 421 f.ID3v2Tag()->setTitle("ABCDEFGHIJ"); 422 f.save(); 423 } 424 { 425 FLAC::File f(copy.fileName().c_str()); 426 CPPUNIT_ASSERT_EQUAL(String("ABCDEFGHIJ"), f.ID3v2Tag()->title()); 427 } 428 } 429 testEmptyID3v2()430 void testEmptyID3v2() 431 { 432 ScopedFileCopy copy("no-tags", ".flac"); 433 434 { 435 FLAC::File f(copy.fileName().c_str()); 436 f.ID3v2Tag(true); 437 f.save(); 438 } 439 { 440 FLAC::File f(copy.fileName().c_str()); 441 CPPUNIT_ASSERT(!f.hasID3v2Tag()); 442 } 443 } 444 testStripTags()445 void testStripTags() 446 { 447 ScopedFileCopy copy("silence-44-s", ".flac"); 448 449 { 450 FLAC::File f(copy.fileName().c_str()); 451 f.xiphComment(true)->setTitle("XiphComment Title"); 452 f.ID3v1Tag(true)->setTitle("ID3v1 Title"); 453 f.ID3v2Tag(true)->setTitle("ID3v2 Title"); 454 f.save(); 455 } 456 { 457 FLAC::File f(copy.fileName().c_str()); 458 CPPUNIT_ASSERT(f.hasXiphComment()); 459 CPPUNIT_ASSERT(f.hasID3v1Tag()); 460 CPPUNIT_ASSERT(f.hasID3v2Tag()); 461 CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); 462 CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); 463 CPPUNIT_ASSERT_EQUAL(String("ID3v2 Title"), f.ID3v2Tag()->title()); 464 f.strip(FLAC::File::ID3v2); 465 f.save(); 466 } 467 { 468 FLAC::File f(copy.fileName().c_str()); 469 CPPUNIT_ASSERT(f.hasXiphComment()); 470 CPPUNIT_ASSERT(f.hasID3v1Tag()); 471 CPPUNIT_ASSERT(!f.hasID3v2Tag()); 472 CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); 473 CPPUNIT_ASSERT_EQUAL(String("ID3v1 Title"), f.ID3v1Tag()->title()); 474 f.strip(FLAC::File::ID3v1); 475 f.save(); 476 } 477 { 478 FLAC::File f(copy.fileName().c_str()); 479 CPPUNIT_ASSERT(f.hasXiphComment()); 480 CPPUNIT_ASSERT(!f.hasID3v1Tag()); 481 CPPUNIT_ASSERT(!f.hasID3v2Tag()); 482 CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); 483 f.strip(FLAC::File::XiphComment); 484 f.save(); 485 } 486 { 487 FLAC::File f(copy.fileName().c_str()); 488 CPPUNIT_ASSERT(f.hasXiphComment()); 489 CPPUNIT_ASSERT(!f.hasID3v1Tag()); 490 CPPUNIT_ASSERT(!f.hasID3v2Tag()); 491 CPPUNIT_ASSERT(f.xiphComment()->isEmpty()); 492 CPPUNIT_ASSERT_EQUAL(String("reference libFLAC 1.1.0 20030126"), f.xiphComment()->vendorID()); 493 } 494 } 495 testRemoveXiphField()496 void testRemoveXiphField() 497 { 498 ScopedFileCopy copy("silence-44-s", ".flac"); 499 500 { 501 FLAC::File f(copy.fileName().c_str()); 502 f.xiphComment(true)->setTitle("XiphComment Title"); 503 f.ID3v2Tag(true)->setTitle("ID3v2 Title"); 504 f.save(); 505 } 506 { 507 FLAC::File f(copy.fileName().c_str()); 508 CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title()); 509 f.xiphComment()->removeFields("TITLE"); 510 f.save(); 511 } 512 { 513 FLAC::File f(copy.fileName().c_str()); 514 CPPUNIT_ASSERT_EQUAL(String(), f.xiphComment()->title()); 515 } 516 } 517 testEmptySeekTable()518 void testEmptySeekTable() 519 { 520 ScopedFileCopy copy("empty-seektable", ".flac"); 521 { 522 FLAC::File f(copy.fileName().c_str()); 523 CPPUNIT_ASSERT(f.isValid()); 524 f.xiphComment(true)->setTitle("XiphComment Title"); 525 f.save(); 526 } 527 { 528 FLAC::File f(copy.fileName().c_str()); 529 CPPUNIT_ASSERT(f.isValid()); 530 f.seek(42); 531 const ByteVector data = f.readBlock(4); 532 CPPUNIT_ASSERT_EQUAL(ByteVector("\x03\x00\x00\x00", 4), data); 533 } 534 } 535 536 }; 537 538 CPPUNIT_TEST_SUITE_REGISTRATION(TestFLAC); 539