diff --git a/qt6-after/qt6-migrated-legatus/TetraIF/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/TetraIF/CMakeLists.txt index 2440f9340..7d18701d5 100644 --- a/qt6-after/qt6-migrated-legatus/TetraIF/CMakeLists.txt +++ b/qt6-after/qt6-migrated-legatus/TetraIF/CMakeLists.txt @@ -7,3 +7,4 @@ ADD_SUBDIRECTORY(CoPrtCnv) ADD_SUBDIRECTORY(LogView) ADD_SUBDIRECTORY(PrtCnv) +ADD_SUBDIRECTORY(unit-test) diff --git a/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/CMakeLists.txt new file mode 100644 index 000000000..d3c1f8220 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/CMakeLists.txt @@ -0,0 +1,50 @@ +# +# Unit tests for the TetraIF modules (CoPrtCnv, LogView, PrtCnv). +# One test executable per module, each registered with ctest. +# +# LogView: the module's classes are compiled into the LogView executable, +# so the test compiles the pure class source (IntegerCalculator) directly +# instead of linking a library. +# + +if (NOT BUILD_TESTING) + return() +endif() + +# --- CoPrtCnv: telegram classes and helpers -------------------------------- +if (TARGET CoPrtCnv) + ADD_EXECUTABLE(coprtcnv_unit_test coprtcnv_unit_test.cpp) + TARGET_LINK_LIBRARIES(coprtcnv_unit_test + CoPrtCnv + NGLLogger + Qt6::Core + Qt6::Test + ) + ADD_TEST(NAME coprtcnv_unit_test COMMAND $) +endif() + +# --- LogView: IntegerCalculator -------------------------------------------- +ADD_EXECUTABLE(logview_unit_test + logview_unit_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../LogView/IntegerCalculator.cpp) +TARGET_LINK_LIBRARIES(logview_unit_test + Qt6::Core + Qt6::Test +) +ADD_TEST(NAME logview_unit_test COMMAND $) + +# --- PrtCnv: PrtCnvMember (fragmented telegram handling) -------------------- +# PrtCnvLib is built with hidden visibility, so the test compiles the class +# source directly instead of linking the library. +if (TARGET CoPrtCnv) + ADD_EXECUTABLE(prtcnv_unit_test + prtcnv_unit_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../PrtCnv/PrtCnvMember.cpp) + TARGET_LINK_LIBRARIES(prtcnv_unit_test + CoPrtCnv + NGLLogger + Qt6::Core + Qt6::Test + ) + ADD_TEST(NAME prtcnv_unit_test COMMAND $) +endif() diff --git a/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/coprtcnv_unit_test.cpp b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/coprtcnv_unit_test.cpp new file mode 100644 index 000000000..b36c132c5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/coprtcnv_unit_test.cpp @@ -0,0 +1,175 @@ +// +// Unit tests for the CoPrtCnv module: central telegram, radio telegram +// (header fields, payload access, fragmentation) and the string helpers. +// +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +class coprtcnv_unit_test : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void centralTelegramFromId(); + void centralTelegramDataRoundtrip(); + void centralTelegramCounter(); + + void radioTelegramSourceIssi(); + void radioTelegramDestIssi(); + void radioTelegramIdentity(); + void radioTelegramCounter(); + void radioTelegramPayloadBytes(); + void radioTelegramLengthChecks(); + void radioTelegramFragmentationRoundtrip(); + void radioTelegramLoginVsNormal(); + + void telegramIdToString(); + void wirkfeldTypToString(); +}; + +void coprtcnv_unit_test::initTestCase() +{ + NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false); +} + +void coprtcnv_unit_test::centralTelegramFromId() +{ + N_CoPrtCnv::CoPrtCnvCentralTelegram tele(TrainingAreaBase::TetraIcd::BESTAETIGUNG); + QCOMPARE(tele.telegramId(), TrainingAreaBase::TetraIcd::BESTAETIGUNG); +} + +void coprtcnv_unit_test::centralTelegramDataRoundtrip() +{ + N_CoPrtCnv::CoPrtCnvCentralTelegram tele(TrainingAreaBase::TetraIcd::BESTAETIGUNG); + const QByteArray data(QByteArrayLiteral("central-telegram-payload")); + tele.setData(data); + QCOMPARE(tele.data(), data); +} + +void coprtcnv_unit_test::centralTelegramCounter() +{ + N_CoPrtCnv::CoPrtCnvCentralTelegram tele(TrainingAreaBase::TetraIcd::BESTAETIGUNG); + tele.setCounter(42); + QCOMPARE(tele.counter(), 42); +} + +void coprtcnv_unit_test::radioTelegramSourceIssi() +{ + N_CoPrtCnv::CoPrtCnvRadioTelegram tele(0x12345678); + QCOMPARE(tele.getSrcISSI(), 0x12345678u); +} + +void coprtcnv_unit_test::radioTelegramDestIssi() +{ + N_CoPrtCnv::CoPrtCnvRadioTelegram tele(0x00000001); + tele.setDestISSI(0x87654321); + QCOMPARE(tele.getDestISSI(), 0x87654321u); +} + +void coprtcnv_unit_test::radioTelegramIdentity() +{ + // Identity is only a field of the DataIndReq payload (the (id, size, identity) constructor); other PDU types have no identity area. + N_CoPrtCnv::CoPrtCnvRadioTelegram tele(GEF_BESTAETIGUNG, 32, 0xBEEF); + QCOMPARE(tele.getIdentity(), 0xBEEF); +} + +void coprtcnv_unit_test::radioTelegramCounter() +{ + N_CoPrtCnv::CoPrtCnvRadioTelegram tele(0x00000003); + tele.setZaehler(7); + QCOMPARE(tele.zaehler(), 7); +} + +void coprtcnv_unit_test::radioTelegramPayloadBytes() +{ + N_CoPrtCnv::CoPrtCnvRadioTelegram tele(GEF_BESTAETIGUNG, 32, 0x3333); + QCOMPARE(tele.getPayloadSize(), 32); + + const uint8_t pattern[4] = {0xDE, 0xAD, 0xBE, 0xEF}; + tele.setPayloadData(pattern, 4); + QCOMPARE(tele.getPayloadDataUInt8(3), 0xDE); + QCOMPARE(tele.getPayloadDataUInt8(4), 0xAD); + QCOMPARE(tele.getPayloadDataUInt8(5), 0xBE); + QCOMPARE(tele.getPayloadDataUInt8(6), 0xEF); + // getPayloadDataUInt32 assembles the bytes big endian + QCOMPARE(tele.getPayloadDataUInt32(3, 4), 0xDEADBEEFu); +} + +void coprtcnv_unit_test::radioTelegramLengthChecks() +{ + // isTelegramLengthOk: length <= MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + // + DATA_IND_REQ_HEADER_LENGTH - sizeof(GwHeader) - PAYLOAD_STATUS_LENGTH + // = 249 + 27 - 4 - 1 = 271 + QVERIFY(N_CoPrtCnv::CoPrtCnvRadioTelegram::isTelegramLengthOk(271)); + QVERIFY(!N_CoPrtCnv::CoPrtCnvRadioTelegram::isTelegramLengthOk(272)); + QVERIFY(!N_CoPrtCnv::CoPrtCnvRadioTelegram::isTelegramLengthOk(1000)); +} + +void coprtcnv_unit_test::radioTelegramFragmentationRoundtrip() +{ + // Build a big telegram whose payload must be fragmented, reassemble the + // fragments and compare the payload area against the original data. + const uint16_t payloadSize = 300; + std::shared_ptr bigPtr(new N_CoPrtCnv::CoPrtCnvRadioTelegram(GEF_BESTAETIGUNG, payloadSize, 0x1111)); + + const int payloadPart = bigPtr->getSize() - N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH; + const uint8_t maxFragPayload = N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + - N_CoPrtCnv::CoPrtCnvRadioTelegram::PAYLOAD_STATUS_LENGTH; + const uint8_t nbrFragments = static_cast((payloadPart - 1) / maxFragPayload); + QVERIFY(nbrFragments >= 1); + + std::vector > frags; + std::multimap fragList; + for (uint8_t i = 0; i <= nbrFragments; ++i) + { + bool error = true; + auto frag = std::make_shared(bigPtr, i, error); + QVERIFY2(!error, "fragment creation failed"); + frags.push_back(frag); + fragList.insert(std::make_pair(frag->getFragmentCounter(), frag.get())); + } + QCOMPARE(static_cast(fragList.size()), static_cast(nbrFragments) + 1); + + const QByteArray unfragmented = N_CoPrtCnv::CoPrtCnvRadioTelegram::getUnfragmentedData(fragList); + QVERIFY(!unfragmented.isEmpty()); + QCOMPARE(unfragmented.size(), bigPtr->data().size()); + QCOMPARE(unfragmented.mid(N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1), + bigPtr->data().mid(N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1)); +} + +void coprtcnv_unit_test::radioTelegramLoginVsNormal() +{ + N_CoPrtCnv::CoPrtCnvRadioTelegram login(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginRequest, 0x2222, 0x00000005); + QVERIFY(login.isLoginTelegram()); + QVERIFY(!login.isNormalTelegram()); +} + +void coprtcnv_unit_test::telegramIdToString() +{ + // telegramId2String / wirkfeldTyp2Str are declared in global scope + const QByteArray known = telegramId2String(GEF_BESTAETIGUNG); + QVERIFY(!known.isEmpty()); +} + +void coprtcnv_unit_test::wirkfeldTypToString() +{ + const QString text = wirkfeldTyp2Str(TrainingAreaBase::TetraIcd::AreaEffectSimulationType::BeStillWarning); + QVERIFY(!text.isEmpty()); +} + +QTEST_MAIN(coprtcnv_unit_test) +#include "coprtcnv_unit_test.moc" diff --git a/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/logview_unit_test.cpp b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/logview_unit_test.cpp new file mode 100644 index 000000000..3b45bcd3a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/logview_unit_test.cpp @@ -0,0 +1,150 @@ +// +// Unit tests for the LogView module: IntegerCalculator (the module's pure +// logic class, compiled from the LogView sources). +// +#include "../LogView/IntegerCalculator.hpp" + +#include + +#include + +using IntegerCalculator = N_SST::IntegerCalculator; +using E_Error = IntegerCalculator::E_Error; + +class logview_unit_test : public QObject +{ + Q_OBJECT + +private slots: + void basicArithmetic(); + void operatorPriority(); + void parentheses(); + void unaryMinus(); + void divisionByZero(); + void noOperandBetweenOperators(); + void missingRightParanthesis(); + void missingLeftParanthesis(); + void unknownVariable(); + void variables(); +}; + +void logview_unit_test::basicArithmetic() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_UnknownError; + + QCOMPARE(calc.calc("1+2", error), 3); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("10-4", error), 6); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("6*7", error), 42); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("84/4", error), 21); + QCOMPARE(error, E_Error::e_Ok); +} + +void logview_unit_test::operatorPriority() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_UnknownError; + + QCOMPARE(calc.calc("1+2*3", error), 7); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("2*3+4", error), 10); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("10-2*3", error), 4); + QCOMPARE(error, E_Error::e_Ok); +} + +void logview_unit_test::parentheses() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_UnknownError; + + QCOMPARE(calc.calc("(1+2)*3", error), 9); + QCOMPARE(error, E_Error::e_Ok); + + QCOMPARE(calc.calc("((2+3)*4)", error), 20); + QCOMPARE(error, E_Error::e_Ok); +} + +void logview_unit_test::unaryMinus() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_UnknownError; + + QCOMPARE(calc.calc("-3+5", error), 2); + QCOMPARE(error, E_Error::e_Ok); +} + +void logview_unit_test::divisionByZero() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_Ok; + + (void)calc.calc("1/0", error); + QCOMPARE(error, E_Error::e_DivisionByZero); +} + +void logview_unit_test::noOperandBetweenOperators() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_Ok; + + (void)calc.calc("1+*2", error); + QCOMPARE(error, E_Error::e_NoOperandBetweenOperators); +} + +void logview_unit_test::missingRightParanthesis() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_Ok; + + (void)calc.calc("(1+2", error); + QCOMPARE(error, E_Error::e_MissingRightParanthesis); +} + +void logview_unit_test::missingLeftParanthesis() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_Ok; + + (void)calc.calc("1+2)", error); + QCOMPARE(error, E_Error::e_MissingLeftParanthesis); +} + +void logview_unit_test::unknownVariable() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_Ok; + + (void)calc.calc("y", error); + QCOMPARE(error, E_Error::e_UnknownOperationOrVariable); +} + +void logview_unit_test::variables() +{ + IntegerCalculator calc; + E_Error error = E_Error::e_UnknownError; + + calc.addVariable("x", 5); + QCOMPARE(calc.calc("x*2", error), 10); + QCOMPARE(error, E_Error::e_Ok); + + calc.addVariable("y", -3); + QCOMPARE(calc.calc("x+y", error), 2); + QCOMPARE(error, E_Error::e_Ok); + + calc.removeVariable("x"); + error = E_Error::e_Ok; + (void)calc.calc("x", error); + QCOMPARE(error, E_Error::e_UnknownOperationOrVariable); +} + +QTEST_MAIN(logview_unit_test) +#include "logview_unit_test.moc" diff --git a/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/prtcnv_unit_test.cpp b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/prtcnv_unit_test.cpp new file mode 100644 index 000000000..e2dee0a2e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/TetraIF/unit-test/prtcnv_unit_test.cpp @@ -0,0 +1,115 @@ +// +// Unit tests for the PrtCnv module: PrtCnvMember (member state and +// fragmented telegram handling / reassembly). +// +#include +#include +#include + +#include + +#include + +#include +#include + +class prtcnv_unit_test : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void memberDefaults(); + void fragmentedListEmptyInitially(); + void addAndClearFragments(); + void unfragmentationRoundtrip(); +}; + +void prtcnv_unit_test::initTestCase() +{ + NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx.props", false); +} + +void prtcnv_unit_test::memberDefaults() +{ + PrtCnv::PrtCnvMember member(7, 0x1234); + QCOMPARE(member.m_Id, 7); + QCOMPARE(member.m_ISSI, 0x1234); + QCOMPARE(member.m_ClaimCount, 0); + QCOMPARE(member.m_loggedInExerciseId, 0); + QCOMPARE(member.m_Licensed, false); +} + +void prtcnv_unit_test::fragmentedListEmptyInitially() +{ + PrtCnv::PrtCnvMember member(1, 0x0001); + QVERIFY(member.isFragmentedListEmpty()); + QVERIFY(member.getUnfragmentedTelegramData().isEmpty()); +} + +void prtcnv_unit_test::addAndClearFragments() +{ + const uint16_t payloadSize = 300; + std::shared_ptr bigPtr(new N_CoPrtCnv::CoPrtCnvRadioTelegram(GEF_BESTAETIGUNG, payloadSize, 0x4321)); + + const int payloadPart = bigPtr->getSize() - N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH; + const uint8_t maxFragPayload = N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + - N_CoPrtCnv::CoPrtCnvRadioTelegram::PAYLOAD_STATUS_LENGTH; + const uint8_t nbrFragments = static_cast((payloadPart - 1) / maxFragPayload); + + PrtCnv::PrtCnvMember member(1, 0x0001); + QVERIFY(member.isFragmentedListEmpty()); + + std::vector > frags; + for (uint8_t i = 0; i <= nbrFragments; ++i) + { + bool error = true; + auto frag = std::make_shared(bigPtr, i, error); + QVERIFY2(!error, "fragment creation failed"); + frags.push_back(frag); + member.addFragmentedTelegram(*frag); + } + + QVERIFY(!member.isFragmentedListEmpty()); + QCOMPARE(member.getFragmentedTelegramId(), GEF_BESTAETIGUNG); + + member.clearFragmentedList(); + QVERIFY(member.isFragmentedListEmpty()); +} + +void prtcnv_unit_test::unfragmentationRoundtrip() +{ + // A big telegram with a known payload pattern, fragmented into the member and reassembled + const uint16_t payloadSize = 300; + std::shared_ptr bigPtr(new N_CoPrtCnv::CoPrtCnvRadioTelegram(GEF_BESTAETIGUNG, payloadSize, 0x5678)); + + QByteArray pattern(payloadSize, 0); + for (int i = 0; i < payloadSize; ++i) + pattern[i] = static_cast(i * 7 + 1); + bigPtr->setPayloadData(reinterpret_cast(pattern.constData()), payloadSize); + + const int payloadPart = bigPtr->getSize() - N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH; + const uint8_t maxFragPayload = N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + - N_CoPrtCnv::CoPrtCnvRadioTelegram::PAYLOAD_STATUS_LENGTH; + const uint8_t nbrFragments = static_cast((payloadPart - 1) / maxFragPayload); + + PrtCnv::PrtCnvMember member(2, 0x0002); + std::vector > frags; + for (uint8_t i = 0; i <= nbrFragments; ++i) + { + bool error = true; + auto frag = std::make_shared(bigPtr, i, error); + QVERIFY2(!error, "fragment creation failed"); + frags.push_back(frag); + member.addFragmentedTelegram(*frag); + } + + const QByteArray unfragmented = member.getUnfragmentedTelegramData(); + QCOMPARE(unfragmented.size(), bigPtr->data().size()); + QCOMPARE(unfragmented.mid(N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1), + bigPtr->data().mid(N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1)); +} + +QTEST_MAIN(prtcnv_unit_test) +#include "prtcnv_unit_test.moc" diff --git a/qt6-after/qt6-migrated-legatus/new_docker_build.sh b/qt6-after/qt6-migrated-legatus/new_docker_build.sh index 7061671ab..799177d27 100755 --- a/qt6-after/qt6-migrated-legatus/new_docker_build.sh +++ b/qt6-after/qt6-migrated-legatus/new_docker_build.sh @@ -232,6 +232,10 @@ RELEASE_BUILD_TARGETS=( ngltools_qt_smoke lts_coord_trans_unit_test test_nglpdfwidget + # TetraIF unit tests (TetraIF/unit-test) + coprtcnv_unit_test + logview_unit_test + prtcnv_unit_test ) APT_PACKAGES_STRING="$(join_by ' ' "${APT_PACKAGES[@]}")"