/* * Copyright 2015 - 2024 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #undef NDEBUG #include #include #include #include template union U { T t; int i; }; U u; union TestUnion { static constexpr auto TypeName = "TestUnion"; static constexpr auto TypeVersion = 1; bool Bool; uint32_t Int = 5; char *String; }; struct TestStructNest { static constexpr auto TypeName = "TestStructNest"; static constexpr auto TypeVersion = 1; bool Bool = false; uint32_t Int = 0; ox::IString<32> String = ""; }; struct TestStruct { static constexpr auto TypeName = "TestStruct"; static constexpr auto TypeVersion = 1; bool Bool = false; int32_t Int = 0; int32_t Int1 = 0; int32_t Int2 = 0; int32_t Int3 = 0; int32_t Int4 = 0; int32_t Int5 = 0; int32_t Int6 = 0; int32_t Int7 = 0; int32_t Int8 = 0; int unionIdx = 1; TestUnion Union; ox::String String{""}; uint32_t List[4] = {0, 0, 0, 0}; ox::HashMap Map; TestStructNest EmptyStruct; TestStructNest Struct; TestStruct() noexcept = default; TestStruct(TestStruct &&other) noexcept; constexpr ~TestStruct() noexcept { if (unionIdx == 2) { ox::safeDelete(Union.String); } } constexpr TestStruct &operator=(TestStruct&&) noexcept; }; constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *obj) noexcept { OX_RETURN_ERROR(io->template setTypeInfo()); OX_RETURN_ERROR(io->field("Bool", &obj->Bool)); OX_RETURN_ERROR(io->field("Int", &obj->Int)); OX_RETURN_ERROR(io->fieldCString("String", &obj->String)); return ox::Error(0); } constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *obj) noexcept { OX_RETURN_ERROR(io->template setTypeInfo()); OX_RETURN_ERROR(io->field("Bool", &obj->Bool)); OX_RETURN_ERROR(io->field("Int", &obj->Int)); OX_RETURN_ERROR(io->field("String", &obj->String)); return ox::Error(0); } constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *obj) noexcept { OX_RETURN_ERROR(io->template setTypeInfo()); OX_RETURN_ERROR(io->field("Bool", &obj->Bool)); OX_RETURN_ERROR(io->field("Int", &obj->Int)); OX_RETURN_ERROR(io->field("Int1", &obj->Int1)); OX_RETURN_ERROR(io->field("Int2", &obj->Int2)); OX_RETURN_ERROR(io->field("Int3", &obj->Int3)); OX_RETURN_ERROR(io->field("Int4", &obj->Int4)); OX_RETURN_ERROR(io->field("Int5", &obj->Int5)); OX_RETURN_ERROR(io->field("Int6", &obj->Int6)); OX_RETURN_ERROR(io->field("Int7", &obj->Int7)); OX_RETURN_ERROR(io->field("Int8", &obj->Int8)); OX_RETURN_ERROR(io->field("unionIdx", &obj->unionIdx)); if (io->opType() == ox::OpType::Reflect) { OX_RETURN_ERROR(io->field("Union", ox::UnionView{&obj->Union, 0})); } else { OX_RETURN_ERROR(io->field("Union", ox::UnionView{&obj->Union, obj->unionIdx})); } OX_RETURN_ERROR(io->field("String", &obj->String)); OX_RETURN_ERROR(io->field("List", obj->List, 4)); OX_RETURN_ERROR(io->field("Map", &obj->Map)); OX_RETURN_ERROR(io->field("EmptyStruct", &obj->EmptyStruct)); OX_RETURN_ERROR(io->field("Struct", &obj->Struct)); return ox::Error(0); } TestStruct::TestStruct(TestStruct &&other) noexcept { ox::moveModel(this, &other); } constexpr TestStruct &TestStruct::operator=(TestStruct &&other) noexcept { ox::moveModel(this, &other); return *this; } const std::map tests = { { { "OrganicClawWriter", [] { // This test doesn't confirm much, but it does show that the writer // doesn't segfault TestStruct ts; return ox::writeOC(ts).error; } }, { "OrganicClawReader", [] { TestStruct testIn; testIn.Bool = true; testIn.Int = 42; testIn.Union.Int = 52; testIn.String = "Test String 1"; testIn.List[0] = 1; testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; testIn.Map["asdf"] = 93; testIn.Map["aoeu"] = 94; testIn.Struct.Bool = false; testIn.Struct.Int = 300; testIn.Struct.String = "Test String 2"; auto [oc, writeErr] = ox::writeOC(testIn); oxAssert(writeErr, "writeOC failed"); oxOutf("{}\n", oc.data()); auto [testOut, readErr] = ox::readOC(oc.data()); oxAssert(readErr, "readOC failed"); oxAssert(testIn.Bool == testOut.Bool, "Bool value mismatch"); oxAssert(testIn.Int == testOut.Int, "Int value mismatch"); oxAssert(testIn.Int1 == testOut.Int1, "Int1 value mismatch"); oxAssert(testIn.Int2 == testOut.Int2, "Int2 value mismatch"); oxAssert(testIn.Int3 == testOut.Int3, "Int3 value mismatch"); oxAssert(testIn.Int4 == testOut.Int4, "Int4 value mismatch"); oxAssert(testIn.Int5 == testOut.Int5, "Int5 value mismatch"); oxAssert(testIn.Int6 == testOut.Int6, "Int6 value mismatch"); oxAssert(testIn.Int7 == testOut.Int7, "Int7 value mismatch"); oxAssert(testIn.Int8 == testOut.Int8, "Int8 value mismatch"); oxAssert(testIn.Union.Int == testOut.Union.Int, "Union.Int value mismatch"); oxAssert(testIn.String == testOut.String, "String value mismatch"); oxAssert(testIn.List[0] == testOut.List[0], "List[0] value mismatch"); oxAssert(testIn.List[1] == testOut.List[1], "List[1] value mismatch"); oxAssert(testIn.List[2] == testOut.List[2], "List[2] value mismatch"); oxAssert(testIn.List[3] == testOut.List[3], "List[3] value mismatch"); oxAssert(testIn.Map["asdf"] == testOut.Map["asdf"], "Map[\"asdf\"] value mismatch"); oxAssert(testIn.Map["aoeu"] == testOut.Map["aoeu"], "Map[\"aoeu\"] value mismatch"); oxAssert(testIn.EmptyStruct.Bool == testOut.EmptyStruct.Bool, "EmptyStruct.Bool value mismatch"); oxAssert(testIn.EmptyStruct.Int == testOut.EmptyStruct.Int, "EmptyStruct.Int value mismatch"); oxAssert(testIn.EmptyStruct.String == testOut.EmptyStruct.String, "EmptyStruct.String value mismatch"); oxAssert(testIn.Struct.Int == testOut.Struct.Int, "Struct.Int value mismatch"); oxAssert(testIn.Struct.String == testOut.Struct.String, "Struct.String value mismatch"); oxAssert(testIn.Struct.Bool == testOut.Struct.Bool, "Struct.Bool value mismatch"); return ox::Error(0); } }, { "OrganicClawModelValue", [] { ox::Buffer dataBuff; TestStruct testIn; testIn.Bool = true; testIn.Int = 42; testIn.String = "Test String 1"; testIn.List[0] = 1; testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; testIn.Struct.Bool = false; testIn.Struct.Int = 300; testIn.Struct.String = "Test String 2"; testIn.unionIdx = 1; testIn.Union.Int = 93; oxAssert(ox::writeOC(testIn).moveTo(dataBuff), "Data generation failed"); ox::TypeStore typeStore; auto type = ox::buildTypeDef(typeStore, testIn); oxAssert(type.error, "Descriptor write failed"); ox::ModelObject testOut; OX_RETURN_ERROR(testOut.setType(type.value)); oxAssert(ox::readOC(dataBuff, testOut), "Data read failed"); oxAssert(testOut.get("Int").unwrap()->get() == testIn.Int, "testOut.Int failed"); oxAssert(testOut.get("Bool").unwrap()->get() == testIn.Bool, "testOut.Bool failed"); oxAssert(testOut.get("String").unwrap()->get() == testIn.String, "testOut.String failed"); auto &testOutStruct = testOut.get("Struct").unwrap()->get(); auto &testOutUnion = testOut.get("Union").unwrap()->get(); auto &testOutList = testOut.get("List").unwrap()->get(); auto testOutStructCopy = testOut.get("Struct").unwrap()->get(); auto testOutUnionCopy = testOut.get("Union").unwrap()->get(); auto testOutListCopy = testOut.get("List").unwrap()->get(); oxAssert(testOutStruct.typeName() == TestStructNest::TypeName, "ModelObject TypeName failed"); oxAssert(testOutStruct.typeVersion() == TestStructNest::TypeVersion, "ModelObject TypeVersion failed"); oxAssert(testOutStruct.get("Bool").unwrap()->get() == testIn.Struct.Bool, "testOut.Struct.Bool failed"); oxAssert(testOutStruct.get("String").unwrap()->get() == testIn.Struct.String.c_str(), "testOut.Struct.String failed"); oxAssert(testOut.get("unionIdx").unwrap()->get() == testIn.unionIdx, "testOut.unionIdx failed"); oxAssert(testOutUnion.unionIdx() == testIn.unionIdx, "testOut.Union idx wrong"); oxAssert(testOutUnion.get("Int").unwrap()->get() == testIn.Union.Int, "testOut.Union.Int failed"); oxAssert(testOutList[0].get() == testIn.List[0], "testOut.List[0] failed"); oxAssert(testOutList[1].get() == testIn.List[1], "testOut.Struct.List[1] failed"); oxAssert(testOutStructCopy.get("Bool").unwrap()->get() == testIn.Struct.Bool, "testOut.Struct.Bool (copy) failed"); oxAssert(testOutStructCopy.get("String").unwrap()->get() == testIn.Struct.String.c_str(), "testOut.Struct.String (copy) failed"); oxAssert(testOutListCopy[0].get() == testIn.List[0], "testOut.Struct.List[0] (copy) failed"); oxAssert(testOutListCopy[1].get() == testIn.List[1], "testOut.Struct.List[1] (copy) failed"); return ox::Error(0); } }, { "OrganicClawDef", [] { TestStruct testIn, testOut; testIn.Bool = true; testIn.Int = 42; testIn.String = "Test String 1"; testIn.List[0] = 1; testIn.List[1] = 2; testIn.List[2] = 3; testIn.List[3] = 4; testIn.Struct.Bool = false; testIn.Struct.Int = 300; testIn.Struct.String = "Test String 2"; auto [oc, ocErr] = ox::writeOC(testIn); oxAssert(ocErr, "Data generation failed"); ox::TypeStore typeStore; auto type = ox::buildTypeDef(typeStore, testIn); oxAssert(type.error, "Descriptor write failed"); OX_RETURN_ERROR(ox::walkModel(type.value, oc.data(), oc.size(), [](const ox::Vector&, const ox::Vector&, const ox::DescriptorField &f, ox::OrganicClawReader *rdr) -> ox::Error { auto fieldName = f.fieldName.c_str(); switch (f.type->primitiveType) { case ox::PrimitiveType::UnsignedInteger: oxOutf("{}:\tuint{}_t:\t", fieldName, f.type->length * 8); switch (f.type->length) { case 1: { uint8_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 2: { uint16_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 4: { uint32_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 8: { uint64_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } } oxOut("\n"); break; case ox::PrimitiveType::SignedInteger: oxOutf("{}:\tint{}_t:\t", fieldName, f.type->length * 8); switch (f.type->length) { case 1: { int8_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 2: { int16_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 4: { int32_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } case 8: { int64_t i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}", i); break; } } oxOut("\n"); break; case ox::PrimitiveType::Bool: { bool i = {}; oxAssert(rdr->field(fieldName, &i), "Walking model failed."); oxOutf("{}:\tbool:\t{}\n", fieldName, i ? "true" : "false"); break; } case ox::PrimitiveType::String: { ox::Vector v(rdr->stringLength(fieldName) + 1); oxAssert(rdr->fieldCString(fieldName, v.data(), v.size()), "Walking model failed."); oxOutf("{}:\tstring:\t{}\n", fieldName, v.data()); break; } case ox::PrimitiveType::Struct: break; case ox::PrimitiveType::Union: break; } return ox::Error(0); } )); return ox::Error(0); } }, } }; int main(int argc, const char **args) { if (argc < 2) { oxError("Must specify test to run"); } auto const testName = args[1]; auto const func = tests.find(testName); if (func != tests.end()) { oxAssert(func->second(), "Test returned Error"); return 0; } return -1; }