Revision control

++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTest/qtest.h>
++
++ #include <QtSerialBus/QCanDbcFileParser>
++ #include <QtSerialBus/QCanMessageDescription>
++ #include <QtSerialBus/QCanSignalDescription>
++ #include <QtSerialBus/QCanUniqueIdDescription>
++
++ #include "qcanmessagedescription_helpers.h"
++ #include "qcanuniqueiddescription_helpers.h"
++
++ QT_USE_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ class tst_QCanDbcFileParser : public QObject
++ {
++ Q_OBJECT
++ private slots:
++ void initTestCase_data();
++ void initTestCase();
++ void construct();
++ void uinqueId();
++ void parseFile_data();
++ void parseFile();
++ void valueDescriptions();
++ void resetState();
++
++ private:
++ QString m_filesDir;
++ };
++
++ void tst_QCanDbcFileParser::initTestCase_data()
++ {
++ QTest::addColumn<bool>("readFromFile");
++ QTest::newRow("from_file") << true;
++ QTest::newRow("from_input_data") << false;
++ }
++
++ void tst_QCanDbcFileParser::initTestCase()
++ {
++ m_filesDir = QString::fromLatin1(QT_TEST_DATADIR) + QDir::separator();
++ }
++
++ void tst_QCanDbcFileParser::construct()
++ {
++ QCanDbcFileParser parser;
++ QCOMPARE(parser.error(), QCanDbcFileParser::Error::None);
++ QVERIFY(parser.errorString().isEmpty());
++ QVERIFY(parser.warnings().isEmpty());
++ QVERIFY(parser.messageDescriptions().isEmpty());
++ QVERIFY(parser.messageValueDescriptions().isEmpty());
++ }
++
++ void tst_QCanDbcFileParser::uinqueId()
++ {
++ QCanUniqueIdDescription expectedDesc;
++ expectedDesc.setSource(QtCanBus::DataSource::FrameId);
++ expectedDesc.setEndian(QSysInfo::Endian::LittleEndian);
++ expectedDesc.setStartBit(0);
++ expectedDesc.setBitLength(29);
++
++ QVERIFY(equals(QCanDbcFileParser::uniqueIdDescription(), expectedDesc));
++ }
++
++ void tst_QCanDbcFileParser::parseFile_data()
++ {
++ QTest::addColumn<QStringList>("fileNames");
++ QTest::addColumn<QCanDbcFileParser::Error>("expectedError");
++ QTest::addColumn<QString>("expectedErrorDescription");
++ QTest::addColumn<QStringList>("expectedWarnings");
++ QTest::addColumn<QList<QCanMessageDescription>>("expectedMessageDescriptions");
++
++ {
++ // We need to get the exact same error string
++ const QString fileName = u"nonexisting_file.dbc"_s;
++ QFile f(m_filesDir + fileName);
++ auto res = f.open(QIODevice::ReadOnly);
++ QVERIFY(!res);
++ const QString errorDesc = f.errorString();
++
++ QTest::addRow("invalid filename")
++ << QStringList{ u"nonexisting_file.dbc"_s }
++ << QCanDbcFileParser::Error::FileReading << errorDesc
++ << QStringList() << QList<QCanMessageDescription>();
++ }
++
++ QStringList expectedWarnings {
++ u"Failed to find message description in string BO_ MessageName : 8 Vector__XXX"_s,
++ u"Failed to find message description in string BO_ 1234 : 8 Vector__XXX"_s,
++ u"Failed to find message description in string BO_ 1234 MessageName : Vector__XXX"_s,
++ u"Failed to find message description in string BO_ 1234 MessageName : 8"_s
++ };
++
++ QTest::addRow("invalid message definitions")
++ << QStringList{ u"invalid_messages.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings
++ << QList<QCanMessageDescription>();
++
++ expectedWarnings = {
++ u"Failed to find signal description in string SG_ : 0|8@1+ (1,0) [0|0] \"unit\" "
++ "Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName 0|8@1+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : |8@1+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 08@1+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|@1+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|81+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@+ (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1 (1,0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ (0) [0|0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ (1,0) "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ (1,0) [0] "
++ "\"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ (1,0) [0|0] "
++ "Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ SignalName : 0|8@1+ (1,0) [0|0] "
++ "\"unit\""_s,
++ u"Message description with unique id 1234 is skipped because it's not valid."_s
++ };
++
++ QTest::addRow("invalid signal definitions")
++ << QStringList{ u"invalid_signals.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings
++ << QList<QCanMessageDescription>();
++
++ QCanMessageDescription messageDesc;
++ messageDesc.setName("Test");
++ messageDesc.setSize(3);
++ messageDesc.setUniqueId(QtCanBus::UniqueId{1234});
++ messageDesc.setTransmitter("Vector__XXX");
++
++ {
++ messageDesc.clearSignalDescriptions();
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("Signal0"); // correct
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("_sIgnal_1"); // correct
++ signalDesc.setStartBit(8);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s_Ig_nAl2_"); // correct
++ signalDesc.setStartBit(16);
++ messageDesc.addSignalDescription(signalDesc);
++
++ // other signal names are not correct DBC identifiers, so they will
++ // result in warnings
++
++ expectedWarnings = {
++ u"Failed to find signal description in string SG_ Singal%1 : 0|8@1+ (1,0) [0|0] \"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ 1Signal : 0|8@1+ (1,0) [0|0] \"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ Signal name : 0|8@1+ (1,0) [0|0] \"unit\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ \"Signal name\" : 0|8@1+ (1,0) [0|0] \"unit\" Vector__XXX"_s,
++ };
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("different signal names")
++ << QStringList{ u"signal_names.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(12);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(12);
++ signalDesc.setPhysicalUnit("unit1");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("valid message and signals")
++ << QStringList{ u"valid_message_and_signals.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(12);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setDataEndian(QSysInfo::Endian::BigEndian);
++ signalDesc.setBitLength(8);
++ signalDesc.setStartBit(23);
++ signalDesc.setPhysicalUnit("");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("different endian")
++ << QStringList{ u"different_endian.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(7);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(12);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setStartBit(12);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Float);
++ signalDesc.setBitLength(32);
++ signalDesc.setStartBit(24);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QCanMessageDescription doubleMessage = messageDesc;
++ doubleMessage.clearSignalDescriptions();
++ doubleMessage.setName("Test1");
++ doubleMessage.setUniqueId(QtCanBus::UniqueId{0x18fef1fe});
++ doubleMessage.setSize(8);
++
++ signalDesc.setName("s3");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Double);
++ signalDesc.setBitLength(64);
++ signalDesc.setStartBit(0);
++ doubleMessage.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc, doubleMessage };
++
++ QTest::addRow("different data types")
++ << QStringList{ u"different_data_types.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(8);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(0.0); // not used - shouldn't really happen in DBC file
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(8);
++ signalDesc.setFactor(1.0); // int values
++ signalDesc.setOffset(-5.0);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setStartBit(16);
++ signalDesc.setFactor(0.625); // float values
++ signalDesc.setOffset(-5.567);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(24);
++ signalDesc.setFactor(1.234e-05); // scientific representation
++ signalDesc.setOffset(-1.056e02);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s4");
++ signalDesc.setStartBit(32);
++ signalDesc.setFactor(0.625); // float and int values
++ signalDesc.setOffset(-5.0);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s5");
++ signalDesc.setStartBit(40);
++ signalDesc.setFactor(2.5); // float and scientific values
++ signalDesc.setOffset(1.000123e01);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s6");
++ signalDesc.setStartBit(48);
++ signalDesc.setFactor(2e-003);
++ signalDesc.setOffset(1e02);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s7");
++ signalDesc.setStartBit(56);
++ signalDesc.setFactor(1.e4);
++ signalDesc.setOffset(2.E-02);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("different factor and offset")
++ << QStringList{ u"different_factor_offset.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(7);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(8);
++ signalDesc.setRange(1, 5); // int values
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setStartBit(16);
++ signalDesc.setRange(-0.625, 0.125); // float values
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(24);
++ signalDesc.setRange(-1.234e-05, 1.234e05); // scientific
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s4");
++ signalDesc.setStartBit(32);
++ signalDesc.setRange(-1, 0.333); // int and float
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s5");
++ signalDesc.setStartBit(40);
++ signalDesc.setRange(2.5, 1.000123e01); // float and scientific values
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s6");
++ signalDesc.setStartBit(48);
++ signalDesc.setRange(0.333, -0.333); // invalid order
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("different ranges")
++ << QStringList{ u"different_ranges.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(4);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Rcv_1");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(8);
++ signalDesc.setReceiver("Rcv2");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setStartBit(16);
++ signalDesc.setReceiver("RCV_3");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(24);
++ signalDesc.setReceiver("rcv4");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("multiple receivers")
++ << QStringList{ u"multiple_receivers.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(7);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(12);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setStartBit(12);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Float);
++ signalDesc.setBitLength(32);
++ signalDesc.setStartBit(24);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QCanMessageDescription doubleMessage = messageDesc;
++ doubleMessage.clearSignalDescriptions();
++ doubleMessage.setName("Test1");
++ doubleMessage.setUniqueId(QtCanBus::UniqueId{0x18fef1fe});
++ doubleMessage.setSize(8);
++
++ signalDesc.setName("s3");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Double);
++ signalDesc.setBitLength(64);
++ signalDesc.setStartBit(0);
++ doubleMessage.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc, doubleMessage };
++
++ QTest::addRow("message and signals in one line")
++ << QStringList{ u"message_signals_in_one_line.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(5);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(8);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setStartBit(16);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(24);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s4");
++ signalDesc.setStartBit(32);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc };
++
++ QTest::addRow("random whitespaces")
++ << QStringList{ u"random_whitespaces.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(2);
++ messageDesc.setComment("Comment for message Test");
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ signalDesc.setComment("Comment for s0");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setStartBit(8);
++ signalDesc.setComment("comment for s1");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QCanMessageDescription otherDesc = messageDesc;
++ otherDesc.clearSignalDescriptions();
++ otherDesc.setName("Test1");
++ otherDesc.setUniqueId(QtCanBus::UniqueId{0x18fef1fe});
++ otherDesc.setSize(1);
++ otherDesc.setComment("comment for Test1.");
++
++ signalDesc.setName("s2");
++ signalDesc.setDataEndian(QSysInfo::Endian::BigEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setStartBit(7);
++ signalDesc.setBitLength(8);
++ signalDesc.setComment("comment for s2");
++ otherDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc, otherDesc };
++
++ QTest::addRow("comments processing")
++ << QStringList{ u"messages_with_comments.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << QStringList() << descriptions;
++
++ messageDesc.setComment("");
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(5);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexorSwitch);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(2);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s1");
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::None);
++ signalDesc.setStartBit(2);
++ signalDesc.setBitLength(6);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s2");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexedSignal);
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(32);
++ signalDesc.addMultiplexSignal("s0", 1u);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.setName("s3");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Float);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexedSignal);
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(32);
++ signalDesc.addMultiplexSignal("s0", 2u);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc};
++
++ // The file also contains a message with simple multiplexing, but two
++ // multiplexor signals. That is incorrect, so a warning is raised, and
++ // this message description is discarded
++ expectedWarnings = {
++ u"Message description with unique id 1235 is skipped because it has invalid "
++ "multiplexing description."_s
++ };
++
++ QTest::addRow("simple multiplexing")
++ << QStringList{ u"simple_multiplexing.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(3);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexorSwitch);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(4);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s1");
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::SwitchAndSignal);
++ signalDesc.setStartBit(4);
++ signalDesc.setBitLength(4);
++ signalDesc.addMultiplexSignal("s0", { {1U, 1U}, {5U, 5U}, {9U, 9U} });
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s2");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexedSignal);
++ signalDesc.setStartBit(4);
++ signalDesc.setBitLength(12);
++ signalDesc.addMultiplexSignal("s0", { {2U, 4U}, {6U, 8U}, {10U, 10U} });
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(8);
++ signalDesc.addMultiplexSignal("s1", 1U);
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s4");
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(8);
++ signalDesc.addMultiplexSignal("s1", { {2U, 3U}, {5U, 5U} });
++ messageDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s5");
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::None);
++ signalDesc.setStartBit(16);
++ signalDesc.setBitLength(8);
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc};
++
++ // The file also contains messages with broken extended multiplexing,
++ // so a warning is raised for every such message, and these message
++ // descriptions are discarded
++ expectedWarnings = {
++ u"Failed to find extended multiplexing description in string "
++ "SG_MUL_VAL_ 1238 s2 s0 2-4, 6-8 10-10;"_s,
++ u"Failed to find extended multiplexing description in string "
++ "SG_MUL_VAL_ 1238 s4 s1 2-3, 5-5"_s,
++ u"Failed to find message description for unique id 12371. Skipping string "
++ "SG_MUL_VAL_ 12371 s4 s1 2-3, 5-5;"_s,
++ u"Failed to find signal description for signal s11. Skipping string "
++ "SG_MUL_VAL_ 2566844926 s4 s11 2-3, 5-5;"_s,
++ u"Failed to find signal description for signal s6. Skipping string "
++ "SG_MUL_VAL_ 1236 s6 s1 2-3, 5-5;"_s,
++ u"Message description with unique id 419361278 is skipped because it has invalid "
++ "multiplexing description."_s,
++ u"Message description with unique id 1236 is skipped because it has invalid "
++ "multiplexing description."_s,
++ u"Message description with unique id 1237 is skipped because it has invalid "
++ "multiplexing description."_s,
++ u"Message description with unique id 1238 is skipped because it has invalid "
++ "multiplexing description."_s,
++ };
++
++ QTest::addRow("extended multiplexing")
++ << QStringList{ u"extended_multiplexing.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings << descriptions;
++ }
++
++ {
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(8);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(8);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit(" !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"
++ "[]^_`abcdefghijklmnopqrstuvwxyz{|}~°");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QList<QCanMessageDescription> descriptions { messageDesc};
++
++ expectedWarnings = {
++ u"Failed to find signal description in string SG_ s1 : 0|8@1+ (1,0) [0|0] "
++ "\"double quote: \"\" Vector__XXX"_s,
++ u"Failed to find signal description in string SG_ s2 : 0|8@1+ (1,0) [0|0] "
++ "\"backslash: \\\" Vector__XXX"_s,
++ };
++
++ QTest::addRow("char string test")
++ << QStringList{ u"char_string_test.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings << descriptions;
++ }
++
++ {
++ const QString fileName = u"invalid_message_pos.dbc"_s;
++ const QString expectedError = u"Failed to parse file %1. Unexpected position "
++ "of BO_ section."_s.arg(m_filesDir + fileName);
++
++ QTest::addRow("invalid message desc pos")
++ << QStringList{ fileName }
++ << QCanDbcFileParser::Error::Parsing << expectedError
++ << QStringList() << QList<QCanMessageDescription>();
++ }
++
++ {
++ const QString fileName = u"invalid_signal_pos.dbc"_s;
++ const QString expectedError = u"Failed to parse file %1. Unexpected position "
++ "of SG_ section."_s.arg(m_filesDir + fileName);
++
++ QTest::addRow("invalid signal desc pos")
++ << QStringList{ fileName }
++ << QCanDbcFileParser::Error::Parsing << expectedError
++ << QStringList() << QList<QCanMessageDescription>();
++ }
++
++ {
++ // startBit and bitLength narrow to quint16, size narrows to quint8, so a
++ // value above the field width must be rejected at ingestion rather than
++ // silently wrapped into a small, seemingly valid offset.
++ QCanMessageDescription oversizedMsg;
++ oversizedMsg.setName("Test");
++ oversizedMsg.setSize(8);
++ oversizedMsg.setUniqueId(QtCanBus::UniqueId{1234});
++ oversizedMsg.setTransmitter("Vector__XXX");
++
++ QCanSignalDescription goodSignal;
++ goodSignal.setName("goodSignal");
++ goodSignal.setDataEndian(QSysInfo::Endian::LittleEndian);
++ goodSignal.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ goodSignal.setDataSource(QtCanBus::DataSource::Payload);
++ goodSignal.setStartBit(0);
++ goodSignal.setBitLength(8);
++ goodSignal.setFactor(1.0);
++ goodSignal.setOffset(0.0);
++ goodSignal.setRange(0.0, 0.0);
++ goodSignal.setPhysicalUnit("");
++ goodSignal.setReceiver("Vector__XXX");
++ oversizedMsg.addSignalDescription(goodSignal);
++
++ const QStringList expectedWarnings = {
++ u"Failed to parse start bit for signal badStart"_s,
++ u"Failed to parse signal description from string "
++ "SG_ badStart : 70000|8@1+ (1,0) [0|0] \"\" Vector__XXX"_s,
++ u"Failed to parse bit length for signal badLength"_s,
++ u"Failed to parse signal description from string "
++ "SG_ badLength : 0|70000@1+ (1,0) [0|0] \"\" Vector__XXX"_s,
++ };
++
++ QTest::addRow("out-of-range signal fields")
++ << QStringList{ u"oversized_signal.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings
++ << QList<QCanMessageDescription>{ oversizedMsg };
++ }
++
++ {
++ // Check that we get message descriptions and warnings from all files
++ messageDesc.clearSignalDescriptions();
++ messageDesc.setSize(4);
++
++ QCanSignalDescription signalDesc;
++ signalDesc.setName("Signal0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::Float);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(32);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("unit");
++ signalDesc.setReceiver("Vector__XXX");
++ messageDesc.addSignalDescription(signalDesc);
++
++ QCanMessageDescription otherDesc;
++ otherDesc.setName("Test1");
++ otherDesc.setSize(3);
++ otherDesc.setUniqueId(QtCanBus::UniqueId{1235});
++ otherDesc.setTransmitter("Vector__XXX");
++
++ signalDesc.setName("s0");
++ signalDesc.setDataEndian(QSysInfo::Endian::LittleEndian);
++ signalDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger);
++ signalDesc.setDataSource(QtCanBus::DataSource::Payload);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexorSwitch);
++ signalDesc.setStartBit(0);
++ signalDesc.setBitLength(4);
++ signalDesc.setFactor(1.0);
++ signalDesc.setOffset(0.0);
++ signalDesc.setRange(0.0, 0.0);
++ signalDesc.setPhysicalUnit("");
++ signalDesc.setReceiver("Vector__XXX");
++ otherDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s1");
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::SwitchAndSignal);
++ signalDesc.setStartBit(4);
++ signalDesc.setBitLength(4);
++ signalDesc.addMultiplexSignal("s0", { {1U, 1U}, {5U, 5U}, {9U, 9U} });
++ otherDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s2");
++ signalDesc.setDataFormat(QtCanBus::DataFormat::SignedInteger);
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::MultiplexedSignal);
++ signalDesc.setStartBit(4);
++ signalDesc.setBitLength(12);
++ signalDesc.addMultiplexSignal("s0", { {2U, 4U}, {6U, 8U}, {10U, 10U} });
++ otherDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s3");
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(8);
++ signalDesc.addMultiplexSignal("s1", 1U);
++ otherDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s4");
++ signalDesc.setStartBit(8);
++ signalDesc.setBitLength(8);
++ signalDesc.addMultiplexSignal("s1", { {2U, 3U}, {5U, 5U} });
++ otherDesc.addSignalDescription(signalDesc);
++
++ signalDesc.clearMultiplexSignals();
++ signalDesc.setName("s5");
++ signalDesc.setMultiplexState(QtCanBus::MultiplexState::None);
++ signalDesc.setStartBit(16);
++ signalDesc.setBitLength(8);
++ otherDesc.addSignalDescription(signalDesc);
++
++ expectedWarnings = {
++ u"Failed to find signal description in string "
++ "SG_ Singal%1 : 0|8@1+ (1,0) [0|0] \"unit\" Vector__XXX"_s,
++ u"Failed to find signal description for signal s6. Skipping string "
++ "SG_MUL_VAL_ 1236 s6 s1 2-3, 5-5;"_s,
++ u"Message description with unique id 1236 is skipped because it has invalid "
++ "multiplexing description."_s,
++ };
++
++ QList<QCanMessageDescription> descriptions { messageDesc, otherDesc };
++
++ QTest::addRow("multiple files")
++ << QStringList{ u"multiple_files_1.dbc"_s, u"multiple_files_2.dbc"_s }
++ << QCanDbcFileParser::Error::None << QString()
++ << expectedWarnings << descriptions;
++ }
++
++ {
++ const QString fileName = u"invalid_message_pos.dbc"_s;
++ const QString expectedError = u"Failed to parse file %1. Unexpected position "
++ "of BO_ section."_s.arg(m_filesDir + fileName);
++
++ QTest::addRow("multiple files and error")
++ << QStringList{ u"multiple_files_1.dbc"_s, fileName }
++ << QCanDbcFileParser::Error::Parsing << expectedError
++ << QStringList() << QList<QCanMessageDescription>();
++ }
++ }
++
++ void tst_QCanDbcFileParser::parseFile()
++ {
++ QFETCH(QStringList, fileNames);
++ QFETCH(QCanDbcFileParser::Error, expectedError);
++ QFETCH(QString, expectedErrorDescription);
++ QFETCH(QStringList, expectedWarnings);
++ QFETCH(QList<QCanMessageDescription>, expectedMessageDescriptions);
++ QFETCH_GLOBAL(bool, readFromFile);
++
++ QVERIFY(!fileNames.isEmpty());
++
++ QCanDbcFileParser parser;
++ if (readFromFile) {
++ if (fileNames.size() > 1) {
++ QStringList files;
++ for (const auto &fn : fileNames)
++ files.push_back(m_filesDir + fn);
++ parser.parse(files);
++ } else {
++ parser.parse(m_filesDir + fileNames.first());
++ }
++ } else {
++ // read the file outside of the parser
++ if (fileNames.size() > 1)
++ QSKIP("parseData() does not support multiple files");
++
++ QByteArray allData;
++ QFile f(m_filesDir + fileNames.first());
++ if (f.open(QIODevice::ReadOnly))
++ allData = f.readAll();
++ f.close();
++ if (allData.isEmpty())
++ QSKIP("No valid input file provided");
++ const QString inputData = QString::fromUtf8(allData);
++ parser.parseData(inputData);
++ }
++
++ QCOMPARE_EQ(parser.error(), expectedError);
++ if (!readFromFile && expectedErrorDescription.contains("Failed to parse file"))
++ expectedErrorDescription.replace(QRegularExpression(u"file .+dbc"_s), u"input data"_s);
++ QCOMPARE_EQ(parser.errorString(), expectedErrorDescription);
++
++ if (expectedError != QCanDbcFileParser::Error::None)
++ return; // no need to compare other data if we already had an error
++
++ // Some warnings may come in unspecified order, so sort them before
++ // comparing
++ auto parserWarnings = parser.warnings();
++ std::sort(parserWarnings.begin(), parserWarnings.end());
++ std::sort(expectedWarnings.begin(), expectedWarnings.end());
++ QCOMPARE_EQ(parserWarnings, expectedWarnings);
++
++ // parser.messageDescriptions() generates a list from a QHash, so the order
++ // is unspecified and we need to sort them before comparison.
++ auto messageDescComparator =
++ [](const QCanMessageDescription &lhs, const QCanMessageDescription &rhs) {
++ return lhs.name() < rhs.name();
++ };
++ auto messageDescriptions = parser.messageDescriptions();
++ std::sort(messageDescriptions.begin(), messageDescriptions.end(), messageDescComparator);
++ std::sort(expectedMessageDescriptions.begin(), expectedMessageDescriptions.end(),
++ messageDescComparator);
++ QVERIFY(equals(messageDescriptions, expectedMessageDescriptions));
++ }
++
++ static bool parseHelper(QCanDbcFileParser *parser, const QString &name, bool readFromFile)
++ {
++ if (readFromFile)
++ return parser->parse(name);
++
++ QString data;
++ QFile f(name);
++ if (f.open(QIODevice::ReadOnly)) {
++ data = QString::fromUtf8(f.readAll());
++ f.close();
++ }
++ return parser->parseData(data);
++ }
++
++ void tst_QCanDbcFileParser::valueDescriptions()
++ {
++ QFETCH_GLOBAL(bool, readFromFile);
++
++ QCanDbcFileParser::ValueDescriptions s0_test_value_descriptions;
++ s0_test_value_descriptions.insert(0, u"Description for the value '0x0'"_s);
++ s0_test_value_descriptions.insert(1, u"Description for the value '0x1'"_s);
++ s0_test_value_descriptions.insert(2, u"Description for the value '0x2'"_s);
++
++ QCanDbcFileParser::ValueDescriptions s1_test_value_descriptions;
++ s1_test_value_descriptions.insert(3, u"red"_s);
++ s1_test_value_descriptions.insert(4, u"green"_s);
++ s1_test_value_descriptions.insert(5, u"blue"_s);
++
++ const QCanDbcFileParser::SignalValueDescriptions test_value_descriptions {
++ qMakePair("s0", s0_test_value_descriptions), qMakePair("s1", s1_test_value_descriptions)
++ };
++
++ QCanDbcFileParser::ValueDescriptions s1_test1_value_descriptions;
++ s1_test1_value_descriptions.insert(3, u"r"_s);
++ s1_test1_value_descriptions.insert(4, u"g"_s);
++ s1_test1_value_descriptions.insert(5, u"b"_s);
++
++ QCanDbcFileParser::ValueDescriptions s2_test1_value_descriptions;
++ s2_test1_value_descriptions.insert(0, u"Value0"_s);
++ s2_test1_value_descriptions.insert(1, u"Value1"_s);
++ s2_test1_value_descriptions.insert(2, u"Value2"_s);
++ s2_test1_value_descriptions.insert(3, u"Value3"_s);
++ s2_test1_value_descriptions.insert(4, u"Value4"_s);
++
++ const QCanDbcFileParser::SignalValueDescriptions test1_value_descriptions {
++ qMakePair("s1", s1_test1_value_descriptions), qMakePair("s2", s2_test1_value_descriptions)
++ };
++
++ QCanDbcFileParser::MessageValueDescriptions expectedDescriptions;
++ expectedDescriptions.insert(QtCanBus::UniqueId{1234}, test_value_descriptions);
++ expectedDescriptions.insert(QtCanBus::UniqueId{0x18fef1fe}, test1_value_descriptions);
++
++ const QStringList expectedWarnings {
++ u"Failed to find message description for unique id 1236. Skipping string "
++ "VAL_ 1236 s2 4 \"Value4\" 3 \"Value3\" 2 \"Value2\" 1 \"Value1\" 0 \"Value0\" ;"_s,
++ u"Failed to find signal description for signal s3. Skipping string "
++ "VAL_ 2566844926 s3 4 \"Value4\" 3 \"Value3\" 2 \"Value2\" 1 \"Value1\" 0 \"Value0\" ;"_s,
++ u"Failed to parse value description from string "
++ "VAL_ 2566844926 s2 4 \"Value4\" 3 \"Value3\" 2 \"Value2\" 1 \"Value1\" 0 ;"_s
++ };
++
++ QCanDbcFileParser parser;
++ const QString fileName = u"value_descriptions.dbc"_s;
++ QVERIFY(parseHelper(&parser, m_filesDir + fileName, readFromFile));
++
++ QCOMPARE(parser.error(), QCanDbcFileParser::Error::None);
++ QCOMPARE(parser.messageValueDescriptions(), expectedDescriptions);
++ QCOMPARE(parser.warnings(), expectedWarnings);
++ }
++
++ void tst_QCanDbcFileParser::resetState()
++ {
++ QFETCH_GLOBAL(bool, readFromFile);
++
++ // Test that the state is correctly reset between parsings
++ const QString fileName = u"value_descriptions.dbc"_s;
++ QCanDbcFileParser parser;
++ QVERIFY(parseHelper(&parser, m_filesDir + fileName, readFromFile));
++
++ QCOMPARE(parser.error(), QCanDbcFileParser::Error::None);
++ QVERIFY(!parser.messageDescriptions().isEmpty());
++ QVERIFY(!parser.warnings().isEmpty());
++ QVERIFY(!parser.messageValueDescriptions().isEmpty());
++
++ // Now when we parse an invalid file, we should get an error, and all
++ // other getters should return default values
++ const QString invalidName = u"invalid_file"_s;
++ QVERIFY(!parseHelper(&parser, m_filesDir + invalidName, readFromFile));
++
++ const auto expectedError = readFromFile ? QCanDbcFileParser::Error::FileReading
++ : QCanDbcFileParser::Error::Parsing;
++ QCOMPARE(parser.error(), expectedError);
++ QVERIFY(parser.messageDescriptions().isEmpty());
++ QVERIFY(parser.warnings().isEmpty());
++ QVERIFY(parser.messageValueDescriptions().isEmpty());
++ }
++
++ QTEST_MAIN(tst_QCanDbcFileParser)
++
++ #include "tst_qcandbcfileparser.moc"