Files
Vasily Plotnikov a0d8c7080d Initial commit
2020-11-09 23:38:45 +02:00

3062 lines
67 KiB
C++

//
// EDRAv2. Unit test
// Variant.
//
#include "pch.h"
using namespace openEdr;
inline constexpr size_t c_nValueTypeCount = static_cast<size_t>(variant::detail::RawValueType::Max);
//////////////////////////////////////////////////////////////////////////
//
// TestObject
//
//
//
//
class ISimpleTestObject : OBJECT_INTERFACE
{
public:
virtual void callMethod() = 0;
};
//
//
//
class INotImplementedInterface : OBJECT_INTERFACE
{
public:
virtual void callMethod2() = 0;
};
//
//
//
class SimpleTestObject : public ObjectBase <>, public ISimpleTestObject
{
public:
virtual void callMethod() override {}
};
//
//
//
TEST_CASE("VariantValueKeeper. Test type()")
{
using RawValueType = openEdr::variant::detail::RawValueType;
typedef openEdr::variant::detail::Value TVariant;
using IntKeeper = openEdr::Variant::IntValue;
TVariant vTmp;
REQUIRE(vTmp.getType() == RawValueType::Null);
vTmp.set(nullptr);
REQUIRE(vTmp.getType() == RawValueType::Null);
vTmp.set(false);
REQUIRE(vTmp.getType() == RawValueType::Boolean);
IntKeeper nSrcVal = 10;
vTmp.set(nSrcVal);
REQUIRE(vTmp.getType() == RawValueType::Integer);
auto pStringValue = openEdr::variant::detail::StringValue ::create("asdasd");
vTmp.set(pStringValue);
REQUIRE(vTmp.getType() == RawValueType::String);
ObjPtr<IObject> pObj = createObject<SimpleTestObject>();
vTmp.set(pObj);
REQUIRE(vTmp.getType() == RawValueType::Object);
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. Scalar type.
//
//
//
//
TEST_CASE("Variant. Copy/assign/move Variant")
{
using openEdr::Variant;
SECTION("Copy")
{
Variant vTest1("qqqqqqqqqqqqqqq");
REQUIRE(vTest1 == "qqqqqqqqqqqqqqq");
Variant vTest2(vTest1);
REQUIRE(vTest2 == "qqqqqqqqqqqqqqq");
std::string_view s1 = vTest1;
std::string_view s2 = vTest2;
REQUIRE(s1.data() == s2.data());
}
SECTION("Assign")
{
Variant vTest1("qqqqqqqqqqqqqqq");
REQUIRE(vTest1 == "qqqqqqqqqqqqqqq");
Variant vTest2("aaaaaaaaaaaaaaaaa");
REQUIRE(vTest2 == "aaaaaaaaaaaaaaaaa");
vTest2 = vTest1;
REQUIRE(vTest2 == "qqqqqqqqqqqqqqq");
std::string_view s1 = vTest1;
std::string_view s2 = vTest2;
REQUIRE(s1.data() == s2.data());
}
SECTION("Move")
{
Variant vTest1("qqqqqqqqqqqqqqq");
REQUIRE(vTest1 == "qqqqqqqqqqqqqqq");
REQUIRE(vTest1.getType() == Variant::ValueType::String);
Variant vTest2(std::move(vTest1));
REQUIRE(vTest2 == "qqqqqqqqqqqqqqq");
REQUIRE(vTest1.getType() == Variant::ValueType::Null);
}
}
//
//
//
TEST_CASE("Variant. Scalar type. Conversion to and from null type")
{
Variant vTest(nullptr);
REQUIRE(vTest.getType() == Variant::ValueType::Null);
REQUIRE(static_cast<nullptr_t>(vTest) == nullptr);
vTest = 10u;
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
vTest = nullptr;
REQUIRE(vTest.getType() == Variant::ValueType::Null);
REQUIRE(static_cast<nullptr_t>(vTest) == nullptr);
nullptr_t Val = vTest;
REQUIRE(Val == nullptr);
}
//
//
//
TEST_CASE("Variant. Scalar type. Conversion to and from bool type")
{
Variant vTest(true);
REQUIRE(vTest.getType() == Variant::ValueType::Boolean);
REQUIRE((bool)vTest);
vTest = nullptr;
REQUIRE(vTest.getType() == Variant::ValueType::Null);
vTest = false;
REQUIRE(vTest.getType() == Variant::ValueType::Boolean);
REQUIRE(!(bool)vTest);
bool Val = vTest;
REQUIRE(!Val);
}
//
//
//
TEST_CASE("Variant. Scalar type. Conversion to and from integer type")
{
SECTION("Simple unsigned")
{
Variant vTest(10u);
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE((uint64_t)vTest == 10);
REQUIRE(vTest == 10);
vTest = nullptr;
REQUIRE(vTest.getType() == Variant::ValueType::Null);
vTest = 20u;
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE(vTest == 20);
uint64_t Val = vTest;
REQUIRE(Val == 20);
}
SECTION("Simple signed")
{
Variant vTest(-10);
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE((int64_t)vTest == -10);
REQUIRE(vTest == -10);
vTest = nullptr;
REQUIRE(vTest.getType() == Variant::ValueType::Null);
vTest = -20;
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE(vTest == -20);
int64_t Val = vTest;
REQUIRE(Val == -20);
}
SECTION("enum")
{
enum class ExampleEnum
{
Elem1,
Elem2,
};
Variant vTest(ExampleEnum::Elem1);
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE((ExampleEnum)vTest == ExampleEnum::Elem1);
vTest = nullptr;
vTest = ExampleEnum::Elem2;
REQUIRE(vTest.getType() == Variant::ValueType::Integer);
REQUIRE(vTest == ExampleEnum::Elem2);
ExampleEnum e = vTest;
REQUIRE(e == ExampleEnum::Elem2);
}
SECTION("IntKeeper")
{
static_assert(Variant::IntValue::IsSupportedType<const Variant::IntValue&>::value, "xxxxx");
{
Variant vTest1(256u);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
//Variant::IntKeeper oVal = vTest1.operator Variant::IntKeeper();
//Variant::IntKeeper oVal = (Variant::IntKeeper) vTest1;
Variant::IntValue oVal = vTest1;
REQUIRE(!oVal.isSigned());
REQUIRE(oVal.asSigned() == 256u);
Variant vTest2(oVal);
REQUIRE(static_cast<uint16_t>(vTest1) == 256);
}
{
Variant vTest1(-256);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
Variant::IntValue oVal = vTest1;
REQUIRE(oVal.isSigned());
REQUIRE(oVal.asSigned() == -256);
Variant vTest2 = oVal;
REQUIRE(static_cast<int16_t>(vTest1) == -256);
}
}
SECTION("numeric_cast. Signed positive")
{
Variant vTest1(255);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
REQUIRE(static_cast<uint16_t>(vTest1) == 255);
REQUIRE(static_cast<int16_t>(vTest1) == 255);
REQUIRE(static_cast<uint8_t>(vTest1) == 255);
REQUIRE_THROWS_AS([&]() {
static_cast<int8_t>(vTest1);
}(), error::ArithmeticError);
}
SECTION("numeric_cast. Signed negative")
{
Variant vTest1(-10);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
REQUIRE(static_cast<int16_t>(vTest1) == -10);
REQUIRE(static_cast<int8_t>(vTest1) == -10);
REQUIRE_THROWS_AS([&]() {
static_cast<uint8_t>(vTest1);
}(), error::ArithmeticError);
REQUIRE_THROWS_AS([&]() {
static_cast<uint32_t>(vTest1);
}(), error::ArithmeticError);
}
SECTION("numeric_cast. -1")
{
Variant vTest1(-1);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
REQUIRE(static_cast<int16_t>(vTest1) == -1);
REQUIRE(static_cast<int8_t>(vTest1) == -1);
REQUIRE(static_cast<uint8_t>(vTest1) == 255);
REQUIRE(static_cast<uint16_t>(vTest1) == 0xFFFF);
REQUIRE(static_cast<uint32_t>(vTest1) == 0xFFFFFFFF);
}
SECTION("numeric_cast. Unsigned")
{
Variant vTest1(256u);
REQUIRE(vTest1.getType() == Variant::ValueType::Integer);
REQUIRE(static_cast<uint16_t>(vTest1) == 256);
REQUIRE(static_cast<int16_t>(vTest1) == 256);
REQUIRE_THROWS_AS([&]() {
static_cast<int8_t>(vTest1);
}(), error::ArithmeticError);
REQUIRE_THROWS_AS([&]() {
static_cast<uint8_t>(vTest1);
}(), error::ArithmeticError);
REQUIRE_THROWS_AS([&]() {
static_cast<int8_t>(vTest1);
}(), error::ArithmeticError);
}
}
//
//
//
template <typename T>
void TestConversionFromString(T Val)
{
// Constructor
Variant vTest(Val);
REQUIRE(vTest.getType() == Variant::ValueType::String);
REQUIRE(vTest == Val);
// operator =
vTest = nullptr;
vTest = Val;
REQUIRE(vTest.getType() == Variant::ValueType::String);
REQUIRE(vTest == Val);
// operator !=
REQUIRE_FALSE(vTest != Val);
}
//
//
//
template <typename T>
void TestConversionToString(T Val)
{
Variant vTest(Val);
// operator Type
T otherVal = vTest;
REQUIRE(otherVal == Val);
REQUIRE(T(vTest) == Val);
}
//
//
//
template <typename Ch>
void TestConversionToString(const Ch* Val)
{
Variant vTest(Val);
// operator Type
const Ch* otherVal = vTest;
REQUIRE(std::basic_string_view<Ch>(Val) == otherVal);
REQUIRE(std::basic_string_view<Ch>(Val) == (const Ch*)(vTest));
}
//
//
//
TEST_CASE("Variant. Scalar type. Conversion to and from string type")
{
SECTION("string")
{
TestConversionFromString(std::string("TestString"));
TestConversionToString(std::string("TestString"));
}
SECTION("wstring")
{
TestConversionFromString(std::wstring(L"TestString"));
TestConversionToString(std::wstring(L"TestString"));
}
SECTION("string_view")
{
TestConversionFromString(std::string_view("TestString"));
TestConversionToString(std::string_view("TestString"));
}
SECTION("wstring_view")
{
TestConversionFromString(std::wstring_view(L"TestString"));
TestConversionToString(std::wstring_view(L"TestString"));
}
SECTION("char")
{
TestConversionFromString((const char*)"TestString");
//TestConversionToString((const char*)"TestString");
}
SECTION("wchar_t")
{
TestConversionFromString((const wchar_t*)L"TestString");
//TestConversionToString((const wchar_t*)L"TestString");
}
SECTION("char string literal")
{
// Constructor
Variant vTest("TestString");
REQUIRE(vTest.getType() == Variant::ValueType::String);
REQUIRE(vTest == "TestString");
// operator =
vTest = nullptr;
vTest = "TestString";
REQUIRE(vTest.getType() == Variant::ValueType::String);
REQUIRE(vTest == "TestString");
// operator !=
REQUIRE_FALSE(vTest != "TestString");
}
}
//
//
//
TEST_CASE("Variant. Scalar type. String encoding conversion")
{
// operator == conversion
{
REQUIRE(Variant("") == L"");
REQUIRE(Variant(L"") == "");
REQUIRE(Variant("TestString") == L"TestString");
REQUIRE(Variant(L"TestString") == "TestString");
REQUIRE(Variant(L"TestString") == Variant("TestString"));
REQUIRE(Variant("TestString") == Variant(L"TestString"));
}
// operator Type conversion
// Utf8 -> Utf16
{
Variant vUtf8("TestString");
std::wstring_view sUtf16 = vUtf8;
REQUIRE(sUtf16 == L"TestString");
std::wstring_view sUtf16_ = vUtf8;
REQUIRE(sUtf16_ == sUtf16);
}
// operator Type conversion
// Utf8 <- Utf16
{
Variant vWchar(L"TestString");
std::string_view sUtf8 = vWchar;
REQUIRE(sUtf8 == "TestString");
std::string_view sUtf8_ = vWchar;
REQUIRE(sUtf8_ == sUtf8);
}
}
//
//
//
template <typename ScalarType>
void testScalarTypeConversion(int nNothrowIndex, std::vector<Variant>& values)
{
for (size_t i=0; i<values.size(); ++i)
{
auto fnConvertor = [&]()
{
ScalarType scalar = values[i];
};
if (i == nNothrowIndex)
CHECK_NOTHROW(fnConvertor());
else
CHECK_THROWS_AS(fnConvertor(), error::TypeError);
}
}
//
//
//
TEST_CASE("Variant.conversion_to_invalid_scalar_type")
{
std::vector<Variant> variantList = { Variant(nullptr), Variant(true), Variant(10), Variant("XXXX"),
Variant(createObject<SimpleTestObject>()), Sequence(), Dictionary() };
SECTION("Null")
{
testScalarTypeConversion<nullptr_t>(0, variantList);
}
SECTION("Boolean")
{
testScalarTypeConversion<bool>(1, variantList);
}
SECTION("Integer")
{
testScalarTypeConversion<int>(2, variantList);
}
SECTION("String")
{
testScalarTypeConversion<std::string>(3, variantList);
}
SECTION("Object")
{
testScalarTypeConversion<ObjPtr<IObject>>(4, variantList);
}
}
//
//
//
bool hasWcharString(const Variant& v)
{
auto pStringValue = variant::detail::convertVariantSafe<std::shared_ptr<variant::detail::StringValue>>(v);
if (!pStringValue.has_value())
return false;
return (*pStringValue)->hasWChar();
}
//
//
//
bool hasUtf8String(const Variant& v)
{
auto pStringValue = variant::detail::convertVariantSafe<std::shared_ptr<variant::detail::StringValue>>(v);
if (!pStringValue.has_value())
return false;
return (*pStringValue)->hasUtf8();
}
//
// Implementation specific test (check internal features)
//
TEST_CASE("Variant. Scalar type. String allocations during encoding")
{
// operator Type returns the same buffer
{
Variant vStr("TestString");
REQUIRE(std::string_view(vStr).data() == std::string_view(vStr).data());
REQUIRE(std::wstring_view(vStr).data() == std::wstring_view(vStr).data());
}
// Check allocation
{
Variant vWchar(L"TestString");
REQUIRE(hasWcharString(vWchar));
REQUIRE_FALSE(hasUtf8String(vWchar));
REQUIRE_FALSE(vWchar == L"XXX");
REQUIRE(hasWcharString(vWchar));
REQUIRE_FALSE(hasUtf8String(vWchar));
REQUIRE_FALSE(vWchar == "XXX");
REQUIRE(hasWcharString(vWchar));
REQUIRE(hasUtf8String(vWchar));
Variant vUtf8("TestString");
REQUIRE_FALSE(hasWcharString(vUtf8));
REQUIRE(hasUtf8String(vUtf8));
REQUIRE_FALSE(vUtf8 == "XXX");
REQUIRE_FALSE(hasWcharString(vUtf8));
REQUIRE(hasUtf8String(vUtf8));
REQUIRE_FALSE(vUtf8 == L"XXX");
REQUIRE(hasWcharString(vUtf8));
REQUIRE(hasUtf8String(vUtf8));
}
// Check allocation empty
{
Variant vUtf8("");
REQUIRE(hasWcharString(vUtf8));
REQUIRE(hasUtf8String(vUtf8));
Variant vWchar(L"");
REQUIRE(hasWcharString(vWchar));
REQUIRE(hasUtf8String(vWchar));
}
}
void IterateLikeSalarType(Variant vOrigValue)
{
Variant vTest = vOrigValue;
// size
REQUIRE(vTest.getSize() == 1);
// empty
REQUIRE(!vTest.isEmpty());
// ranged for
{
size_t nCount = 0;
for (auto vValue : vTest)
{
++nCount;
REQUIRE(vValue == vOrigValue);
}
REQUIRE(nCount == 1);
}
// iterator
{
size_t nCount = 0;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
++nCount;
REQUIRE(*Iter == vOrigValue);
}
REQUIRE(nCount == 1);
}
// unsafe ranged for
{
size_t nCount = 0;
for (auto vValue : vTest.getUnsafeIterable())
{
++nCount;
REQUIRE(vValue == vOrigValue);
}
REQUIRE(nCount == 1);
}
// unsafe iterator
{
size_t nCount = 0;
for (auto Iter = vTest.unsafeBegin(); Iter != vTest.unsafeEnd(); ++Iter)
{
++nCount;
REQUIRE(*Iter == vOrigValue);
}
REQUIRE(nCount == 1);
}
}
//
//
//
TEST_CASE("Variant. Scalar type. Iteration + size + empty (without null)")
{
Variant ScalarValues[] = { Variant(true), Variant(10), Variant("XXXX")};
for (auto vOrigValue : ScalarValues)
IterateLikeSalarType(vOrigValue);
}
//
//
//
TEST_CASE("Variant. Scalar type. Iteration + size + empty (null-type)")
{
Variant vTest(nullptr);
// size
REQUIRE(vTest.getSize() == 0);
// empty
REQUIRE(vTest.isEmpty());
// ranged for
{
size_t nCount = 0;
for (auto vValue : vTest)
{
++nCount;
}
REQUIRE(nCount == 0);
}
// iterator
{
size_t nCount = 0;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
++nCount;
}
REQUIRE(nCount == 0);
}
// unsafe ranged for
{
size_t nCount = 0;
for (auto vValue : vTest.getUnsafeIterable())
{
++nCount;
}
REQUIRE(nCount == 0);
}
// unsafe iterator
{
size_t nCount = 0;
for (auto Iter = vTest.unsafeBegin(); Iter != vTest.unsafeEnd(); ++Iter)
{
++nCount;
}
REQUIRE(nCount == 0);
}
}
//
//
//
void testSequenceMethodsForNotsupportedType(openEdr::Variant vOrigValue)
{
REQUIRE(vOrigValue.getType() != Variant::ValueType::Sequence);
// operator []
REQUIRE_THROWS_AS([&]() {
(void)vOrigValue[0];
}(), error::TypeError);
// get
REQUIRE_THROWS_AS([&]() {
(void)vOrigValue.get(0);
}(), error::TypeError);
// get with default
REQUIRE_NOTHROW([&]() {
REQUIRE(vOrigValue.get(0, 0) == 0);
}());
// put
REQUIRE_THROWS_AS([&]() {
vOrigValue.put(0, Variant(1));
}(), error::TypeError);
// operator +=
REQUIRE_THROWS_AS([&]() {
vOrigValue += Variant(1);
}(), error::TypeError);
// push_back
REQUIRE_THROWS_AS([&]() {
vOrigValue.push_back(Variant(1));
}(), error::TypeError);
// insert
REQUIRE_THROWS_AS([&]() {
vOrigValue.insert(0, Variant(1));
}(), error::TypeError);
// erase
REQUIRE_THROWS_AS([&]() {
vOrigValue.erase(0);
}(), error::TypeError);
}
//
//
//
void testDictionaryMethodsForNotsupportedType(openEdr::Variant vOrigValue)
{
REQUIRE(vOrigValue.getType() != Variant::ValueType::Dictionary);
// has
REQUIRE_THROWS_AS([&]() {
vOrigValue.has("INT");
}(), error::TypeError);
// put
REQUIRE_THROWS_AS([&]() {
vOrigValue.put("INT", 200);
}(), error::TypeError);
// get
REQUIRE_THROWS_AS([&]() {
std::string sStr = vOrigValue.get("STR");
}(), error::TypeError);
// get with default
REQUIRE_NOTHROW([&]() {
Variant vTest = vOrigValue;
REQUIRE(vTest.get("a", 0) == 0);
}());
// operator []
REQUIRE_THROWS_AS([&]() {
uint16_t nVal = vOrigValue["INT"];
}(), error::TypeError);
}
//
//
//
TEST_CASE("Variant. Scalar type. Sequence and Dictionary methods")
{
Variant ScalarValues[] = {Variant(nullptr), Variant(true), Variant(10), Variant("XXXX")};
for (auto vOrigValue : ScalarValues)
{
REQUIRE_NOTHROW(testSequenceMethodsForNotsupportedType(vOrigValue));
REQUIRE_NOTHROW(testDictionaryMethodsForNotsupportedType(vOrigValue));
// clear
REQUIRE_THROWS_AS([&]() {
Variant vTest = vOrigValue;
vTest.clear();
}(), error::TypeError);
}
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. BasicDictionary.
//
//
//
//
template<typename DictionaryLikeType, typename Fn>
void testDictionaryMethodsForSupportedType(Fn fnCreate)
{
// getType
{
DictionaryLikeType vTest = fnCreate(openEdr::Dictionary());
REQUIRE(Variant(vTest).isDictionaryLike());
}
// isEmpty
{
REQUIRE(fnCreate(openEdr::Dictionary()).isEmpty());
REQUIRE_FALSE(fnCreate(openEdr::Dictionary({ {"a", 1} })).isEmpty());
}
// has
{
DictionaryLikeType vTest = fnCreate(openEdr::Dictionary({ {"a", 1} }));
REQUIRE(vTest.has("a"));
REQUIRE_FALSE(vTest.has("b"));
}
// size
{
REQUIRE(fnCreate(openEdr::Dictionary()).getSize() == 0);
REQUIRE(fnCreate(openEdr::Dictionary({ {"a", 1}, {"b", 1} })).getSize() == 2);
}
// put
{
DictionaryLikeType vTest = fnCreate(openEdr::Dictionary());
vTest.put("a", 200);
vTest.put("b", true);
vTest.put("b", "str");
REQUIRE(vTest.getSize() == 2);
REQUIRE(vTest["a"] == 200);
REQUIRE(vTest["b"] == "str");
}
// get
{
DictionaryLikeType vTest = fnCreate(openEdr::Dictionary({ {"a", 1}, {"b", "str"} }));
REQUIRE_NOTHROW([&]() {
REQUIRE(vTest.get("a") == 1);
REQUIRE(vTest.get("b") == "str");
REQUIRE(int(vTest.get("a")) == 1);
REQUIRE(std::string(vTest.get("b")) == "str");
}());
REQUIRE_THROWS_AS([&]() {
uint32_t v = vTest.get("c");
}(), error::OutOfRange);
}
// get with default
{
DictionaryLikeType vTest = fnCreate(openEdr::Dictionary({ {"a", 1}, {"b", "str"} }));
REQUIRE_NOTHROW([&]() {
REQUIRE(vTest.get("a", 0) == 1);
REQUIRE(vTest.get("b", 0) == "str");
REQUIRE(vTest.get("c", 0) == 0);
}());
}
// operator []
{
DictionaryLikeType vTest = fnCreate(Dictionary({ {"a", 1}, {"b", "str"} }));
REQUIRE_NOTHROW([&]() {
REQUIRE(vTest["a"] == 1);
REQUIRE(vTest["b"] == "str");
REQUIRE(int(vTest["a"]) == 1);
REQUIRE(std::string(vTest["b"]) == "str");
}());
REQUIRE_THROWS_AS([&]() {
uint32_t v = vTest["c"];
}(), error::OutOfRange);
}
}
//
//
//
TEST_CASE("Variant. BasicDictionary. Dictionary methods")
{
testDictionaryMethodsForSupportedType<Variant>([](Variant vDict)
{
return Variant(vDict);
});
}
//
//
//
TEST_CASE("Variant. BasicDictionary. Iteration")
{
Variant vSrc = Dictionary({
{"a", nullptr},
{"b", 1},
{"c", "str"},
});
std::vector<Variant> Ethalons;
{
REQUIRE(vSrc.getSize() == 3);
for (auto kv : Dictionary(vSrc))
Ethalons.push_back(kv.second);
REQUIRE(Ethalons.size() == 3);
}
Variant vTest(vSrc);
// size
REQUIRE(vTest.getSize() == Ethalons.size());
// empty
REQUIRE(!vTest.isEmpty());
// ranged for
{
size_t nIndex = 0;
for (auto vValue : vTest)
{
REQUIRE(vValue == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// iterator
{
size_t nIndex = 0;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
REQUIRE(*Iter == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// unsafe ranged for
{
size_t nIndex = 0;
for (auto vValue : vTest.getUnsafeIterable())
{
REQUIRE(vValue == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// unsafe iterator
{
size_t nIndex = 0;
for (auto Iter = vTest.unsafeBegin(); Iter != vTest.unsafeEnd(); ++Iter)
{
REQUIRE(*Iter == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
}
//
//
//
TEST_CASE("Variant. BasicDictionary. Sequence methods")
{
testSequenceMethodsForNotsupportedType(Dictionary({ {"a", 1}, {"b", "str"} }));
}
//
//
//
TEST_CASE("Variant_BasicDictionary_IDictStorage")
{
char chBegin = 'A';
// try to fill from 0 to 28 items
for (char chEnd = 'Z'/*chBegin*/; chEnd <= 'Z'; ++chEnd)
{
Dictionary v;
// Fill
for (char ch = chBegin; ch < chEnd; ++ch)
{
std::string sKey = { ch };
v.put(sKey, sKey);
}
// Check size
REQUIRE(v.getSize() == chEnd - chBegin);
// Check exist items
for (char ch = chBegin; ch < chEnd; ++ch)
{
std::string sKey = { ch };
REQUIRE(v[sKey] == sKey);
}
// Check non-exist items
REQUIRE_THROWS(v[std::string{ chEnd }]);
}
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. BasicSequence.
//
//
//
//
template<typename SequenceLikeType, typename Fn>
void testSequenceMethodsForSupportedType(Fn fnCreate)
{
SequenceLikeType vTest = fnCreate(Sequence());
REQUIRE(Variant(vTest).isSequenceLike());
REQUIRE(vTest.isEmpty());
vTest.push_back(200);
vTest.push_back(-200);
vTest.push_back("XXXX");
REQUIRE(vTest.getSize() == 3);
REQUIRE(!vTest.isEmpty());
vTest.erase(0);
REQUIRE(vTest.getSize() == 2);
REQUIRE((int64_t)vTest.get(0) == -200);
REQUIRE(vTest[1] == std::string_view("XXXX"));
REQUIRE(vTest.get(100, 1234) == 1234);
vTest.put(0, 321);
vTest.put(1, 987);
REQUIRE(vTest.get(0) == 321);
REQUIRE(vTest.get(1) == 987);
vTest.setSize(5);
REQUIRE(vTest.getSize() == 5);
REQUIRE(Variant(vTest[4]).isNull());
vTest.insert(1, 456);
REQUIRE(vTest.getSize() == 6);
REQUIRE(vTest.get(0) == 321);
REQUIRE(vTest.get(1) == 456);
REQUIRE(vTest.get(2) == 987);
vTest.clear();
REQUIRE(vTest.isEmpty());
REQUIRE_THROWS_AS([&]() {
(void)vTest.get(100);
}(), error::OutOfRange);
REQUIRE_THROWS_AS([&]() {
uint32_t v = vTest[100];
}(), error::OutOfRange);
REQUIRE_THROWS_AS([&]() {
vTest.clear();
vTest.push_back(200);
std::string sVal = vTest.get(0);
}(), error::TypeError);
REQUIRE_NOTHROW([&]() {
SequenceLikeType vTest2 = fnCreate(Sequence({ 100 }));
REQUIRE(vTest2.get(0, 0) == 100);
REQUIRE(vTest2.get(1, 0) == 0);
}());
}
//
//
//
template<typename SequenceLikeType, typename Fn>
void testSequenceIteration(Fn fnCreate)
{
std::string sJson = R"json(
[1,-1,"str",{"a":1},[1, "a"],true,null]
)json";
SequenceLikeType vTest = fnCreate(openEdr::variant::deserializeFromJson(sJson));
REQUIRE(Variant(vTest).isSequenceLike());
auto fnChecker = [](const std::vector<openEdr::Variant>& Values)
{
REQUIRE(Values.size() == 7);
REQUIRE(Values[0] == 1);
REQUIRE(Values[1] == -1);
REQUIRE(std::string_view(Values[2]) == "str");
REQUIRE(Values[3]["a"] == 1);
REQUIRE(Values[4][0] == 1);
REQUIRE(Values[5] == true);
REQUIRE(Values[6] == nullptr);
};
{
std::vector<openEdr::Variant> Values;
for (auto Val : vTest)
{
Values.push_back(Val);
}
fnChecker(Values);
}
{
std::vector<openEdr::Variant> Values;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
Values.push_back(*Iter);
}
fnChecker(Values);
}
{
std::vector<openEdr::Variant> Values;
for (auto Val : vTest.getUnsafeIterable())
{
Values.push_back(Val);
}
fnChecker(Values);
}
{
std::vector<openEdr::Variant> Values;
for (auto Iter = vTest.unsafeBegin(); Iter != vTest.unsafeEnd(); ++Iter)
{
Values.push_back(*Iter);
}
fnChecker(Values);
}
}
//
//
//
TEST_CASE("Variant. BasicSequence. Sequence methods")
{
testSequenceMethodsForSupportedType<Variant>([](Variant vSeq)
{
return Variant(vSeq);
});
}
//
//
//
TEST_CASE("Variant. BasicSequence. Iteration")
{
testSequenceIteration<Variant>([](Variant vSeq)
{
return Variant(vSeq);
});
}
//
//
//
TEST_CASE("Variant. BasicSequence. Dictionary methods")
{
testDictionaryMethodsForNotsupportedType(Sequence({1,"str"}));
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. Clone
//
//
//
//
TEST_CASE("Variant. Clone")
{
SECTION("Scalar types + object")
{
Variant vScalars[] = { Variant(nullptr), Variant(true), Variant(10), Variant("XXXX"), Variant(createObject<SimpleTestObject>()) };
for(auto& vScalar : vScalars)
{
Variant v1 = vScalar;
Variant v2 = v1.clone();
v1 = nullptr;
REQUIRE(v2 == vScalar);
}
}
SECTION("Dictionary")
{
Variant v1 = Dictionary({
{"a", 1},
{"b", "str"}
});
Variant v2 = v1.clone();
v1.put("a", true);
v1.put("c", 100);
REQUIRE(v1.getSize() == 3);
REQUIRE(v1["a"] == true);
REQUIRE(v1["b"] == "str");
REQUIRE(v1["c"] == 100);
REQUIRE(v2.getSize() == 2);
REQUIRE(v2["a"] == 1);
REQUIRE(v2["b"] == "str");
}
SECTION("Sequence")
{
Variant v1 = Sequence({ true, "str" });
Variant v2 = v1.clone();
v1.insert(0, 10);
REQUIRE(v1.getSize() == 3);
REQUIRE(v1[0] == 10);
REQUIRE(v1[1] == true);
REQUIRE(v1[2] == "str");
REQUIRE(v2.getSize() == 2);
REQUIRE(v2[0] == true);
REQUIRE(v2[1] == "str");
}
SECTION("Deep structure")
{
Variant v1 = Sequence({
true,
Dictionary({
{"a", "str"},
{"b", Sequence({ 10 })},
{"c", Dictionary({
{ "a", 100}
})},
}),
Sequence({ false })
});
Variant v2 = v1.clone();
v1.erase(0);
Variant(v1[0]).put("a", false);
Variant(v1[0]["b"]).clear();
Variant(v1[0]["c"]).put("b", "str");
Variant(v1[1]).push_back(10);
REQUIRE(v1.getSize() == 2);
REQUIRE(v1[0].getSize() == 3);
REQUIRE(v1[0]["a"] == false);
REQUIRE(v1[0]["b"].getSize() == 0);
REQUIRE(v1[0]["c"].getSize() == 2);
REQUIRE(v1[0]["c"]["a"] == 100);
REQUIRE(v1[0]["c"]["b"] == "str");
REQUIRE(v1[1].getSize() == 2);
REQUIRE(v1[1][0] == false);
REQUIRE(v1[1][1] == 10);
REQUIRE(v2.getSize() == 3);
REQUIRE(v2[0] == true);
REQUIRE(v2[1].getSize() == 3);
REQUIRE(v2[1]["a"] == "str");
REQUIRE(v2[1]["b"].getSize() == 1);
REQUIRE(v2[1]["b"][0] == 10);
REQUIRE(v2[1]["c"].getSize() == 1);
REQUIRE(v2[1]["c"]["a"] == 100);
REQUIRE(v2[2].getSize() == 1);
REQUIRE(v2[2][0] == false);
}
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. Comparison
//
//
//
//
template <size_t TupleIndex, typename T>
void CompareValues(T Tuple, const std::vector<openEdr::Variant>& Values, size_t ValuesIndex)
{
CAPTURE(TupleIndex, ValuesIndex);
REQUIRE_NOTHROW([&]() {
const openEdr::Variant & Value = Values[ValuesIndex];
if (TupleIndex == ValuesIndex)
{
CHECK(Value == std::get<TupleIndex>(Tuple));
CHECK_FALSE(Value != std::get<TupleIndex>(Tuple));
}
else
{
CHECK(Value != std::get<TupleIndex>(Tuple));
CHECK_FALSE(Value == std::get<TupleIndex>(Tuple));
}
}());
}
//
//
//
TEST_CASE("Variant.comparison")
{
SECTION("different_type_of_Variant")
{
std::vector<Variant> Values = { Variant(nullptr), Variant(true), Variant(1), Variant("XXXX"), Dictionary({{"a",1}}), Sequence(1), Variant(createObject<SimpleTestObject>())};
std::vector<Variant> Values2 = Values;
for (size_t i = 0; i < std::size(Values); ++i)
{
for (size_t j = 0; j < std::size(Values2); ++j)
{
CAPTURE(i, j);
if (i == j)
{
CHECK(Values[i] == Values2[j]);
CHECK_FALSE(Values[i] != Values2[j]);
}
else
{
CHECK(Values[i] != Values2[j]);
CHECK_FALSE(Values[i] == Values2[j]);
}
}
}
}
SECTION("Variant_and_literal")
{
std::vector<Variant> Values = { Variant(nullptr), Variant(true), Variant(1), Variant("XXXX"), Dictionary({{"a",1}}), Sequence(1), Variant(createObject<SimpleTestObject>()) };
auto Literals = std::make_tuple(nullptr, true, 1, "XXXX");
for (size_t i = 0; i < std::size(Values); ++i)
{
CompareValues<0>(Literals, Values, i);
CompareValues<1>(Literals, Values, i);
CompareValues<2>(Literals, Values, i);
CompareValues<3>(Literals, Values, i);
}
}
SECTION("Sequence")
{
#define TEST_SEQUENCE_VALUE Sequence({nullptr, true, 1, "XXXX"})
Sequence seq1 = TEST_SEQUENCE_VALUE;
Sequence seq2 = TEST_SEQUENCE_VALUE;
CHECK(seq1 == seq2);
CHECK_FALSE(seq1 != seq2);
CHECK(Variant(seq1) == Variant(seq2));
CHECK_FALSE(Variant(seq1) != Variant(seq2));
seq1.push_back(1);
CHECK_FALSE(seq1 == seq2);
CHECK(seq1 != seq2);
CHECK_FALSE(Variant(seq1) == Variant(seq2));
CHECK(Variant(seq1) != Variant(seq2));
seq2.push_back(0);
CHECK_FALSE(seq1 == seq2);
CHECK(seq1 != seq2);
CHECK_FALSE(Variant(seq1) == Variant(seq2));
CHECK(Variant(seq1) != Variant(seq2));
#undef TEST_SEQUENCE_VALUE
}
SECTION("Dictionary")
{
#define TEST_DICTIONARY_VALUE Dictionary({ {"a", nullptr} , {"b", true}, {"c", 1}, {"d", "XXXX"}})
Dictionary v1 = TEST_DICTIONARY_VALUE;
Dictionary v2 = TEST_DICTIONARY_VALUE;
CHECK(v1 == v2);
CHECK_FALSE(v1 != v2);
CHECK(Variant(v1) == Variant(v2));
CHECK_FALSE(Variant(v1) != Variant(v2));
v1.put("e", 100);
CHECK_FALSE(v1 == v2);
CHECK(v1 != v2);
CHECK_FALSE(Variant(v1) == Variant(v2));
CHECK(Variant(v1) != Variant(v2));
v2.put("e", 200);
CHECK_FALSE(v1 == v2);
CHECK(v1 != v2);
CHECK_FALSE(Variant(v1) == Variant(v2));
CHECK(Variant(v1) != Variant(v2));
#undef TEST_DICTIONARY_VALUE
}
SECTION("deep_comparison")
{
Variant v1 = Sequence({
true,
Dictionary({
{"a", "str"},
{"b", Sequence({ 10 })},
{"c", Dictionary({
{ "a", 100}
})},
}),
Sequence({ false })
});
Variant v2 = Sequence({
true,
Dictionary({
{"a", "str"},
{"b", Sequence({ 10 })},
{"c", Dictionary({
{ "a", 100}
})},
}),
Sequence({ false })
});
CHECK(v1 == v2);
CHECK_FALSE(v1 != v2);
CHECK(Variant(v1) == Variant(v2));
CHECK_FALSE(Variant(v1) != Variant(v2));
Variant(v1[1]).put("e", 100);
CHECK_FALSE(v1 == v2);
CHECK(v1 != v2);
CHECK_FALSE(Variant(v1) == Variant(v2));
CHECK(Variant(v1) != Variant(v2));
Variant(v2[1]).put("e", 100);
CHECK(v1 == v2);
CHECK_FALSE(v1 != v2);
CHECK(Variant(v1) == Variant(v2));
CHECK_FALSE(Variant(v1) != Variant(v2));
}
SECTION("object")
{
Variant vObj1(createObject<SimpleTestObject>());
Variant vObj2(createObject<SimpleTestObject>());
CHECK(vObj1 != vObj2);
CHECK_FALSE(vObj1 == vObj2);
Variant vObj1Copy = vObj1;
CHECK(vObj1 == vObj1Copy);
CHECK_FALSE(vObj1 != vObj1Copy);
}
}
//////////////////////////////////////////////////////////////////////////
//
// Sequence
//
//
//
//
TEST_CASE("Sequence.constructor")
{
SECTION("default_constructor")
{
Sequence seq;
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 0);
}
SECTION("from_null")
{
Sequence seq(Variant(nullptr));
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 0);
}
SECTION("from_scalar_and_object")
{
Variant Scalars[] = { Variant(true), Variant(1), Variant("XXXX"), Variant(createObject<SimpleTestObject>()) };
for (auto& vScalar : Scalars)
{
Sequence seq(vScalar);
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 1);
REQUIRE(seq[0] == vScalar);
}
}
SECTION("from_Sequence")
{
Variant seq1 = Sequence({ 1, "str" });
Sequence seq2(seq1);
REQUIRE(Variant(seq2).getType() == Variant::ValueType::Sequence);
REQUIRE(seq2.getSize() == 2);
// test both refers to same object
seq1.clear();
REQUIRE(seq2.isEmpty());
}
SECTION("from_Dictionary")
{
// Test count
{
Variant vDict = Dictionary({ {"a", 1}, {"b", 10 }, {"c", 100 } });
Sequence seq(vDict);
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 3);
int nSum = 0;
for (auto vVal : seq)
nSum += int(vVal);
REQUIRE(nSum == 111);
}
// Test reference
{
Variant vDict = Dictionary({ {"a", Sequence({1,2,3}) } });
Sequence seq(vDict);
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 1);
REQUIRE(seq[0].getSize() == 3);
Variant(vDict["a"]).clear();
REQUIRE(seq[0].getSize() == 0);
}
}
SECTION("from_initializer_list")
{
Variant vDict = Dictionary();
vDict.put("aaa", 20);
Variant vInt = 10;
Variant vTest = Sequence({ nullptr, true, 1u, -1, "str", vInt, vDict, std::string("std::string") });
REQUIRE(vTest.getType() == Variant::ValueType::Sequence);
REQUIRE(vTest.getSize() == 8);
REQUIRE(vTest[0].isNull());
REQUIRE(vTest[1] == true);
REQUIRE(vTest[2] == 1);
REQUIRE(vTest[3] == -1);
REQUIRE(vTest[4] == "str");
REQUIRE(vTest[5] == 10);
REQUIRE(vTest[6]["aaa"] == 20);
REQUIRE(vTest[7] == "std::string");
}
}
//
//
//
TEST_CASE("Sequence. Iteration")
{
testSequenceIteration<Sequence>([](Variant vSeq)
{
return Sequence(vSeq);
});
}
//
//
//
TEST_CASE("Sequence. Sequence methods")
{
testSequenceMethodsForSupportedType<Sequence>([](Variant vSeq)
{
return Sequence(vSeq);
});
}
//////////////////////////////////////////////////////////////////////////
//
// Dictionary.
//
//
//
//
TEST_CASE("Dictionary. Default constructor")
{
Dictionary vDict;
REQUIRE(vDict.isEmpty());
openEdr::Variant vTest = vDict;
REQUIRE(vTest.getType() == Variant::ValueType::Dictionary);
REQUIRE(vTest.isEmpty());
}
//
//
//
TEST_CASE("Dictionary. Constructor from Variant contained dictionary")
{
std::string sJson = R"json({
"a": 1,
"b": -1,
"c": "str",
"d": {"a":1},
"e": [1, "a"],
"f": true,
"g": null
})json";
Variant vTest = variant::deserializeFromJson(sJson);
REQUIRE(vTest.getType() == Variant::ValueType::Dictionary);
Dictionary vDict(vTest);
REQUIRE(Variant(vDict).getType() == Variant::ValueType::Dictionary);
REQUIRE(vDict["f"] == true);
vDict.put("XXX", 10);
REQUIRE(vDict["XXX"] == 10);
}
//
//
//
TEST_CASE("Dictionary. Constructor from Variant contained not dictionary")
{
Variant NonDicts[] = { Variant(nullptr), Variant(true), Variant(10), Variant("XXXX"), Sequence(), Variant(createObject<SimpleTestObject>()) };
for (Variant& vNonDict : NonDicts)
{
REQUIRE(vNonDict.getType() != Variant::ValueType::Dictionary);
REQUIRE_THROWS_AS([&]() {
Dictionary vDict(vNonDict);
}(), error::TypeError);
}
}
//
//
//
TEST_CASE("Dictionary. Constructor from initializer list")
{
Variant vDict = Dictionary({ {"aaa", 20} });
Variant vTest = Dictionary({
{"a", nullptr},
{"b", true},
{"c", 1u},
{"d", -1},
{"e", "str"},
{"f", Variant(10)},
{"g", vDict},
{"h", Dictionary({ {"bbb", 20} })},
{"j", Sequence({1, 2})},
{"k", std::string("std::string")},
{std::string("l"), 1},
{std::string("m"), 1},
{std::string("") + "n", 1}
});
REQUIRE(vTest.getType() == Variant::ValueType::Dictionary);
REQUIRE(vTest.getSize() == 13);
REQUIRE(vTest["a"].isNull());
REQUIRE(vTest["b"] == true);
REQUIRE(vTest["c"] == 1);
REQUIRE(vTest["d"] == -1);
REQUIRE(vTest["e"] == "str");
REQUIRE(vTest["f"] == 10);
REQUIRE(vTest["g"]["aaa"] == 20);
REQUIRE(vTest["h"]["bbb"] == 20);
REQUIRE(vTest["j"][0] == 1);
REQUIRE(vTest["j"][1] == 2);
REQUIRE(vTest["k"] == "std::string");
REQUIRE(vTest["l"] == 1);
REQUIRE(vTest["m"] == 1);
REQUIRE(vTest["n"] == 1);
}
//
//
//
TEST_CASE("Dictionary. Dictionary methods")
{
testDictionaryMethodsForSupportedType<Dictionary>([](Variant vDict)
{
return Dictionary(vDict);
});
}
//
//
//
TEST_CASE("Dictionary. Iteration")
{
SECTION("Enum values")
{
std::string sJson = R"json({
"a": 1,
"b": -1,
"c": "str",
"d": {"a":1},
"e": [1, "a"],
"f": true,
"g": null
})json";
Dictionary vTest(openEdr::variant::deserializeFromJson(sJson));
REQUIRE(Variant(vTest).getType() == Variant::ValueType::Dictionary);
auto fnChecker = [](const std::vector<size_t>& Counters)
{
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::Integer)] == 2);
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::String)] == 1);
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::Sequence)] == 1);
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::Dictionary)] == 1);
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::Boolean)] == 1);
REQUIRE(Counters[static_cast<size_t>(Variant::ValueType::Null)] == 1);
};
{
std::vector<size_t> Counters(c_nValueTypeCount);
for (auto Item : vTest)
{
Counters[static_cast<size_t>(Variant(Item.second).getType())]++;
}
fnChecker(Counters);
}
{
std::vector<size_t> Counters(c_nValueTypeCount);
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
Counters[static_cast<size_t>(Variant(Iter->second).getType())]++;
}
fnChecker(Counters);
}
{
std::vector<size_t> Counters(c_nValueTypeCount);
for (auto Item : vTest.getUnsafeIterable())
{
Counters[static_cast<size_t>(Variant(Item.second).getType())]++;
}
fnChecker(Counters);
}
}
SECTION("Enum keys")
{
std::string sJson = R"json({
"a": 1,
"b": -1,
"c": "str",
"d": {"a":1},
"e": [1, "a"],
"f": true,
"g": null
})json";
Dictionary vTest(openEdr::variant::deserializeFromJson(sJson));
REQUIRE(Variant(vTest).getType() == Variant::ValueType::Dictionary);
std::vector<std::string> ExpectedResult = { "a", "b", "c", "d", "e", "f", "g" };
{
std::vector<std::string> TestKeyList;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
TestKeyList.push_back(std::string(Iter->first));
}
std::sort(TestKeyList.begin(), TestKeyList.end());
REQUIRE(TestKeyList == ExpectedResult);
}
{
std::vector<std::string> TestKeyList;
for (auto Item : vTest)
{
TestKeyList.push_back(std::string(Item.first));
}
std::sort(TestKeyList.begin(), TestKeyList.end());
REQUIRE(TestKeyList == ExpectedResult);
}
}
}
//
//
//
TEST_CASE("Variant. queryInterface")
{
// object type
{
ObjPtr<SimpleTestObject> pClass = createObject<SimpleTestObject>();
Variant vTest(pClass);
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterface<IObject>(vTest);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterface<ISimpleTestObject>(vTest);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterface<SimpleTestObject>(vTest);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_THROWS_AS([&]() {
auto pObj = queryInterface<INotImplementedInterface>(vTest);
}(), std::runtime_error);
}
// other types
{
Variant NonObjects[] = { Variant(nullptr), Variant(true), Variant(10), Variant("XXXX"), Sequence(), Dictionary() };
for (auto& vTest : NonObjects)
{
REQUIRE_THROWS_AS([&]() {
auto pObj = queryInterface<IObject>(vTest);
}(), error::TypeError);
}
}
}
//
//
//
TEST_CASE("Variant. queryInterfaceSafe")
{
// object type
{
ObjPtr<SimpleTestObject> pClass = createObject<SimpleTestObject>();
Variant vTest(pClass);
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterfaceSafe<IObject>(vTest);
REQUIRE_FALSE(pObj == nullptr);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterfaceSafe<ISimpleTestObject>(vTest);
REQUIRE_FALSE(pObj == nullptr);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterfaceSafe<SimpleTestObject>(vTest);
REQUIRE_FALSE(pObj == nullptr);
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterfaceSafe<INotImplementedInterface>(vTest);
REQUIRE(pObj == nullptr);
}());
}
// other types
{
Variant NonObjects[] = { Variant(nullptr), Variant(true), Variant(10), Variant("XXXX"), Sequence(), Dictionary() };
for (auto& vTest : NonObjects)
{
REQUIRE_NOTHROW([&]() {
auto pObj = queryInterfaceSafe<INotImplementedInterface>(vTest);
REQUIRE(pObj == nullptr);
}());
}
}
}
//////////////////////////////////////////////////////////////////////////
//
// Variant. Object
//
//
//
//
TEST_CASE("Variant. Object. Conversion")
{
// Constructor
{
ObjPtr<SimpleTestObject> pClass = createObject<SimpleTestObject>();
Variant vTest(pClass);
REQUIRE(vTest.getType() == Variant::ValueType::Object);
ObjPtr<IObject> pObj = vTest;
REQUIRE(pObj->getId() == pClass->getId());
}
// operator =
{
ObjPtr<SimpleTestObject> pClass = createObject<SimpleTestObject>();
Variant vTest;
vTest = pClass;
REQUIRE(vTest.getType() == Variant::ValueType::Object);
ObjPtr<IObject> pObj = vTest;
REQUIRE(pObj->getId() == pClass->getId());
}
// operator Type
{
ObjPtr<SimpleTestObject> pClass = createObject<SimpleTestObject>();
Variant vTest(pClass);
REQUIRE(vTest.getType() == Variant::ValueType::Object);
REQUIRE_NOTHROW([&]() {
ObjPtr<IObject> pObj = vTest;
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
ObjPtr<ISimpleTestObject> pObj = vTest;
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_NOTHROW([&]() {
ObjPtr<SimpleTestObject> pObj = vTest;
REQUIRE(pObj->getId() == pClass->getId());
}());
REQUIRE_THROWS_AS([&]() {
ObjPtr<INotImplementedInterface> pObj = vTest;
}(), std::runtime_error);
}
}
//
//
//
TEST_CASE("Variant. Object. Iteration + size + empty")
{
IterateLikeSalarType(Variant(createObject<SimpleTestObject>()));
}
//
//
//
TEST_CASE("Variant. Object. Sequence and Dictionary methods")
{
REQUIRE_NOTHROW(testSequenceMethodsForNotsupportedType(Variant(createObject<SimpleTestObject>())));
REQUIRE_NOTHROW(testDictionaryMethodsForNotsupportedType(Variant(createObject<SimpleTestObject>())));
// clear
REQUIRE_THROWS_AS([&]() {
Variant vTest = Variant(createObject<SimpleTestObject>());
vTest.clear();
}(), error::TypeError);
}
//
//
//
TEST_CASE("Variant. Object. Clone")
{
Variant vObj(createObject<SimpleTestObject>());
Variant vObjClone = vObj.clone();
CHECK(vObj == vObjClone);
}
//////////////////////////////////////////////////////////////////////////
//
// Dictionary Proxy
//
//
// TestDictionaryProxy is plain wrapper of Dictionary
//
class TestDictionaryProxy : public ObjectBase <>, public variant::IDictionaryProxy
{
Dictionary m_Dict;
class Iterator : public variant::IIterator
{
Dictionary::const_iterator m_Iter;
Dictionary::const_iterator m_End;
public:
Iterator(Dictionary Dict, bool fUnsafe)
{
m_Iter = fUnsafe ? Dict.unsafeBegin() : Dict.begin();
m_End = fUnsafe ? Dict.unsafeEnd() : Dict.end();
}
void goNext() override { ++m_Iter; }
bool isEnd() const override { return !(m_Iter != m_End); }
variant::DictionaryKeyRefType getKey() const override { return m_Iter->first; }
const Variant getValue() const override { return m_Iter->second; }
};
public:
void finalConstruct(Variant vCfg)
{
m_Dict = Dictionary(vCfg);
}
Size getSize() const override { return m_Dict.getSize(); }
bool isEmpty() const override { return m_Dict.isEmpty(); }
bool has(variant::DictionaryKeyRefType sName) const override { return m_Dict.has(sName); }
Size erase(variant::DictionaryKeyRefType sName) override { return m_Dict.erase(sName); }
void clear() override { return m_Dict.clear(); }
Variant get(variant::DictionaryKeyRefType sName) const override { return m_Dict.get(sName); }
void put(variant::DictionaryKeyRefType sName, const Variant& Value) override { return m_Dict.put(sName, Value); }
std::unique_ptr<variant::IIterator> createIterator(bool fUnsafe) const override
{
return std::make_unique<Iterator>(m_Dict, fUnsafe);
}
std::optional<Variant> getSafe(variant::DictionaryKeyRefType sName) const override
{
if (!m_Dict.has(sName))
return {};
return m_Dict.get(sName);
}
Variant getInternaValue()
{
return m_Dict;
}
};
//
//
//
TEST_CASE("Variant. IDictionaryProxy. Dictionary methods")
{
testDictionaryMethodsForSupportedType<Variant>([](Variant vDict)
{
return Variant(createObject<TestDictionaryProxy>(vDict));
});
}
//
//
//
TEST_CASE("Variant.IDictionaryProxy_sequence_methods_support")
{
testSequenceMethodsForNotsupportedType(Variant(createObject<TestDictionaryProxy>(Dictionary())));
}
//
//
//
TEST_CASE("Variant.IDictionaryProxy_comparison")
{
#define TEST_DICTIONARY_VALUE Dictionary({ {"a", nullptr} , {"b", true}, {"c", Dictionary ({{"a", 1}}) }})
Variant v1 = createObject<TestDictionaryProxy>(TEST_DICTIONARY_VALUE);
Variant v2 = TEST_DICTIONARY_VALUE;
CHECK(v1 == v2);
CHECK(v2 == v1);
CHECK_FALSE(v2 != v1);
CHECK_FALSE(v1 != v2);
v1.put("e", 100);
CHECK_FALSE(v1 == v2);
CHECK_FALSE(v2 == v1);
CHECK(v2 != v1);
CHECK(v1 != v2);
v2.put("e", 200);
CHECK_FALSE(v1 == v2);
CHECK_FALSE(v2 == v1);
CHECK(v2 != v1);
CHECK(v1 != v2);
#undef TEST_DICTIONARY_VALUE
}
//
//
//
TEST_CASE("Variant.IDictionaryProxy_iteration_support")
{
Variant vSrc = Dictionary({
{"a", nullptr},
{"b", 1},
{"c", "str"},
});
std::vector<Variant> Ethalons;
{
REQUIRE(vSrc.getSize() == 3);
for (auto kv : Dictionary(vSrc))
Ethalons.push_back(kv.second);
REQUIRE(Ethalons.size() == 3);
}
Variant vTest(createObject<TestDictionaryProxy>(Dictionary(vSrc)));
// size
REQUIRE(vTest.getSize() == Ethalons.size());
// empty
REQUIRE(!vTest.isEmpty());
// ranged for
{
size_t nIndex = 0;
for (auto vValue : vTest)
{
REQUIRE(vValue == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// iterator
{
size_t nIndex = 0;
for (auto Iter = vTest.begin(); Iter != vTest.end(); ++Iter)
{
REQUIRE(*Iter == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// unsafe ranged for
{
size_t nIndex = 0;
for (auto vValue : vTest.getUnsafeIterable())
{
REQUIRE(vValue == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
// unsafe iterator
{
size_t nIndex = 0;
for (auto Iter = vTest.unsafeBegin(); Iter != vTest.unsafeEnd(); ++Iter)
{
REQUIRE(*Iter == Ethalons.at(nIndex));
++nIndex;
}
REQUIRE(nIndex == Ethalons.size());
}
}
//
//
//
//ctorVariantWithIDictionaryProxy
//constructor_from_Variant_with_IDictionaryProxy
TEST_CASE("Dictionary. Constructor from Variant contained IDictionaryProxy")
{
std::string sJson = R"json({
"a": 1,
"b": -1,
"c": "str",
"d": {"a":1},
"e": [1, "a"],
"f": true,
"g": null
})json";
Variant vTest = createObject<TestDictionaryProxy>(variant::deserializeFromJson(sJson));
REQUIRE(vTest.getType() == Variant::ValueType::Object);
REQUIRE(vTest.isDictionaryLike());
Dictionary vDict(vTest);
REQUIRE(Variant(vDict).getType() == Variant::ValueType::Object);
REQUIRE(Variant(vDict).isDictionaryLike());
REQUIRE(vDict["f"] == true);
vDict.put("XXX", 10);
REQUIRE(vDict["XXX"] == 10);
}
//
//
//
TEST_CASE("Sequence. Constructor from IDictionaryProxy")
{
Variant vDictionaryProxy = createObject<TestDictionaryProxy>(Dictionary({ {"a", 1}, {"b", 10 }, {"c", 100 } }));
Sequence seq(vDictionaryProxy);
REQUIRE(Variant(seq).getType() == Variant::ValueType::Sequence);
REQUIRE(seq.getSize() == 3);
int nSum = 0;
for (auto vVal : seq)
nSum += int(vVal);
REQUIRE(nSum == 111);
}
//////////////////////////////////////////////////////////////////////////
//
// Sequence Proxy
//
//
// TestSequenceProxy is plain wrapper of Dictionary
//
class TestSequenceProxy : public ObjectBase <>, public variant::ISequenceProxy
{
Sequence m_Seq;
class Iterator : public variant::IIterator
{
Sequence::const_iterator m_Iter;
Sequence::const_iterator m_End;
public:
Iterator(Sequence Cont, bool fUnsafe)
{
m_Iter = fUnsafe ? Cont.unsafeBegin() : Cont.begin();
m_End = fUnsafe ? Cont.unsafeEnd() : Cont.end();
}
virtual void goNext() override { ++m_Iter; }
virtual bool isEnd() const override { return !(m_Iter != m_End); }
virtual variant::DictionaryKeyRefType getKey() const override { error::TypeError(SL).throwException(); }
virtual const Variant getValue() const override { return *m_Iter; }
};
public:
void finalConstruct(Variant vCfg)
{
m_Seq = Sequence(vCfg);
}
virtual Size getSize() const { return m_Seq.getSize(); }
virtual bool isEmpty() const { return m_Seq.isEmpty(); }
virtual void clear() { return m_Seq.clear(); }
virtual Size erase(Size nIndex) override { return m_Seq.erase(nIndex); }
virtual Variant get(Size nIndex) const override { return m_Seq.get(nIndex); }
virtual void put(Size nIndex, const Variant& Value) override { return m_Seq.put(nIndex, Value); }
virtual void push_back(const Variant& vValue) override { return m_Seq.push_back(vValue); }
virtual void setSize(Size nSize) override { return m_Seq.setSize(nSize); }
virtual void insert(Size nIndex, const Variant& vValue) override { return m_Seq.insert(nIndex, vValue); }
virtual std::unique_ptr<variant::IIterator> createIterator(bool fUnsafe) const override
{
return std::make_unique<Iterator>(m_Seq, fUnsafe);
}
virtual std::optional<Variant> getSafe(Size nIndex) const override
{
if(nIndex >= m_Seq.getSize())
return {};
return m_Seq.get(nIndex);
}
};
//
//
//
TEST_CASE("Variant. ISequenceProxy. Dictionary methods")
{
testDictionaryMethodsForNotsupportedType(createObject<TestSequenceProxy>(Sequence({ 1,"str" })));
}
//
//
//
TEST_CASE("Variant. ISequenceProxy. Sequence methods")
{
testSequenceMethodsForSupportedType<Variant>([](Variant vSeq)
{
return Variant(createObject<TestSequenceProxy>(vSeq));
});
}
//
//
//
TEST_CASE("Variant. ISequenceProxy. Iteration")
{
testSequenceIteration<Variant>([](Variant vSeq)
{
return Variant(createObject<TestSequenceProxy>(vSeq));
});
}
//
//
//
TEST_CASE("Sequence. Constructor from ISequenceProxy")
{
Variant vSeqProxy(createObject<TestSequenceProxy>(Sequence({ 1, "str" })));
Sequence seq(vSeqProxy);
REQUIRE(Variant(seq).getType() == Variant::ValueType::Object);
REQUIRE(vSeqProxy.getSize() == 2);
REQUIRE(Variant(seq) == vSeqProxy);
// test both refers to same object
seq.clear();
REQUIRE(vSeqProxy.isEmpty());
}
//
//
//
TEST_CASE("Variant.ISequenceProxy_comparison")
{
#define TEST_SEQUENCE_VALUE Sequence({nullptr, true, 1, "XXXX", Dictionary ({{"a", 1}})})
Variant v1 = createObject<TestSequenceProxy>(TEST_SEQUENCE_VALUE);
Variant v2 = TEST_SEQUENCE_VALUE;
CHECK(v1 == v2);
CHECK(v2 == v1);
CHECK_FALSE(v2 != v1);
CHECK_FALSE(v1 != v2);
v1.push_back(1);
CHECK_FALSE(v1 == v2);
CHECK_FALSE(v2 == v1);
CHECK(v2 != v1);
CHECK(v1 != v2);
v2.push_back(0);
CHECK_FALSE(v1 == v2);
CHECK_FALSE(v2 == v1);
CHECK(v2 != v1);
CHECK(v1 != v2);
#undef TEST_SEQUENCE_VALUE
}
//////////////////////////////////////////////////////////////////////////
//
//
//
TEST_CASE("Variant.getByPath")
{
SECTION("EmptyString")
{
Variant v = Dictionary({ {"a", 1} });
REQUIRE(variant::getByPath(v, "") == v);
REQUIRE(*variant::getByPathSafe(v, "") == v);
}
SECTION("SimpleDictKey")
{
Variant v = Dictionary({ {"abc", 1} });
REQUIRE(variant::getByPath(v, "abc") == 1);
REQUIRE(variant::getByPath(v, "abc", Variant()) == 1);
REQUIRE(*variant::getByPathSafe(v, "abc") == 1);
}
SECTION("SimpleSeqKey")
{
Variant v = Sequence({"abc", 1});
REQUIRE(variant::getByPath(v, "[1]") == 1);
REQUIRE(variant::getByPath(v, "[1]", Variant()) == 1);
REQUIRE(*variant::getByPathSafe(v, "[1]") == 1);
}
SECTION("ComplexKey")
{
Variant v = Dictionary({
{"a", Sequence({
1,
Dictionary({
{"a", 1},
})
})},
});
REQUIRE(variant::getByPath(v, "a[1].a") == 1);
REQUIRE(variant::getByPath(v, "a[1].a", Variant()) == 1);
REQUIRE(*variant::getByPathSafe(v, "a[1].a") == 1);
}
SECTION("InvalidType")
{
Variant v(10);
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, "a");
}(), error::TypeError);
REQUIRE(variant::getByPath(v, "a", Variant()).isNull());
REQUIRE_FALSE(variant::getByPathSafe(v, "a").has_value());
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, "[1]");
}(), error::TypeError);
REQUIRE(variant::getByPath(v, "[1]", Variant()).isNull());
REQUIRE_FALSE(variant::getByPathSafe(v, "[1]").has_value());
}
SECTION("OutOfRange_seq")
{
Variant v = Sequence({1});
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, "[1]");
}(), error::OutOfRange);
REQUIRE(variant::getByPath(v, "[1]", Variant()).isNull());
REQUIRE_FALSE(variant::getByPathSafe(v, "[1]").has_value());
}
SECTION("OutOfRange_dict")
{
Variant v = Dictionary({ {"a", 1} });
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, "b");
}(), error::OutOfRange);
REQUIRE(variant::getByPath(v, "b", Variant()).isNull());
REQUIRE_FALSE(variant::getByPathSafe(v, "b").has_value());
}
SECTION("Invalid path")
{
Variant v = Sequence({ Dictionary({ {"a", 1} }) });
std::string invalidPaths[] = { "[a]", ".", "[0]a", "[0]..a","[1000000000000000000000]" };
for (auto sInvalidPath : invalidPaths)
{
CAPTURE(sInvalidPath);
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, sInvalidPath);
}(), error::InvalidArgument);
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPath(v, sInvalidPath, Variant());
}(), error::InvalidArgument);
REQUIRE_THROWS_AS([&]() {
(void)variant::getByPathSafe(v, sInvalidPath);
}(), error::InvalidArgument);
}
}
}
//
//
//
TEST_CASE("Variant.putByPath")
{
SECTION("SimpleDictKey")
{
Variant v = Dictionary({ {"aaa", 1} });
variant::putByPath(v, "bbb", 2);
REQUIRE(v == Dictionary({ {"aaa", 1}, {"bbb", 2} }));
}
SECTION("SimpleSeqKey")
{
Variant v = Sequence({0, 1, 2});
variant::putByPath(v, "[1]", 100);
REQUIRE(v == Sequence({ 0, 100, 2 }));
}
SECTION("ComplexKey")
{
Variant v = Dictionary({
{"a", Sequence({
1,
Dictionary({
{"a", 1},
})
})},
});
variant::putByPath(v, "a[1].b", 100);
variant::putByPath(v, "a[0]", 200);
Variant vEthalon = Dictionary({
{"a", Sequence({
200,
Dictionary({
{"a", 1},
{"b", 100},
})
})},
});
REQUIRE(v == vEthalon);
}
SECTION("InvalidType")
{
Variant v(10);
REQUIRE_THROWS_AS([&]() {
variant::putByPath(v, "a", 100);
}(), error::TypeError);
REQUIRE_THROWS_AS([&]() {
variant::putByPath(v, "[1]", 100);
}(), error::TypeError);
}
SECTION("OutOfRange_seq")
{
Variant v = Sequence({ 1 });
REQUIRE_THROWS_AS([&]() {
variant::putByPath(v, "[1]", 100);
}(), error::OutOfRange);
}
SECTION("Invalid path")
{
Variant v = Sequence();
std::string invalidPaths[] = { "[a]", ".", "[0]a", "[1000000000000000000000]" };
for (auto sInvalidPath : invalidPaths)
{
CAPTURE(sInvalidPath);
REQUIRE_THROWS_AS([&]() {
variant::putByPath(v, sInvalidPath, 100);
}(), error::InvalidArgument);
}
}
}
//
//
//
TEST_CASE("Variant.putByPath_null")
{
auto fnScenario = [&](Variant vData, std::string_view sPath, Variant vEthalon)
{
Variant vResult = vData.clone();
variant::putByPath(vResult, sPath, nullptr);
REQUIRE(vResult == vEthalon);
};
const std::map<std::string, std::tuple<Variant, std::string, Variant>> mapData{
// sName: vData, sPath, vEthalon
{ "dictionary", {
Dictionary({ { "aaa", 1 }, { "bbb", 2 } }),
"aaa",
Dictionary({ { "aaa", nullptr }, { "bbb", 2 } })
} },
{ "sequence_exist", {
Sequence({ 0, 1, 2 }),
"[1]",
Sequence({ 0, nullptr, 2 })
} },
};
for (const auto& [sName, params] : mapData)
{
const auto& [vData, sPath, vEthalon] = params;
DYNAMIC_SECTION(sName)
{
REQUIRE_NOTHROW(fnScenario(vData, sPath, vEthalon));
}
}
}
//
//
//
TEST_CASE("Variant.putByPath_create_paths")
{
auto fnScenario = [&](Variant vData, std::string_view sPath, Variant vExpected)
{
Variant vResult = vData.clone();
variant::putByPath(vResult, sPath, "x", /* fCreatePaths */ true);
REQUIRE(vResult == vExpected);
};
const std::map<std::string, std::tuple<Variant, std::string, Variant>> mapData{
// sName: vData, sPath, vExpected
{ "dict_1", { Dictionary(), "a.b", Dictionary({ { "a", Dictionary({ { "b", "x" } }) } }) } },
{ "dict_2", { Dictionary(), "a.b.c",
Dictionary({ { "a", Dictionary({ { "b", Dictionary({ { "c", "x" } }) } }) } }) } },
{ "mixed_1", { Dictionary({ { "a", Dictionary({ { "b", Sequence({ Dictionary() }) } }) } }),
"a.b[0].c",
Dictionary({ { "a", Dictionary({ { "b", Sequence({ Dictionary({ { "c", "x" } }) }) } }) } }) } },
};
for (const auto& [sName, params] : mapData)
{
const auto& [vData, sPath, vExpected] = params;
DYNAMIC_SECTION(sName)
{
REQUIRE_NOTHROW(fnScenario(vData, sPath, vExpected));
}
}
const std::map<std::string, std::tuple<std::string, Variant, std::string>> mapBad{
// sName: sMsg, vData, sPath
{ "dict_nonexistent", { "Invalid sequence index: 0. Size: 0",
Dictionary(), "a[0]" } },
{ "seq_nonexistent_1", { "put() is not applicable for current type <Sequence>. Key: <a>",
Sequence(), "a[0]" } },
{ "seq_nonexistent_2", { "Invalid sequence index <0>",
Sequence(), "[0].a" } },
{ "seq_nonexistent_3", { "Invalid sequence index <5>",
Sequence(), "[5].a" } },
{ "mixed_nonexistent_seq", { "Invalid sequence index: 0. Size: 0",
Dictionary(), "a.b.c[0]" } },
// Dictionary({ { "a", Dictionary({ { "b", Dictionary({ { "c", Sequence({ "x" }) } }) } }) } }) } },
};
for (const auto& [sName, params] : mapBad)
{
const auto& [sMsg, vData, sPath] = params;
DYNAMIC_SECTION(sName)
{
REQUIRE_THROWS_WITH(fnScenario(vData, sPath, nullptr), sMsg);
}
}
}
//
//
//
TEST_CASE("Variant.deleteByPath")
{
auto fnScenario = [&](Variant vData, std::string_view sPath, Variant vEthalon)
{
Variant vResult = vData.clone();
variant::deleteByPath(vResult, sPath);
REQUIRE(vResult == vEthalon);
};
const std::map<std::string, std::tuple<Variant, std::string, Variant>> mapData{
// sName: vData, sPath, vExpected
{ "dict_exist", {
Dictionary({ { "aaa", 1 }, { "bbb", 2 } }),
"bbb",
Dictionary({ { "aaa", 1 } })
} },
{ "dict_not_exist_1", {
Dictionary({ { "aaa", 1 }, { "bbb", 2 } }),
"ccc",
Dictionary({ { "aaa", 1 }, { "bbb", 2 } }),
} },
{ "dict_not_exist_2", {
Dictionary({ { "a", 1 }, { "b", 2 } }),
"a.a1.a11",
Dictionary({ { "a", 1 }, { "b", 2 } }),
} },
{ "seq_exist", {
Sequence({ 0, 1, 2 }),
"[1]",
Sequence({ 0, 2 })
} },
{ "seq_not_exist", {
Sequence({ 0, 1, 2 }),
"[3]",
Sequence({ 0, 1, 2 }),
} },
};
for (const auto& [sName, params] : mapData)
{
const auto& [vData, sPath, vEthalon] = params;
DYNAMIC_SECTION(sName)
{
REQUIRE_NOTHROW(fnScenario(vData, sPath, vEthalon));
}
}
}
//
// Testing of dictionary merging for DictionaryLike types.
// Src and Dst can be different types
//
// @param fnCreateDst Function to create Dst from Dictionary
// @param fnCreateSrc Function to create Src from Dictionary
//
template<typename FnCreateDst, typename FnCreateSrc>
void testDictionaryMerging(FnCreateDst fnCreateDst, FnCreateSrc fnCreateSrc)
{
// MergeMode::New
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", 2}, {"c", 2} }));
vDst.merge(vSrc, Variant::MergeMode::New);
REQUIRE(vDst == Dictionary({ {"a", 1}, {"b", 1} , {"c", 2} }));
}
// MergeMode::Exist
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", 2}, {"c", 2} }));
vDst.merge( vSrc, Variant::MergeMode::Exist);
REQUIRE(vDst == Dictionary({ {"a", 1}, {"b", 2} }));
}
// MergeMode::All
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", 2}, {"c", 2} }));
vDst.merge( vSrc, Variant::MergeMode::All);
REQUIRE(vDst == Dictionary({ {"a", 1}, {"b", 2} , {"c", 2} }));
}
// MergeMode::CheckType
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", 2}, {"c", 2} }));
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::CheckType);
REQUIRE(vDst == Dictionary({ {"a", 1}, {"b", 2} , {"c", 2} }));
}
// MergeMode::CheckType
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", 2}, {"c", true} }));
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::CheckType);
REQUIRE(vDst == Dictionary({ {"a", 1}, {"b", 2} , {"c", true} }));
}
// MergeMode::CheckType
{
Variant vDst = fnCreateDst(Dictionary({ {"a", 1}, {"b", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"b", true}, {"c", true} }));
REQUIRE_THROWS_AS([&]() {
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::CheckType);
}(), error::InvalidArgument);
}
// MergeMode::MergeNestedDict
{
Variant vDst = fnCreateDst(Dictionary({ {"a", Dictionary({ {"a", 1}, {"b", 1}}) } }));
Variant vSrc = fnCreateSrc(Dictionary({ {"a", Dictionary({ {"a", 2}, {"c", 2}}) } }));
vDst.merge( vSrc, Variant::MergeMode::All);
REQUIRE(vDst == Dictionary({ {"a", Dictionary({ {"a", 2}, {"c", 2}}) } }));
}
// MergeMode::MergeNestedDict
{
Variant vDst = fnCreateDst(Dictionary({ {"a", Dictionary({ {"a", 1}, {"b", 1}}) } }));
Variant vSrc = fnCreateSrc(Dictionary({ {"a", Dictionary({ {"a", 2}, {"c", 2}}) } }));
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::MergeNestedDict);
REQUIRE(vDst == Dictionary({ {"a", Dictionary({ {"a", 2}, {"b", 1}, {"c", 2}}) } }));
}
// MergeMode::MergeNestedSeq
{
Variant vDst = fnCreateDst(Dictionary({ {"a", Sequence({1,2}) } }));
Variant vSrc = fnCreateSrc(Dictionary({ {"a", Sequence({3,4}) } }));
vDst.merge( vSrc, Variant::MergeMode::All);
REQUIRE(vDst == Dictionary({ {"a", Sequence({3,4}) } }));
}
// delete with null
{
Variant vDst = fnCreateDst(Dictionary({ {"a", Dictionary({ {"a", 1}, {"b", 1}}) }, {"b", 1}, {"c", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"a", Dictionary({ {"b", nullptr}}) }, {"b", nullptr}, {"d", nullptr} }));
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::MergeNestedDict);
REQUIRE(vDst == Dictionary({ {"a", Dictionary({ {"a", 1}}) }, {"c", 1} }));
}
// MergeMode::NonErasingNulls
{
Variant vDst = fnCreateDst(Dictionary({ {"a", Dictionary({ {"a", 1}, {"b", 1}}) }, {"b", 1}, {"c", 1} }));
Variant vSrc = fnCreateSrc(Dictionary({ {"a", Dictionary({ {"b", nullptr}}) }, {"b", nullptr}, {"d", nullptr} }));
vDst.merge( vSrc, Variant::MergeMode::All | Variant::MergeMode::MergeNestedDict |
Variant::MergeMode::NonErasingNulls);
REQUIRE(vDst == Dictionary({
{"a", Dictionary({ {"a", 1}, {"b", nullptr} })},
{"b", nullptr},
{"c", 1},
{"d", nullptr}
}));
}
}
//
//
//
TEST_CASE("Variant.merge")
{
SECTION("merging_with_null")
{
Variant DstList[] = {
// Scalar
Variant(), Variant(true), Variant(1), Variant("XXXX"),
// Dictionary and Sequence
Dictionary({{"a", 1}}), Sequence({"a", 1}),
// Simple object
Variant(createObject<SimpleTestObject>()),
// Dictionary proxy
createObject<TestDictionaryProxy>(Dictionary({ {"a", 1} })),
// Sequence proxy
createObject<TestSequenceProxy>(Sequence({ 1, "str" })),
};
for (auto vDst : DstList)
{
INFO("v type = " << (int)vDst.getType());
REQUIRE_NOTHROW([&]() {
CHECK(vDst.clone().merge(Variant(nullptr), Variant::MergeMode::CheckType).isNull());
}());
}
}
SECTION("scalar_merging")
{
Variant DstList[] = {
// Scalar
Variant(), Variant(true), Variant(1), Variant("XXXX"),
// Dictionary and Sequence
Dictionary({{"a", 1}}), Sequence({"a", 1}),
// Simple object
Variant(createObject<SimpleTestObject>()),
// Dictionary proxy
createObject<TestDictionaryProxy>(Dictionary({ {"a", 1} })),
// Sequence proxy
createObject<TestSequenceProxy>(Sequence({ 1, "str" })),
};
Variant SrcList[] = { Variant(true), Variant(1), Variant("XXXX"), Variant(createObject<SimpleTestObject>()) };
for (auto vDst : DstList)
{
for (auto vSrc : SrcList)
{
INFO("vDst type = " << (int) vDst.getType());
INFO("vSrc type = " << (int) vSrc.getType());
CHECK(vDst.clone().merge(vSrc, Variant::MergeMode::All) == vSrc);
if (vSrc.getType() == vDst.getType())
{
REQUIRE_NOTHROW([&]() {
CHECK(vDst.clone().merge(vSrc, Variant::MergeMode::CheckType) == vSrc);
}());
}
else
{
REQUIRE_THROWS_AS([&]() {
vDst.clone().merge(vSrc, Variant::MergeMode::CheckType);
}(), error::InvalidArgument);
}
}
}
}
SECTION("sequence_merging_into_sequence")
{
Variant SrcList[] = { Sequence({1,2}), createObject<TestSequenceProxy>(Sequence({ 1, 2 })) };
Variant vDst = Sequence({ 1,2 });
for (auto vSrc : SrcList)
{
vDst.merge(vSrc, Variant::MergeMode::All);
}
REQUIRE(vDst == Sequence({ 1,2,1,2,1,2 }));
}
SECTION("sequence_merging_into_sequence_proxy")
{
Variant vInternalSeq = Sequence({ 1, 2 });
Variant vDst = createObject<TestSequenceProxy>(vInternalSeq);
Variant SrcList[] = { Sequence({1,2}), createObject<TestSequenceProxy>(Sequence({ 1, 2 })) };
for (auto vSrc : SrcList)
{
vDst.merge(vSrc, Variant::MergeMode::All);
}
REQUIRE(vInternalSeq == Sequence({ 1,2,1,2,1,2 }));
}
SECTION("dictionary_merging_into_dictionary")
{
auto fnBasicDictionaryCreator = [](Dictionary dict) { return dict.clone(); };
auto fnDictionaryProxyCreator = [](Dictionary dict) { return Variant(createObject<TestDictionaryProxy>(dict)); };
std::function<Variant(Dictionary) > fnCreators[] = { fnBasicDictionaryCreator, fnDictionaryProxyCreator };
for (auto fnCreator1 : fnCreators)
for (auto fnCreator2 : fnCreators)
testDictionaryMerging(fnCreator1, fnCreator2);
}
}
//
//
//
class SimpleTestObjectWithClsid12345678 : public ObjectBase <ClassId(0x12345678)>
{
};
//
//
//
class ExamplePrintableObject : public ObjectBase<>, public IPrintable
{
public:
virtual std::string print() const override
{
return "ExamplePrintableObject_representation";
}
};
//
//
//
void testVariantPrint(Variant v, std::string sEthalon)
{
INFO("type of v: " << v.getType());
std::string sPrintResult = v.print();
CHECK(sPrintResult == sEthalon);
}
//
//
//
TEST_CASE("Variant.print")
{
SECTION("null")
{
testVariantPrint(Variant(nullptr), "null");
}
SECTION("boolean")
{
testVariantPrint(Variant(true), "true");
testVariantPrint(Variant(false), "false");
}
SECTION("int")
{
testVariantPrint(Variant(1), "1");
testVariantPrint(Variant(-1), "-1");
}
SECTION("string")
{
testVariantPrint(Variant("abc123"), "abc123");
testVariantPrint(Variant(L"abc\"123"), "abc\"123");
}
SECTION("simple_object")
{
ObjPtr<IObject> pObj = createObject<SimpleTestObjectWithClsid12345678>();
std::string sEthalon = FMT("Object<" << SimpleTestObjectWithClsid12345678::c_nClassId << ":" << hex(pObj->getId()) << ">");
testVariantPrint(Variant(pObj), sEthalon);
}
SECTION("IDictionaryProxy")
{
ObjPtr<IObject> pObj = createObject<TestDictionaryProxy>(Dictionary());
std::string sEthalon = FMT("Object<0xFFFFFFFF:"<< hex(pObj->getId()) << ">(IDictionaryProxy)");
testVariantPrint(Variant(pObj), sEthalon);
}
SECTION("IPrintable")
{
ObjPtr<IObject> pObj = createObject<ExamplePrintableObject>();
testVariantPrint(Variant(pObj), "ExamplePrintableObject_representation");
}
SECTION("sequence")
{
testVariantPrint(Sequence(), "[]");
testVariantPrint(Sequence({ nullptr, 1, true, "str" }), "[\n null,\n 1,\n true,\n \"str\",\n]");
}
SECTION("dictionary")
{
testVariantPrint(Dictionary(), "{}");
testVariantPrint(Dictionary({ {"a",nullptr} , {"b", 1}, {"c", "str"} }),
"{\n \"a\": null,\n \"b\": 1,\n \"c\": \"str\",\n}");
}
SECTION("indentation")
{
testVariantPrint(Dictionary({ {"a",Sequence({1,2})} , {"b", Dictionary()}, {"c", Dictionary({{"a", 1}})} }),
"{\n \"a\": [\n 1,\n 2,\n ],\n \"b\": {},\n \"c\": {\n \"a\": 1,\n },\n}");
}
}
//
//
//
TEST_CASE("Variant.operator_output")
{
Variant testList[] = { nullptr, true, 1, "str", createObject<SimpleTestObjectWithClsid12345678>(),
Dictionary({{"a", 1}}), Sequence({1,2}) };
for (auto v : testList)
{
std::string sPrinted = v.print();
std::ostringstream oStrStream;
oStrStream << v;
CHECK(oStrStream.str() == sPrinted);
}
}