Migrates residual Lts sources to generated rtiddsgen API

Adds NmeaManagement tests, fixes codegen includes and Qt6 SerialPort CMake target.
This commit is contained in:
berrux-dev committed 2026-09-23 12:05:37 +02:00
1 parent 5485a90385
commit 881d44f4dd
512 files changed
+6887 -6426

No files matched your search

@@ -129,7 +129,8 @@ if(BUILD_TESTING)
svcAesCommon svcAlmCommon svcAsmCommon svcAvmCommon svcCodCommon
svcEemCommon svcEsmCommon svcEscCommon svcEvlCommon svcExmCommon
svcGimCommon svcIsmCommon svcMdmCommon svcMsgCommon svcMrkCommon
svcNomCommon svcOpmCommon svcOssCommon svcPdmCommon svcPpmCommon
svcNomCommon svcNmeCommon svcOpmCommon svcOssCommon svcPdmCommon
svcPpmCommon
svcPreCommon svcRepCommon svcSomCommon svcSrmCommon svcSrpCommon
svcTgtCommon svcWhgCommon)
if(TARGET ${_svc_common})
@@ -164,6 +165,15 @@ if(BUILD_TESTING)
add_dependencies(migration_tests svcBldCommon)
endif()
# test_nme_dtc / test_nmea_handler include the server-topic
# interfaces (nme_dts.h, nme_fts.h) that CODEGEN_SERVER_TOPICS
# generates into the svcNmeServer target - same ordering
# requirement as the blocks above (without it a parallel make
# can start migration_tests before those headers exist).
if(TARGET svcNmeServer)
add_dependencies(migration_tests svcNmeServer)
endif()
# Enable Qt Meta-Object Compiler for tests with Q_OBJECT
set_target_properties(migration_tests PROPERTIES AUTOMOC ON)
@@ -270,6 +280,13 @@ endif()
target_link_libraries(migration_tests PRIVATE nglwidgets)
endif()
# NmeaManagement tests instantiate the generated nme types (nme.hpp
# default ctors, ServiceControlEnum stream operator) and the service
# logger (logger.cpp) - both compiled into svcNmeCommon.
if(TARGET svcNmeCommon)
target_link_libraries(migration_tests PRIVATE svcNmeCommon)
endif()
# FLM/FLT service tests include the Q_OBJECT custom headers of the
# migrated services (flm_db_impl_custom.h, flm_dtc_impl_custom.h).
# Their moc output references the generated bases' staticMetaObject
@@ -0,0 +1,231 @@
/**
* @file test_nme_dtc.cpp
* @brief Verhaltenstests für die generierte NmeaManagement Dtc/Ftc/Dts/Fts-Schnittstelle
*
* Diese Tests prüfen die von CODEGEN_CLIENT_TOPICS_IFC / CODEGEN_SERVER_TOPICS
* aus nme_service.xml generierten Header (NmeaManagement/nme_dtc.h, nme_ftc.h,
* nme_dts.h, nme_fts.h) aus dem Build-Tree sowie die plainen DDS-Typ-Member
* aus nme.hpp (BatteryState, BatteryAlarm, PositionExcon, ServiceControl,
* TurnRadioMasterOffRequest), die der migrierte NmeaHandler, DdsHandler und
* SocketHandler direkt als Felder und Member-Assignment nutzen.
*/
#include "NmeaManagement/nme.hpp"
#include "NmeaManagement/nme_dtc.h"
#include "NmeaManagement/nme_dts.h"
#include "NmeaManagement/nme_ftc.h"
#include "NmeaManagement/nme_fts.h"
#include <QObject>
#include <gtest/gtest.h>
#include <type_traits>
/**
* @test Der generierte Dtc ist von QObject abgeleitet (Signale für
* Topic-Updates); alle vier Interfaces sind abstrakt.
*/
TEST(nme_dtc_Generated, InterfacesAreAbstract)
{
static_assert(std::is_base_of<QObject, NmeaManagement::Interface::Dtc>::value,
"generierter Dtc muss von QObject abgeleitet sein");
static_assert(std::is_abstract<NmeaManagement::Interface::Dtc>::value, "Dtc muss abstrakt sein");
static_assert(std::is_abstract<NmeaManagement::Interface::Ftc>::value, "Ftc muss abstrakt sein");
static_assert(std::is_abstract<NmeaManagement::Interface::Dts>::value, "Dts muss abstrakt sein");
static_assert(std::is_abstract<NmeaManagement::Interface::Fts>::value, "Fts muss abstrakt sein");
SUCCEED();
}
/**
* @test DdsHandler::init nutzt diese Dts-Enable-Methoden — sie müssen
* über nme_dts.h vorliegen (Adresse ableitbar = existiert und ist
* public).
*/
TEST(nme_dtc_Generated, DtsEnableMethodsExist)
{
using Dts = NmeaManagement::Interface::Dts;
// Member-Funktionszeiger: auto deduziert ohne '*' (auto* kann
// Member-Pointer-Typen nicht deduzieren).
auto enableBatteryAlarm = &Dts::enableBatteryAlarm;
auto enableServiceControl = &Dts::enableServiceControl;
auto enableBatteryState = &Dts::enableBatteryState;
auto enablePositionExcon = &Dts::enablePositionExcon;
(void)enableBatteryAlarm;
(void)enableServiceControl;
(void)enableBatteryState;
(void)enablePositionExcon;
SUCCEED();
}
/**
* @test SocketHandler erbt von Fts::TurnRadioMasterOffRequestHandler und
* antwortet über Fts::turnRadioMasterOffReply — beides muss über
* nme_fts.h vollständig vorliegen.
*/
TEST(nme_dtc_Generated, FtsTurnRadioMasterOffInterface)
{
using namespace NmeaManagement::Interface;
static_assert(std::is_class<Fts::TurnRadioMasterOffRequestHandler>::value,
"Fts::TurnRadioMasterOffRequestHandler fehlt");
// turnRadioMasterOffReply ist überladen (FunctionReply / FunctionReplyObject) —
// den vom SocketHandler genutzten FunctionReplyObject-Overload explizit nehmen.
auto reply = static_cast<bool (Fts::*)(const rti::core::SampleIdentity&, const Core::FunctionReplyObject&)>(
&Fts::turnRadioMasterOffReply);
(void)reply;
SUCCEED();
}
/**
* @test Die DDS-Typen aus nme.hpp müssen plain-Member bereitstellen
* (rtiddsgen 7.7): BatteryState, den der migrierte NmeaHandler
* (IIXDR) per Member-Assignment befüllt.
*/
TEST(nme_dtc_Generated, BatteryStateHasPlainMembers)
{
using BatteryState = NmeaManagement::BatteryState;
static_assert(std::is_same<decltype(BatteryState{}.id), int32_t>::value, "id muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.battery), int32_t>::value,
"battery muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.temperature), int32_t>::value,
"temperature muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.frequency), int32_t>::value,
"frequency muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.generic), double>::value,
"generic muss ein plain double-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.current), int32_t>::value,
"current muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.switchOrValue), bool>::value,
"switchOrValue muss ein plain bool-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.voltage), int32_t>::value,
"voltage muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryState{}.serialNumber), std::string>::value,
"serialNumber muss ein plain std::string-Member sein");
// Wie im migrierten IIXDR-Pfad: Member-Assignment statt Setter.
BatteryState bs;
bs.id = 1;
bs.battery = 4;
bs.temperature = 25;
bs.frequency = 100;
bs.generic = 1.5;
bs.current = 55;
bs.switchOrValue = true;
bs.voltage = 3300;
bs.serialNumber = "SER123";
EXPECT_EQ(bs.id, 1);
EXPECT_EQ(bs.battery, 4);
EXPECT_EQ(bs.temperature, 25);
EXPECT_EQ(bs.frequency, 100);
EXPECT_DOUBLE_EQ(bs.generic, 1.5);
EXPECT_EQ(bs.current, 55);
EXPECT_TRUE(bs.switchOrValue);
EXPECT_EQ(bs.voltage, 3300);
EXPECT_EQ(bs.serialNumber, "SER123");
}
/**
* @test BatteryAlarm (IIALR-Pfad) muss plain-Member bereitstellen.
*/
TEST(nme_dtc_Generated, BatteryAlarmHasPlainMembers)
{
using BatteryAlarm = NmeaManagement::BatteryAlarm;
static_assert(std::is_same<decltype(BatteryAlarm{}.id), int32_t>::value, "id muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryAlarm{}.description), std::string>::value,
"description muss ein plain std::string-Member sein");
static_assert(std::is_same<decltype(BatteryAlarm{}.acknowledge), bool>::value,
"acknowledge muss ein plain bool-Member sein");
static_assert(std::is_same<decltype(BatteryAlarm{}.condition), bool>::value,
"condition muss ein plain bool-Member sein");
static_assert(std::is_same<decltype(BatteryAlarm{}.uniqueAlarmNumber), int32_t>::value,
"uniqueAlarmNumber muss ein plain int32_t-Member sein");
static_assert(std::is_same<decltype(BatteryAlarm{}.time), int64_t>::value,
"time muss ein plain int64_t-Member sein");
BatteryAlarm alarm;
alarm.id = 0;
alarm.time = 1700000000000LL;
alarm.description = "overvoltage";
alarm.acknowledge = true;
alarm.condition = false;
alarm.uniqueAlarmNumber = 7;
EXPECT_EQ(alarm.id, 0);
EXPECT_EQ(alarm.time, 1700000000000LL);
EXPECT_EQ(alarm.description, "overvoltage");
EXPECT_TRUE(alarm.acknowledge);
EXPECT_FALSE(alarm.condition);
EXPECT_EQ(alarm.uniqueAlarmNumber, 7);
}
/**
* @test PositionExcon (GPGGA-Pfad): position ist Core::GeoPosition2D mit
* plain double-Member lat/lon; valid ist plain bool.
*/
TEST(nme_dtc_Generated, PositionExconHasPlainMembers)
{
using PositionExcon = NmeaManagement::PositionExcon;
static_assert(std::is_same<decltype(PositionExcon{}.position), Core::GeoPosition2D>::value,
"position muss ein plain Core::GeoPosition2D-Member sein");
static_assert(std::is_same<decltype(PositionExcon{}.position.lat), double>::value,
"position.lat muss ein plain double-Member sein");
static_assert(std::is_same<decltype(PositionExcon{}.position.lon), double>::value,
"position.lon muss ein plain double-Member sein");
static_assert(std::is_same<decltype(PositionExcon{}.valid), bool>::value, "valid muss ein plain bool-Member sein");
// Wie im migrierten GPGGA-Pfad: Member-Assignment statt Setter.
PositionExcon pos;
pos.position.lat = 1.0;
pos.position.lon = 2.0;
pos.valid = true;
EXPECT_DOUBLE_EQ(pos.position.lat, 1.0);
EXPECT_DOUBLE_EQ(pos.position.lon, 2.0);
EXPECT_TRUE(pos.valid);
}
/**
* @test ServiceControl (IIXDR-ServiceControl-Pfad) muss das
* ServiceControlState-Enum-Member bereitstellen.
*/
TEST(nme_dtc_Generated, ServiceControlHasEnumMember)
{
using ServiceControl = NmeaManagement::ServiceControl;
static_assert(
std::is_same<decltype(ServiceControl{}.ServiceControlState), NmeaManagement::ServiceControlEnum>::value,
"ServiceControlState muss ein plain ServiceControlEnum-Member sein");
ServiceControl sc;
sc.ServiceControlState = NmeaManagement::ServiceControlEnum::SHUTDOWN;
EXPECT_EQ(sc.ServiceControlState, NmeaManagement::ServiceControlEnum::SHUTDOWN);
}
/**
* @test TurnRadioMasterOffRequest muss das plain bool-Member shutdown
* bereitstellen (Request-Typ der Fts-Funktions-Topic).
*/
TEST(nme_dtc_Generated, TurnRadioMasterOffRequestHasPlainMember)
{
using TurnRadioMasterOffRequest = NmeaManagement::TurnRadioMasterOffRequest;
static_assert(std::is_same<decltype(TurnRadioMasterOffRequest{}.shutdown), bool>::value,
"shutdown muss ein plain bool-Member sein");
TurnRadioMasterOffRequest req;
req.shutdown = true;
EXPECT_TRUE(req.shutdown);
}
/**
* @test Include-Guard: mehrfaches Einbinden der generierten Header ist erlaubt.
*/
TEST(nme_dtc_Generated, MultipleIncludeGuard)
{
// Including the header multiple times should not cause errors
#include "NmeaManagement/nme.hpp"
#include "NmeaManagement/nme_dtc.h"
#include "NmeaManagement/nme_dts.h"
#include "NmeaManagement/nme_ftc.h"
#include "NmeaManagement/nme_fts.h"
SUCCEED();
}
@@ -0,0 +1,54 @@
/**
* @file test_nme_enums.cpp
* @brief Verhaltenstests für die generierten NmeaManagement-Enumerationen
*
* Diese Tests prüfen den von CODEGEN_IDL_SVC + rtiddsgen aus nme_service.xml
* generierten Header (Lts/Services/NmeaManagement/nme_enums.hpp) aus dem
* Build-Tree. Sie sichern die Enum-Form ab, die der migrierte NmeaHandler
* (IIXDR-Pfad) direkt nutzt: ServiceControlEnum::{PRESHUTDOWN,SHUTDOWN,STOP}
* ohne die legacy *_def-Wrapper (rtiddsgen 7.7 generiert plain enum class).
*/
#include "NmeaManagement/nme_enums.hpp"
#include <gtest/gtest.h>
#include <type_traits>
/**
* @test ServiceControlEnum muss mit den Werten aus nme_service.xml generiert
* werden (PRESHUTDOWN=0, SHUTDOWN=1, STOP=2).
*/
TEST(nme_enums_Generated, ServiceControlEnumValues)
{
static_assert(std::is_enum<::NmeaManagement::ServiceControlEnum>::value,
"ServiceControlEnum muss aus nme_enums.hpp vorliegen");
static_assert(static_cast<int32_t>(::NmeaManagement::ServiceControlEnum::PRESHUTDOWN) == 0,
"ServiceControlEnum::PRESHUTDOWN muss 0 sein");
static_assert(static_cast<int32_t>(::NmeaManagement::ServiceControlEnum::SHUTDOWN) == 1,
"ServiceControlEnum::SHUTDOWN muss 1 sein");
static_assert(static_cast<int32_t>(::NmeaManagement::ServiceControlEnum::STOP) == 2,
"ServiceControlEnum::STOP muss 2 sein");
SUCCEED();
}
/**
* @test Die Enum-Werte müssen wie im migrierten NmeaHandler zugewiesen
* werden können (IIXDR: ServiceControl.ServiceControlState = ...).
*/
TEST(nme_enums_Generated, ServiceControlEnumAssignable)
{
::NmeaManagement::ServiceControlEnum state = ::NmeaManagement::ServiceControlEnum::PRESHUTDOWN;
state = ::NmeaManagement::ServiceControlEnum::SHUTDOWN;
state = ::NmeaManagement::ServiceControlEnum::STOP;
EXPECT_EQ(state, ::NmeaManagement::ServiceControlEnum::STOP);
}
/**
* @test Include-Guard: mehrfaches Einbinden des generierten Headers ist erlaubt.
*/
TEST(nme_enums_Generated, MultipleIncludeGuard)
{
// Including the header multiple times should not cause errors
#include "NmeaManagement/nme_enums.hpp"
SUCCEED();
}
@@ -0,0 +1,101 @@
/**
* @file test_nmea_handler.cpp
* @brief Verhaltenstests für den migrierten NmeaHandler (Lts/Services/NmeaManagement)
*
* Der NmeaHandler gehört zu den Executable-Quellen von svcNmeaManagement
* (kein Library-Symbol), daher wird nmea_handler.cpp hier direkt in das
* Test-Binary kompiliert. Vollständiges DDS-Publishing ist ohne laufende
* DDS-Domain nicht testbar (wie bei flmTests) — getestet wird deshalb:
*
* - Checksummen-Verhalten (Annahme und Ablehnung),
* - der regex-basierte NMEA-Paket-Split (Qt5 QRegExp -> Qt6
* QRegularExpression: package.split(sep) statt package.split(sep.pattern()),
* das letztere auf das Literal "(,|*)" splitten würde und das Parsing
* aus dem Ruder laufen ließe),
* - den GPRMC-Parse-Pfad End-to-end (liest pkt_data[1..11] — mit kaputtem
* Split out-of-range und damit crash-geprüft),
* - die Dispatch-Verdrahtung (GPGGA mit gültiger Checksumme erreicht den
* Publish auf dem leeren DdsHandler — der Crash ist der beobachtbare
* Nachweis, dass Dispatch statt Parse-Abbruch stattfand).
*/
#include "NmeaManagement/nmea_handler.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <gtest/gtest.h>
// Die Service-Implementierung direkt in dieses Test-Binary kompilieren —
// keine Library stellt die NmeaHandler-Symbole bereit.
#include "../Lts/Services/NmeaManagement/nmea_handler.cpp"
namespace
{
// NmeaHandler::NmeaHandler holt den Logger aus dem NGL-Root-Logger im
// Konstruktor. createNGLRootLogger() ist idempotent (ignorealreadycreated),
// daher sicher pro Test aufrufbar.
NGL::INGLLogger& ensureRootLogger()
{
return NGL::NGLLoggerBootstrap::createNGLRootLogger(QString(), true, QString(), true);
}
// NmeaHandler mit leerem DdsHandler: Pfade ohne Publish (GPRMC) sind damit
// lauffähig; Pfade mit Publish (IIXDR/IIALR/GPGGA) liefern null-Deref —
// dort ist der Crash der Observable (siehe GpggaValidChecksumDispatches).
NmeaManagement::NmeaHandler makeHandler()
{
ensureRootLogger();
std::shared_ptr<NmeaManagement::DdsHandler> noDds;
return NmeaManagement::NmeaHandler(noDds, nullptr);
}
} // namespace
/**
* @test GPRMC mit gültiger Checksumme durchläuft den kompletten Parse-Pfad
* (Split auf ,/* via QRegularExpression und pkt_data[1..11]); GPRMC
* publiziert nicht, daher läuft der Test ohne DDS. Wäre der Split auf
* das Literal "(,|*)" kaputt, würde pkt_data[2..11] out-of-range und
* der Test crashen.
*/
TEST(nmea_handler_Decode, GprmcValidChecksumParses)
{
NmeaManagement::NmeaHandler handler = makeHandler();
// Checksumme 0x6A ist gültig (XOR aller Zeichen zwischen $ und *).
EXPECT_NO_THROW(
handler.decode(QStringLiteral("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A")));
}
/**
* @test GPGGA mit gültiger Checksumme (0x73) wird nicht abgebrochen, sondern
* nach GPGGA() publiziert — auf dem leeren DdsHandler ist das eine
* null-Deref: der Crash beweist, dass Checksumme + Dispatch korrekt
* sind.
*/
TEST(nmea_handler_Decode, GpggaValidChecksumDispatches)
{
NmeaManagement::NmeaHandler handler = makeHandler();
EXPECT_DEATH(
handler.decode(QStringLiteral("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,54.5,M,46.9,M,00,0000*73")), ".*");
}
/**
* @test GPGGA mit ungültiger Checksumme (0x00 statt 0x73) wird verworfen:
* kein GPGGA()-Aufruf, kein Publish, kein Crash.
*/
TEST(nmea_handler_Decode, GpggaBadChecksumRejected)
{
NmeaManagement::NmeaHandler handler = makeHandler();
EXPECT_NO_THROW(
handler.decode(QStringLiteral("$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,54.5,M,46.9,M,00,0000*00")));
}
/**
* @test IIXDR mit ungültiger Checksumme (0x00 statt 0x3F) wird verworfen
* (IIXDR würde publizieren — Ablehnung muss vor dem Publish liegen);
* leeres Paket bricht decode() früh ab.
*/
TEST(nmea_handler_Decode, IixdrBadChecksumRejectedAndEmptyPackage)
{
NmeaManagement::NmeaHandler handler = makeHandler();
EXPECT_NO_THROW(handler.decode(QStringLiteral("$IIXDR,123519,SER123,B,42,C,25,F,100,I,55,S,0,U,3300*00")));
EXPECT_NO_THROW(handler.decode(QStringLiteral("")));
}