From 75d4252480aec7756f8746d12dff7d7b0d750511 Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Sun, 1 Oct 2023 19:56:33 +0200 Subject: [PATCH] Variant type as container for complex properties --- taglib/CMakeLists.txt | 2 + taglib/toolkit/tvariant.cpp | 391 ++++++++++++++++++++++++++++++++++++ taglib/toolkit/tvariant.h | 202 +++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_variant.cpp | 204 +++++++++++++++++++ 5 files changed, 800 insertions(+) create mode 100644 taglib/toolkit/tvariant.cpp create mode 100644 taglib/toolkit/tvariant.h create mode 100644 tests/test_variant.cpp diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 447eadaa..3ffe978a 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -40,6 +40,7 @@ set(tag_HDRS toolkit/tstringlist.h toolkit/tbytevector.h toolkit/tbytevectorlist.h + toolkit/tvariant.h toolkit/tbytevectorstream.h toolkit/tiostream.h toolkit/tfile.h @@ -300,6 +301,7 @@ set(toolkit_SRCS toolkit/tstringlist.cpp toolkit/tbytevector.cpp toolkit/tbytevectorlist.cpp + toolkit/tvariant.cpp toolkit/tbytevectorstream.cpp toolkit/tiostream.cpp toolkit/tfile.cpp diff --git a/taglib/toolkit/tvariant.cpp b/taglib/toolkit/tvariant.cpp new file mode 100644 index 00000000..98ab6848 --- /dev/null +++ b/taglib/toolkit/tvariant.cpp @@ -0,0 +1,391 @@ +/*************************************************************************** + copyright : (C) 2023 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * 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 "tvariant.h" + +#include +#include + +#include "tstring.h" +#include "tstringlist.h" +#include "tbytevector.h" +#include "tbytevectorlist.h" + +using namespace TagLib; + +namespace { + +// The number and order of the template parameters must correspond to the +// enum values in Variant::Type! +using StdVariantType = std::variant< + std::monostate, + bool, + int, + unsigned int, + long long, + unsigned long long, + double, + TagLib::String, + TagLib::StringList, + TagLib::ByteVector, + TagLib::ByteVectorList, + List, + Map +>; + +template +T getVariantValue(StdVariantType *v, bool *ok) +{ + if(const auto valPtr = std::get_if(v)) { + if(ok) { + *ok = true; + } + return *valPtr; + } + if(ok) { + *ok = false; + } + return {}; +} + +// Visitor to print a possibly recursive Variant to an ostream. +// The representation is JSON with hex strings for ByteVector. +class OStreamVisitor { +public: + OStreamVisitor(std::ostream &os) : s(os) + { + } + + void operator()(std::monostate v) + { + s << "null"; + } + + void operator()(bool v) + { + s << (v ? "true" : "false"); + } + + void operator()(int v) + { + s << v; + } + + void operator()(unsigned int v) + { + s << v; + } + + void operator()(long long v) + { + s << v; + } + + void operator()(unsigned long long v) + { + s << v; + } + + void operator()(double v) + { + s << v; + } + + void operator()(const TagLib::String &v) + { + s << '"'; + for (char c : v.to8Bit()) { + if(c == '"') { + s << "\\\""; + } + else { + s << c; + } + } + s << '"'; + } + + void operator()(const TagLib::StringList &v) + { + s << '['; + for(auto it = v.cbegin(); it != v.cend(); ++it) { + if(it != v.cbegin()) { + s << ", "; + } + operator()(*it); + } + s << ']'; + } + + void operator()(const TagLib::ByteVector &v) + { + s << '"'; + for(char c : v) { + s << "\\x" << std::setfill('0') << std::setw(2) << std::right << std::hex + << (static_cast(c) & 0xff); + } + s << std::dec << '"'; + } + + void operator()(const TagLib::ByteVectorList &v) + { + s << '['; + for(auto it = v.cbegin(); it != v.cend(); ++it) { + if(it != v.cbegin()) { + s << ", "; + } + operator()(*it); + } + s << ']'; + } + + void operator()(const List &v) { + s << '['; + for(auto it = v.cbegin(); it != v.cend(); ++it) { + if(it != v.cbegin()) { + s << ", "; + } + s << *it; + } + s << ']'; + } + + void operator()(const Map &v) + { + s << '{'; + for(auto it = v.cbegin(); it != v.cend(); ++it) { + if(it != v.cbegin()) { + s << ", "; + } + operator()(it->first); + s << ": "; + s << it->second; + } + s << '}'; + } + +private: + std::ostream &s; +}; + +} // namespace + +class Variant::VariantPrivate +{ +public: + VariantPrivate() = default; + VariantPrivate(const StdVariantType &v) : data(v) {} + StdVariantType data; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Variant::Variant() : + d(std::make_shared()) +{ +} + +Variant::Variant(int val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(unsigned int val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(long long val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(unsigned long long val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(bool val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(double val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const char *val) : + d(std::make_shared(TagLib::String(val))) +{ +} + +Variant::Variant(const TagLib::String &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const TagLib::StringList &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const TagLib::ByteVector &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const TagLib::ByteVectorList &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const TagLib::List &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const TagLib::Map &val) : + d(std::make_shared(val)) +{ +} + +Variant::Variant(const Variant &) = default; + +//////////////////////////////////////////////////////////////////////////////// + +Variant::~Variant() = default; + +Variant::Type Variant::type() const { + return static_cast(d->data.index()); +} + +bool Variant::isEmpty() const +{ + return type() == Void; +} + +template +T Variant::value(bool *ok) const +{ + return getVariantValue(&d->data, ok); +} + +template bool Variant::value(bool *ok) const; +template int Variant::value(bool *ok) const; +template unsigned int Variant::value(bool *ok) const; +template long long Variant::value(bool *ok) const; +template unsigned long long Variant::value(bool *ok) const; +template double Variant::value(bool *ok) const; +template String Variant::value(bool *ok) const; +template StringList Variant::value(bool *ok) const; +template ByteVector Variant::value(bool *ok) const; +template ByteVectorList Variant::value(bool *ok) const; +template VariantList Variant::value(bool *ok) const; +template VariantMap Variant::value(bool *ok) const; + +bool Variant::toBool(bool *ok) const +{ + return value(ok); +} + +int Variant::toInt(bool *ok) const +{ + return value(ok); +} + +unsigned int Variant::toUInt(bool *ok) const +{ + return value(ok); +} + +long long Variant::toLongLong(bool *ok) const +{ + return value(ok); +} + +unsigned long long Variant::toULongLong(bool *ok) const +{ + return value(ok); +} + +double Variant::toDouble(bool *ok) const +{ + return value(ok); +} + +TagLib::String Variant::toString(bool *ok) const +{ + return value(ok); +} + +TagLib::StringList Variant::toStringList(bool *ok) const +{ + return value(ok); +} + +TagLib::ByteVector Variant::toByteVector(bool *ok) const +{ + return value(ok); +} + +TagLib::ByteVectorList Variant::toByteVectorList(bool *ok) const +{ + return value(ok); +} + +TagLib::List Variant::toList(bool *ok) const +{ + return value>(ok); +} + +TagLib::Map Variant::toMap(bool *ok) const +{ + return value>(ok); +} + +bool Variant::operator==(const Variant &v) const +{ + return (d == v.d || d->data == v.d->data); +} + +bool Variant::operator!=(const Variant &v) const +{ + return !(*this == v); +} + +Variant &Variant::operator=(const Variant &) = default; + +//////////////////////////////////////////////////////////////////////////////// +// related non-member functions +//////////////////////////////////////////////////////////////////////////////// + +std::ostream &operator<<(std::ostream &s, const TagLib::Variant &v) +{ + std::visit(OStreamVisitor(s), v.d->data); + return s; +} diff --git a/taglib/toolkit/tvariant.h b/taglib/toolkit/tvariant.h new file mode 100644 index 00000000..9f34fc20 --- /dev/null +++ b/taglib/toolkit/tvariant.h @@ -0,0 +1,202 @@ +/*************************************************************************** + copyright : (C) 2023 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * 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_VARIANT_H +#define TAGLIB_VARIANT_H + +#include +#include + +#include "tlist.h" +#include "tmap.h" +#include "taglib_export.h" + +// Forward declaration needed for friend function +namespace TagLib { class Variant; } + +/*! + * \relates TagLib::Variant + * + * Send the variant to an output stream. + */ +TAGLIB_EXPORT std::ostream &operator<<(std::ostream &s, const TagLib::Variant &v); + +namespace TagLib { + + class String; + class StringList; + class ByteVector; + class ByteVectorList; + + /*! + * This is an implicitly shared discriminated union. + * + * The use of implicit sharing means that copying a variant is cheap. + * These Variant objects are immutable (have only const methods). + */ + class TAGLIB_EXPORT Variant + { + public: + /*! + * Types which can be stored in a variant. + */ + // The number and order of these types must correspond to the template + // parameters for StdVariantType in tvariant.cpp! + enum Type { + Void, + Bool, + Int, + UInt, + LongLong, + ULongLong, + Double, + String, + StringList, + ByteVector, + ByteVectorList, + VariantList, + VariantMap + }; + + /*! + * Constructs an empty Variant. + */ + Variant(); + + /*! + * Constructs a Variant from an integer value. + */ + Variant(int val); + + Variant(unsigned int val); + Variant(long long val); + Variant(unsigned long long val); + Variant(bool val); + Variant(double val); + Variant(const char *val); + Variant(const TagLib::String &val); + Variant(const TagLib::StringList &val); + Variant(const TagLib::ByteVector &val); + Variant(const TagLib::ByteVectorList &val); + Variant(const TagLib::List &val); + Variant(const TagLib::Map &val); + + /*! + * Make a shallow, implicitly shared, copy of \a v. Because this is + * implicitly shared, this method is lightweight and suitable for + * pass-by-value usage. + */ + Variant(const Variant &v); + + /*! + * Destroys this Variant instance. + */ + ~Variant(); + + /*! + * Get the type which is currently stored in this Variant. + */ + Type type() const; + + /*! + * Returns true if the Variant is empty. + */ + bool isEmpty() const; + + /*! + * Extracts an integer value from the Variant. + * If \a ok is passed, its boolean variable will be set to true if the + * Variant contains the correct type, and the returned value is the value + * of the Variant. Otherwise, the \a ok variable is set to false and + * a dummy default value is returned. + */ + int toInt(bool *ok = nullptr) const; + + unsigned int toUInt(bool *ok = nullptr) const; + long long toLongLong(bool *ok = nullptr) const; + unsigned long long toULongLong(bool *ok = nullptr) const; + bool toBool(bool *ok = nullptr) const; + double toDouble(bool *ok = nullptr) const; + TagLib::String toString(bool *ok = nullptr) const; + TagLib::StringList toStringList(bool *ok = nullptr) const; + TagLib::ByteVector toByteVector(bool *ok = nullptr) const; + TagLib::ByteVectorList toByteVectorList(bool *ok = nullptr) const; + TagLib::List toList(bool *ok = nullptr) const; + TagLib::Map toMap(bool *ok = nullptr) const; + + /*! + * Extracts value of type \a T from the Variant. + * If \a ok is passed, its boolean variable will be set to true if the + * Variant contains the correct type, and the returned value is the value + * of the Variant. Otherwise, the \a ok variable is set to false and + * a dummy default value is returned. + */ + template + T value(bool *ok = nullptr) const; + + /*! + * Returns true it the Variant and \a v are of the same type and contain the + * same value. + */ + bool operator==(const Variant &v) const; + + /*! + * Returns true it the Variant and \a v differ in type or value. + */ + bool operator!=(const Variant &v) const; + + /*! + * Performs a shallow, implicitly shared, copy of \a v, overwriting the + * Variant's current data. + */ + Variant &operator=(const Variant &v); + + private: + friend std::ostream& ::operator<<(std::ostream &s, const TagLib::Variant &v); + class VariantPrivate; + std::shared_ptr d; + }; + + /*! A list of Variant elements. */ + using VariantList = TagLib::List; + + /*! A map with String keys and Variant values. */ + using VariantMap = TagLib::Map; + + extern template TAGLIB_EXPORT bool Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT int Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT unsigned int Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT long long Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT unsigned long long Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT double Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT String Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT StringList Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT ByteVector Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT ByteVectorList Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT VariantList Variant::value(bool *ok) const; + extern template TAGLIB_EXPORT VariantMap Variant::value(bool *ok) const; +} // namespace TagLib + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4b02303c..8ee72478 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ SET(test_runner_SRCS test_bytevectorstream.cpp test_string.cpp test_propertymap.cpp + test_variant.cpp test_file.cpp test_fileref.cpp test_id3v1.cpp diff --git a/tests/test_variant.cpp b/tests/test_variant.cpp new file mode 100644 index 00000000..c652e353 --- /dev/null +++ b/tests/test_variant.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + copyright : (C) 2023 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * 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 "tbytevector.h" +#include "tvariant.h" +#include "tstringlist.h" +#include "tbytevectorlist.h" +#include +#include +#include "utils.h" + +using namespace TagLib; + +class TestVariant : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestVariant); + CPPUNIT_TEST(testVariantTypes); + CPPUNIT_TEST(testVariantToOStream); + CPPUNIT_TEST_SUITE_END(); + +public: + void testVariantTypes() + { + ByteVectorList bvl {"first", "second"}; + StringList sl {"el0", "el"}; + VariantList vl {"1st", "2nd"}; + VariantMap vm {{"key1", "value1"}, {"key2", "value2"}}; + + Variant varVoid; + Variant varBool(true); + Variant varInt(-4); + Variant varUInt(5U); + Variant varLongLong(-6LL); + Variant varULongLong(7ULL); + Variant varDouble(1.23); + Variant varString(String("test")); + Variant varString2("charp"); + Variant varStringList(sl); + Variant varByteVector(ByteVector("data")); + Variant varByteVectorList(bvl); + Variant varVariantList(vl); + Variant varVariantMap(vm); + + static const std::array, 14> varTypes { + std::tuple{varVoid, Variant::Void}, + std::tuple{varBool, Variant::Bool}, + std::tuple{varInt, Variant::Int}, + std::tuple{varUInt, Variant::UInt}, + std::tuple{varLongLong, Variant::LongLong}, + std::tuple{varULongLong, Variant::ULongLong}, + std::tuple{varDouble, Variant::Double}, + std::tuple{varString, Variant::String}, + std::tuple{varString2, Variant::String}, + std::tuple{varStringList, Variant::StringList}, + std::tuple{varByteVector, Variant::ByteVector}, + std::tuple{varByteVectorList, Variant::ByteVectorList}, + std::tuple{varVariantList, Variant::VariantList}, + std::tuple{varVariantMap, Variant::VariantMap} + }; + + for(const auto &t : varTypes) { + CPPUNIT_ASSERT_EQUAL(std::get<1>(t), std::get<0>(t).type()); + if(std::get<0>(t).type() == Variant::Void) { + CPPUNIT_ASSERT(std::get<0>(t).isEmpty()); + } + else { + CPPUNIT_ASSERT(!std::get<0>(t).isEmpty()); + } + } + + bool ok; + CPPUNIT_ASSERT_EQUAL(true, varBool.toBool(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(true, varBool.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(-4, varInt.toInt(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(-4, varInt.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(5U, varUInt.toUInt(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(5U, varUInt.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(-6LL, varLongLong.toLongLong(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(-6LL, varLongLong.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(7ULL, varULongLong.toULongLong(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(7ULL, varULongLong.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(1.23, varDouble.toDouble(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(1.23, varDouble.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(String("test"), varString.toString(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(String("test"), varString.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(String("charp"), varString2.toString(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(String("charp"), varString2.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(sl, varStringList.toStringList(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(sl, varStringList.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(ByteVector("data"), varByteVector.toByteVector(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(ByteVector("data"), varByteVector.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(bvl, varByteVectorList.toByteVectorList(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(bvl, varByteVectorList.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(vl, varVariantList.toList(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(vl, varVariantList.value(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(vm, varVariantMap.toMap(&ok)); + CPPUNIT_ASSERT(ok); + CPPUNIT_ASSERT_EQUAL(vm, varVariantMap.value(&ok)); + CPPUNIT_ASSERT(ok); + + CPPUNIT_ASSERT_EQUAL(0, varBool.toInt(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(0U, varInt.toUInt(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(0LL, varUInt.toLongLong(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(0ULL, varLongLong.toULongLong(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(0.0, varULongLong.toDouble(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(String(), varDouble.toString(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(StringList(), varString.toStringList(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(ByteVector(), varStringList.toByteVector(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT(varByteVector.toByteVectorList(&ok).isEmpty()); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(VariantList(), varByteVectorList.toList(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(VariantMap(), varVariantList.toMap(&ok)); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(false, varVariantMap.toBool(&ok)); + CPPUNIT_ASSERT(!ok); + + CPPUNIT_ASSERT(varUInt == varUInt); + CPPUNIT_ASSERT(varUInt == Variant(5U)); + CPPUNIT_ASSERT(varUInt != Variant(6U)); + CPPUNIT_ASSERT(varUInt != Variant(5)); + CPPUNIT_ASSERT(varUInt != varInt); + + Variant varUInt2(varUInt); + CPPUNIT_ASSERT(varUInt == varUInt2); + varUInt2 = 6U; + CPPUNIT_ASSERT(varUInt != varUInt2); + CPPUNIT_ASSERT_EQUAL(5U, varUInt.toUInt()); + CPPUNIT_ASSERT_EQUAL(6U, varUInt2.toUInt()); + } + + void testVariantToOStream() + { + std::stringstream ss; + VariantMap vm { + {"strlist", StringList {"first", "second"}}, + {"varlist", VariantList {Variant(), 1U, -10LL, 4.32, false}}, + {"data", ByteVector("\xa9\x01\x7f", 3)} + }; + ss << vm; + + CPPUNIT_ASSERT_EQUAL( + R"({"data": "\xa9\x01\x7f", "strlist": ["first", "second"],)" + R"( "varlist": [null, 1, -10, 4.32, false]})"s, + ss.str()); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestVariant);