Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/TetraIF/LogView/unittest/LogViewTetraTelegramsParserTest.cpp
T
2026-09-12 13:54:34 +02:00

245 lines
7.5 KiB
C++

/**
* @file LogViewTetraTelegramsParserTest.cpp
* @brief Unit tests for N_LogView::LogViewTetraTelegramsParser.
*
* The parser reads the C++ telegram specification header and exposes the
* structs, enums and POD types it found. It is pure logic with no GUI
* dependency, so each test feeds it a small specification written to a
* temporary file and inspects the resulting model.
*/
#include <gtest/gtest.h>
#include <QtCore/QString>
#include <QtCore/QTemporaryDir>
#include "LogViewTetraTelegramsParser.hpp"
using N_LogView::EnumInformation;
using N_LogView::LogViewTetraTelegramsParser;
using N_LogView::MemberInformation;
using N_LogView::PodInformation;
using N_LogView::StructInformation;
namespace
{
// Writes a specification snippet to a file inside dir and parses it.
bool parseSpecification(LogViewTetraTelegramsParser &parser,
const QTemporaryDir &dir,
const QString &content)
{
const QString path = dir.filePath(QStringLiteral("spec.h"));
QFile f(path);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate))
return false;
f.write(content.toUtf8());
f.close();
return parser.parse(path);
}
const MemberInformation *findMember(const StructInformation &si, const QString &name)
{
for (const MemberInformation &mi : si.member)
{
if (mi.name == name)
return &mi;
}
return nullptr;
}
} // namespace
TEST(LogViewTetraTelegramsParserTest, BuiltinPodTypesAreKnownUpFront)
{
const LogViewTetraTelegramsParser parser;
EXPECT_TRUE(parser.isPod(QStringLiteral("bool")));
EXPECT_TRUE(parser.isPod(QStringLiteral("int32_t")));
EXPECT_FALSE(parser.isPod(QStringLiteral("NotAType")));
const PodInformation pod = parser.podFromType(QStringLiteral("int32_t"));
EXPECT_EQ(pod.byteLen, 4);
EXPECT_TRUE(pod.isIntegral);
EXPECT_TRUE(pod.isSigned);
}
TEST(LogViewTetraTelegramsParserTest, ParsingAMissingFileFails)
{
LogViewTetraTelegramsParser parser;
EXPECT_FALSE(parser.parse(QStringLiteral("/does/not/exist/spec.h")));
}
TEST(LogViewTetraTelegramsParserTest, ASimpleStructIsCollected)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"struct Header\n"
"{\n"
" int32_t equipmentNumber;\n"
" double offset;\n"
"};\n")));
ASSERT_TRUE(parser.isStruct(QStringLiteral("Header")));
const StructInformation si = parser.structFromType(QStringLiteral("Header"));
EXPECT_EQ(si.type, QStringLiteral("Header"));
EXPECT_TRUE(si.baseStruct.isEmpty());
ASSERT_EQ(si.member.size(), 2);
const MemberInformation *eqNr = findMember(si, QStringLiteral("equipmentNumber"));
ASSERT_NE(eqNr, nullptr);
EXPECT_EQ(eqNr->type, QStringLiteral("int32_t"));
EXPECT_TRUE(eqNr->isPod);
EXPECT_FALSE(eqNr->isArray);
}
TEST(LogViewTetraTelegramsParserTest, UnsignedFixedWidthTypesAreNotBuiltinPods)
{
// The built-in POD table only holds bool, int, int32_t, qint64, float and
// double. Unsigned fixed-width types are therefore not recognised as PODs.
const LogViewTetraTelegramsParser parser;
EXPECT_FALSE(parser.isPod(QStringLiteral("uint32_t")));
EXPECT_FALSE(parser.isPod(QStringLiteral("uint8_t")));
}
TEST(LogViewTetraTelegramsParserTest, BaseStructIsRecorded)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"struct Header\n"
"{\n"
" uint32_t equipmentNumber;\n"
"};\n"
"struct Position : public Header\n"
"{\n"
" int32_t easting;\n"
"};\n")));
ASSERT_TRUE(parser.isStruct(QStringLiteral("Position")));
const StructInformation si = parser.structFromType(QStringLiteral("Position"));
EXPECT_EQ(si.baseStruct, QStringLiteral("Header"));
// Base members are merged in after parsing, so the derived struct sees both.
EXPECT_NE(findMember(si, QStringLiteral("easting")), nullptr);
EXPECT_NE(findMember(si, QStringLiteral("equipmentNumber")), nullptr);
}
TEST(LogViewTetraTelegramsParserTest, EnumValuesAreNumberedImplicitlyAndExplicitly)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"enum class State\n"
"{\n"
" Unknown = 0,\n"
" Alive = 1,\n"
" Dead = 7,\n"
"};\n")));
ASSERT_TRUE(parser.isEnum(QStringLiteral("State")));
const EnumInformation ei = parser.enumFromType(QStringLiteral("State"));
ASSERT_EQ(ei.values.size(), 3);
EXPECT_EQ(ei.values.at(0).key, QStringLiteral("Unknown"));
EXPECT_EQ(ei.values.at(0).value, 0);
EXPECT_EQ(ei.values.at(1).key, QStringLiteral("Alive"));
EXPECT_EQ(ei.values.at(1).value, 1);
EXPECT_EQ(ei.values.at(2).key, QStringLiteral("Dead"));
EXPECT_EQ(ei.values.at(2).value, 7);
}
TEST(LogViewTetraTelegramsParserTest, ImplicitEnumValuesKeepTheTrailingComma)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"enum class State\n"
"{\n"
" Unknown,\n"
" Alive,\n"
"};\n")));
const EnumInformation ei = parser.enumFromType(QStringLiteral("State"));
ASSERT_EQ(ei.values.size(), 2);
// Known parser limitation: the trailing comma is only stripped from the value
// of an "Key = value," entry, never from the key of an implicitly numbered
// one. The shipped specification files always use explicit values.
EXPECT_EQ(ei.values.at(0).key, QStringLiteral("Unknown,"));
EXPECT_EQ(ei.values.at(0).value, 0);
EXPECT_EQ(ei.values.at(1).key, QStringLiteral("Alive,"));
EXPECT_EQ(ei.values.at(1).value, 1);
}
TEST(LogViewTetraTelegramsParserTest, StringAndBinaryMembersAreCategorised)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"struct Payload\n"
"{\n"
" std::string name;\n"
" std::vector<unsigned char> raw;\n"
"};\n")));
const StructInformation si = parser.structFromType(QStringLiteral("Payload"));
const MemberInformation *name = findMember(si, QStringLiteral("name"));
const MemberInformation *raw = findMember(si, QStringLiteral("raw"));
ASSERT_NE(name, nullptr);
ASSERT_NE(raw, nullptr);
EXPECT_TRUE(name->isString);
EXPECT_TRUE(raw->isBinary);
}
TEST(LogViewTetraTelegramsParserTest, VectorMembersAreMarkedAsArrays)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"struct Group\n"
"{\n"
" std::vector<uint32_t> issis;\n"
"};\n")));
const StructInformation si = parser.structFromType(QStringLiteral("Group"));
const MemberInformation *issis = findMember(si, QStringLiteral("issis"));
ASSERT_NE(issis, nullptr);
EXPECT_TRUE(issis->isArray);
}
TEST(LogViewTetraTelegramsParserTest, GetInnerTypeUnwrapsTemplateArguments)
{
EXPECT_EQ(LogViewTetraTelegramsParser::getInnerType(QStringLiteral("std::vector<uint32_t>")),
QStringLiteral("uint32_t"));
EXPECT_TRUE(LogViewTetraTelegramsParser::getInnerType(QStringLiteral("uint32_t")).isEmpty());
}
TEST(LogViewTetraTelegramsParserTest, NoTelegramsAreVisibleWithoutConstraints)
{
QTemporaryDir dir;
ASSERT_TRUE(dir.isValid());
LogViewTetraTelegramsParser parser;
ASSERT_TRUE(parseSpecification(parser, dir, QStringLiteral(
"struct Header\n"
"{\n"
" uint32_t equipmentNumber;\n"
"};\n")));
EXPECT_TRUE(parser.telegramsToCreate().isEmpty());
EXPECT_TRUE(parser.nameFromTelegramId(1).isEmpty());
}