From a2d474cc4f30c17b7354221a1c0e55dfb20c6de4 Mon Sep 17 00:00:00 2001 From: Nicholas Van Sickle Date: Thu, 9 Dec 2021 17:47:01 -0800 Subject: [PATCH] Add some intial tests Signed-off-by: Nicholas Van Sickle --- Code/Framework/AzCore/AzCore/DOM/DomValue.cpp | 123 +++++++++- Code/Framework/AzCore/AzCore/DOM/DomValue.h | 28 ++- .../AzCore/AzCore/DOM/DomValueWriter.cpp | 4 +- .../AzCore/Tests/DOM/DomValueBenchmarks.cpp | 0 .../AzCore/Tests/DOM/DomValueTests.cpp | 211 ++++++++++++++++++ .../AzCore/Tests/azcoretests_files.cmake | 2 + 6 files changed, 350 insertions(+), 18 deletions(-) create mode 100644 Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp create mode 100644 Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp index 68a88ed35a..2f41835a37 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.cpp @@ -97,6 +97,16 @@ namespace AZ::Dom return Value(&value); } + Value::Value(int32_t value) + : m_value(aznumeric_cast(value)) + { + } + + Value::Value(uint32_t value) + : m_value(aznumeric_cast(value)) + { + } + Value::Value(int64_t value) : m_value(value) { @@ -107,6 +117,11 @@ namespace AZ::Dom { } + Value::Value(float value) + : m_value(aznumeric_cast(value)) + { + } + Value::Value(double value) : m_value(value) { @@ -117,6 +132,40 @@ namespace AZ::Dom { } + Value::Value(Type type) + { + switch (type) + { + case Type::NullType: + // Null is the default initialized value + break; + case Type::FalseType: + m_value = false; + break; + case Type::TrueType: + m_value = true; + break; + case Type::ObjectType: + SetObject(); + break; + case Type::ArrayType: + SetArray(); + break; + case Type::StringType: + SetString(""); + break; + case Type::NumberType: + m_value = 0.0; + break; + case Type::NodeType: + SetNode(""); + break; + case Type::OpaqueType: + AZ_Assert(false, "AZ::Dom::Value may not be constructed with an empty opaque type"); + break; + } + } + Value& Value::operator=(const Value& other) { m_value = other.m_value; @@ -139,11 +188,11 @@ namespace AZ::Dom { if (IsInt()) { - return GetInt() == rhs.GetInt(); + return GetInt64() == rhs.GetInt64(); } else if (IsUint()) { - return GetUint() == rhs.GetUint(); + return GetUint64() == rhs.GetUint64(); } else { @@ -315,7 +364,7 @@ namespace AZ::Dom AZ_Assert( type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or a node"); - if (type == Type::ObjectType) + if (type == Type::ArrayType) { return AZStd::get(m_value)->m_values; } @@ -331,7 +380,7 @@ namespace AZ::Dom AZ_Assert( type == Type::ArrayType || type == Type::NodeType, "AZ::Dom::Value: attempted to retrieve an array from a value that isn't an array or node"); - if (type == Type::ObjectType) + if (type == Type::ArrayType) { return CheckCopyOnWrite(AZStd::get(m_value))->m_values; } @@ -702,7 +751,7 @@ namespace AZ::Dom return Value(); } - int64_t Value::GetInt() const + int64_t Value::GetInt64() const { switch (m_value.index()) { @@ -717,12 +766,22 @@ namespace AZ::Dom return {}; } - void Value::SetInt(int64_t value) + void Value::SetInt64(int64_t value) { m_value = value; } - uint64_t Value::GetUint() const + int32_t Value::GetInt32() const + { + return aznumeric_cast(GetInt64()); + } + + void Value::SetInt32(int32_t value) + { + m_value = aznumeric_cast(value); + } + + uint64_t Value::GetUint64() const { switch (m_value.index()) { @@ -737,11 +796,21 @@ namespace AZ::Dom return {}; } - void Value::SetUint(uint64_t value) + void Value::SetUint64(uint64_t value) { m_value = value; } + uint32_t Value::GetUint32() const + { + return aznumeric_cast(GetUint64()); + } + + void Value::SetUint32(uint32_t value) + { + m_value = aznumeric_cast(value); + } + bool Value::GetBool() const { if (IsBool()) @@ -777,6 +846,16 @@ namespace AZ::Dom m_value = value; } + float Value::GetFloat() const + { + return aznumeric_cast(GetDouble()); + } + + void Value::SetFloat(float value) + { + m_value = aznumeric_cast(value); + } + AZStd::string_view Value::GetString() const { switch (m_value.index()) @@ -861,6 +940,11 @@ namespace AZ::Dom const Object::ContainerType& object = GetObjectInternal(); for (const Object::EntryType& entry : object) { + result = visitor.Key(entry.first); + if (!result.IsSuccess()) + { + return; + } result = entry.second.Accept(visitor, copyStrings); if (!result.IsSuccess()) { @@ -896,6 +980,11 @@ namespace AZ::Dom const Object::ContainerType& object = GetObjectInternal(); for (const Object::EntryType& entry : object) { + result = visitor.Key(entry.first); + if (!result.IsSuccess()) + { + return; + } result = entry.second.Accept(visitor, copyStrings); if (!result.IsSuccess()) { @@ -932,6 +1021,16 @@ namespace AZ::Dom bool Value::DeepCompareIsEqual(const Value& other) const { + if (IsString() && other.IsString()) + { + // If we both hold the same ref counted string we don't need to do a full comparison + if (AZStd::holds_alternative>(m_value) && m_value == other.m_value) + { + return true; + } + return GetString() == other.GetString(); + } + if (m_value.index() != other.m_value.index()) { return false; @@ -1045,4 +1144,12 @@ namespace AZ::Dom }, m_value); } + + Value Value::DeepCopy(bool copyStrings) const + { + Value newValue; + AZStd::unique_ptr writer = newValue.GetWriteHandler(); + Accept(*writer, copyStrings); + return newValue; + } } // namespace AZ::Dom diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValue.h b/Code/Framework/AzCore/AzCore/DOM/DomValue.h index a460274602..61c3207ea4 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValue.h +++ b/Code/Framework/AzCore/AzCore/DOM/DomValue.h @@ -109,10 +109,15 @@ namespace AZ::Dom Value(Value&&) noexcept; Value(AZStd::string_view string, bool copy); - explicit Value(int64_t value); - explicit Value(uint64_t value); - explicit Value(double value); - explicit Value(bool value); + Value(int32_t value); + Value(uint32_t value); + Value(int64_t value); + Value(uint64_t value); + Value(float value); + Value(double value); + Value(bool value); + + explicit Value(Type type); static Value FromOpaqueValue(AZStd::any& value); @@ -226,12 +231,16 @@ namespace AZ::Dom Value GetNodeValue() const; // int API... - int64_t GetInt() const; - void SetInt(int64_t); + int64_t GetInt64() const; + void SetInt64(int64_t); + int32_t GetInt32() const; + void SetInt32(int32_t); // uint API... - uint64_t GetUint() const; - void SetUint(uint64_t); + uint64_t GetUint64() const; + void SetUint64(uint64_t); + uint32_t GetUint32() const; + void SetUint32(uint32_t); // bool API... bool GetBool() const; @@ -240,6 +249,8 @@ namespace AZ::Dom // double API... double GetDouble() const; void SetDouble(double); + float GetFloat() const; + void SetFloat(float); // string API... AZStd::string_view GetString() const; @@ -264,6 +275,7 @@ namespace AZ::Dom AZStd::unique_ptr GetWriteHandler(); bool DeepCompareIsEqual(const Value& other) const; + Value DeepCopy(bool copyStrings = true) const; private: const Node& GetNodeInternal() const; diff --git a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp index 71710f543b..a760cc5b28 100644 --- a/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp +++ b/Code/Framework/AzCore/AzCore/DOM/DomValueWriter.cpp @@ -39,13 +39,13 @@ namespace AZ::Dom Visitor::Result ValueWriter::Int64(AZ::s64 value) { - CurrentValue().SetInt(value); + CurrentValue().SetInt64(value); return FinishWrite(); } Visitor::Result ValueWriter::Uint64(AZ::u64 value) { - CurrentValue().SetUint(value); + CurrentValue().SetUint64(value); return FinishWrite(); } diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueBenchmarks.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp new file mode 100644 index 0000000000..02777bf6dd --- /dev/null +++ b/Code/Framework/AzCore/Tests/DOM/DomValueTests.cpp @@ -0,0 +1,211 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +namespace AZ::Dom::Tests +{ + class DomValueTests : public UnitTest::AllocatorsFixture + { + public: + void SetUp() override + { + UnitTest::AllocatorsFixture::SetUp(); + NameDictionary::Create(); + } + + void TearDown() override + { + m_value = Value(); + + NameDictionary::Destroy(); + UnitTest::AllocatorsFixture::TearDown(); + } + + void PerformValueChecks() + { + Value shallowCopy = m_value; + EXPECT_EQ(m_value, shallowCopy); + EXPECT_TRUE(m_value.DeepCompareIsEqual(shallowCopy)); + + Value deepCopy = m_value.DeepCopy(); + EXPECT_TRUE(m_value.DeepCompareIsEqual(deepCopy)); + } + + Value m_value; + }; + + TEST_F(DomValueTests, EmptyArray) + { + m_value.SetArray(); + + EXPECT_TRUE(m_value.IsArray()); + EXPECT_EQ(m_value.Size(), 0); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, SimpleArray) + { + m_value.SetArray(); + + for (int i = 0; i < 5; ++i) + { + m_value.PushBack(Value(i)); + EXPECT_EQ(m_value.Size(), i + 1); + EXPECT_EQ(m_value[i].GetInt32(), i); + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, NestedArrays) + { + m_value.SetArray(); + for (int j = 0; j < 5; ++j) + { + Value nestedArray(Type::ArrayType); + for (int i = 0; i < 5; ++i) + { + nestedArray.PushBack(Value(i)); + } + m_value.PushBack(AZStd::move(nestedArray)); + } + + EXPECT_EQ(m_value.Size(), 5); + for (int i = 0; i < 3; ++i) + { + EXPECT_EQ(m_value[i].Size(), 5); + for (int j = 0; j < 5; ++j) + { + EXPECT_EQ(m_value[i][j].GetInt32(), 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].GetInt32(), i); + } + + PerformValueChecks(); + } + + TEST_F(DomValueTests, NestedObjects) + { + m_value.SetObject(); + for (int j = 0; j < 3; ++j) + { + Value nestedObject(Type::ObjectType); + 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)].GetInt32(), 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.Size(), 0); + + PerformValueChecks(); + } + + TEST_F(DomValueTests, SimpleNode) + { + m_value.SetNode("Test"); + + for (int i = 0; i < 10; ++i) + { + m_value.PushBack(Value(i)); + EXPECT_EQ(m_value.Size(), i + 1); + EXPECT_EQ(m_value[i].GetInt32(), 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].GetInt32(), 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::NodeType); + childNode.SetNodeName(childNodeName); + childNode.SetNodeValue(i); + + childNode.AddMember("foo", i); + childNode.AddMember("bar", Value("test", false)); + + m_value.PushBack(childNode); + } + + EXPECT_EQ(m_value.Size(), 5); + for (int i = 0; i < 5; ++i) + { + const Value& childNode = m_value[i]; + EXPECT_EQ(childNode.GetNodeName(), childNodeName); + EXPECT_EQ(childNode.GetNodeValue().GetInt32(), i); + EXPECT_EQ(childNode["foo"].GetInt32(), i); + EXPECT_EQ(childNode["bar"].GetString(), "test"); + } + + PerformValueChecks(); + } +} // namespace AZ::Dom::Tests diff --git a/Code/Framework/AzCore/Tests/azcoretests_files.cmake b/Code/Framework/AzCore/Tests/azcoretests_files.cmake index 0fca75e2a8..14a8bbbac0 100644 --- a/Code/Framework/AzCore/Tests/azcoretests_files.cmake +++ b/Code/Framework/AzCore/Tests/azcoretests_files.cmake @@ -215,6 +215,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