Add per-module unit-test dirs for the Release 2 scope
One unit-test/ directory per module/target, mirroring the existing TetraIF/unit-test/ layout (one QtTest executable per module, guarded by BUILD_TESTING and the module target, each registered with ctest): Lts/Common/CodeGen codegen_unit_test Lts/Common/LtsCoordinate ltscoordinate_unit_test Lts/Common/LtsSession ltssession_unit_test Lts/DDS/Core ddscore_unit_test Lts/DDS/QosProfile ddsqos_unit_test Lts/Tools/LogViewer logviewer_unit_test Lts/Tools/SqlBrowser sqlbrowser_unit_test Z/src/NGLLogger ngllogger_unit_test Z/src/WMS_Interface wms_json_ds_unit_test Z/src/nglapplication nglcoreapplication_unit_test Z/src/nglcompression nglcompression_unit_test Z/src/nglsettings nglsettings_unit_test Z/src/ngltranslator ngltranslator_unit_test Where a module is built with hidden visibility and has no export macro (LogViewer, SqlBrowser, ngltranslator, NGLLogger, nglcompression, nglsettings), its sources are compiled directly into the test executable, the same pattern as prtcnv_unit_test. ctest baseline in the container (QT_QPA_PLATFORM=offscreen): 20/23 passed (13 new + 7 pre-existing in-scope tests), 3 Not Run as before (nglcomp_test, GrantleeParserSmoke, utils_test). README: ctest baseline table extended to the 23 registered tests and section 2.4 now documents the per-module test layout. ddsview has no unit test (GUI tool, XmlHelper needs the DDS dynamic-xtypes runtime).
This commit is contained in:
1 parent
a62b729577
commit
09b6df8bc3
40 files changed
+4966
-17
No files matched your search
@@ -170,3 +170,8 @@ endif()
|
||||
ELSE()
|
||||
message(STATUS "BUILD_GRANTLEE disabled or Grantlee not found; skipping codegen_grantlee, ngl_codegen, and codegen_gui targets")
|
||||
ENDIF()
|
||||
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
ADD_SUBDIRECTORY(unit-test)
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Unit tests for the CodeGen module.
|
||||
# One test executable per module, each registered with ctest.
|
||||
#
|
||||
if (NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# --- CodeGen: string helpers, data classes and Grantlee template-context
|
||||
# --- wrappers (pure logic; the KTextTemplate engine is covered by
|
||||
# --- GrantleeParserSmoke)
|
||||
if (TARGET codegen_grantlee)
|
||||
ADD_EXECUTABLE(codegen_unit_test codegen_unit_test.cpp)
|
||||
TARGET_LINK_LIBRARIES(codegen_unit_test
|
||||
codegen_grantlee
|
||||
Qt6::Core
|
||||
Qt6::Test
|
||||
)
|
||||
ADD_TEST(NAME codegen_unit_test COMMAND $<TARGET_FILE:codegen_unit_test>)
|
||||
endif()
|
||||
@@ -0,0 +1,701 @@
|
||||
//
|
||||
// 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 <CodeGen/GrantleeWrappers.h>
|
||||
#include <CodeGen/SqlGenerator.h>
|
||||
#include <CodeGen/StructBuilder.h>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtTest/QTest>
|
||||
|
||||
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<int>(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<dds::core::optional<MyStruct>>(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<MemberInformation> 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<MemberInformation>(), &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<DataTypeInfo> 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<IdlDataWrapper*>()->name(), QStringLiteral("id"));
|
||||
|
||||
IdlData trData;
|
||||
trData.name = QStringLiteral("tr1");
|
||||
trData.dataTypeInfo = intType;
|
||||
QVector<IdlData> 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<DataTypeInfo> podType(new TestDataTypeInfo(QStringLiteral("int"), QStringLiteral("int"), true));
|
||||
QSharedPointer<DataTypeInfo> 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<IdlDataWrapper*>()->name(), QStringLiteral("a"));
|
||||
}
|
||||
|
||||
void codegen_unit_test::dbStructWrapper()
|
||||
{
|
||||
QObject parent;
|
||||
|
||||
QSharedPointer<TableInformation> tableA(new TableInformation());
|
||||
tableA->name = QStringLiteral("tbl_a");
|
||||
QSharedPointer<TableInformation> 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<ForeignKeyInformation>(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*>();
|
||||
TableWrapper* twB = structures.value(1).value<TableWrapper*>();
|
||||
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<TableWrapper*>();
|
||||
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<DataTypeInfo>(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<TableInformation> ti(new TableInformation());
|
||||
ti->name = QStringLiteral("t");
|
||||
csi.primaryTable = ti;
|
||||
QCOMPARE(csi.isEmpty(), false);
|
||||
}
|
||||
|
||||
QTEST_MAIN(codegen_unit_test)
|
||||
#include "codegen_unit_test.moc"
|
||||
Reference in new issue
Block a user