/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include namespace UnitTest { using namespace AZ; namespace Test1 { class TestClass { public: AZ_TYPE_INFO(TestClass, "{731F8B22-086E-4CDE-9645-23078C6277C1}"); static void Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(1) ->Field("int", &TestClass::m_int) ->Field("float", &TestClass::m_float) ->Field("string", &TestClass::m_string) ->Field("unordered_map", &TestClass::m_unorderedMap) ->Field("array", &TestClass::m_array) ->Field("vector", &TestClass::m_vector) ; } } int m_int = 0; float m_float = 0; AZStd::string m_string = "TestClass"; AZStd::unordered_map m_unorderedMap; AZStd::array m_array; AZStd::vector m_vector; void Init() { m_unorderedMap.emplace(1, "one"); m_unorderedMap.emplace(5, "five"); m_array[1] = "ONE"; m_vector.push_back("anything"); m_vector.push_back("something"); } bool operator == (const TestClass& other) const { return m_int == other.m_int && m_float == other.m_float && m_string == other.m_string && m_unorderedMap == other.m_unorderedMap && m_array == other.m_array && m_vector == other.m_vector ; } }; } namespace Test2 { // Test class which has same class name with TestClass but difference class id reflected in SerializeContext class TestClass { public: AZ_TYPE_INFO(TestClass, "{DAC825C5-AB14-4D9D-AAC2-124E56E1F8FD}"); static void Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) { serializeContext->Class() ->Version(1) ->Field("SomeData", &TestClass::m_someData) ; } } int m_someData = 0; }; } class JsonSerializationUtilsTests : public AllocatorsTestFixture { protected: void SetUp() override { AllocatorsTestFixture::SetUp(); m_serializeContext = AZStd::make_unique(); m_jsonRegistrationContext = AZStd::make_unique(); m_jsonSystemComponent = AZStd::make_unique(); m_serializationSettings.m_serializeContext = m_serializeContext.get(); m_serializationSettings.m_registrationContext = m_jsonRegistrationContext.get(); m_deserializationSettings.m_serializeContext = m_serializeContext.get(); m_deserializationSettings.m_registrationContext = m_jsonRegistrationContext.get(); m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); Test1::TestClass::Reflect(m_serializeContext.get()); Test2::TestClass::Reflect(m_serializeContext.get()); } void TearDown() override { m_jsonRegistrationContext->EnableRemoveReflection(); m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get()); m_jsonRegistrationContext->DisableRemoveReflection(); m_serializeContext->EnableRemoveReflection(); Test1::TestClass::Reflect(m_serializeContext.get()); Test2::TestClass::Reflect(m_serializeContext.get()); m_serializeContext->DisableRemoveReflection(); m_jsonRegistrationContext.reset(); m_serializeContext.reset(); m_jsonSystemComponent.reset(); AllocatorsTestFixture::TearDown(); } AZStd::unique_ptr m_serializeContext; AZStd::unique_ptr m_jsonRegistrationContext; AZStd::unique_ptr m_jsonSystemComponent; JsonSerializerSettings m_serializationSettings; JsonDeserializerSettings m_deserializationSettings; }; TEST_F(JsonSerializationUtilsTests, SaveLoadObjectToStream_Success) { char buffer[1024]; IO::MemoryStream stream(buffer, 1024, 0); m_serializationSettings.m_keepDefaults = true; Test1::TestClass dataToSave; dataToSave.Init(); dataToSave.m_float = 10; dataToSave.m_string = "SaveObjectToStreamSuccess"; Outcome saveResult = JsonSerializationUtils::SaveObjectToStream(&dataToSave, stream, (Test1::TestClass*)nullptr, &m_serializationSettings); EXPECT_TRUE(saveResult.IsSuccess()); Test1::TestClass loadedData; stream.Seek(0, IO::GenericStream::ST_SEEK_BEGIN); Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(loadedData, stream, &m_deserializationSettings); EXPECT_TRUE(loadResult.IsSuccess()); EXPECT_TRUE(dataToSave == loadedData); } TEST_F(JsonSerializationUtilsTests, SaveObjectToStream_Failed_NoSerializationContext) { char buffer[1024]; IO::MemoryStream stream(buffer, 1024, 0); m_serializationSettings.m_keepDefaults = true; Test1::TestClass dataToSave; dataToSave.m_float = 10; Outcome saveResult = JsonSerializationUtils::SaveObjectToStream(&dataToSave, stream); EXPECT_TRUE(!saveResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, WriteJson) { rapidjson::Document document; document.SetObject(); document.AddMember("a", 1, document.GetAllocator()); document.AddMember("b", 2, document.GetAllocator()); document.AddMember("c", 3, document.GetAllocator()); const char* expectedJsonText = "{\n" " \"a\": 1,\n" " \"b\": 2,\n" " \"c\": 3\n" "}"; AZStd::string outString; AZ::Outcome result1 = JsonSerializationUtils::WriteJsonString(document, outString); EXPECT_TRUE(result1.IsSuccess()); EXPECT_STREQ(expectedJsonText, outString.c_str()); AZStd::vector outBuffer; AZ::IO::ByteContainerStream> outStream{&outBuffer}; AZ::Outcome result2 = JsonSerializationUtils::WriteJsonStream(document, outStream); EXPECT_TRUE(result2.IsSuccess()); outBuffer.push_back(0); EXPECT_STREQ(expectedJsonText, outBuffer.data()); // Unfortunately we can't unit test WriteJsonFile because core unit tests don't have access to the local file IO system. } TEST_F(JsonSerializationUtilsTests, ReadJsonString) { const char* jsonText = R"( { "a": 1, "b": 2, "c": 3 })"; AZ::Outcome result = JsonSerializationUtils::ReadJsonString(jsonText); EXPECT_TRUE(result.IsSuccess()); EXPECT_TRUE(result.GetValue().IsObject()); EXPECT_TRUE(result.GetValue().HasMember("a")); EXPECT_TRUE(result.GetValue().HasMember("b")); EXPECT_TRUE(result.GetValue().HasMember("c")); EXPECT_EQ(result.GetValue()["a"].GetInt(), 1); EXPECT_EQ(result.GetValue()["b"].GetInt(), 2); EXPECT_EQ(result.GetValue()["c"].GetInt(), 3); } TEST_F(JsonSerializationUtilsTests, ReadJsonString_ErrorReportsLineNumber) { const char* jsonText = R"( { "a": "This line is missing a comma" "b": 2, "c": 3 } )"; AZ::Outcome result = JsonSerializationUtils::ReadJsonString(jsonText); EXPECT_FALSE(result.IsSuccess()); EXPECT_TRUE(result.GetError().find("JSON parse error at line 4:") == 0); } TEST_F(JsonSerializationUtilsTests, LoadJsonStream) { const char* jsonText = R"( { "a": 1, "b": 2, "c": 3 })"; IO::MemoryStream stream(jsonText, strlen(jsonText)); AZ::Outcome result = JsonSerializationUtils::ReadJsonStream(stream); EXPECT_TRUE(result.IsSuccess()); EXPECT_TRUE(result.GetValue().IsObject()); EXPECT_TRUE(result.GetValue().HasMember("a")); EXPECT_TRUE(result.GetValue().HasMember("b")); EXPECT_TRUE(result.GetValue().HasMember("c")); EXPECT_EQ(result.GetValue()["a"].GetInt(), 1); EXPECT_EQ(result.GetValue()["b"].GetInt(), 2); EXPECT_EQ(result.GetValue()["c"].GetInt(), 3); } TEST_F(JsonSerializationUtilsTests, LoadJsonStream_ErrorReportsLineNumber) { const char* jsonText = R"( { "a": 1, "b": "This line is missing a comma" "c": 3 } )"; IO::MemoryStream stream(jsonText, strlen(jsonText)); AZ::Outcome result = JsonSerializationUtils::ReadJsonStream(stream); EXPECT_FALSE(result.IsSuccess()); EXPECT_TRUE(result.GetError().find("JSON parse error at line 5:") == 0); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_ParseError) { char buffer[1024] = "Not a Json"; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NotJsonSerialization) { char buffer[1024] = "{}"; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NoClassInfo) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\" " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_MismatchClassName) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"NotTestClass\", " " \"ClassData\" : {} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_NoSerializeContext) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : {} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_HaltMismatchClassMember) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"uint\":\"10\", " " \"bad name2\":\"blabla\"} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Failed_WrongValueType) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"int\":\"Ten\"} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Success_LessField) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"int\":\"10\"} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &m_deserializationSettings); EXPECT_TRUE(loadResult.IsSuccess()); EXPECT_TRUE(dataToLoad.m_int == 10); } TEST_F(JsonSerializationUtilsTests, LoadObjectFromStream_Success_CustomizeCallback) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"int\":\"10\"} " "} "; IO::MemoryStream stream(buffer, 1024); Test1::TestClass dataToLoad; AZStd::string callbackString; auto issueReportingCallback = [&callbackString](AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view target) -> JsonSerializationResult::ResultCode { using namespace JsonSerializationResult; AZ_UNUSED(message); AZ_UNUSED(target); callbackString = "issueReportingCallback"; return result; }; auto settings = m_deserializationSettings; settings.m_reporting = issueReportingCallback; Outcome loadResult = JsonSerializationUtils::LoadObjectFromStream(dataToLoad, stream, &settings); EXPECT_TRUE(loadResult.IsSuccess()); EXPECT_TRUE(dataToLoad.m_int == 10); EXPECT_TRUE(!callbackString.empty()); } TEST_F(JsonSerializationUtilsTests, LoadAnyObjectFromStream_Success) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"int\":\"10\"} " "} "; IO::MemoryStream stream(buffer, 1024); Outcome loadResult = JsonSerializationUtils::LoadAnyObjectFromStream(stream, &m_deserializationSettings); EXPECT_TRUE(loadResult.IsSuccess()); EXPECT_TRUE(loadResult.GetValue().type() == Test1::TestClass::TYPEINFO_Uuid()); Test1::TestClass test = AZStd::any_cast(loadResult.GetValue()); EXPECT_TRUE(test.m_int == 10); } TEST_F(JsonSerializationUtilsTests, LoadAnyObjectFromStream_Failed_WrongValueType) { char buffer[1024] = "{ " " \"Type\": \"JsonSerialization\", " " \"ClassName\": \"TestClass\", " " \"ClassData\" : { \"int\":\"Ten\"} " "} "; IO::MemoryStream stream(buffer, 1024); Outcome loadResult = JsonSerializationUtils::LoadAnyObjectFromStream(stream, &m_deserializationSettings); EXPECT_TRUE(!loadResult.IsSuccess()); } } // namespace UnitTest