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
@@ -152,3 +152,8 @@ NGL_INSTALL_TARGET(svcCoreClient)
|
||||
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
|
||||
|
||||
ENDIF(BUILD_RTI)
|
||||
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
ADD_SUBDIRECTORY(unit-test)
|
||||
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Unit tests for the DDS Core module.
|
||||
# One test executable per module, each registered with ctest.
|
||||
#
|
||||
if (NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# --- DDS Core: generated function-reply objects and the CORE logger facade
|
||||
if (TARGET ddscore)
|
||||
ADD_EXECUTABLE(ddscore_unit_test ddscore_unit_test.cpp)
|
||||
TARGET_LINK_LIBRARIES(ddscore_unit_test
|
||||
ddscore
|
||||
NGLLogger
|
||||
Qt6::Core
|
||||
Qt6::Test
|
||||
)
|
||||
ADD_TEST(NAME ddscore_unit_test COMMAND $<TARGET_FILE:ddscore_unit_test>)
|
||||
endif()
|
||||
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// Unit tests for the ddscore module (Lts/DDS/Core): FunctionReplyObject
|
||||
// (the module's pure data/logic class) and the CORE.* logger helper.
|
||||
//
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QString>
|
||||
|
||||
#include <Core/function_reply_object.h>
|
||||
#include <Core/logger.h>
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ddscore_unit_test : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void functionReplyObjectDefault();
|
||||
void functionReplyObjectFromIdAndString();
|
||||
void functionReplyObjectFromStringOnly();
|
||||
void functionReplyObjectNullString();
|
||||
void functionReplyObjectWithArgs();
|
||||
void functionReplyObjectWithArgsAndAdditionalInfo();
|
||||
void functionReplyObjectRvalueArgs();
|
||||
void argOverloads();
|
||||
void setters();
|
||||
void formattedWithoutPlaceholders();
|
||||
void formattedExtraArgsIgnored();
|
||||
void translatedMatchesFormatted();
|
||||
void uniqueIdRoundtrip();
|
||||
void coreGetLoggerName();
|
||||
};
|
||||
|
||||
void ddscore_unit_test::initTestCase()
|
||||
{
|
||||
NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false);
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectDefault()
|
||||
{
|
||||
Core::FunctionReplyObject reply;
|
||||
QCOMPARE(reply.id(), -1);
|
||||
QVERIFY(!reply.error());
|
||||
QCOMPARE(reply.str(), std::string());
|
||||
QCOMPARE(reply.additionalInfo(), std::string());
|
||||
QCOMPARE(reply.args().size(), 0u);
|
||||
QCOMPARE(reply.uniqueId(), int64_t(0));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectFromIdAndString()
|
||||
{
|
||||
Core::FunctionReplyObject reply(42, "error %1");
|
||||
QCOMPARE(reply.id(), 42);
|
||||
QVERIFY(reply.error());
|
||||
QCOMPARE(reply.str(), std::string("error %1"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectFromStringOnly()
|
||||
{
|
||||
Core::FunctionReplyObject reply("plain error");
|
||||
QCOMPARE(reply.id(), -1);
|
||||
QVERIFY(reply.error());
|
||||
QCOMPARE(reply.str(), std::string("plain error"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectNullString()
|
||||
{
|
||||
Core::FunctionReplyObject reply(7, static_cast<const char *>(nullptr));
|
||||
QCOMPARE(reply.id(), 7);
|
||||
QVERIFY(!reply.error());
|
||||
QCOMPARE(reply.str(), std::string());
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectWithArgs()
|
||||
{
|
||||
Core::FunctionReplyObject reply(5, "%1/%2", std::vector<std::string>{"a", "b"});
|
||||
QCOMPARE(reply.id(), 5);
|
||||
QCOMPARE(reply.str(), std::string("%1/%2"));
|
||||
const std::vector<std::string> expectedArgs = {"a", "b"};
|
||||
QCOMPARE(reply.args(), expectedArgs);
|
||||
QCOMPARE(reply(), std::string("a/b"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectWithArgsAndAdditionalInfo()
|
||||
{
|
||||
Core::FunctionReplyObject reply(5, "failed: %1", std::vector<std::string>{"x"}, "context");
|
||||
QCOMPARE(reply.id(), 5);
|
||||
QCOMPARE(reply.str(), std::string("failed: %1"));
|
||||
QCOMPARE(reply.additionalInfo(), std::string("context"));
|
||||
QCOMPARE(reply(), std::string("failed: x"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::functionReplyObjectRvalueArgs()
|
||||
{
|
||||
Core::FunctionReplyObject reply(
|
||||
9, std::string("moved %1"), std::vector<std::string>{"v"}, std::string("info"));
|
||||
QCOMPARE(reply.id(), 9);
|
||||
QCOMPARE(reply.str(), std::string("moved %1"));
|
||||
QCOMPARE(reply.additionalInfo(), std::string("info"));
|
||||
QCOMPARE(reply(), std::string("moved v"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::argOverloads()
|
||||
{
|
||||
Core::FunctionReplyObject reply(1, "v=%1 s=%2 q=%3");
|
||||
reply.arg(7).arg(std::string("hello")).arg(QStringLiteral("qt"));
|
||||
const std::vector<std::string> expectedArgs = {"7", "hello", "qt"};
|
||||
QCOMPARE(reply.args(), expectedArgs);
|
||||
QCOMPARE(reply(), std::string("v=7 s=hello q=qt"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::setters()
|
||||
{
|
||||
Core::FunctionReplyObject reply;
|
||||
reply.setId(11);
|
||||
reply.setStr("updated %1");
|
||||
reply.setAdditionalInfo("note");
|
||||
QCOMPARE(reply.id(), 11);
|
||||
QCOMPARE(reply.str(), std::string("updated %1"));
|
||||
QVERIFY(reply.error());
|
||||
QCOMPARE(reply.additionalInfo(), std::string("note"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::formattedWithoutPlaceholders()
|
||||
{
|
||||
Core::FunctionReplyObject reply(1, "no placeholder");
|
||||
reply.arg(QStringLiteral("ignored"));
|
||||
QCOMPARE(reply(), std::string("no placeholder"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::formattedExtraArgsIgnored()
|
||||
{
|
||||
Core::FunctionReplyObject reply(1, "only %1");
|
||||
reply.arg(QStringLiteral("a")).arg(QStringLiteral("b"));
|
||||
QCOMPARE(reply(), std::string("only a"));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::translatedMatchesFormatted()
|
||||
{
|
||||
// No translator is installed, so QCoreApplication::translate returns the
|
||||
// source string and translated() must equal the plain formatted result.
|
||||
Core::FunctionReplyObject reply(1, "translated %1", std::vector<std::string>{"t"});
|
||||
QCOMPARE(reply.translated().toStdString(), std::string("translated t"));
|
||||
QCOMPARE(reply.translated().toStdString(), reply());
|
||||
}
|
||||
|
||||
void ddscore_unit_test::uniqueIdRoundtrip()
|
||||
{
|
||||
Core::FunctionReplyObject reply;
|
||||
reply.setUniqueId(123456);
|
||||
QCOMPARE(reply.uniqueId(), int64_t(123456));
|
||||
}
|
||||
|
||||
void ddscore_unit_test::coreGetLoggerName()
|
||||
{
|
||||
// Core::getLogger prefixes the requested name with the "CORE." namespace.
|
||||
NGL::INGLLogger &logger = Core::getLogger("UnitTest");
|
||||
QCOMPARE(logger.getLoggerName(), std::string("CORE.UnitTest"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ddscore_unit_test)
|
||||
#include "ddscore_unit_test.moc"
|
||||
@@ -37,3 +37,8 @@ NGL_TARGET_LINK_LIBRARIES(ddsQoS
|
||||
)
|
||||
|
||||
ENDIF(BUILD_RTI)
|
||||
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
ADD_SUBDIRECTORY(unit-test)
|
||||
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Unit tests for the DDS QosProfile module.
|
||||
# One test executable per module, each registered with ctest.
|
||||
#
|
||||
if (NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# --- DDS QosProfile: default QoS profile helpers
|
||||
if (TARGET ddsQoS)
|
||||
ADD_EXECUTABLE(ddsqos_unit_test ddsqos_unit_test.cpp)
|
||||
# Path of the module's default_profile.xml fixture (see ddsqos_unit_test.cpp)
|
||||
TARGET_COMPILE_DEFINITIONS(ddsqos_unit_test PRIVATE
|
||||
DDSQOS_UNIT_TEST_PROFILE_XML="${CMAKE_CURRENT_SOURCE_DIR}/../default_profile.xml")
|
||||
TARGET_LINK_LIBRARIES(ddsqos_unit_test
|
||||
ddsQoS
|
||||
NGLLogger
|
||||
Qt6::Core
|
||||
Qt6::Test
|
||||
)
|
||||
# ddsQoS links the RTI DDS libraries PRIVATE - the test needs them itself
|
||||
# for the RTI policy inline functions used by the QoS helpers
|
||||
if (TARGET RTI::nddsc)
|
||||
TARGET_LINK_LIBRARIES(ddsqos_unit_test
|
||||
RTI::nddsc
|
||||
RTI::nddscpp2
|
||||
)
|
||||
endif()
|
||||
ADD_TEST(NAME ddsqos_unit_test COMMAND $<TARGET_FILE:ddsqos_unit_test>)
|
||||
endif()
|
||||
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// Unit tests for the ddsQoS module (Lts/DDS/QosProfile): DDS_DefaultQos.
|
||||
// The functions under test only read QoS profiles through the RTI QosProvider
|
||||
// (policy structs) - no DomainParticipant, Publication or Subscription is
|
||||
// created and no DDS runtime/network access happens.
|
||||
//
|
||||
// The location of the default_profile.xml shipped with the module is passed
|
||||
// via the DDSQOS_UNIT_TEST_PROFILE_XML compile definition
|
||||
// (see Lts/DDS/QosProfile/unit-test/CMakeLists.txt).
|
||||
//
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <QosProfile/DDS_DefaultQos.h>
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace DDS::QosProfile;
|
||||
|
||||
const QString &profileXml()
|
||||
{
|
||||
static const QString path = QStringLiteral(DDSQOS_UNIT_TEST_PROFILE_XML);
|
||||
return path;
|
||||
}
|
||||
|
||||
bool containsPeer(const dds::core::StringSeq &seq, const std::string &needle)
|
||||
{
|
||||
for (const std::string &s : seq)
|
||||
if (s == needle)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class ddsqos_unit_test : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void setDefaultLoadsProfile();
|
||||
void shmUdpParticipantQos();
|
||||
void shmParticipantQos();
|
||||
void shmUdpLocalhostParticipantQos();
|
||||
void domainParticipantTcpBindPort();
|
||||
void domainParticipantTcpV4LanPeers();
|
||||
void dataWriterDefaults();
|
||||
void dataWriterFunctionTopic();
|
||||
void dataReaderDefaults();
|
||||
void dataReaderFunctionTopic();
|
||||
void dataReaderDefaultByConnectionType();
|
||||
void printSmokeTest();
|
||||
};
|
||||
|
||||
void ddsqos_unit_test::initTestCase()
|
||||
{
|
||||
NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false);
|
||||
|
||||
if (!QFile::exists(profileXml()))
|
||||
QSKIP("default_profile.xml not available");
|
||||
QVERIFY(setDefault(QStringList{profileXml()}));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::setDefaultLoadsProfile()
|
||||
{
|
||||
// Re-loading the known-good profile into the default provider must succeed.
|
||||
QVERIFY(setDefault(QStringList{profileXml()}));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::shmUdpParticipantQos()
|
||||
{
|
||||
const dds::domain::qos::DomainParticipantQos qos = domainParticipantShmUdp();
|
||||
const uint64_t expected = (rti::core::policy::TransportBuiltinMask::shmem()
|
||||
| rti::core::policy::TransportBuiltinMask::udpv4()).to_ulong();
|
||||
QCOMPARE(qos->transport_builtin.mask().to_ulong(), expected);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::shmParticipantQos()
|
||||
{
|
||||
const dds::domain::qos::DomainParticipantQos qos = domainParticipantShm();
|
||||
QCOMPARE(qos->transport_builtin.mask().to_ulong(),
|
||||
rti::core::policy::TransportBuiltinMask::shmem().to_ulong());
|
||||
const dds::core::StringSeq peers = qos->discovery.initial_peers();
|
||||
QCOMPARE(peers.size(), 1u);
|
||||
QCOMPARE(peers.at(0), std::string("30@builtin.shmem://"));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::shmUdpLocalhostParticipantQos()
|
||||
{
|
||||
const dds::domain::qos::DomainParticipantQos qos = domainParticipantShmUdpLocalhost();
|
||||
const dds::core::StringSeq peers = qos->discovery.initial_peers();
|
||||
QVERIFY(containsPeer(peers, "builtin.shmem://"));
|
||||
QVERIFY(containsPeer(peers, "builtin.udpv4://127.0.0.1"));
|
||||
QVERIFY(qos->property.exists("dds.transport.UDPv4.builtin.multicast_enabled"));
|
||||
QCOMPARE(qos->property.get("dds.transport.UDPv4.builtin.multicast_enabled"),
|
||||
std::string("0"));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::domainParticipantTcpBindPort()
|
||||
{
|
||||
// Non-tcpv4lan scheme: the port is written into the TCPv4 transport property.
|
||||
const dds::domain::qos::DomainParticipantQos qos =
|
||||
domainParticipantShmTcp(QUrl(QStringLiteral("tcp://10.0.0.5:9999")));
|
||||
QVERIFY(qos->property.exists("dds.transport.TCPv4.tcp1.server_bind_port"));
|
||||
QCOMPARE(qos->property.get("dds.transport.TCPv4.tcp1.server_bind_port"),
|
||||
std::string("9999"));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::domainParticipantTcpV4LanPeers()
|
||||
{
|
||||
// tcpv4lan scheme: the fixed-up URL becomes the single initial peer with
|
||||
// the "0@" domain prefix (fixupSchemeName in DDS_DefaultQos).
|
||||
const dds::domain::qos::DomainParticipantQos qos =
|
||||
domainParticipantShmTcp(QUrl(QStringLiteral("tcpv4lan://10.0.0.5:9999")));
|
||||
const dds::core::StringSeq peers = qos->discovery.initial_peers();
|
||||
QCOMPARE(peers.size(), 1u);
|
||||
QCOMPARE(peers.at(0), std::string("0@tcpv4_lan://10.0.0.5:9999"));
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::dataWriterDefaults()
|
||||
{
|
||||
// Default (DataTopic) writer QoS comes from the is_default_qos "Base" profile.
|
||||
const dds::pub::qos::DataWriterQos qos = dataWriterDefault(TopicType::DataTopic);
|
||||
QCOMPARE(qos->reliability.kind(), dds::core::policy::ReliabilityKind::RELIABLE);
|
||||
QCOMPARE(qos->durability.kind(), dds::core::policy::DurabilityKind::TRANSIENT_LOCAL);
|
||||
QCOMPARE(qos->history.kind(), dds::core::policy::HistoryKind::KEEP_LAST);
|
||||
QCOMPARE(qos->history.depth(), 1);
|
||||
QCOMPARE(qos->publish_mode.kind(), rti::core::policy::PublishModeKind::ASYNCHRONOUS);
|
||||
QCOMPARE(qos->resource_limits.max_samples(), 8000);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::dataWriterFunctionTopic()
|
||||
{
|
||||
const dds::pub::qos::DataWriterQos qos = dataWriterDefault(TopicType::FunctionTopic);
|
||||
QCOMPARE(qos->durability.kind(), dds::core::policy::DurabilityKind::TRANSIENT_LOCAL);
|
||||
QCOMPARE(qos->history.kind(), dds::core::policy::HistoryKind::KEEP_LAST);
|
||||
QCOMPARE(qos->history.depth(), 1);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::dataReaderDefaults()
|
||||
{
|
||||
const dds::sub::qos::DataReaderQos qos = dataReaderDefault(TopicType::DataTopic);
|
||||
QCOMPARE(qos->reliability.kind(), dds::core::policy::ReliabilityKind::RELIABLE);
|
||||
QCOMPARE(qos->durability.kind(), dds::core::policy::DurabilityKind::TRANSIENT_LOCAL);
|
||||
QCOMPARE(qos->history.kind(), dds::core::policy::HistoryKind::KEEP_LAST);
|
||||
QCOMPARE(qos->history.depth(), 1);
|
||||
QCOMPARE(qos->resource_limits.max_samples(), 8000);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::dataReaderFunctionTopic()
|
||||
{
|
||||
// The FT reader profile overrides the Base reader history with KEEP_ALL.
|
||||
const dds::sub::qos::DataReaderQos ftQos = dataReaderDefault(TopicType::FunctionTopic);
|
||||
QCOMPARE(ftQos->history.kind(), dds::core::policy::HistoryKind::KEEP_ALL);
|
||||
|
||||
const dds::sub::qos::DataReaderQos dataQos = dataReaderDefault(TopicType::DataTopic);
|
||||
QCOMPARE(dataQos->history.kind(), dds::core::policy::HistoryKind::KEEP_LAST);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::dataReaderDefaultByConnectionType()
|
||||
{
|
||||
// The ShmUdp participant profile inherits the Base reader QoS.
|
||||
const dds::sub::qos::DataReaderQos qos = dataReaderDefault(ConnectionType::ShmUdp);
|
||||
QCOMPARE(qos->reliability.kind(), dds::core::policy::ReliabilityKind::RELIABLE);
|
||||
QCOMPARE(qos->history.kind(), dds::core::policy::HistoryKind::KEEP_LAST);
|
||||
QCOMPARE(qos->resource_limits.max_samples(), 8000);
|
||||
}
|
||||
|
||||
void ddsqos_unit_test::printSmokeTest()
|
||||
{
|
||||
// print() walks the profile policies - must not throw.
|
||||
NGL::INGLLogger &logger =
|
||||
NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("DDS.DefaultQosTest");
|
||||
print(domainParticipantShmUdp(), logger);
|
||||
print(dataWriterDefault(TopicType::DataTopic), logger);
|
||||
print(dataReaderDefault(TopicType::DataTopic), logger);
|
||||
}
|
||||
|
||||
QTEST_MAIN(ddsqos_unit_test)
|
||||
#include "ddsqos_unit_test.moc"
|
||||
Reference in new issue
Block a user