Merge pull request #6395 from aws-lumberyard-dev/nvsickle/GenericDomDocument

Add a generic Value type to the Generic DOM
This commit is contained in:
Nicholas Van Sickle
2022-01-13 16:45:32 -08:00
committed by GitHub
14 changed files with 2933 additions and 23 deletions
@@ -8,9 +8,10 @@
#if defined(HAVE_BENCHMARK)
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/DOM/DomValue.h>
#include <AzCore/JSON/document.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
@@ -25,27 +26,31 @@ namespace Benchmark
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
AZ::NameDictionary::Create();
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Create();
}
void SetUp(::benchmark::State& st) override
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
AZ::NameDictionary::Create();
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Create();
}
void TearDown(::benchmark::State& st) override
{
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Destroy();
AZ::NameDictionary::Destroy();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
void TearDown(const ::benchmark::State& st) override
{
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Destroy();
AZ::NameDictionary::Destroy();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
rapidjson::Document GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength)
{
rapidjson::Document document;
document.SetObject();
@@ -103,11 +108,28 @@ namespace Benchmark
document.SetObject();
document.AddMember("entries", createObject(), document.GetAllocator());
return document;
}
AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
{
rapidjson::Document document = GenerateDomJsonBenchmarkDocument(entryCount, stringTemplateLength);
AZStd::string serializedJson;
auto result = AZ::JsonSerializationUtils::WriteJsonString(document, serializedJson);
AZ_Assert(result.IsSuccess(), "Failed to serialize generated JSON");
return serializedJson;
}
template <class T>
void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state)
{
{
T instance = AZStd::move(value);
state.PauseTiming();
}
state.ResumeTiming();
}
};
// Helper macro for registering JSON benchmarks
@@ -119,7 +141,7 @@ namespace Benchmark
->Args({ 100, 500 }) \
->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocumentInPlace)(benchmark::State& state)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
@@ -136,14 +158,38 @@ namespace Benchmark
return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor);
});
benchmark::DoNotOptimize(result.GetValue());
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
}
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocumentInPlace)
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocument)(benchmark::State& state)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
for (auto _ : state)
{
state.PauseTiming();
AZStd::string payloadCopy = serializedPayload;
state.ResumeTiming();
auto result = AZ::Dom::Utils::WriteToValue(
[&](AZ::Dom::Visitor& visitor)
{
return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor);
});
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
}
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjson)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
@@ -156,14 +202,34 @@ namespace Benchmark
return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor);
});
benchmark::DoNotOptimize(result.GetValue());
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
}
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocument)
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjson)
BENCHMARK_DEFINE_F(DomJsonBenchmark, JsonUtilsDeserializeToDocument)(benchmark::State& state)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValue)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
for (auto _ : state)
{
auto result = AZ::Dom::Utils::WriteToValue(
[&](AZ::Dom::Visitor& visitor)
{
return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor);
});
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
}
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValue)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
@@ -172,12 +238,78 @@ namespace Benchmark
{
auto result = AZ::JsonSerializationUtils::ReadJsonString(serializedPayload);
benchmark::DoNotOptimize(result.GetValue());
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
}
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, JsonUtilsDeserializeToDocument)
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonMakeComplexObject)(benchmark::State& state)
{
for (auto _ : state)
{
TakeAndDiscardWithoutTimingDtor(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)), state);
}
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonMakeComplexObject)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonLookupMemberByString)(benchmark::State& state)
{
rapidjson::Document document(rapidjson::kObjectType);
AZStd::vector<AZStd::string> keys;
for (int64_t i = 0; i < state.range(0); ++i)
{
AZStd::string key(AZStd::string::format("key%" PRId64, i));
keys.push_back(key);
document.AddMember(rapidjson::Value(key.data(), static_cast<rapidjson::SizeType>(key.size()), document.GetAllocator()), rapidjson::Value(i), document.GetAllocator());
}
for (auto _ : state)
{
for (const AZStd::string& key : keys)
{
benchmark::DoNotOptimize(document.FindMember(key.data()));
}
}
state.SetItemsProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(DomJsonBenchmark, RapidjsonLookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeepCopy)(benchmark::State& state)
{
rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1));
for (auto _ : state)
{
rapidjson::Document copy;
copy.CopyFrom(original, copy.GetAllocator(), true);
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeepCopy)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonCopyAndMutate)(benchmark::State& state)
{
rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1));
for (auto _ : state)
{
rapidjson::Document copy;
copy.CopyFrom(original, copy.GetAllocator(), true);
copy["entries"]["Key0"].PushBack(42, copy.GetAllocator());
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonCopyAndMutate)
#undef BENCHMARK_REGISTER_JSON
} // namespace Benchmark
@@ -0,0 +1,263 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/DOM/DomValue.h>
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <cinttypes>
namespace AZ::Dom::Benchmark
{
class DomValueBenchmark : public UnitTest::AllocatorsBenchmarkFixture
{
public:
void SetUp(const ::benchmark::State& st) override
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
AZ::NameDictionary::Create();
AZ::AllocatorInstance<ValueAllocator>::Create();
}
void SetUp(::benchmark::State& st) override
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
AZ::NameDictionary::Create();
AZ::AllocatorInstance<ValueAllocator>::Create();
}
void TearDown(::benchmark::State& st) override
{
AZ::AllocatorInstance<ValueAllocator>::Destroy();
AZ::NameDictionary::Destroy();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
void TearDown(const ::benchmark::State& st) override
{
AZ::AllocatorInstance<ValueAllocator>::Destroy();
AZ::NameDictionary::Destroy();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
Value GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
{
Value root(Type::Object);
AZStd::string entryTemplate;
while (entryTemplate.size() < static_cast<size_t>(stringTemplateLength))
{
entryTemplate += "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ";
}
entryTemplate.resize(stringTemplateLength);
AZStd::string buffer;
auto createString = [&](int n) -> Value
{
return Value(AZStd::string::format("#%i %s", n, entryTemplate.c_str()), true);
};
auto createEntry = [&](int n) -> Value
{
Value entry(Type::Object);
entry.AddMember("string", createString(n));
entry.AddMember("int", Value(n));
entry.AddMember("double", Value(static_cast<double>(n) * 0.5));
entry.AddMember("bool", Value(n % 2 == 0));
entry.AddMember("null", Value(Type::Null));
return entry;
};
auto createArray = [&]() -> Value
{
Value array(Type::Array);
for (int i = 0; i < entryCount; ++i)
{
array.ArrayPushBack(createEntry(i));
}
return array;
};
auto createObject = [&]() -> Value
{
Value object;
object.SetObject();
for (int i = 0; i < entryCount; ++i)
{
buffer = AZStd::string::format("Key%i", i);
object.AddMember(AZ::Name(buffer), createArray());
}
return object;
};
root["entries"] = createObject();
return root;
}
template<class T>
void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state)
{
{
T instance = AZStd::move(value);
state.PauseTiming();
}
state.ResumeTiming();
}
};
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state)
{
for (auto _ : state)
{
TakeAndDiscardWithoutTimingDtor(GenerateDomBenchmarkPayload(state.range(0), state.range(1)), state);
}
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueMakeComplexObject)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopy)(benchmark::State& state)
{
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
for (auto _ : state)
{
Value copy = original;
benchmark::DoNotOptimize(copy);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopy)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kNanosecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueCopyAndMutate)(benchmark::State& state)
{
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
for (auto _ : state)
{
Value copy = original;
copy["entries"]["Key0"].ArrayPushBack(Value(42));
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueCopyAndMutate)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kNanosecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueDeepCopy)(benchmark::State& state)
{
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
for (auto _ : state)
{
Value copy = Utils::DeepCopy(original);
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueDeepCopy)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByName)(benchmark::State& state)
{
Value value(Type::Object);
AZStd::vector<AZ::Name> keys;
for (int64_t i = 0; i < state.range(0); ++i)
{
AZ::Name key(AZStd::string::format("key%" PRId64, i));
keys.push_back(key);
value[key] = i;
}
for (auto _ : state)
{
for (const AZ::Name& key : keys)
{
benchmark::DoNotOptimize(value[key]);
}
}
state.SetItemsProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByName)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByString)(benchmark::State& state)
{
Value value(Type::Object);
AZStd::vector<AZStd::string> keys;
for (int64_t i = 0; i < state.range(0); ++i)
{
AZStd::string key(AZStd::string::format("key%" PRId64, i));
keys.push_back(key);
value[key] = i;
}
for (auto _ : state)
{
for (const AZStd::string& key : keys)
{
benchmark::DoNotOptimize(value[key]);
}
}
state.SetItemsProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByStringComparison)(benchmark::State& state)
{
Value value(Type::Object);
AZStd::vector<AZStd::string> keys;
for (int64_t i = 0; i < state.range(0); ++i)
{
AZStd::string key(AZStd::string::format("key%" PRId64, i));
keys.push_back(key);
value[key] = i;
}
for (auto _ : state)
{
for (const AZStd::string& key : keys)
{
const Object::ContainerType& object = value.GetObject();
benchmark::DoNotOptimize(AZStd::find_if(
object.cbegin(), object.cend(),
[&key](const Object::EntryType& entry)
{
return key == entry.first.GetStringView();
}));
}
}
state.SetItemsProcessed(state.iterations() * state.range(0));
}
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByStringComparison)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
} // namespace AZ::Dom::Benchmark
@@ -0,0 +1,392 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/DOM/DomValue.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/numeric.h>
namespace AZ::Dom::Tests
{
class DomValueTests : public UnitTest::AllocatorsFixture
{
public:
void SetUp() override
{
UnitTest::AllocatorsFixture::SetUp();
NameDictionary::Create();
AZ::AllocatorInstance<ValueAllocator>::Create();
}
void TearDown() override
{
m_value = Value();
AZ::AllocatorInstance<ValueAllocator>::Destroy();
NameDictionary::Destroy();
UnitTest::AllocatorsFixture::TearDown();
}
void PerformValueChecks()
{
Value shallowCopy = m_value;
EXPECT_EQ(m_value, shallowCopy);
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, shallowCopy));
Value deepCopy = Utils::DeepCopy(m_value);
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, deepCopy));
}
Value m_value;
};
TEST_F(DomValueTests, EmptyArray)
{
m_value.SetArray();
EXPECT_TRUE(m_value.IsArray());
EXPECT_EQ(m_value.ArraySize(), 0);
PerformValueChecks();
}
TEST_F(DomValueTests, SimpleArray)
{
m_value.SetArray();
for (int i = 0; i < 5; ++i)
{
m_value.ArrayPushBack(Value(i));
EXPECT_EQ(m_value.ArraySize(), i + 1);
EXPECT_EQ(m_value[i].GetInt64(), i);
}
PerformValueChecks();
}
TEST_F(DomValueTests, NestedArrays)
{
Value x(5);
m_value.SetArray();
for (int j = 0; j < 5; ++j)
{
Value nestedArray(Type::Array);
for (int i = 0; i < 5; ++i)
{
nestedArray.ArrayPushBack(Value(i));
}
m_value.ArrayPushBack(AZStd::move(nestedArray));
}
EXPECT_EQ(m_value.ArraySize(), 5);
for (int i = 0; i < 3; ++i)
{
EXPECT_EQ(m_value[i].ArraySize(), 5);
for (int j = 0; j < 5; ++j)
{
EXPECT_EQ(m_value[i][j].GetInt64(), j);
}
}
PerformValueChecks();
}
TEST_F(DomValueTests, EmptyObject)
{
m_value.SetObject();
EXPECT_EQ(m_value.MemberCount(), 0);
PerformValueChecks();
}
TEST_F(DomValueTests, SimpleObject)
{
m_value.SetObject();
for (int i = 0; i < 5; ++i)
{
AZStd::string key = AZStd::string::format("Key%i", i);
m_value.AddMember(key, Value(i));
EXPECT_EQ(m_value.MemberCount(), i + 1);
EXPECT_EQ(m_value[key].GetInt64(), i);
}
PerformValueChecks();
}
TEST_F(DomValueTests, NestedObjects)
{
m_value.SetObject();
for (int j = 0; j < 3; ++j)
{
Value nestedObject(Type::Object);
for (int i = 0; i < 5; ++i)
{
nestedObject.AddMember(AZStd::string::format("Key%i", i), Value(i));
}
m_value.AddMember(AZStd::string::format("Obj%i", j), AZStd::move(nestedObject));
}
EXPECT_EQ(m_value.MemberCount(), 3);
for (int j = 0; j < 3; ++j)
{
const Value& nestedObject = m_value[AZStd::string::format("Obj%i", j)];
EXPECT_EQ(nestedObject.MemberCount(), 5);
for (int i = 0; i < 5; ++i)
{
EXPECT_EQ(nestedObject[AZStd::string::format("Key%i", i)].GetInt64(), i);
}
}
PerformValueChecks();
}
TEST_F(DomValueTests, EmptyNode)
{
m_value.SetNode("Test");
EXPECT_EQ(m_value.GetNodeName(), AZ::Name("Test"));
EXPECT_EQ(m_value.MemberCount(), 0);
EXPECT_EQ(m_value.ArraySize(), 0);
PerformValueChecks();
}
TEST_F(DomValueTests, SimpleNode)
{
m_value.SetNode("Test");
for (int i = 0; i < 10; ++i)
{
m_value.ArrayPushBack(Value(i));
EXPECT_EQ(m_value.ArraySize(), i + 1);
EXPECT_EQ(m_value[i].GetInt64(), i);
if (i < 5)
{
AZ::Name key = AZ::Name(AZStd::string::format("TwoTimes%i", i));
m_value.AddMember(key, Value(i * 2));
EXPECT_EQ(m_value.MemberCount(), i + 1);
EXPECT_EQ(m_value[key].GetInt64(), i * 2);
}
}
PerformValueChecks();
}
TEST_F(DomValueTests, NestedNodes)
{
m_value.SetNode("TopLevel");
const AZ::Name childNodeName("ChildNode");
for (int i = 0; i < 5; ++i)
{
Value childNode(Type::Node);
childNode.SetNodeName(childNodeName);
childNode.SetNodeValue(Value(i));
childNode.AddMember("foo", Value(i));
childNode.AddMember("bar", Value("test", false));
m_value.ArrayPushBack(childNode);
}
EXPECT_EQ(m_value.ArraySize(), 5);
for (int i = 0; i < 5; ++i)
{
const Value& childNode = m_value[i];
EXPECT_EQ(childNode.GetNodeName(), childNodeName);
EXPECT_EQ(childNode.GetNodeValue().GetInt64(), i);
EXPECT_EQ(childNode["foo"].GetInt64(), i);
EXPECT_EQ(childNode["bar"].GetString(), "test");
}
PerformValueChecks();
}
TEST_F(DomValueTests, Int64)
{
m_value.SetObject();
m_value["int64_min"] = AZStd::numeric_limits<int64_t>::min();
m_value["int64_max"] = AZStd::numeric_limits<int64_t>::max();
EXPECT_EQ(m_value["int64_min"].GetType(), Type::Int64);
EXPECT_EQ(m_value["int64_min"].GetInt64(), AZStd::numeric_limits<int64_t>::min());
EXPECT_EQ(m_value["int64_max"].GetType(), Type::Int64);
EXPECT_EQ(m_value["int64_max"].GetInt64(), AZStd::numeric_limits<int64_t>::max());
PerformValueChecks();
}
TEST_F(DomValueTests, Uint64)
{
m_value.SetObject();
m_value["uint64_min"] = AZStd::numeric_limits<uint64_t>::min();
m_value["uint64_max"] = AZStd::numeric_limits<uint64_t>::max();
EXPECT_EQ(m_value["uint64_min"].GetType(), Type::Uint64);
EXPECT_EQ(m_value["uint64_min"].GetInt64(), AZStd::numeric_limits<uint64_t>::min());
EXPECT_EQ(m_value["uint64_max"].GetType(), Type::Uint64);
EXPECT_EQ(m_value["uint64_max"].GetInt64(), AZStd::numeric_limits<uint64_t>::max());
PerformValueChecks();
}
TEST_F(DomValueTests, Double)
{
m_value.SetObject();
m_value["double_min"] = AZStd::numeric_limits<double>::min();
m_value["double_max"] = AZStd::numeric_limits<double>::max();
EXPECT_EQ(m_value["double_min"].GetType(), Type::Double);
EXPECT_EQ(m_value["double_min"].GetDouble(), AZStd::numeric_limits<double>::min());
EXPECT_EQ(m_value["double_max"].GetType(), Type::Double);
EXPECT_EQ(m_value["double_max"].GetDouble(), AZStd::numeric_limits<double>::max());
PerformValueChecks();
}
TEST_F(DomValueTests, Null)
{
m_value.SetObject();
m_value["null_value"] = Value(Type::Null);
EXPECT_EQ(m_value["null_value"].GetType(), Type::Null);
EXPECT_EQ(m_value["null_type"], Value());
PerformValueChecks();
}
TEST_F(DomValueTests, Bool)
{
m_value.SetObject();
m_value["true_value"] = true;
m_value["false_value"] = false;
EXPECT_EQ(m_value["true_value"].GetType(), Type::Bool);
EXPECT_EQ(m_value["true_value"].GetBool(), true);
EXPECT_EQ(m_value["false_value"].GetType(), Type::Bool);
EXPECT_EQ(m_value["false_value"].GetBool(), false);
PerformValueChecks();
}
TEST_F(DomValueTests, String)
{
const char* s1 = "reference string long enough to avoid SSO";
const char* s2 = "copy string long enough to avoid SSO";
m_value.SetObject();
AZStd::string stringToReference = s1;
m_value["no_copy"] = Value(stringToReference, false);
AZStd::string stringToCopy = s2;
m_value["copy"] = Value(stringToCopy, true);
EXPECT_EQ(m_value["no_copy"].GetType(), Type::String);
EXPECT_EQ(m_value["no_copy"].GetString(), s1);
stringToReference.at(0) = 'F';
EXPECT_NE(m_value["no_copy"].GetString(), s1);
EXPECT_EQ(m_value["copy"].GetType(), Type::String);
EXPECT_EQ(m_value["copy"].GetString(), s2);
stringToCopy.at(0) = 'F';
EXPECT_EQ(m_value["copy"].GetString(), s2);
PerformValueChecks();
}
TEST_F(DomValueTests, CopyOnWrite_Object)
{
Value v1(Type::Object);
v1["foo"] = 5;
Value nestedObject(Type::Object);
v1["obj"] = nestedObject;
Value v2 = v1;
EXPECT_EQ(&v1.GetObject(), &v2.GetObject());
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
v2["foo"] = 0;
EXPECT_NE(&v1.GetObject(), &v2.GetObject());
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
v2["obj"]["key"] = true;
EXPECT_NE(&v1.GetObject(), &v2.GetObject());
EXPECT_NE(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
v2 = v1;
EXPECT_EQ(&v1.GetObject(), &v2.GetObject());
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
}
TEST_F(DomValueTests, CopyOnWrite_Array)
{
Value v1(Type::Array);
v1.ArrayPushBack(Value(1));
v1.ArrayPushBack(Value(2));
Value nestedArray(Type::Array);
v1.ArrayPushBack(nestedArray);
Value v2 = v1;
EXPECT_EQ(&v1.GetArray(), &v2.GetArray());
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
v2[0] = 0;
EXPECT_NE(&v1.GetArray(), &v2.GetArray());
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
v2[2].ArrayPushBack(Value(42));
EXPECT_NE(&v1.GetArray(), &v2.GetArray());
EXPECT_NE(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
v2 = v1;
EXPECT_EQ(&v1.GetArray(), &v2.GetArray());
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
}
TEST_F(DomValueTests, CopyOnWrite_Node)
{
Value v1;
v1.SetNode("TopLevel");
v1.ArrayPushBack(Value(1));
v1.ArrayPushBack(Value(2));
v1["obj"].SetNode("Nested");
Value v2 = v1;
EXPECT_EQ(&v1.GetNode(), &v2.GetNode());
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
v2[0] = 0;
EXPECT_NE(&v1.GetNode(), &v2.GetNode());
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
v2["obj"].ArrayPushBack(Value(42));
EXPECT_NE(&v1.GetNode(), &v2.GetNode());
EXPECT_NE(&v1["obj"].GetNode(), &v2["obj"].GetNode());
v2 = v1;
EXPECT_EQ(&v1.GetNode(), &v2.GetNode());
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
}
} // namespace AZ::Dom::Tests
@@ -217,6 +217,8 @@ set(FILES
AZStd/VectorAndArray.cpp
DOM/DomJsonTests.cpp
DOM/DomJsonBenchmarks.cpp
DOM/DomValueTests.cpp
DOM/DomValueBenchmarks.cpp
)
# Prevent the following files from being grouped in UNITY builds