diff --git a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp index 8401cca7b9c..de9b02ed7a6 100644 --- a/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/ECSqlStatementTests.cpp @@ -11800,4 +11800,444 @@ TEST_F(ECSqlStatementTestFixture, SelectAnySomeAll) ASSERT_EQ(expected, GetHelper().ExecuteSelectECSql(ecsql)); } } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECSqlStatementTestFixture, UpdateToNullBinding) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null.ecdb", SchemaItem(R"xml( + + + + + + + + + + + + + + + + + )xml"))); + + ///*** Insertable data + auto i = 123; + auto p2d = DPoint2d::From(23.22, 31.11); + auto st_p2d = DPoint2d::From(53.22, 31.11); + auto st_st_b = true; + auto st_st_i = 45; + + //*** Update all values to null + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + stmt.GetBinder(3).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET p2d=?, st=?, array_i=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); + for (int i = 1; i <= 3; ++i) + { + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(i)); + } + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(4, key.GetInstanceId())); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + for (int i = 0; i < stmt.GetColumnCount(); ++i) + { + ASSERT_TRUE(stmt.IsValueNull(i)) << "no values bound to " << stmt.GetECSql(); + } + + const int expectedMembersCount = (int) m_ecdb.Schemas().GetClass("TestSchema", "ComplexStruct")->GetPropertyCount(true); + IECSqlValue const& structVal = stmt.GetValue(3); + int actualMembersCount = 0; + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + actualMembersCount++; + ASSERT_TRUE(memberVal.IsNull()); + } + ASSERT_EQ(expectedMembersCount, actualMembersCount); + + IECSqlValue const& structArrayVal = stmt.GetValue(4); + ASSERT_EQ(0, structArrayVal.GetArrayLength()); + } + + //*** Update array to contain two null elements + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (array_i) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + stmt.GetBinder(1).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET array_i=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); + IECSqlBinder& arrayBinder = stmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); + ASSERT_EQ(ECSqlStatus::Success, arrayBinder.AddArrayElement().BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(2, key.GetInstanceId())); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String selectEcsql("SELECT array_i FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + IECSqlValue const& val = stmt.GetValue(0); + ASSERT_FALSE(val.IsNull()) << stmt.GetECSql(); + ASSERT_EQ(2, val.GetArrayLength()); + for (IECSqlValue const& elementVal : val.GetArrayIterable()) + { + ASSERT_TRUE(elementVal.IsNull()) << stmt.GetECSql(); + + if (val.GetColumnInfo().GetDataType().IsStructArray()) + { + for (IECSqlValue const& memberVal : elementVal.GetStructIterable()) + { + ASSERT_TRUE(memberVal.IsNull()); + } + } + } + } + + // Update points to be partially unset + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=?, st.p2d.X=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); + + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(1)); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindNull(2)); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(3, key.GetInstanceId())); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; + for (int i = 0; i < stmt.GetColumnCount(); i++) + { + IECSqlValue const& val = stmt.GetValue(i); + Utf8String propPath = val.GetColumnInfo().GetPropertyPath().ToString(); + const bool expectedToBeNull = nullItems.find(propPath) != nullItems.end(); + ASSERT_EQ(expectedToBeNull, val.IsNull()) << "Select clause item " << i << " in " << stmt.GetECSql(); + } + } + + //*** Update nested struct to be partially unset + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + auto& st = stmt.GetBinder(1); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET st=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); + + auto& elementBinder = stmt.GetBinder(1); + ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["i"].BindNull()); // Set st.st.i = null + ASSERT_EQ(ECSqlStatus::Success, elementBinder["st"]["b"].BindBoolean(st_st_b)); // Set st.st.b = true, so that the whole structure doesn't become null + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(2, key.GetInstanceId())); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("st")) + { + int memberCount = 0; + for (IECSqlValue const& nestedMemberVal : memberVal.GetStructIterable()) + { + memberCount++; + if (nestedMemberVal.GetColumnInfo().GetProperty()->GetName().Equals("b")) + ASSERT_FALSE(nestedMemberVal.IsNull()); + else + ASSERT_TRUE(nestedMemberVal.IsNull()); + } + ASSERT_EQ((int) memberVal.GetColumnInfo().GetStructType()->GetPropertyCount(true), memberCount); + } + else + ASSERT_TRUE(memberVal.IsNull()); + } + } + + //*** Update nested struct to have all properties null + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + auto& st = stmt.GetBinder(1); + st["st"]["i"].BindInt(st_st_i); + st["st"]["b"].BindBoolean(st_st_b); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String updateEcsql("UPDATE ONLY ts.TestClass SET st.p2d=?, st.st.i=?, st.st.b=? WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.c_str())) << updateEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(1).BindPoint2d(p2d)); // Make at least one arg in non-nested st not null. + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).BindNull()); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(4, key.GetInstanceId())); + + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + Utf8String selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=?"); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.c_str())) << selectEcsql.c_str(); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("p2d")) + ASSERT_FALSE(memberVal.IsNull()); + else + ASSERT_TRUE(memberVal.IsNull()); // st will fall under this check + } + } + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(ECSqlStatementTestFixture, UpdateToNullInline) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("ec_sql_update_to_null_inline.ecdb", SchemaItem(R"xml( + + + + + + + + + + + + + + + + + )xml"))); + + ///*** Insertable data + auto i = 123; + auto p2d = DPoint2d::From(23.22, 31.11); + auto st_p2d = DPoint2d::From(53.22, 31.11); + auto st_st_b = true; + auto st_st_i = 45; + + //*** Update all values to null + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st, array_i) VALUES (?,?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + stmt.GetBinder(3).AddArrayElement().BindInt(i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d=NULL, st=NULL, array_i=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st, array_i FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + for (int i = 0; i < stmt.GetColumnCount(); ++i) + { + ASSERT_TRUE(stmt.IsValueNull(i)) << "no values bound to " << stmt.GetECSql(); + } + + const int expectedMembersCount = (int) m_ecdb.Schemas().GetClass("TestSchema", "ComplexStruct")->GetPropertyCount(true); + IECSqlValue const& structVal = stmt.GetValue(3); + int actualMembersCount = 0; + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + actualMembersCount++; + ASSERT_TRUE(memberVal.IsNull()); + } + ASSERT_EQ(expectedMembersCount, actualMembersCount); + + IECSqlValue const& structArrayVal = stmt.GetValue(4); + ASSERT_EQ(0, structArrayVal.GetArrayLength()); + } + + // Update points to be partially unset + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (p2d, st) VALUES (?,?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + stmt.BindPoint2d(1, p2d); + auto& st = stmt.GetBinder(2); + st["p2d"].BindPoint2d(st_p2d); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET p2d.X=NULL, st.p2d.X=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString selectEcsql("SELECT p2d, p2d.X, p2d.Y, st.p2d, st.p2d.X, st.p2d.Y FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + std::set nullItems { "p2d", "p2d.X", "st.p2d", "st.p2d.X" }; + for (int i = 0; i < stmt.GetColumnCount(); i++) + { + IECSqlValue const& val = stmt.GetValue(i); + Utf8String propPath = val.GetColumnInfo().GetPropertyPath().ToString(); + const bool expectedToBeNull = nullItems.find(propPath) != nullItems.end(); + ASSERT_EQ(expectedToBeNull, val.IsNull()) << "Select clause item " << i << " in " << stmt.GetECSql(); + } + } + + //*** Update nested struct to be partially unset + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["st"]["b"].BindBoolean(st_st_b); + st["st"]["i"].BindBoolean(st_st_i); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=TRUE WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("st")) + { + int memberCount = 0; + for (IECSqlValue const& nestedMemberVal : memberVal.GetStructIterable()) + { + memberCount++; + if (nestedMemberVal.GetColumnInfo().GetProperty()->GetName().Equals("b")) + ASSERT_FALSE(nestedMemberVal.IsNull()); + else + ASSERT_TRUE(nestedMemberVal.IsNull()); + } + ASSERT_EQ((int) memberVal.GetColumnInfo().GetStructType()->GetPropertyCount(true), memberCount); + } + else + ASSERT_TRUE(memberVal.IsNull()); + } + } + + //*** Update nested struct to have all properties null + { + Utf8String insertEcsql("INSERT INTO ts.TestClass (st) VALUES (?)"); + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, insertEcsql.c_str())) << insertEcsql.c_str(); + + auto& st = stmt.GetBinder(1); + st["p2d"].BindPoint2d(p2d); + st["st"]["i"].BindInt(st_st_i); + st["st"]["b"].BindBoolean(st_st_b); + + ECInstanceKey key; + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString updateEcsql("UPDATE ONLY ts.TestClass SET st.st.i=NULL, st.st.b=NULL WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, updateEcsql.GetUtf8CP())) << updateEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step()) << stmt.GetECSql(); + stmt.Finalize(); + + SqlPrintfString selectEcsql("SELECT st FROM ts.TestClass WHERE ECInstanceId=%s", key.GetInstanceId().ToString().c_str()); + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, selectEcsql.GetUtf8CP())) << selectEcsql.GetUtf8CP(); + ASSERT_EQ(BE_SQLITE_ROW, stmt.Step()) << stmt.GetECSql(); + + ASSERT_FALSE(stmt.IsValueNull(0)); + IECSqlValue const& structVal = stmt.GetValue(0); + for (IECSqlValue const& memberVal : structVal.GetStructIterable()) + { + if (memberVal.GetColumnInfo().GetProperty()->GetName().Equals("p2d")) + ASSERT_FALSE(memberVal.IsNull()); + else + ASSERT_TRUE(memberVal.IsNull()); // st will fall under this check + } + } + } END_ECDBUNITTESTS_NAMESPACE diff --git a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp index 9b79b2f108d..7016136d089 100644 --- a/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp +++ b/iModelCore/ECDb/Tests/NonPublished/JsonUpdaterTests.cpp @@ -1021,4 +1021,97 @@ TEST_F(JsonUpdaterTests, UpdateTimeOfDayValues) EXPECT_EQ(JsonValue("[{\"StartTime\": \"00:00:00.000\", \"EndTime\":\"23:59:59.999\"}]"), GetHelper().ExecuteSelectECSql(Utf8PrintfString("SELECT StartTime,EndTime FROM ts.CalendarEntry WHERE ECInstanceId=%s", key2.GetInstanceId().ToString().c_str()).c_str())); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(JsonUpdaterTests, UpdateStructPropertyToNull) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("updateStructPropertyToNull.ecdb", SchemaItem(R"xml( + + + + + + + + + )xml"))); + + ECInstanceKey key; + { + // Insert test instance + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "INSERT INTO ts.TestClass(IntProp,ClassProp) VALUES(?,?)")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindInt(1, 15)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2)["MyStructNumber"].BindInt(17)); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); + } + + // Check for correct ClassProp value to be stored + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT IntProp,ClassProp FROM ts.TestClass WHERE ECInstanceId=?")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ClassProp\":{\"MyStructNumber\":17}}]"), GetHelper().ExecutePreparedECSql(stmt)); + + // Update test instance + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); + JsonUpdater updater(m_ecdb, *testClass, nullptr); + ASSERT_TRUE(updater.IsValid()); + ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\": 6, \"ClassProp\": null}").m_value)); + + // Check for ClassProp to not exist (be null) + stmt.Reset(); + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecutePreparedECSql(stmt)); + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//+---------------+---------------+---------------+---------------+---------------+------ +TEST_F(JsonUpdaterTests, UpdateArrayPropertyToNull) + { + ASSERT_EQ(BentleyStatus::SUCCESS, SetupECDb("updateArrayPropertyToNull.ecdb", SchemaItem(R"xml( + + + + + + + + + + )xml"))); + + ECInstanceKey key; + { + // Insert test instance + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "INSERT INTO ts.TestClass(IntProp,ArrBoolProp,ArrStructProp) VALUES(?,?,?)")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindInt(1, 15)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).AddArrayElement().BindBoolean(true)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(2).AddArrayElement().BindBoolean(false)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(17)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(5)); + ASSERT_EQ(ECSqlStatus::Success, stmt.GetBinder(3).AddArrayElement()["DeepNumber"].BindInt(12)); + ASSERT_EQ(BE_SQLITE_DONE, stmt.Step(key)); + } + + // Check for correct ArrBoolProp and ArrStructProp values to be stored + ECSqlStatement stmt; + ASSERT_EQ(ECSqlStatus::Success, stmt.Prepare(m_ecdb, "SELECT IntProp,ArrBoolProp,ArrStructProp FROM ts.TestClass WHERE ECInstanceId=?")); + ASSERT_EQ(ECSqlStatus::Success, stmt.BindId(1, key.GetInstanceId())); + EXPECT_EQ(JsonValue("[{\"IntProp\":15,\"ArrBoolProp\":[true, false],\"ArrStructProp\":[{\"DeepNumber\":17},{\"DeepNumber\":5},{\"DeepNumber\":12}]}]"), GetHelper().ExecutePreparedECSql(stmt)); + + // Update test instance + ECClassCP testClass = m_ecdb.Schemas().GetClass("TestSchema", "TestClass"); + ASSERT_TRUE(testClass != nullptr); + JsonUpdater updater(m_ecdb, *testClass, nullptr); + ASSERT_TRUE(updater.IsValid()); + ASSERT_EQ(BE_SQLITE_OK, updater.Update(key.GetInstanceId(), JsonValue("{\"IntProp\":6,\"ArrBoolProp\": null, \"ArrStructProp\": null}").m_value)); + + // Check for ArrBoolProp and ArrStructProp to not exist (be null) + stmt.Reset(); + EXPECT_EQ(JsonValue("[{\"IntProp\":6}]"), GetHelper().ExecutePreparedECSql(stmt)); + } + END_ECDBUNITTESTS_NAMESPACE diff --git a/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h b/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h index d62997f1802..0b3c5d34e32 100644 --- a/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h +++ b/iModelCore/ecobjects/PublicApi/ECObjects/ECJsonUtilities.h @@ -474,7 +474,7 @@ struct JsonECInstanceConverter final ~JsonECInstanceConverter() = delete; //JsonCpp - static BentleyStatus JsonToECInstance(ECN::IECInstanceR, BeJsConst, ECN::ECClassCR currentClass, Utf8StringCR currentAccessString, IECClassLocaterR, bool ignoreUnknownProperties = false, IECSchemaRemapperCP remapper = nullptr, std::function shouldSerializeProperty = nullptr); + static BentleyStatus JsonToECInstance(ECN::IECInstanceR, BeJsConst, ECN::ECClassCR currentClass, Utf8StringCR currentAccessString, IECClassLocaterR, bool ignoreUnknownProperties = false, IECSchemaRemapperCP remapper = nullptr, std::function shouldSerializeProperty = nullptr, bool isDeepNull = false); static BentleyStatus JsonToPrimitiveECValue(ECN::ECValueR value, BeJsConst json, ECN::PrimitiveType type, Utf8CP extendedTypeName); static BentleyStatus JsonToArrayECValue(ECN::IECInstanceR, BeJsConst, ECN::ArrayECPropertyCR, Utf8StringCR currentAccessString, IECClassLocaterR); diff --git a/iModelCore/ecobjects/src/ECJsonUtilities.cpp b/iModelCore/ecobjects/src/ECJsonUtilities.cpp index ea7edf7f045..3267e01de86 100644 --- a/iModelCore/ecobjects/src/ECJsonUtilities.cpp +++ b/iModelCore/ecobjects/src/ECJsonUtilities.cpp @@ -706,13 +706,66 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(ECN::IECInstanceR instan // @bsimethod //--------------------------------------------------------------------------------------- BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, BeJsConst jsonValue, ECClassCR currentClass, Utf8StringCR currentAccessString, - IECClassLocaterR classLocater, bool ignoreUnknownProperties, IECSchemaRemapperCP remapper, std::function shouldSerializeProperty) + IECClassLocaterR classLocater, bool ignoreUnknownProperties, IECSchemaRemapperCP remapper, std::function shouldSerializeProperty, bool isDeepNull) { - if (!jsonValue.isObject()) + if (!jsonValue.isObject() && !isDeepNull) return ERROR; bool checkShouldSerializeProperty = shouldSerializeProperty != nullptr; BentleyStatus stat = SUCCESS; + + if (isDeepNull) + { + ClassLayoutR classLayout = currentClass.GetDefaultStandaloneEnabler()->GetClassLayout(); + uint32_t propIndex = 0; + if (ECObjectsStatus::Success != classLayout.GetPropertyIndex(propIndex, "")) // we need to select the root of the currentClass + return ERROR; + + uint32_t childIndex = classLayout.GetFirstChildPropertyIndex(propIndex); + while (0 != childIndex) + { + Utf8CP innerMemberName; + if (ECObjectsStatus::Success != classLayout.GetAccessStringByIndex(innerMemberName, childIndex)) + return ERROR; + + ECPropertyP ecProperty = currentClass.GetPropertyP(innerMemberName); + Utf8String childAccessString = currentAccessString.empty() ? Utf8String(innerMemberName) : currentAccessString + "." + Utf8String(innerMemberName); + if (ecProperty->GetIsPrimitive()) + { + ECValue ecValue; + ecValue.SetToNull(); + ECObjectsStatus ecStatus = instance.SetInternalValue(childAccessString.c_str(), ecValue); + if (ecStatus != ECObjectsStatus::Success && ecStatus != ECObjectsStatus::PropertyValueMatchesNoChange) + { + stat = ERROR; + BeAssert(ecStatus == ECObjectsStatus::Success || ecStatus == ECObjectsStatus::PropertyValueMatchesNoChange); + } + } + else if (ecProperty->GetIsStruct()) + { + BentleyStatus status = JsonToECInstance(instance, jsonValue, ecProperty->GetAsStructProperty()->GetType(), childAccessString, classLocater, ignoreUnknownProperties, remapper, nullptr, true); + if (status != SUCCESS) + { + stat = ERROR; + BeAssert(SUCCESS == status); + } + } + else if (ecProperty->GetIsArray()) + { + ECObjectsStatus ecStatus = instance.ClearArray(childAccessString.c_str()); + if (ECObjectsStatus::Success != ecStatus) + { + stat = ERROR; + BeAssert(ECObjectsStatus::Success == ecStatus); + } + } + + childIndex = classLayout.GetNextChildPropertyIndex(propIndex, childIndex); + } + + return stat; + } + jsonValue.ForEachProperty([&](Utf8CP memberName, BeJsConst childJsonValue) { auto convertOne = [&]() { @@ -747,19 +800,13 @@ BentleyStatus JsonECInstanceConverter::JsonToECInstance(IECInstanceR instance, B } else if (ecProperty->GetIsStruct()) { - if (childJsonValue.isNull()) - return false; - - if (SUCCESS != JsonToECInstance(instance, childJsonValue, ecProperty->GetAsStructProperty()->GetType(), accessString, classLocater, ignoreUnknownProperties, remapper)) + if (SUCCESS != JsonToECInstance(instance, childJsonValue, ecProperty->GetAsStructProperty()->GetType(), accessString, classLocater, ignoreUnknownProperties, remapper, nullptr, childJsonValue.isNull())) return true; return false; } else if (ecProperty->GetIsArray()) { - if (childJsonValue.isNull()) - return false; - if (SUCCESS != JsonToArrayECValue(instance, childJsonValue, *ecProperty->GetAsArrayProperty(), accessString, classLocater)) return true; } @@ -995,28 +1042,21 @@ BentleyStatus JsonECInstanceConverter::JsonToPrimitiveECValue(ECValueR ecValue, //--------------------------------------------------------------------------------------- BentleyStatus JsonECInstanceConverter::JsonToArrayECValue(IECInstanceR instance, BeJsConst jsonValue, ArrayECPropertyCR property, Utf8StringCR accessString, IECClassLocaterR classLocater) { - if (!jsonValue.isArray()) + if (!jsonValue.isArray() && !jsonValue.isNull()) return ERROR; - const uint32_t length = jsonValue.size(); - ECValue arrayValue; instance.GetValue(arrayValue, accessString.c_str()); - uint32_t currentLength = arrayValue.IsNull()? 0: arrayValue.GetArrayInfo().GetCount(); - if (length < currentLength) - { - // We need to shorten the array. Start by emptying it out. - if (ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) - return ERROR; - currentLength = 0; - // Now make the array the size we need - } - if (length > currentLength) - { - uint32_t xlength = length - currentLength; - if (ECObjectsStatus::Success != instance.AddArrayElements(accessString.c_str(), xlength)) - return ERROR; - } + uint32_t currentLength = arrayValue.IsNull() ? 0 : arrayValue.GetArrayInfo().GetCount(); + if (currentLength != 0 && ECObjectsStatus::Success != instance.ClearArray(accessString.c_str())) + return ERROR; + + if (jsonValue.isNull()) + return SUCCESS; + + const uint32_t length = jsonValue.size(); + if (length > 0 && ECObjectsStatus::Success != instance.AddArrayElements(accessString.c_str(), length)) + return ERROR; if (property.GetIsStructArray()) { diff --git a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp index 3a49a7a261a..e085513a33a 100644 --- a/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp +++ b/iModelCore/ecobjects/test/NonPublished/ECJsonUtilitiesTests.cpp @@ -15,7 +15,7 @@ struct ECJsonUtilitiesTestFixture : ECTestFixture { protected: - static BentleyStatus ParseJson(BeJsDocument& json, Utf8StringCR jsonStr) { json.Parse(jsonStr); return json.hasParseError() ? ERROR : SUCCESS; } + static BentleyStatus ParseJson(BeJsDocument& json, Utf8StringCR jsonStr) { json.Parse(jsonStr); return json.hasParseError() ? ERROR : SUCCESS; } static BentleyStatus ParseJson(rapidjson::Document& json, Utf8StringCR jsonStr) { return json.Parse<0>(jsonStr.c_str()).HasParseError() ? ERROR : SUCCESS; } }; @@ -29,71 +29,71 @@ void PrintTo(BeInt64Id id, std::ostream* os) { *os << id.GetValueUnchecked(); } //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToId) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; //Ids formatted numerically in JSON Utf8CP jsonStr = "1234"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = "-10"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = "3.14"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr << " floating numbers are rounded"; //Ids formatted as decimal strings in JSON jsonStr = R"json("1234")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1234)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("1099511627775")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(1099511627775)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("-10")json"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(uint64_t(-10)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("3.14")json"; // Not a valid value for an Id, but failing the method is not worth the overhead. So the test documents the behavior of the API - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr << " floating numbers are rounded"; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(3), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr << " floating numbers are rounded"; //Ids formatted as hexadecimal strings in JSON jsonStr = R"json("0x123")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(0x123)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; jsonStr = R"json("0xFFFFFFFFFF")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(jsonDoc)) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); EXPECT_EQ(BeInt64Id(UINT64_C(0xFFFFFFFFFF)), ECJsonUtilities::JsonToId(rapidJson)) << jsonStr; @@ -104,14 +104,14 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToId) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; BeInt64Id id(UINT64_C(1234)); - ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); - ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); - ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << id.ToString(); + ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); + ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); + ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << id.ToString(); ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(rapidJson, id, rapidJson.GetAllocator())) << id.ToString(); ASSERT_TRUE(rapidJson.IsString()) << id.ToString(); @@ -119,9 +119,9 @@ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) id = BeInt64Id(UINT64_C(1099511627775)); - ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); - ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); - ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << id.ToString(); + ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(jsonDoc, id)) << id.ToString(); + ASSERT_TRUE(jsonDoc.isString()) << id.ToString(); + ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << id.ToString(); ASSERT_EQ(SUCCESS, ECJsonUtilities::IdToJson(rapidJson, id, rapidJson.GetAllocator())) << id.ToString(); ASSERT_TRUE(rapidJson.IsString()) << id.ToString(); @@ -133,7 +133,7 @@ TEST_F(ECJsonUtilitiesTestFixture, IdToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; Utf8CP jsonStr = nullptr; @@ -141,8 +141,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //formatted numerically in JSON jsonStr = "1234"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1234), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -150,8 +150,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1234), val) << jsonStr; jsonStr = "-10"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(-10), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -159,8 +159,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(-10), val) << jsonStr; jsonStr = "3.14"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; EXPECT_EQ(INT64_C(3), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -169,8 +169,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //formatted as decimal strings in JSON jsonStr = R"json("1234")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1234), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -178,8 +178,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1234), val) << jsonStr; jsonStr = R"json("1099511627775")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(1099511627775), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -187,8 +187,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(1099511627775), val) << jsonStr; jsonStr = R"json("-10")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(-10), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -196,8 +196,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(-10), val) << jsonStr; jsonStr = R"json("3.14")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr << " floating numbers are rounded"; EXPECT_EQ(INT64_C(3), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -206,8 +206,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //Ids formatted as hexadecimal strings in JSON jsonStr = R"json("0x123")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(0x123), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -215,8 +215,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) EXPECT_EQ(INT64_C(0x123), val) << jsonStr; jsonStr = R"json("0xFFFFFFFFFF")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToInt64(val, jsonDoc)) << jsonStr; EXPECT_EQ(INT64_C(0xFFFFFFFFFF), val) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -229,32 +229,32 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToInt64) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; int64_t val = INT64_C(1234); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("1234", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("1234", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; ASSERT_STRCASEEQ("1234", rapidJson.GetString()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("0x4d2", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsHexadecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -262,27 +262,27 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) val = INT64_C(0xffffffffff); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("1099511627775", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("1099511627775", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; ASSERT_STRCASEEQ("1099511627775", rapidJson.GetString()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsHexadecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("0xffffffffff", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsHexadecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -291,18 +291,18 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) val = INT64_C(-10); - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); - // TODO: add isIntegral() function - // ASSERT_TRUE(jsonDoc.isIntegral()) << val; - ASSERT_EQ(val, jsonDoc.asInt64()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsNumber); + // TODO: add isIntegral() function + // ASSERT_TRUE(jsonDoc.isIntegral()) << val; + ASSERT_EQ(val, jsonDoc.asInt64()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsNumber); ASSERT_TRUE(rapidJson.IsInt64()) << val; ASSERT_EQ(val, rapidJson.GetInt64()) << val; - ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); - ASSERT_TRUE(jsonDoc.isString()) << val; - ASSERT_STRCASEEQ("-10", jsonDoc.asCString()) << val; + ECJsonUtilities::Int64ToJson(jsonDoc, val, ECJsonInt64Format::AsDecimalString); + ASSERT_TRUE(jsonDoc.isString()) << val; + ASSERT_STRCASEEQ("-10", jsonDoc.asCString()) << val; ECJsonUtilities::Int64ToJson(rapidJson, val, rapidJson.GetAllocator(), ECJsonInt64Format::AsDecimalString); ASSERT_TRUE(rapidJson.IsString()) << val; @@ -314,15 +314,15 @@ TEST_F(ECJsonUtilitiesTestFixture, Int64ToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; Utf8CP jsonStr = nullptr; DateTime dt; jsonStr = R"json("2017-11-20T10:45:32.111Z")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -330,8 +330,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20 10:45:32.111Z")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -339,8 +339,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20T10:45:32.111")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -348,8 +348,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20 10:45:32.111")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -357,8 +357,8 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) EXPECT_EQ(DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111), dt) << jsonStr; jsonStr = R"json("2017-11-20")json"; - ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); - EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; + ASSERT_EQ(SUCCESS, ParseJson(jsonDoc, jsonStr)); + EXPECT_EQ(SUCCESS, ECJsonUtilities::JsonToDateTime(dt, jsonDoc)) << jsonStr; EXPECT_EQ(DateTime(2017, 11, 20), dt) << jsonStr; ASSERT_EQ(SUCCESS, ParseJson(rapidJson, jsonStr)); @@ -371,14 +371,14 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToDateTime) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) { - BeJsDocument jsonDoc; + BeJsDocument jsonDoc; rapidjson::Document rapidJson; DateTime dt(DateTime::Kind::Utc, 2017, 11, 20, 10, 45, 32, 111); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20T10:45:32.111Z", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20T10:45:32.111Z", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -386,9 +386,9 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) dt = DateTime(DateTime::Kind::Unspecified, 2017, 11, 20, 10, 45, 32, 111); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20T10:45:32.111", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20T10:45:32.111", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -396,9 +396,9 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) dt = DateTime(2017, 11, 20); - ECJsonUtilities::DateTimeToJson(jsonDoc, dt); - ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); - ASSERT_STRCASEEQ("2017-11-20", jsonDoc.asCString()) << dt.ToString(); + ECJsonUtilities::DateTimeToJson(jsonDoc, dt); + ASSERT_TRUE(jsonDoc.isString()) << dt.ToString(); + ASSERT_STRCASEEQ("2017-11-20", jsonDoc.asCString()) << dt.ToString(); ECJsonUtilities::DateTimeToJson(rapidJson, dt, rapidJson.GetAllocator()); ASSERT_TRUE(rapidJson.IsString()) << dt.ToString(); @@ -410,8 +410,8 @@ TEST_F(ECJsonUtilitiesTestFixture, DateTimeToJson) //+---------------+---------------+---------------+---------------+---------------+------ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) { - BeJsDocument jsonDocVal; - BeJsValue jsonDoc(jsonDocVal); + BeJsDocument jsonDocVal; + BeJsValue jsonDoc(jsonDocVal); rapidjson::Document rapidJsonVal; BeJsValue rapidJson(rapidJsonVal); @@ -422,11 +422,11 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) bvector actualByteVector; ByteStream actualByteStream; - jsonDoc.SetBinary(expectedByteVector); - EXPECT_EQ(SUCCESS, jsonDoc.GetBinary(actualByteVector)); + jsonDoc.SetBinary(expectedByteVector); + EXPECT_EQ(SUCCESS, jsonDoc.GetBinary(actualByteVector)); EXPECT_EQ(0, memcmp(expectedByteVector.data(), actualByteVector.data(), expectedByteVector.size())); - jsonDoc.SetBinary(expectedByteStream.data(), expectedByteStream.size()); - jsonDoc.GetBinary(actualByteStream); + jsonDoc.SetBinary(expectedByteStream.data(), expectedByteStream.size()); + jsonDoc.GetBinary(actualByteStream); EXPECT_EQ(0, memcmp(expectedByteStream.GetData(), actualByteStream.GetData(), expectedByteStream.GetSize())); actualByteVector.clear(); @@ -436,15 +436,15 @@ TEST_F(ECJsonUtilitiesTestFixture, JsonToBinary) EXPECT_EQ(0, memcmp(expectedByteVector.data(), actualByteVector.data(), expectedByteVector.size())); rapidJson.GetBinary(actualByteStream); EXPECT_EQ(0, memcmp(expectedByteStream.GetData(), actualByteStream.GetData(), expectedByteStream.GetSize())); - EXPECT_EQ(jsonDoc, rapidJson); + EXPECT_EQ(jsonDoc, rapidJson); - jsonDoc = "bad"; - jsonDoc.From(rapidJson); - EXPECT_EQ(jsonDoc, rapidJson); + jsonDoc = "bad"; + jsonDoc.From(rapidJson); + EXPECT_EQ(jsonDoc, rapidJson); rapidJson = "bad"; - rapidJson.From(jsonDoc); - EXPECT_EQ(jsonDoc, rapidJson); + rapidJson.From(jsonDoc); + EXPECT_EQ(jsonDoc, rapidJson); } //--------------------------------------------------------------------------------------- @@ -700,7 +700,7 @@ TEST_F(ECJsonUtilitiesTestFixture, IGeometryIModelJsonRoundTrip) struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture { protected: - BeJsDocument m_jsDoc; + BeJsDocument m_jsDoc; rapidjson::Document m_rapidJson; static IECInstancePtr CreateTestInstance(ECSchemaR schema, PrimitiveType propertyType) @@ -725,12 +725,12 @@ struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture { if (SUCCESS == expectedStatus) { - ASSERT_EQ(SUCCESS, ParseJson(m_jsDoc, jsonString)); + ASSERT_EQ(SUCCESS, ParseJson(m_jsDoc, jsonString)); ASSERT_EQ(SUCCESS, ParseJson(m_rapidJson, jsonString)); } else { - ASSERT_NE(SUCCESS, ParseJson(m_jsDoc, jsonString)); + ASSERT_NE(SUCCESS, ParseJson(m_jsDoc, jsonString)); ASSERT_NE(SUCCESS, ParseJson(m_rapidJson, jsonString)); } } @@ -755,6 +755,14 @@ struct JsonECInstanceConverterTestFixture : ECJsonUtilitiesTestFixture ASSERT_EQ(ECObjectsStatus::Success, instance.GetValue(ecValue, accessString)); ASSERT_TRUE(ecValue.IsNull()); } + + void AssertEmptyArray(IECInstanceCR instance, Utf8CP accessString) + { + ECValue ecValue; + ASSERT_EQ(ECObjectsStatus::Success, instance.GetValue(ecValue, accessString)); + ASSERT_TRUE(ecValue.IsArray()); + ASSERT_EQ(0, ecValue.GetArrayInfo().GetCount()); + } }; struct InSchemaClassLocater final : ECN::IECClassLocater @@ -785,43 +793,43 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) }; ParsePropertyJson("100"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(100, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(100, GetPropertyValue(*instance)); ParsePropertyJson("1.111111"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.111111, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.111111, GetPropertyValue(*instance)); ParsePropertyJson("1000000000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e15, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e15, GetPropertyValue(*instance)); ParsePropertyJson("1000000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e12, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e12, GetPropertyValue(*instance)); ParsePropertyJson("1000000000"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e9, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e9, GetPropertyValue(*instance)); ParsePropertyJson("1.0e18"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e18, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e18, GetPropertyValue(*instance)); ParsePropertyJson("1.0e-24"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_EQ(1.0e-24, GetPropertyValue(*instance)); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); EXPECT_EQ(1.0e-24, GetPropertyValue(*instance)); @@ -830,7 +838,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) ParsePropertyJson("\"ABCDE\""); { DISABLE_ASSERTS - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); } #endif @@ -838,7 +846,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_DoubleProperty) ParsePropertyJson("\"1.111111\""); { DISABLE_ASSERTS - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*instance, m_rapidJson, classLocater)); } } @@ -863,7 +871,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) } IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); EXPECT_TRUE(structInstance.IsValid()); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); // expect error when not initialized // construct entity instance with a struct property @@ -879,7 +887,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) } IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); EXPECT_TRUE(testInstance.IsValid()); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized // Test for expected JSON parse errors @@ -892,11 +900,11 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) //------------------------------------------------------------------------- ParseJsonString("null"); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); ParseJsonString("{}"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); AssertNullValue(*structInstance, "doubleProperty"); AssertNullValue(*structInstance, "stringProperty"); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); @@ -904,7 +912,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*structInstance, "stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1" })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); AssertDoubleValue(*structInstance, "doubleProperty", 1.1); AssertStringValue(*structInstance, "stringProperty", "S1"); EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); @@ -916,11 +924,11 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) //------------------------------------------------------------------------- ParseJsonString("null"); - EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); ParseJsonString("{}"); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "doubleProperty"); AssertNullValue(*testInstance, "stringProperty"); AssertNullValue(*testInstance, "testStruct"); @@ -934,7 +942,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*testInstance, "testStruct.stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": null, "stringProperty": null })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "doubleProperty"); AssertNullValue(*testInstance, "stringProperty"); AssertNullValue(*testInstance, "testStruct"); @@ -948,7 +956,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertNullValue(*testInstance, "testStruct.stringProperty"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1" })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "testStruct"); AssertNullValue(*testInstance, "testStruct.doubleProperty"); AssertNullValue(*testInstance, "testStruct.stringProperty"); @@ -962,7 +970,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "stringProperty", "S1"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1", "testStruct": null })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertNullValue(*testInstance, "testStruct"); AssertNullValue(*testInstance, "testStruct.doubleProperty"); AssertNullValue(*testInstance, "testStruct.stringProperty"); @@ -976,7 +984,7 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "stringProperty", "S1"); ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 1.1, "stringProperty": "S1", "testStruct": { "doubleProperty": 2.2, "stringProperty": "S2" } })*")); - EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); AssertDoubleValue(*testInstance, "doubleProperty", 1.1); AssertStringValue(*testInstance, "stringProperty", "S1"); AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 2.2); @@ -988,4 +996,468 @@ TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct) AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); } +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Struct_InitializedInstance) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(structInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the struct instance with initial data + ECValue ecValue; + ecValue.SetDouble(22.2); + (*structInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS.S1-Init"); + (*structInstance).SetInternalValue("StringProperty", ecValue); + + // construct entity instance with a struct property + ECEntityClassP testClass; + { + StructECPropertyP structProperty; + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreateStructProperty(structProperty, "TestStruct", *structClass); + testClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + testClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the test instance with initial data + ecValue.SetDouble(0.6); + (*testInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("S1-Init"); + (*testInstance).SetInternalValue("StringProperty", ecValue); + ecValue.SetDouble(22.2); + (*testInstance).SetInternalValue("TestStruct.DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS.S1-Init"); + (*testInstance).SetInternalValue("TestStruct.StringProperty", ecValue); + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> struct instance tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); // properties should remain unchanged + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.2); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.2); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 22.3, "stringProperty": "TS.S1-New" })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.3); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-New"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*structInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*structInstance, "doubleProperty", 22.3); + AssertStringValue(*structInstance, "stringProperty", "TS.S1-New"); + + //------------------------------------------------------------------------- + // JSON --> entity instance with struct property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 0.6); + AssertStringValue(*testInstance, "stringProperty", "S1-Init"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 0.6); + AssertStringValue(*testInstance, "stringProperty", "S1-Init"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": null, "stringProperty": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertNullValue(*testInstance, "doubleProperty"); + AssertNullValue(*testInstance, "stringProperty"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertNullValue(*testInstance, "doubleProperty"); + AssertNullValue(*testInstance, "stringProperty"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 4.2, "stringProperty": "S1-new-new" })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 4.2); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 4.2); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 22.2); + AssertStringValue(*testInstance, "testStruct.stringProperty", "TS.S1-Init"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 5.3, "stringProperty": "S1-new-new-new", "testStruct": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 5.3); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new-new"); + AssertNullValue(*testInstance, "testStruct"); + AssertNullValue(*testInstance, "testStruct.doubleProperty"); + AssertNullValue(*testInstance, "testStruct.stringProperty"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 5.3); + AssertStringValue(*testInstance, "stringProperty", "S1-new-new-new"); + AssertNullValue(*testInstance, "testStruct"); + AssertNullValue(*testInstance, "testStruct.doubleProperty"); + AssertNullValue(*testInstance, "testStruct.stringProperty"); + + ParseJsonString(Utf8Chars(u8R"*({ "doubleProperty": 111.1, "stringProperty": "S1", "testStruct": { "doubleProperty": 4.4, "stringProperty": "S2" } })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 111.1); + AssertStringValue(*testInstance, "stringProperty", "S1"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 4.4); + AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertDoubleValue(*testInstance, "doubleProperty", 111.1); + AssertStringValue(*testInstance, "stringProperty", "S1"); + AssertDoubleValue(*testInstance, "testStruct.doubleProperty", 4.4); + AssertStringValue(*testInstance, "testStruct.stringProperty", "S2"); + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Array) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + ECEntityClassP testClass; + PrimitiveArrayECPropertyP testIntegerArrayProperty; + StructArrayECPropertyP testStructArrayProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreatePrimitiveArrayProperty(testIntegerArrayProperty, "IntArrayProp", PRIMITIVETYPE_Integer); + testClass->CreateStructArrayProperty(testStructArrayProperty, "StructArrayProp", *structClass); + + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> entity instance with array property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": null, "StructArrayProp": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [], "StructArrayProp": [] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [4, 5, 6, 9, 12], "StructArrayProp": [{ "DoubleProperty": 3.41, "StringProperty": "NewVal1" }, { "DoubleProperty": 12.92, "StringProperty": null }] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + } + +//--------------------------------------------------------------------------------------- +// @bsimethod +//--------------------------------------------------------------------------------------- +TEST_F(JsonECInstanceConverterTestFixture, JsonToECInstance_Array_InitializedInstance) + { + ECSchemaPtr schema; + ECSchema::CreateSchema(schema, "Schema", "sch", 1, 0, 0); + InSchemaClassLocater classLocater(*schema); + + // construct struct instance + ECStructClassP structClass; + { + PrimitiveECPropertyP doubleProperty; + PrimitiveECPropertyP stringProperty; + schema->CreateStructClass(structClass, "TestStruct"); + structClass->CreatePrimitiveProperty(doubleProperty, "DoubleProperty", PRIMITIVETYPE_Double); + structClass->CreatePrimitiveProperty(stringProperty, "StringProperty", PRIMITIVETYPE_String); + } + + ECEntityClassP testClass; + PrimitiveArrayECPropertyP testIntegerArrayProperty; + StructArrayECPropertyP testStructArrayProperty; + schema->CreateEntityClass(testClass, "TestClass"); + testClass->CreatePrimitiveArrayProperty(testIntegerArrayProperty, "IntArrayProp", PRIMITIVETYPE_Integer); + testClass->CreateStructArrayProperty(testStructArrayProperty, "StructArrayProp", *structClass); + IECInstancePtr testInstance = testClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(testInstance.IsValid()); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); // expect error when not initialized + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); // expect error when not initialized + + // Initialize the test instance with initial data + ECValue ecValue; + ecValue.SetInteger(9); + (*testInstance).AddArrayElements("IntArrayProp", 3); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 0); + ecValue.SetInteger(16); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 1); + ecValue.SetInteger(25); + (*testInstance).SetInternalValue("IntArrayProp", ecValue, 2); + + (*testInstance).AddArrayElements("StructArrayProp", 1); + IECInstancePtr structInstance = structClass->GetDefaultStandaloneEnabler()->CreateInstance(); + EXPECT_TRUE(structInstance.IsValid()); + ecValue.SetDouble(22.2); + (*structInstance).SetInternalValue("DoubleProperty", ecValue); + ecValue.SetUtf8CP("TS1-Init"); + (*structInstance).SetInternalValue("StringProperty", ecValue); + ecValue.SetStruct(structInstance.get()); + (*testInstance).SetInternalValue("StructArrayProp", ecValue, 0); + + // Test for expected JSON parse errors + ParseJsonString(nullptr, ERROR); + ParseJsonString("", ERROR); + ParseJsonString("undefined", ERROR); + + //------------------------------------------------------------------------- + // JSON --> entity instance with array property tests + //------------------------------------------------------------------------- + + ParseJsonString("null"); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + EXPECT_NE(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + + ParseJsonString("{}"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 3); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 16); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 25); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 1); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 22.2); + AssertStringValue(*structInst, "StringProperty", "TS1-Init"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 3); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 16); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 25); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 1); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 22.2); + AssertStringValue(*structInst, "StringProperty", "TS1-Init"); + } + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": null, "StructArrayProp": null })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [4, 5, 6, 9, 12], "StructArrayProp": [{ "DoubleProperty": 3.41, "StringProperty": "NewVal1" }, { "DoubleProperty": 12.92, "StringProperty": null }] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + { + ECValue intArrayProp; + ECValue structArrayProp; + IECInstancePtr structInst; + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp")); + ASSERT_EQ(intArrayProp.GetArrayInfo().GetCount(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 0)); + ASSERT_EQ(intArrayProp.GetInteger(), 4); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 1)); + ASSERT_EQ(intArrayProp.GetInteger(), 5); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 2)); + ASSERT_EQ(intArrayProp.GetInteger(), 6); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 3)); + ASSERT_EQ(intArrayProp.GetInteger(), 9); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(intArrayProp, "IntArrayProp", 4)); + ASSERT_EQ(intArrayProp.GetInteger(), 12); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp")); + ASSERT_EQ(structArrayProp.GetArrayInfo().GetCount(), 2); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 0)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 3.41); + AssertStringValue(*structInst, "StringProperty", "NewVal1"); + ASSERT_EQ(ECObjectsStatus::Success, (*testInstance).GetValue(structArrayProp, "StructArrayProp", 1)); + structInst = structArrayProp.GetStruct(); + AssertDoubleValue(*structInst, "DoubleProperty", 12.92); + AssertNullValue(*structInst, "StringProperty"); + } + + ParseJsonString(Utf8Chars(u8R"*({ "IntArrayProp": [], "StructArrayProp": [] })*")); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_jsDoc, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + EXPECT_EQ(SUCCESS, JsonECInstanceConverter::JsonToECInstance(*testInstance, m_rapidJson, classLocater)); + AssertEmptyArray(*testInstance, "IntArrayProp"); + AssertEmptyArray(*testInstance, "StructArrayProp"); + } + END_BENTLEY_ECN_TEST_NAMESPACE \ No newline at end of file