From 9c8c215c30aa109737bdd6d94afd9be40055015b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 26 Aug 2012 10:12:40 +0900 Subject: [PATCH] Support INFO tags of RIFF wave files. --- taglib/riff/rifffile.cpp | 77 +++++++---- taglib/riff/rifffile.h | 27 +++- taglib/riff/wav/infotag.cpp | 250 ++++++++++++++++++++++++++++++++++++ taglib/riff/wav/infotag.h | 179 ++++++++++++++++++++++++++ taglib/riff/wav/wavfile.cpp | 116 +++++++++++++---- taglib/riff/wav/wavfile.h | 27 +++- tests/test_fileref.cpp | 8 +- tests/test_info.cpp | 45 +++++++ 8 files changed, 682 insertions(+), 47 deletions(-) create mode 100644 taglib/riff/wav/infotag.cpp create mode 100644 taglib/riff/wav/infotag.h create mode 100644 tests/test_info.cpp diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index df3b3663..22fd3184 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -138,34 +138,44 @@ ByteVector RIFF::File::chunkData(uint i) return readBlock(d->chunks[i].size); } -void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) +void RIFF::File::setChunkData(uint i, const ByteVector &data) +{ + // First we update the global size + + d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); + insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); + + // Now update the specific chunk + + writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8); + + d->chunks[i].size = data.size(); + d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; + + // Now update the internal offsets + + for(i++; i < d->chunks.size(); i++) + d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding; +} + +void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { if(d->chunks.size() == 0) { debug("RIFF::File::setChunkData - No valid chunks found."); return; } - for(uint i = 0; i < d->chunks.size(); i++) { - if(d->chunks[i].name == name) { + if(alwaysCreate && name != "LIST") { + debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks."); + return; + } - // First we update the global size - - d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); - insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); - - // Now update the specific chunk - - writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8); - - d->chunks[i].size = data.size(); - d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; - - // Now update the internal offsets - - for(i++; i < d->chunks.size(); i++) - d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding; - - return; + if(!alwaysCreate) { + for(uint i = 0; i < d->chunks.size(); i++) { + if(d->chunks[i].name == name) { + setChunkData(i, data); + return; + } } } @@ -181,7 +191,8 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) // Now add the chunk to the file - writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0); + long len = length(); + writeChunk(name, data, offset, std::max(0, length() - offset), (offset & 1) ? 1 : 0); // And update our internal structure @@ -199,6 +210,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) d->chunks.push_back(chunk); } +void RIFF::File::removeChunk(uint i) +{ + if(i >= d->chunks.size()) + return; + + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + d->chunks.erase(d->chunks.begin() + i); +} + +void RIFF::File::removeChunk(const ByteVector &name) +{ + std::vector newChunks; + for(size_t i = 0; i < d->chunks.size(); ++i) { + if(d->chunks[i].name == name) + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + else + newChunks.push_back(d->chunks[i]); + } + + d->chunks.swap(newChunks); +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/riff/rifffile.h b/taglib/riff/rifffile.h index e418dbb6..274549ae 100644 --- a/taglib/riff/rifffile.h +++ b/taglib/riff/rifffile.h @@ -95,14 +95,39 @@ namespace TagLib { */ ByteVector chunkData(uint i); + /*! + * Sets the data for the the specified chunk to \a data. + * + * \warning This will update the file immediately. + */ + void setChunkData(uint i, const ByteVector &data); + /*! * Sets the data for the chunk \a name to \a data. If a chunk with the * given name already exists it will be overwritten, otherwise it will be * created after the existing chunks. * + * \note If \a alwaysCreate is true, a new chunk is created regardless of + * existence of chunk \a name. It should be used for only "LIST" chunks. + * * \warning This will update the file immediately. */ - void setChunkData(const ByteVector &name, const ByteVector &data); + void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate = false); + + /*! + * Removes the specified chunk. + * + * \warning This will update the file immediately. + */ + void removeChunk(uint i); + + /*! + * Removes the chunk \a name. + * + * \warning This will update the file immediately. + * \warning This removes all the chunks with the given name. + */ + void removeChunk(const ByteVector &name); private: File(const File &); diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp new file mode 100644 index 00000000..e3587775 --- /dev/null +++ b/taglib/riff/wav/infotag.cpp @@ -0,0 +1,250 @@ +/*************************************************************************** + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include +#include + +#include "infotag.h" + +using namespace TagLib; +using namespace RIFF::Info; + +namespace { + static bool isValidChunkID(const ByteVector &name) + { + if(name.size() != 4) + return false; + + for(int i = 0; i < 4; i++) { + if(name[i] < 32 || name[i] > 127) + return false; + } + + return true; + } +} + +class RIFF::Info::Tag::TagPrivate +{ +public: + TagPrivate() + {} + + FieldListMap fieldListMap; + + static const StringHandler *stringHandler; +}; + + + +StringHandler::StringHandler() +{ +} + +StringHandler::~StringHandler() +{ +} + +String RIFF::Info::StringHandler::parse(const ByteVector &data) const +{ + return String(data, String::UTF8); +} + +ByteVector RIFF::Info::StringHandler::render(const String &s) const +{ + return s.data(String::UTF8); +} + + + +static const StringHandler defaultStringHandler; +const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler; + +RIFF::Info::Tag::Tag(const ByteVector &data) : TagLib::Tag() +{ + d = new TagPrivate; + parse(data); +} + +RIFF::Info::Tag::Tag() : TagLib::Tag() +{ + d = new TagPrivate; +} + +RIFF::Info::Tag::~Tag() +{ +} + +String RIFF::Info::Tag::title() const +{ + return fieldText("INAM"); +} + +String RIFF::Info::Tag::artist() const +{ + return fieldText("IART"); +} + +String RIFF::Info::Tag::album() const +{ + return fieldText("IPRD"); +} + +String RIFF::Info::Tag::comment() const +{ + return fieldText("ICMT"); +} + +String RIFF::Info::Tag::genre() const +{ + return fieldText("IGNR"); +} + +TagLib::uint RIFF::Info::Tag::year() const +{ + return fieldText("ICRD").substr(0, 4).toInt(); +} + +TagLib::uint RIFF::Info::Tag::track() const +{ + return fieldText("IPRT").toInt(); +} + +void RIFF::Info::Tag::setTitle(const String &s) +{ + setFieldText("INAM", s); +} + +void RIFF::Info::Tag::setArtist(const String &s) +{ + setFieldText("IART", s); +} + +void RIFF::Info::Tag::setAlbum(const String &s) +{ + setFieldText("IPRD", s); +} + +void RIFF::Info::Tag::setComment(const String &s) +{ + setFieldText("ICMT", s); +} + +void RIFF::Info::Tag::setGenre(const String &s) +{ + setFieldText("IGNR", s); +} + +void RIFF::Info::Tag::setYear(uint i) +{ + if(i != 0) + setFieldText("ICRD", String::number(i)); + else + d->fieldListMap.erase("ICRD"); +} + +void RIFF::Info::Tag::setTrack(uint i) +{ + if(i != 0) + setFieldText("IPRT", String::number(i)); + else + d->fieldListMap.erase("IPRT"); +} + +bool RIFF::Info::Tag::isEmpty() const +{ + return d->fieldListMap.isEmpty(); +} + +String RIFF::Info::Tag::fieldText(const ByteVector &id) const +{ + if(d->fieldListMap.contains(id)) + return String(d->fieldListMap[id]); + else + return String(); +} + +void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) +{ + // id must be four-byte long pure ascii string. + if(!isValidChunkID(id)) + return; + + if(!s.isEmpty()) + d->fieldListMap[id] = s; + else + removeField(id); +} + +void RIFF::Info::Tag::removeField(const ByteVector &id) +{ + if(d->fieldListMap.contains(id)) + d->fieldListMap.erase(id); +} + +ByteVector RIFF::Info::Tag::render() const +{ + ByteVector data("INFO"); + + FieldListMap::ConstIterator it = d->fieldListMap.begin(); + for(; it != d->fieldListMap.end(); ++it) { + ByteVector text = TagPrivate::stringHandler->render(it->second); + if(text.isEmpty()) + continue; + + data.append(it->first); + data.append(ByteVector::fromUInt(text.size() + 1, false)); + data.append(text); + + do { + data.append('\0'); + } while(data.size() & 1); + } + + if(data.size() == 4) + return ByteVector(); + else + return data; +} + +void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) +{ + if(handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; +} + +void RIFF::Info::Tag::parse(const ByteVector &data) +{ + uint p = 4; + while(p < data.size()) { + uint size = data.mid(p + 4, 4).toUInt(false); + d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + + p += ((size + 1) & ~1) + 8; + } +} + diff --git a/taglib/riff/wav/infotag.h b/taglib/riff/wav/infotag.h new file mode 100644 index 00000000..72414841 --- /dev/null +++ b/taglib/riff/wav/infotag.h @@ -0,0 +1,179 @@ +/*************************************************************************** + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_INFOTAG_H +#define TAGLIB_INFOTAG_H + +#include "tag.h" +#include "tmap.h" +#include "tstring.h" +#include "tstringlist.h" +#include "tbytevector.h" +#include "taglib_export.h" + +namespace TagLib { + + class File; + + //! A RIFF Info tag implementation. + namespace RIFF { + namespace Info { + + typedef Map FieldListMap; + + //! A abstraction for the string to data encoding in Info tags. + + /*! + * RIFF Info tag has no clear definitions about character encodings. + * In practice, local encoding of each system is largely used and UTF-8 is + * popular too. + * + * Here is an option to read and write tags in your preferrd encoding + * by subclassing this class, reimplementing parse() and render() and setting + * your reimplementation as the default with Info::Tag::setStringHandler(). + * + * \see ID3v1::Tag::setStringHandler() + */ + + class TAGLIB_EXPORT StringHandler + { + public: + StringHandler(); + ~StringHandler(); + + /*! + * Decode a string from \a data. The default implementation assumes that + * \a data is an UTF-8 character array. + */ + virtual String parse(const ByteVector &data) const; + + /*! + * Encode a ByteVector with the data from \a s. The default implementation + * assumes that \a s is an UTF-8 string. + */ + virtual ByteVector render(const String &s) const; + }; + + //! The main class in the ID3v2 implementation + + /*! + * This is the main class in the INFO tag implementation. RIFF INFO tag is a + * metadata format found in WAV audio and AVI video files. Though it is a part + * of Microsoft/IBM's RIFF specification, the author could not find the official + * documents about it. So, this implementation is refering to unofficial documents + * online and some applications' behaviors especially Windows Explorer. + */ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: + /*! + * Constructs an empty Info tag. + */ + Tag(); + + /*! + * Constructs an Info tag read from \a data which is contents of "LIST" chunk. + */ + Tag(const ByteVector &data); + + virtual ~Tag(); + + // Reimplementations + + virtual String title() const; + virtual String artist() const; + virtual String album() const; + virtual String comment() const; + virtual String genre() const; + virtual uint year() const; + virtual uint track() const; + + virtual void setTitle(const String &s); + virtual void setArtist(const String &s); + virtual void setAlbum(const String &s); + virtual void setComment(const String &s); + virtual void setGenre(const String &s); + virtual void setYear(uint i); + virtual void setTrack(uint i); + + virtual bool isEmpty() const; + /* + * Gets the value of the field with the ID \a id. + */ + String fieldText(const ByteVector &id) const; + + /* + * Sets the value of the field with the ID \a id to \a s. + * If the field does not exist, it is created. + * If \s is empty, the field is removed. + * + * \note fieldId must be four-byte long pure ascii string. This function + * performs nothing if fieldId is invalid. + */ + void setFieldText(const ByteVector &id, const String &s); + + /* + * Removes the field with the ID \a id. + */ + void removeField(const ByteVector &id); + + /*! + * Render the tag back to binary data, suitable to be written to disk. + * + * \note Returns empty ByteVector is the tag contains no fields. + */ + ByteVector render() const; + + /*! + * Sets the string handler that decides how the text data will be + * converted to and from binary data. + * If the parameter \a handler is null, the previous handler is + * released and default UTF-8 handler is restored. + * + * \note The caller is responsible for deleting the previous handler + * as needed after it is released. + * + * \see StringHandler + */ + static void setStringHandler(const StringHandler *handler); + + protected: + /*! + * Pareses the body of the tag in \a data. + */ + void parse(const ByteVector &data); + + + private: + Tag(const Tag &); + Tag &operator=(const Tag &); + + class TagPrivate; + TagPrivate *d; + }; + }} +} + +#endif diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index 613db4ef..dd7da2c8 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -23,36 +23,42 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include -#include -#include -#include -#include +#include "tbytevector.h" +#include "tdebug.h" +#include "tstringlist.h" +#include "tpropertymap.h" #include "wavfile.h" +#include "id3v2tag.h" +#include "infotag.h" +#include "tagunion.h" using namespace TagLib; +namespace +{ + enum { ID3v2Index = 0, InfoIndex = 1 }; +} + class RIFF::WAV::File::FilePrivate { public: FilePrivate() : properties(0), - tag(0), tagChunkID("ID3 ") { - } ~FilePrivate() { delete properties; - delete tag; } Properties *properties; - ID3v2::Tag *tag; + ByteVector tagChunkID; + + TagUnion tag; }; //////////////////////////////////////////////////////////////////////////////// @@ -80,28 +86,42 @@ RIFF::WAV::File::~File() delete d; } -ID3v2::Tag *RIFF::WAV::File::tag() const +Tag *RIFF::WAV::File::tag() const { - return d->tag; + return &d->tag; +} + +ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const +{ + return d->tag.access(ID3v2Index, false); +} + +RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const +{ + return d->tag.access(InfoIndex, false); } PropertyMap RIFF::WAV::File::properties() const { - return d->tag->properties(); + return d->tag.properties(); } PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties) { - return d->tag->setProperties(properties); + return d->tag.setProperties(properties); } - RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const { return d->properties; } bool RIFF::WAV::File::save() +{ + return RIFF::WAV::File::save(AllTags); +} + +bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version) { if(readOnly()) { debug("RIFF::WAV::File::save() -- File is read only."); @@ -113,7 +133,25 @@ bool RIFF::WAV::File::save() return false; } - setChunkData(d->tagChunkID, d->tag->render()); + if(stripOthers) + strip(static_cast(AllTags & ~tags)); + + ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); + if(!id3v2tag->isEmpty()) { + if(tags & ID3v2) + setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); + } + + Info::Tag *infotag = d->tag.access(InfoIndex, false); + if(!infotag->isEmpty()) { + if(tags & Info) { + int chunkId = findInfoTagChunk(); + if(chunkId != -1) + setChunkData(chunkId, infotag->render()); + else + setChunkData("LIST", infotag->render(), true); + } + } return true; } @@ -127,19 +165,53 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties ByteVector formatData; uint streamLength = 0; for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { + String name = chunkName(i); + if(name == "ID3 " || name == "id3 ") { d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); + d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); } - else if(chunkName(i) == "fmt " && readProperties) + else if(name == "fmt " && readProperties) formatData = chunkData(i); - else if(chunkName(i) == "data" && readProperties) + else if(name == "data" && readProperties) streamLength = chunkDataSize(i); + else if(name == "LIST") { + ByteVector data = chunkData(i); + ByteVector type = data.mid(0, 4); + + if(type == "INFO") + d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); + } } + if (!d->tag[ID3v2Index]) + d->tag.set(ID3v2Index, new ID3v2::Tag); + + if (!d->tag[InfoIndex]) + d->tag.set(InfoIndex, new RIFF::Info::Tag); + if(!formatData.isEmpty()) d->properties = new Properties(formatData, streamLength, propertiesStyle); - - if(!d->tag) - d->tag = new ID3v2::Tag; +} + +void RIFF::WAV::File::strip(TagTypes tags) +{ + if(tags & ID3v2) + removeChunk(d->tagChunkID); + + if(tags & Info){ + uint chunkId = findInfoTagChunk(); + if(chunkId != -1) + removeChunk(chunkId); + } +} + +uint RIFF::WAV::File::findInfoTagChunk() +{ + for(uint i = 0; i < chunkCount(); ++i) { + if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") { + return i; + } + } + + return -1; } diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index 1c470870..c0fa122e 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -28,6 +28,7 @@ #include "rifffile.h" #include "id3v2tag.h" +#include "infotag.h" #include "wavproperties.h" namespace TagLib { @@ -57,6 +58,17 @@ namespace TagLib { class TAGLIB_EXPORT File : public TagLib::RIFF::File { public: + enum TagTypes { + //! Empty set. Matches no tag types. + NoTags = 0x0000, + //! Matches ID3v2 tags. + ID3v2 = 0x0001, + //! Matches Info tags. + Info = 0x0002, + //! Matches all tag types. + AllTags = 0xffff + }; + /*! * Contructs an WAV file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If @@ -84,7 +96,11 @@ namespace TagLib { /*! * Returns the Tag for this file. */ - virtual ID3v2::Tag *tag() const; + virtual Tag *tag() const; + + ID3v2::Tag *ID3v2Tag() const; + + Info::Tag *InfoTag() const; /*! * Implements the unified property interface -- export function. @@ -109,12 +125,21 @@ namespace TagLib { */ virtual bool save(); + bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4); + private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void strip(TagTypes tags); + + /*! + * Returns the index of the chunk that its name is "LIST" and list type is "INFO". + */ + uint findInfoTagChunk(); + class FilePrivate; FilePrivate *d; }; diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index 3268fdb4..d0c8a3a5 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -29,6 +29,7 @@ class TestFileRef : public CppUnit::TestFixture CPPUNIT_TEST(testMP4_3); CPPUNIT_TEST(testTrueAudio); CPPUNIT_TEST(testAPE); + CPPUNIT_TEST(testWav); CPPUNIT_TEST_SUITE_END(); public: @@ -127,6 +128,11 @@ public: fileRefSave("no-tags", ".3g2"); } + void testWav() + { + fileRefSave("empty", ".wav"); + } + void testOGA_FLAC() { FileRef *f = new FileRef(TEST_FILE_PATH_C("empty_flac.oga")); @@ -143,7 +149,7 @@ public: void testAPE() { - fileRefSave("mac-399.ape", ".ape"); + fileRefSave("mac-399", ".ape"); } }; diff --git a/tests/test_info.cpp b/tests/test_info.cpp new file mode 100644 index 00000000..f76fd67a --- /dev/null +++ b/tests/test_info.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestInfoTag : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestInfoTag); + CPPUNIT_TEST(testTitle); + CPPUNIT_TEST(testNumericFields); + CPPUNIT_TEST_SUITE_END(); + +public: + void testTitle() + { + RIFF::Info::Tag tag; + + CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); + tag.setTitle("Test title 1"); + CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title()); + } + + void testNumericFields() + { + RIFF::Info::Tag tag; + + CPPUNIT_ASSERT_EQUAL((uint)0, tag.track()); + tag.setTrack(1234); + CPPUNIT_ASSERT_EQUAL((uint)1234, tag.track()); + CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT")); + + CPPUNIT_ASSERT_EQUAL((uint)0, tag.year()); + tag.setYear(1234); + CPPUNIT_ASSERT_EQUAL((uint)1234, tag.year()); + CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD")); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag);