3#include "../id3/id3genres.h"
4#include "../tagvalue.h"
6#include <c++utilities/chrono/format.h>
7#include <c++utilities/conversion/conversionexception.h>
11#include <cppunit/TestFixture.h>
12#include <cppunit/extensions/HelperMacros.h>
17using namespace CPPUNIT_NS;
34 CPPUNIT_TEST_SUITE_END();
37 void setUp()
override;
64 CPPUNIT_ASSERT(TagValue::empty().isEmpty());
70 const TagValue binary(
"123", 3, TagDataType::Binary);
71 CPPUNIT_ASSERT_EQUAL(TagDataType::Binary, binary.
type());
73 CPPUNIT_ASSERT_THROW(binary.
toString(), ConversionException);
74 CPPUNIT_ASSERT_THROW(binary.
toInteger(), ConversionException);
83 CPPUNIT_ASSERT(!integer.isEmpty());
84 CPPUNIT_ASSERT_EQUAL(TagDataType::Integer, integer.type());
85 CPPUNIT_ASSERT_EQUAL(
static_cast<std::int32_t
>(42), integer.toInteger());
86 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint64_t
>(42), integer.toUnsignedInteger());
87 CPPUNIT_ASSERT_EQUAL(
"42"s, integer.toString());
88 integer.assignInteger(2);
89 CPPUNIT_ASSERT_EQUAL(
"Country"s,
string(Id3Genres::stringFromIndex(integer.toStandardGenreIndex())));
90 integer.assignInteger(Id3Genres::emptyGenreIndex());
91 CPPUNIT_ASSERT_EQUAL(Id3Genres::emptyGenreIndex(), integer.toStandardGenreIndex());
93 CPPUNIT_ASSERT_EQUAL(Id3Genres::emptyGenreIndex(), integer.toStandardGenreIndex());
96 integer.assignInteger(-25);
97 CPPUNIT_ASSERT_EQUAL(
"-25"s, integer.toString());
98 CPPUNIT_ASSERT_EQUAL(
PositionInSet(-25), integer.toPositionInSet());
99 CPPUNIT_ASSERT_THROW(integer.toStandardGenreIndex(), ConversionException);
102 integer.assignInteger(0);
103 CPPUNIT_ASSERT_MESSAGE(
"explicitly assigned zero not considered empty", !integer.isEmpty());
104 CPPUNIT_ASSERT_EQUAL(
"0"s, integer.toString());
105 CPPUNIT_ASSERT_EQUAL(
DateTime(), integer.toDateTime());
106 CPPUNIT_ASSERT_EQUAL(
TimeSpan(), integer.toTimeSpan());
110 CPPUNIT_ASSERT_MESSAGE(
"cleared vale considered empty", integer.isEmpty());
111 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"only date (but not type) cleared"s, TagDataType::Integer, integer.type());
112 CPPUNIT_ASSERT_EQUAL(
static_cast<std::int32_t
>(0), integer.toInteger());
113 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint64_t
>(0), integer.toUnsignedInteger());
114 CPPUNIT_ASSERT_EQUAL(
string(), integer.toString());
115 CPPUNIT_ASSERT_EQUAL(
DateTime(), integer.toDateTime());
116 CPPUNIT_ASSERT_EQUAL(
TimeSpan(), integer.toTimeSpan());
121 auto unsignedInteger =
TagValue(
static_cast<std::uint64_t
>(42ul));
122 CPPUNIT_ASSERT(!unsignedInteger.isEmpty());
123 CPPUNIT_ASSERT_EQUAL(TagDataType::UnsignedInteger, unsignedInteger.type());
124 CPPUNIT_ASSERT_EQUAL(
static_cast<std::int32_t
>(42), unsignedInteger.toInteger());
125 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint64_t
>(42), unsignedInteger.toUnsignedInteger());
126 CPPUNIT_ASSERT_EQUAL(
"42"s, unsignedInteger.toString());
127 unsignedInteger.assignUnsignedInteger(2);
128 CPPUNIT_ASSERT_EQUAL(
"Country"s,
string(Id3Genres::stringFromIndex(unsignedInteger.toStandardGenreIndex())));
129 unsignedInteger.assignInteger(Id3Genres::emptyGenreIndex());
130 CPPUNIT_ASSERT_EQUAL(Id3Genres::emptyGenreIndex(), unsignedInteger.toStandardGenreIndex());
131 unsignedInteger.clearData();
132 CPPUNIT_ASSERT_EQUAL(Id3Genres::emptyGenreIndex(), unsignedInteger.toStandardGenreIndex());
135 unsignedInteger.assignInteger(0);
136 CPPUNIT_ASSERT_MESSAGE(
"explicitly assigned zero not considered empty", !unsignedInteger.isEmpty());
137 CPPUNIT_ASSERT_EQUAL(
"0"s, unsignedInteger.toString());
138 CPPUNIT_ASSERT_EQUAL(
DateTime(), unsignedInteger.toDateTime());
139 CPPUNIT_ASSERT_EQUAL(
TimeSpan(), unsignedInteger.toTimeSpan());
146 CPPUNIT_ASSERT_EQUAL(4, test.
toInteger());
148 CPPUNIT_ASSERT_EQUAL(
"4/23"s, test.
toString());
150 CPPUNIT_ASSERT_THROW(test.
toDateTime(), ConversionException);
151 CPPUNIT_ASSERT_THROW(test.
toTimeSpan(), ConversionException);
156 const TimeSpan fiveMinutes(TimeSpan::fromMinutes(5));
159 CPPUNIT_ASSERT_EQUAL(timeSpan,
TagValue(timeSpan));
160 CPPUNIT_ASSERT_EQUAL(fiveMinutes, timeSpan.
toTimeSpan());
161 CPPUNIT_ASSERT_EQUAL(fiveMinutes.toString(), timeSpan.
toString());
162 CPPUNIT_ASSERT_THROW(timeSpan.
toInteger(), ConversionException);
163 CPPUNIT_ASSERT_THROW(timeSpan.
toDateTime(), ConversionException);
169 const DateTime now(DateTime::now());
172 CPPUNIT_ASSERT_EQUAL(dateTime,
TagValue(dateTime));
173 CPPUNIT_ASSERT_EQUAL(now, dateTime.
toDateTime());
174 CPPUNIT_ASSERT_EQUAL(now.toString(DateTimeOutputFormat::IsoOmittingDefaultComponents), dateTime.
toString());
175 CPPUNIT_ASSERT_THROW(dateTime.
toInteger(), ConversionException);
176 CPPUNIT_ASSERT_THROW(dateTime.
toTimeSpan(), ConversionException);
182 const auto tagValue =
TagValue(
Popularity{ .
user =
"foo", .rating = 42, .playCounter = 123, .scale = TagType::VorbisComment });
184 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (user)",
"foo"s, popularity.user);
185 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (rating)", 42.0, popularity.rating);
186 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (play counter)", std::uint64_t(123), popularity.playCounter);
187 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (scale)", TagType::VorbisComment, popularity.scale);
188 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to string",
"foo|42|123"s, tagValue.toString());
189 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to string (only rating)",
"43"s,
TagValue(
Popularity{ .
rating = 43 }).toString());
190 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to integer", 42, tagValue.toInteger());
191 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to unsigned integer",
static_cast<std::uint64_t
>(42), tagValue.toUnsignedInteger());
192 CPPUNIT_ASSERT_THROW_MESSAGE(
193 "failing conversion to other type",
TagValue(
"foo|bar"sv, TagTextEncoding::Latin1).toPopularity(), ConversionException);
201 CPPUNIT_ASSERT_EQUAL(
"\x31\0\x35\0"s,
TagValue(15).toString(TagTextEncoding::Utf16LittleEndian));
202 CPPUNIT_ASSERT_EQUAL(
"\0\x31\0\x35"s,
TagValue(15).toString(TagTextEncoding::Utf16BigEndian));
203 CPPUNIT_ASSERT_EQUAL(15,
TagValue(
"\0\x31\0\x35"s, TagTextEncoding::Utf16BigEndian).toInteger());
204 CPPUNIT_ASSERT_EQUAL(
static_cast<std::uint64_t
>(15),
TagValue(
"\0\x31\0\x35"s, TagTextEncoding::Utf16BigEndian).toUnsignedInteger());
205 CPPUNIT_ASSERT_EQUAL_MESSAGE(
207 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"original encoding preserved",
"\0\x31\0\x35"s,
208 TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian).toString(TagTextEncoding::Unspecified));
209 CPPUNIT_ASSERT_EQUAL_MESSAGE(
211 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"UTF-16 LE BOM truncated",
"\0t\0\xe4\0s\0t"s,
212 TagValue(
"\xff\xfe\0t\0\xe4\0s\0t", 10, TagTextEncoding::Utf16LittleEndian).toString(TagTextEncoding::Unspecified));
213 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"UTF-16 BE BOM truncated",
"t\0\xe4\0s\0t\0"s,
214 TagValue(
"\xfe\xfft\0\xe4\0s\0t\0", 10, TagTextEncoding::Utf16BigEndian).toString(TagTextEncoding::Unspecified));
215 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion via c'tor",
"15\xe4"s,
218 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to int", 15,
TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian).toInteger());
221 CPPUNIT_ASSERT_EQUAL_MESSAGE(
222 "conversion to pos",
PositionInSet(15),
TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian).toPositionInSet());
223 CPPUNIT_ASSERT_THROW_MESSAGE(
"failing conversion pos",
TagValue(
"a4 / 15", 7,
TagTextEncoding::Utf8).toPositionInSet(), ConversionException);
224 CPPUNIT_ASSERT_EQUAL_MESSAGE(
226 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to date from UTF-16", DateTime::fromDate(2015, 4, 15),
227 TagValue(
"\0\x32\0\x30\0\x31\0\x35\0\x2d\0\x30\0\x34\0\x2d\0\x31\0\x35", 20, TagTextEncoding::Utf16BigEndian).toDateTime());
229 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to time span", TimeSpan::fromHours(1.5),
TagValue(
"01:30:00", 10,
TagTextEncoding::Utf8).toTimeSpan());
230 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to time span from UTF-16", TimeSpan::fromHours(1.5),
231 TagValue(
"\0\x31\0\x3a\0\x33\0\x30\0\x3a\0\x30\0\x30", 14, TagTextEncoding::Utf16BigEndian).toTimeSpan());
233 CPPUNIT_ASSERT_EQUAL_MESSAGE(
234 "conversion to genre from index", 15,
TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian).toStandardGenreIndex());
235 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to genre from name", 2,
TagValue(
"Country", 7, TagTextEncoding::Latin1).toStandardGenreIndex());
236 CPPUNIT_ASSERT_THROW_MESSAGE(
237 "failing conversion to genre",
TagValue(
"Kountry", 7, TagTextEncoding::Latin1).toStandardGenreIndex(), ConversionException);
239 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (user)",
"foo"s, popularity.user);
240 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (rating)", 42.0, popularity.rating);
241 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"conversion to popularity (play counter)", std::uint64_t(123), popularity.playCounter);
242 CPPUNIT_ASSERT_THROW_MESSAGE(
"failing conversion to popularity",
TagValue(
"foo|bar"sv).toPopularity(), ConversionException);
247 CPPUNIT_ASSERT_MESSAGE(
"equality requires identical types or identical string representation"s,
TagValue(0) != TagValue::empty());
248 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"comparison of equal types"s,
TagValue(15),
TagValue(15));
249 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"types might differ"s,
TagValue(
"15", 2, TagTextEncoding::Latin1),
TagValue(15));
251 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"comparison of equal UTF-16 strings"s,
TagValue(
"\x31\0\x32\0", 4, TagTextEncoding::Utf16LittleEndian),
252 TagValue(
"\x31\0\x32\0", 4, TagTextEncoding::Utf16LittleEndian));
253 CPPUNIT_ASSERT_MESSAGE(
"comparison of different UTF-16 strings"s,
254 TagValue(
"\x31\0\x33\0", 4, TagTextEncoding::Utf16LittleEndian) !=
TagValue(
"\x31\0\x32\0", 4, TagTextEncoding::Utf16LittleEndian));
255 CPPUNIT_ASSERT_EQUAL_MESSAGE(
256 "comparison of equal binary data"s,
TagValue(
"\x31\0\x32\0", 4, TagDataType::Binary),
TagValue(
"\x31\0\x32\0", 4, TagDataType::Binary));
257 CPPUNIT_ASSERT_MESSAGE(
258 "comparison of different binary data"s,
TagValue(
"\x31\0\x33\0", 4, TagDataType::Binary) !=
TagValue(
"\x31\0\x32\0", 4, TagDataType::Binary));
259 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"different encodings are converted if neccassary"s,
TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian),
260 TagValue(
"15", 2, TagTextEncoding::Latin1));
261 CPPUNIT_ASSERT_EQUAL_MESSAGE(
262 "encoding is ignored when not relevant for types"s,
TagValue(
"\0\x31\0\x35", 4, TagTextEncoding::Utf16BigEndian),
TagValue(15));
263 const TagValue fooTagValue(
"foo", 3, TagDataType::Text), fOoTagValue(
"fOo", 3, TagDataType::Text);
264 CPPUNIT_ASSERT_MESSAGE(
"string comparison case-sensitive by default"s, fooTagValue != fOoTagValue);
265 CPPUNIT_ASSERT_MESSAGE(
"case-insensitive string comparison"s, fooTagValue.compareTo(fOoTagValue, TagValueComparisionFlags::CaseInsensitive));
266 const auto popularity =
Popularity{ .
user =
"some user", .rating = 200, .playCounter = 0 };
268 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"comparison of equal popularity (string and binary representation)"s,
TagValue(
"some user|200.0"sv), first);
269 CPPUNIT_ASSERT_EQUAL_MESSAGE(
"comparison of equal popularity (only binary representation)"s, first, second);
271 CPPUNIT_ASSERT_MESSAGE(
"popularity not equal"s, first !=
TagValue(
Popularity({ .rating = 200 })));
276 CPPUNIT_ASSERT_MESSAGE(
"meta-data must be equal"s, withDescription !=
TagValue(15));
277 CPPUNIT_ASSERT_MESSAGE(
"different meta-data ignored"s, withDescription.
compareTo(
TagValue(15), TagValueComparisionFlags::IgnoreMetaData));
278 TagValue withDescription2(withDescription);
279 CPPUNIT_ASSERT_EQUAL(withDescription, withDescription2);
281 CPPUNIT_ASSERT(withDescription != withDescription2);
283 CPPUNIT_ASSERT_EQUAL(withDescription, withDescription2);
285 CPPUNIT_ASSERT_MESSAGE(
"meta-data case must match by default"s, withDescription != withDescription2);
286 CPPUNIT_ASSERT_MESSAGE(
"meta-data case ignored"s, withDescription.
compareTo(withDescription2, TagValueComparisionFlags::CaseInsensitive));
The PositionInSet class describes the position of an element in a set which consists of a certain num...
The TagValue class wraps values of different types.
void setMimeType(std::string_view mimeType)
Sets the MIME type.
const std::string & mimeType() const
Returns the MIME type.
bool compareTo(const TagValue &other, TagValueComparisionFlags options=TagValueComparisionFlags::None) const
Returns whether both instances are equal.
CppUtilities::DateTime toDateTime() const
Converts the value of the current TagValue object to its equivalent DateTime representation.
void setDescription(std::string_view value, TagTextEncoding encoding=TagTextEncoding::Latin1)
Sets the description.
std::uint64_t toUnsignedInteger() const
std::int32_t toInteger() const
Converts the value of the current TagValue object to its equivalent integer representation.
PositionInSet toPositionInSet() const
Converts the value of the current TagValue object to its equivalent PositionInSet representation.
TagDataType type() const
Returns the type of the assigned value.
void assignTimeSpan(CppUtilities::TimeSpan value)
Assigns the given TimeSpan value.
Popularity toPopularity() const
Converts the value of the current TagValue object to its equivalent Popularity representation.
void assignDateTime(CppUtilities::DateTime value)
Assigns the given DateTime value.
std::size_t dataSize() const
Returns the size of the assigned value in bytes.
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
int toStandardGenreIndex() const
Converts the value of the current TagValue object to its equivalent standard genre index.
CppUtilities::TimeSpan toTimeSpan() const
Converts the value of the current TagValue object to its equivalent TimeSpan representation.
char * dataPointer()
Returns a pointer to the raw data assigned to the current instance.
The TagValueTests class tests the TagParser::TagValue class.
void testUnsignedInteger()
void testEqualityOperator()
Contains all classes and functions of the TagInfo library.
std::string user
The user who gave the rating / played the file, e.g. identified by e-mail address.
double rating
The rating on a tag type specific scale.
CPPUNIT_TEST_SUITE_REGISTRATION(TagValueTests)