// // Unit tests for the ngl_codegen module (Lts/Common/CodeGen): the pure-logic // string helpers (StructBuilder statics), the codegen_base data classes and // the KTextTemplate context wrappers (GrantleeWrappers). // No template execution is done here; the Grantlee/KTextTemplate engine // itself is covered by the GrantleeParserSmoke smoke test. // #include #include #include #include #include #include #include #include using namespace CodeGen; // Minimal concrete DataTypeInfo for the IdlDataWrapper tests (the module's // own concrete data types are private to StructBuilder.cpp). class TestDataTypeInfo : public DataTypeInfo { public: TestDataTypeInfo(const QString& idlType, const QString& cppType, bool isPod) : DataTypeInfo(QStringLiteral("test"), isPod), m_idlType(idlType), m_cppType(cppType) { } QString idlType() const override final { return m_idlType; } QString ddsType() const override final { return m_cppType; } QString sqlType() const override final { return m_cppType; } QString cppType() const override final { return m_cppType; } private: QString m_idlType; QString m_cppType; }; static MemberInformation makeMember(const QString& table, const QString& column) { MemberInformation mi; mi.ea.table = table; mi.ea.column = column; mi.db.table = table; mi.db.columnName = column; mi.cpp.memberName = column; return mi; } class codegen_unit_test : public QObject { Q_OBJECT private slots: void firstCharLowercase(); void camelCaseString(); void camelCaseToUnderline(); void nameWrapperTransforms(); void memberWrapperGetters(); void memberWrapperVariantConversion(); void memberWrapperDdsToJsonElement(); void memberWrapperJsonElementToDds(); void memberWrapperDbToJsonElement(); void memberWrapperJsonElementToDb(); void memberWrapperJsonPrePostFallback(); void memberListWrapperFilters(); void dataTopicWrapper(); void functionTopicWrapper(); void idlWrappers(); void dbStructWrapper(); void codegenBaseDataClasses(); }; void codegen_unit_test::firstCharLowercase() { QCOMPARE(StructBuilder::firstCharLowercase(QString()), QString()); QCOMPARE(StructBuilder::firstCharLowercase(QStringLiteral("FooBar")), QStringLiteral("fooBar")); QCOMPARE(StructBuilder::firstCharLowercase(QStringLiteral("abc")), QStringLiteral("abc")); } void codegen_unit_test::camelCaseString() { // underscore-separated -> camelCase (default keeps the first character lower case) QCOMPARE(StructBuilder::camelCaseString(QStringLiteral("foo_bar")), QStringLiteral("fooBar")); QCOMPARE(StructBuilder::camelCaseString(QStringLiteral("foo_bar"), true), QStringLiteral("FooBar")); QCOMPARE(StructBuilder::camelCaseString(QStringLiteral("foo")), QStringLiteral("foo")); QCOMPARE(StructBuilder::camelCaseString(QStringLiteral("_foo")), QStringLiteral("Foo")); // trailing underscore sets the "next is upper" flag but appends nothing QCOMPARE(StructBuilder::camelCaseString(QStringLiteral("foo_")), QStringLiteral("foo")); QCOMPARE(StructBuilder::camelCaseString(QString()), QString()); } void codegen_unit_test::camelCaseToUnderline() { QCOMPARE(StructBuilder::camelCaseToUnderline(QStringLiteral("FooBarBaz")), QStringLiteral("foo_bar_baz")); QCOMPARE(StructBuilder::camelCaseToUnderline(QStringLiteral("foo")), QStringLiteral("foo")); QCOMPARE(StructBuilder::camelCaseToUnderline(QStringLiteral("A")), QStringLiteral("a")); QCOMPARE(StructBuilder::camelCaseToUnderline(QStringLiteral("aB")), QStringLiteral("a_b")); // digits are kept, an upper case after a digit still splits QCOMPARE(StructBuilder::camelCaseToUnderline(QStringLiteral("Foo1Bar")), QStringLiteral("foo1_bar")); QCOMPARE(StructBuilder::camelCaseToUnderline(QString()), QString()); } void codegen_unit_test::nameWrapperTransforms() { QObject parent; NameWrapper w(QStringLiteral("foo_bar"), &parent); QCOMPARE(w.name(), QStringLiteral("foo_bar")); QCOMPARE(w.nameCc(), QStringLiteral("FooBar")); QCOMPARE(w.nameFlc(), QStringLiteral("fooBar")); NameWrapper w2(QStringLiteral("FooBar"), &parent); QCOMPARE(w2.nameCc(), QStringLiteral("FooBar")); QCOMPARE(w2.nameFlc(), QStringLiteral("fooBar")); } void codegen_unit_test::memberWrapperGetters() { QObject parent; MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("a_name")); mi.ea.type = QStringLiteral("varchar(255)"); mi.db.datatype = QStringLiteral("varchar(255)"); mi.db.columnNameView = QStringLiteral("a_name_v"); mi.db.hasSequence = true; mi.cpp.structName = QStringLiteral("AStruct"); mi.cpp.defaultValue = QStringLiteral("''"); mi.cpp.type = QStringLiteral("QString"); mi.cpp.ddsType = QStringLiteral("dds::core::string255"); mi.cpp.isOptional = true; mi.cpp.usedForCppStruct = true; MemberInformationWrapper w(mi, &parent); QCOMPARE(w.cppMemberName(), QStringLiteral("a_name")); QCOMPARE(w.cppMemberNameCc(), QStringLiteral("AName")); QCOMPARE(w.cppStructName(), QStringLiteral("AStruct")); QCOMPARE(w.cppType(), QStringLiteral("QString")); QCOMPARE(w.cppDefault(), QStringLiteral("''")); QCOMPARE(w.dbTableName(), QStringLiteral("tbl_a")); QCOMPARE(w.dbColumnName(), QStringLiteral("a_name")); QCOMPARE(w.dbColumnNameView(), QStringLiteral("a_name_v")); QCOMPARE(w.cppDdsType(), QStringLiteral("dds::core::string255")); QCOMPARE(w.dbHasSequence(), true); QCOMPARE(w.isOptional(), true); QCOMPARE(w.isCppStruct(), true); QCOMPARE(w.info().db.table, QStringLiteral("tbl_a")); // view column name and dds type fall back when empty MemberInformation mi2 = makeMember(QStringLiteral("tbl_b"), QStringLiteral("b_id")); mi2.cpp.type = QStringLiteral("int"); MemberInformationWrapper w2(mi2, &parent); QCOMPARE(w2.dbColumnNameView(), QStringLiteral("b_id")); QCOMPARE(w2.cppDdsType(), QStringLiteral("int")); } void codegen_unit_test::memberWrapperVariantConversion() { QObject parent; MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.type = QStringLiteral("int"); mi.cpp.isOptional = false; MemberInformationWrapper w(mi, &parent); QCOMPARE(w.ddsToQVariant(), QStringLiteral("toQVariant(data.foo)")); QCOMPARE(w.qVariantToDds(), QStringLiteral("data.foo = fromQVariant(q.value(i++))")); // optional members are wrapped in dds::core::optional<> mi.cpp.isOptional = true; mi.cpp.ddsType = QStringLiteral("MyStruct"); MemberInformationWrapper w2(mi, &parent); QCOMPARE(w2.qVariantToDds(), QStringLiteral("data.foo = fromQVariant>(q.value(i++))")); } void codegen_unit_test::memberWrapperDdsToJsonElement() { QObject parent; // plain member MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.type = QStringLiteral("int"); MemberInformationWrapper w(mi, &parent); QCOMPARE(w.ddsToJsonElement(), QStringLiteral("obj[QStringLiteral(\"foo\")] = element.foo")); // optional member goes through has_value() mi.cpp.isOptional = true; MemberInformationWrapper w2(mi, &parent); QCOMPARE(w2.ddsToJsonElement(), QStringLiteral("if (element.foo.has_value()) obj[QStringLiteral(\"foo\")] = *element.foo")); // empty member name falls back to the member type MemberInformation mi3; mi3.cpp.type = QStringLiteral("int"); MemberInformationWrapper w3(mi3, &parent); QCOMPARE(w3.ddsToJsonElement(), QStringLiteral("obj[QStringLiteral(\"int\")] = element;")); } void codegen_unit_test::memberWrapperJsonElementToDds() { QObject parent; MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.type = QStringLiteral("int"); MemberInformationWrapper w(mi, &parent); QCOMPARE(w.jsonElementToDds(), QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"foo\"))); element.foo = val")); mi.cpp.isOptional = true; MemberInformationWrapper w2(mi, &parent); QCOMPARE(w2.jsonElementToDds(), QStringLiteral( "const QJsonValue val(objElement.value(QStringLiteral(\"foo\"))); " "if (!val.isNull()) element.foo = val")); // empty member name falls back to the member type MemberInformation mi3; mi3.cpp.type = QStringLiteral("int"); MemberInformationWrapper w3(mi3, &parent); QCOMPARE(w3.jsonElementToDds(), QStringLiteral("element = objElement.value(QStringLiteral(\"int\"))")); } void codegen_unit_test::memberWrapperDbToJsonElement() { QObject parent; // plain member MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.type = QStringLiteral("int"); MemberInformationWrapper w(mi, &parent); QCOMPARE(w.dbToJsonElement(), QStringLiteral("obj[QStringLiteral(\"foo\")] = element.foo")); // optional (non-QDateTime) member is only written when it has a value mi.cpp.type = QStringLiteral("QString"); mi.cpp.isOptional = true; MemberInformationWrapper w2(mi, &parent); QCOMPARE(w2.dbToJsonElement(), QStringLiteral("if (!element.foo.isNull()) obj[QStringLiteral(\"foo\")] = element.foo")); // optional QDateTime member uses isValid() mi.cpp.type = QStringLiteral("QDateTime"); MemberInformationWrapper w3(mi, &parent); QCOMPARE(w3.dbToJsonElement(), QStringLiteral("if (element.foo.isValid()) obj[QStringLiteral(\"foo\")] = element.foo")); // empty member name falls back to the member type MemberInformation mi4; mi4.cpp.type = QStringLiteral("int"); MemberInformationWrapper w4(mi4, &parent); QCOMPARE(w4.dbToJsonElement(), QStringLiteral("obj[QStringLiteral(\"int\")] = element;")); } void codegen_unit_test::memberWrapperJsonElementToDb() { QObject parent; MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.type = QStringLiteral("int"); MemberInformationWrapper w(mi, &parent); QCOMPARE(w.jsonElementToDb(), QStringLiteral("const QJsonValue val(objElement.value(QStringLiteral(\"foo\"))); element.foo = val")); mi.cpp.isOptional = true; MemberInformationWrapper w2(mi, &parent); QCOMPARE(w2.jsonElementToDb(), QStringLiteral( "const QJsonValue val(objElement.value(QStringLiteral(\"foo\"))); " "if (!val.isUndefined()) element.foo = val")); // empty member name falls back to the member type MemberInformation mi3; mi3.cpp.type = QStringLiteral("int"); MemberInformationWrapper w3(mi3, &parent); QCOMPARE(w3.jsonElementToDb(), QStringLiteral("element = objElement.value(QStringLiteral(\"int\"))")); } void codegen_unit_test::memberWrapperJsonPrePostFallback() { QObject parent; // json pre/post strings win over the variant pre/post strings when set MemberInformation mi = makeMember(QStringLiteral("tbl_a"), QStringLiteral("foo")); mi.cpp.toVariantPre = QStringLiteral("T("); mi.cpp.toVariantPost = QStringLiteral(")"); mi.cpp.fromVariantPre2 = QStringLiteral("V("); mi.cpp.fromVariantPost = QStringLiteral(")"); mi.cpp.toJsonPre = QStringLiteral("J("); mi.cpp.toJsonPost = QStringLiteral(")"); mi.cpp.fromJsonPre2 = QStringLiteral("F("); mi.cpp.fromJsonPost = QStringLiteral(")"); MemberInformationWrapper w(mi, &parent); QCOMPARE(w.cppToQvPre(), QStringLiteral("T(")); QCOMPARE(w.cppToQvPost(), QStringLiteral(")")); QCOMPARE(w.cppFromQvPre1(), QString()); QCOMPARE(w.cppFromQvPre2(), QStringLiteral("V(")); QCOMPARE(w.cppFromQvPost(), QStringLiteral(")")); QCOMPARE(w.cppToJsonPre(), QStringLiteral("J(")); QCOMPARE(w.cppToJsonPost(), QStringLiteral(")")); QCOMPARE(w.cppFromJsonPre2(), QStringLiteral("F(")); QCOMPARE(w.cppFromJsonPost(), QStringLiteral(")")); // without json strings the variant strings are used MemberInformation mi2 = makeMember(QStringLiteral("tbl_a"), QStringLiteral("bar")); mi2.cpp.toVariantPre = QStringLiteral("T("); mi2.cpp.fromVariantPre2 = QStringLiteral("V("); MemberInformationWrapper w2(mi2, &parent); QCOMPARE(w2.cppToJsonPre(), QStringLiteral("T(")); QCOMPARE(w2.cppToJsonPost(), QString()); QCOMPARE(w2.cppFromJsonPre2(), QStringLiteral("V(")); QCOMPARE(w2.cppFromJsonPost(), QString()); } void codegen_unit_test::memberListWrapperFilters() { QObject parent; MemberInformation cppMi = makeMember(QStringLiteral("t"), QStringLiteral("m1")); cppMi.cpp.usedForCppStruct = true; MemberInformation jsonMi = makeMember(QStringLiteral("t"), QStringLiteral("m2")); jsonMi.cpp.usedForJson = true; MemberInformation fkMi = makeMember(QStringLiteral("t"), QStringLiteral("m3")); fkMi.db.isForeignKey = true; MemberInformation selMi = makeMember(QStringLiteral("t"), QStringLiteral("m4")); selMi.db.generateSelectRemove = true; MemberInformation viewMi = makeMember(QStringLiteral("t"), QStringLiteral("m5")); viewMi.db.usedForView = true; MemberInformation keyMi = makeMember(QStringLiteral("t"), QStringLiteral("m6")); keyMi.isDdsKey = true; MemberInformation pkMi = makeMember(QStringLiteral("t"), QStringLiteral("m7")); pkMi.db.isPrimaryKey = true; QVector members; members = {cppMi, jsonMi, fkMi, selMi, viewMi, keyMi, pkMi}; MemberInformationListWrapper w(QStringLiteral("members"), members, &parent); QCOMPARE(w.member().size(), 7); QCOMPARE(w.cppMember().size(), 1); QCOMPARE(w.jsonMember().size(), 1); QCOMPARE(w.fkMember().size(), 1); QCOMPARE(w.selectRemoveMember().size(), 1); QCOMPARE(w.viewMember().size(), 1); MemberInformationWrapper* key = w.ddsKey(); QVERIFY(key != nullptr); QCOMPARE(key->cppMemberName(), QStringLiteral("m6")); MemberInformationWrapper* pk = w.primaryKey(); QVERIFY(pk != nullptr); QCOMPARE(pk->cppMemberName(), QStringLiteral("m7")); // an empty list has no key and no primary key MemberInformationListWrapper w2(QStringLiteral("empty"), QVector(), &parent); QCOMPARE(w2.member().size(), 0); QVERIFY(w2.ddsKey() == nullptr); QVERIFY(w2.primaryKey() == nullptr); } void codegen_unit_test::dataTopicWrapper() { QObject parent; CompleteTopicTypeInformation ctti; ctti.name = QStringLiteral("Topic"); ctti.serviceName = QStringLiteral("Svc"); DataTopicWrapper w(ctti, &parent); QCOMPARE(w.name(), QStringLiteral("Topic")); QCOMPARE(w.serviceName(), QStringLiteral("Svc")); QCOMPARE(w.type(), QStringLiteral("Topic")); QCOMPARE(w.typeFq(), QStringLiteral("Svc::Topic")); QCOMPARE(w.dtsImpl(), true); QCOMPARE(w.dtcImpl(), true); QCOMPARE(w.routeToClient(), true); QCOMPARE(w.routeToServer(), false); QCOMPARE(w.routeInReplay(), true); QCOMPARE(w.isAutoSubscribe(), true); QCOMPARE(w.recordTopic(), false); QVERIFY(w.recTable().isNull()); QVERIFY(w.key().isNull()); QCOMPARE(w.hasTrColumns(), false); // an already service-qualified name is not qualified again ctti.name = QStringLiteral("Svc::Topic"); DataTopicWrapper w2(ctti, &parent); QCOMPARE(w2.typeFq(), QStringLiteral("Svc::Topic")); // without a service name the type is returned as-is CompleteTopicTypeInformation ctti3; ctti3.name = QStringLiteral("Topic3"); DataTopicWrapper w3(ctti3, &parent); QCOMPARE(w3.typeFq(), QStringLiteral("Topic3")); // key column and translatable columns QSharedPointer intType(new TestDataTypeInfo(QStringLiteral("int"), QStringLiteral("int"), true)); IdlData keyData; keyData.name = QStringLiteral("id"); keyData.dataTypeInfo = intType; w.setKeyColumnData(keyData); QVERIFY(!w.key().isNull()); QCOMPARE(w.key().value()->name(), QStringLiteral("id")); IdlData trData; trData.name = QStringLiteral("tr1"); trData.dataTypeInfo = intType; QVector trColumns; trColumns = {trData}; w.setTrColumns(trColumns); QCOMPARE(w.hasTrColumns(), true); QCOMPARE(w.trColumns().size(), 1); } void codegen_unit_test::functionTopicWrapper() { QObject parent; FunctionTopic ft; ft.name = QStringLiteral("MyFunc"); ft.requestTopicName = QStringLiteral("ReqT"); ft.replyTopicName = QStringLiteral("RepT"); ft.replyTopicType = QStringLiteral("Core::FunctionReply"); ft.routeInReplay = true; CompleteTopicTypeInformation req; req.name = QStringLiteral("ReqType"); CompleteTopicTypeInformation rep; rep.name = QStringLiteral("RepType"); FunctionTopicWrapper w(ft, req, rep, &parent); QCOMPARE(w.name(), QStringLiteral("MyFunc")); QCOMPARE(w.onReply(), QStringLiteral("onRepT")); QCOMPARE(w.onRequest(), QStringLiteral("onReqT")); QCOMPARE(w.onError(), QStringLiteral("onMyFuncError")); QCOMPARE(w.isCoreFt(), true); QCOMPARE(w.routeInReplay(), true); QCOMPARE(w.reply(), QStringLiteral("RepT")); QCOMPARE(w.replyFlc(), QStringLiteral("repT")); QCOMPARE(w.replyHandler(), QStringLiteral("RepTHandler")); QCOMPARE(w.replyHandlerFlc(), QStringLiteral("repTHandler")); QCOMPARE(w.replyType(), QStringLiteral("RepType")); QCOMPARE(w.replyTypeFro(), QStringLiteral("Core::FunctionReplyObject")); QCOMPARE(w.request(), QStringLiteral("ReqT")); QCOMPARE(w.requestFlc(), QStringLiteral("reqT")); QCOMPARE(w.requestHandler(), QStringLiteral("ReqTHandler")); QCOMPARE(w.requestHandlerFlc(), QStringLiteral("reqTHandler")); QCOMPARE(w.requestType(), QStringLiteral("ReqType")); QCOMPARE(w.replyHandlerHash(), QString()); // a non-core reply type keeps its own type name ft.replyTopicType = QStringLiteral("SomeReply"); FunctionTopicWrapper w2(ft, req, rep, &parent); QCOMPARE(w2.isCoreFt(), false); QCOMPARE(w2.replyTypeFro(), QStringLiteral("RepType")); } void codegen_unit_test::idlWrappers() { QObject parent; QSharedPointer podType(new TestDataTypeInfo(QStringLiteral("int"), QStringLiteral("int"), true)); QSharedPointer structType(new TestDataTypeInfo(QStringLiteral("Svc::MyStruct"), QStringLiteral("Svc::MyStruct"), false)); IdlData idl; idl.name = QStringLiteral("foo"); idl.isKey = true; idl.isOptional = false; idl.dataTypeInfo = podType; IdlDataWrapper w(idl, &parent); QCOMPARE(w.name(), QStringLiteral("foo")); QCOMPARE(w.type(), QStringLiteral("int")); QCOMPARE(w.cppType(), QStringLiteral("int")); // POD types are passed by value (no const reference) QCOMPARE(w.cppTypeCr(), QStringLiteral("int")); QCOMPARE(w.isKey(), QStringLiteral(" \t //@key")); QCOMPARE(w.isOptional(), QString()); QCOMPARE(w.nameGetter(), QStringLiteral("foo")); IdlData opt; opt.name = QStringLiteral("bar"); opt.isOptional = true; opt.dataTypeInfo = structType; IdlDataWrapper w2(opt, &parent); QCOMPARE(w2.cppTypeCr(), QStringLiteral("const Svc::MyStruct &")); QCOMPARE(w2.isKey(), QString()); QCOMPARE(w2.isOptional(), QStringLiteral(" \t //@Optional")); // optional members are unwrapped via .get() QCOMPARE(w2.nameGetter(), QStringLiteral("bar.get()")); // IdlStructWrapper CompleteTopicTypeInformation ctti; ctti.name = QStringLiteral("MyStruct"); ctti.comment = QStringLiteral("struct comment"); ctti.baseValueType = QStringLiteral("BaseType"); IdlData m1; m1.name = QStringLiteral("a"); m1.dataTypeInfo = podType; IdlData m2; m2.name = QStringLiteral("b"); m2.dataTypeInfo = structType; ctti.members = {m1, m2}; IdlStructWrapper sw(ctti, &parent); QCOMPARE(sw.name(), QStringLiteral("MyStruct")); QCOMPARE(sw.comment(), QStringLiteral("struct comment")); QCOMPARE(sw.baseValueType(), QStringLiteral("BaseType")); // the default extensibility maps to the same string with and without RTI QCOMPARE(sw.extensibility(), QStringLiteral("EXTENSIBLE_EXTENSIBILITY")); QCOMPARE(sw.member().size(), 2); QCOMPARE(sw.member().value(0).value()->name(), QStringLiteral("a")); } void codegen_unit_test::dbStructWrapper() { QObject parent; QSharedPointer tableA(new TableInformation()); tableA->name = QStringLiteral("tbl_a"); QSharedPointer tableB(new TableInformation()); tableB->name = QStringLiteral("tbl_b"); CompleteStructInformation csi; csi.structName = QStringLiteral("MyStruct"); csi.viewName = QStringLiteral("my_view"); csi.tables = {tableA, tableB}; csi.primaryTable = tableA; MemberInformation pkA = makeMember(QStringLiteral("tbl_a"), QStringLiteral("a_id")); pkA.db.isPrimaryKey = true; pkA.db.usedForInsert = true; MemberInformation nameA = makeMember(QStringLiteral("tbl_a"), QStringLiteral("a_name")); nameA.db.usedForInsert = true; MemberInformation fkB = makeMember(QStringLiteral("tbl_b"), QStringLiteral("b_a_id")); fkB.db.usedForInsert = true; fkB.fk = QSharedPointer(new ForeignKeyInformation()); fkB.fk->destination.tableName = QStringLiteral("tbl_a"); csi.member = {pkA, nameA, fkB}; DbNotifcationInformation notif1; notif1.methodName = QStringLiteral("onInsertA"); DbNotifcationInformation notif2; notif2.methodName = QStringLiteral("onDeleteA"); csi.dbNotifications = {notif1, notif2}; DbStructWrapper w(csi, &parent); QCOMPARE(w.name(), QStringLiteral("MyStruct")); QCOMPARE(w.member().size(), 3); QCOMPARE(w.deleteWithoutPkAllowed(), false); // two tables QCOMPARE(w.viewTableName(), QStringLiteral("my_view")); // multi-table -> view name MemberInformationWrapper* pk = w.primaryKey(); QVERIFY(pk != nullptr); QCOMPARE(pk->dbColumnName(), QStringLiteral("a_id")); QCOMPARE(w.dbNotifications().size(), 2); QCOMPARE(w.dbNotifications().value(0).toString(), QStringLiteral("onInsertA")); QCOMPARE(w.dbNotifications().value(1).toString(), QStringLiteral("onDeleteA")); // a single-table struct uses the primary table name as view name CompleteStructInformation csiSingle; csiSingle.structName = QStringLiteral("Single"); csiSingle.viewName = QStringLiteral("unused"); csiSingle.tables = {tableA}; csiSingle.primaryTable = tableA; MemberInformation soloPk = makeMember(QStringLiteral("tbl_a"), QStringLiteral("a_id")); soloPk.db.isPrimaryKey = true; soloPk.db.hasSequence = true; soloPk.db.usedForInsert = true; csiSingle.member = {soloPk}; DbStructWrapper wSingle(csiSingle, &parent); QCOMPARE(wSingle.deleteWithoutPkAllowed(), true); QCOMPARE(wSingle.viewTableName(), QStringLiteral("tbl_a")); // insertStructures groups the members per table (two tables) QVariantList structures = w.insertStructures(); QCOMPARE(structures.size(), 2); TableWrapper* twA = structures.value(0).value(); TableWrapper* twB = structures.value(1).value(); QVERIFY(twA != nullptr); QVERIFY(twB != nullptr); QCOMPARE(twA->name(), QStringLiteral("tbl_a")); QCOMPARE(twA->member().size(), 2); QVERIFY(twA->primaryKey() != nullptr); QCOMPARE(twA->primaryKey()->dbColumnName(), QStringLiteral("a_id")); // first of several tables carries the base id QCOMPARE(twA->baseId()->dbColumnName(), QStringLiteral("a_id")); // the FK member pointing to tbl_a becomes its serial member QCOMPARE(twA->serialMember()->dbColumnName(), QStringLiteral("b_a_id")); QCOMPARE(twB->name(), QStringLiteral("tbl_b")); QCOMPARE(twB->member().size(), 1); QVERIFY(twB->primaryKey() == nullptr); QVERIFY(twB->baseId() == nullptr); QVERIFY(twB->serialMember() == nullptr); // insertStructures for a single table with a sequence primary key QVariantList single = wSingle.insertStructures(); QCOMPARE(single.size(), 1); TableWrapper* twSolo = single.value(0).value(); QVERIFY(twSolo != nullptr); QCOMPARE(twSolo->name(), QStringLiteral("tbl_a")); QVERIFY(twSolo->primaryKey() != nullptr); // the sequence PK is used as the serial member QCOMPARE(twSolo->serialMember()->dbColumnName(), QStringLiteral("a_id")); QVERIFY(twSolo->baseId() == nullptr); } void codegen_unit_test::codegenBaseDataClasses() { // StructBuilder::ServiceTypeStruct StructBuilder::ServiceTypeStruct sts; sts.service = QStringLiteral("Svc"); sts.type = QStringLiteral("Type"); sts.bIsSequence = true; QCOMPARE(sts.isExternalType(), true); QCOMPARE(sts.isSequence(), true); QCOMPARE(sts.fqType(), QStringLiteral("Svc::Type")); StructBuilder::ServiceTypeStruct local; local.type = QStringLiteral("Plain"); QCOMPARE(local.isExternalType(), false); // SqlGenerator::SqlStatements::isEmpty only considers the DROP statements SqlGenerator::SqlStatements stmts; QCOMPARE(stmts.isEmpty(), true); stmts.createTables.append(QStringLiteral("CREATE TABLE t()")); QCOMPARE(stmts.isEmpty(), true); stmts.dropTables.append(QStringLiteral("DROP TABLE t")); QCOMPARE(stmts.isEmpty(), false); // EnumInformation EnumInformation ei; QCOMPARE(ei.isValid(), false); ei.enumName = QStringLiteral("E"); QCOMPARE(ei.isValid(), true); // MemberInformation isEmpty / toString (CSV format) MemberInformation mi; QCOMPARE(mi.isEmpty(), true); mi.ea.table = QStringLiteral("t"); mi.ea.column = QStringLiteral("c"); mi.ea.type = QStringLiteral("int"); mi.db.table = QStringLiteral("t"); mi.db.columnName = QStringLiteral("c"); mi.cpp.memberName = QStringLiteral("c"); QCOMPARE(mi.isEmpty(), false); QCOMPARE(mi.toString(), QStringLiteral("t;c;int;t;c;;false;false;false;false;;c;false;false;;;;;;")); // CompleteTopicTypeInformation CompleteTopicTypeInformation ctti; QCOMPARE(ctti.isValid(), false); // no name ctti.name = QStringLiteral("S"); QCOMPARE(ctti.isValid(), false); // still no members ctti.baseValueType = QStringLiteral("B"); QCOMPARE(ctti.hasBaseValue(), true); IdlData m; m.name = QStringLiteral("x"); m.dataTypeInfo = QSharedPointer(new TestDataTypeInfo(QStringLiteral("int"), QStringLiteral("int"), true)); ctti.members = {m}; QCOMPARE(ctti.isValid(), true); // CompleteTableInformation / CompleteStructInformation CompleteTableInformation cti; QCOMPARE(cti.isEmpty(), true); MemberInformation col; col.ea.column = QStringLiteral("c1"); cti.columns = {col}; QCOMPARE(cti.isEmpty(), false); CompleteStructInformation csi; QCOMPARE(csi.isEmpty(), true); QSharedPointer ti(new TableInformation()); ti->name = QStringLiteral("t"); csi.primaryTable = ti; QCOMPARE(csi.isEmpty(), false); } QTEST_MAIN(codegen_unit_test) #include "codegen_unit_test.moc"