#include "dbc_decode_builder.h" #include "dbc_decoder.h" #include "dbc_parser.h" #include "dbc_tree_builder.h" #include #include #include #include namespace { const std::string fixture = R"DBC(BO_ 291 Standard: 2 SenderA SG_ Mode M : 0|8@1+ (1,0) [0|255] "" ReceiverA SG_ State m3 : 8|8@1+ (2,1) [1|511] "km/h" ReceiverA,ReceiverB BO_ 2147483939 Extended: 1 SenderB SG_ State : 7|8@0- (0.5,-3) [-67|60.5] "deg C" ReceiverB BO_ 2565866562 J1939: 0 SenderC CM_ BO_ 291 "Standard frame comment"; CM_ SG_ 291 Mode "Selector comment"; CM_ SG_ 291 State "Standard signal comment"; CM_ BO_ 2147483939 "Extended frame comment"; CM_ SG_ 2147483939 State "Extended signal comment"; VAL_ 291 Mode 3 "Selected"; VAL_ 291 State 4 "Standard four"; VAL_ 2147483939 State -2 "Extended minus two"; )DBC"; DbcParseResult ParseFixture(const std::string &text) { QTemporaryFile file; if (!file.open()) throw std::runtime_error("Cannot create DBC fixture"); if (file.write(text.data(), static_cast(text.size())) != static_cast(text.size()) || !file.flush()) throw std::runtime_error("Cannot write DBC fixture"); return DbcParser().ParseFileWithDiagnostics(file.fileName().toStdString()); } // Independent expectations ensure a parser regression cannot be hidden by // comparing builders only with the same incorrect parsed input. DbcDatabase ExpectedMetadata() { FrameInfo standard; standard.name = "Standard"; standard.canId = 291U; standard.dlc = 2U; standard.transmitter = "SenderA"; standard.comment = "Standard frame comment"; SignalInfo mode; mode.name = "Mode"; mode.multiplexing.role = MultiplexRole::Selector; mode.length = 8U; mode.maximum = 255; mode.receivers = {"ReceiverA"}; mode.comment = "Selector comment"; mode.valueDescriptions = {{std::uint64_t(3), "Selected"}}; SignalInfo state; state.name = "State"; state.multiplexing.role = MultiplexRole::Branch; state.multiplexing.value = 3U; state.startBit = 8U; state.length = 8U; state.factor = 2; state.offset = 1; state.minimum = 1; state.maximum = 511; state.unit = "km/h"; state.receivers = {"ReceiverA", "ReceiverB"}; state.comment = "Standard signal comment"; state.valueDescriptions = {{std::uint64_t(4), "Standard four"}}; standard.signals = {mode, state}; FrameInfo extended; extended.name = "Extended"; extended.canId = 291U; extended.isExtended = true; extended.hasPgn = true; extended.dlc = 1U; extended.transmitter = "SenderB"; extended.comment = "Extended frame comment"; state.multiplexing = Multiplexing(); state.startBit = 7U; state.isLittleEndian = false; state.isSigned = true; state.factor = 0.5; state.offset = -3; state.minimum = -67; state.maximum = 60.5; state.unit = "deg C"; state.receivers = {"ReceiverB"}; state.comment = "Extended signal comment"; state.valueDescriptions = {{std::int64_t(-2), "Extended minus two"}}; extended.signals = {state}; FrameInfo j1939; j1939.name = "J1939"; j1939.canId = 0x18F00442U; j1939.isExtended = true; j1939.hasPgn = true; j1939.pgn = 0xF004U; j1939.transmitter = "SenderC"; return DbcDatabase{{standard, extended, j1939}}; } template void CheckSignalMetadata(const Signal &actual, const SignalInfo &expected) { QCOMPARE(actual.name, expected.name); QCOMPARE(actual.startBit, expected.startBit); QCOMPARE(actual.length, expected.length); QCOMPARE(actual.factor, expected.factor); QCOMPARE(actual.offset, expected.offset); QCOMPARE(actual.minimum, expected.minimum); QCOMPARE(actual.maximum, expected.maximum); QCOMPARE(actual.unit, expected.unit); QVERIFY(actual.receivers == expected.receivers); QCOMPARE(actual.comment, expected.comment); QVERIFY(actual.valueDescriptions == expected.valueDescriptions); QCOMPARE(actual.multiplexing.role, expected.multiplexing.role); QCOMPARE(actual.multiplexing.value, expected.multiplexing.value); } void CheckSignal(const SignalInfo &actual, const SignalInfo &expected) { CheckSignalMetadata(actual, expected); QCOMPARE(actual.isLittleEndian, expected.isLittleEndian); QCOMPARE(actual.isSigned, expected.isSigned); } void CheckSignal(const DecodeSignal &actual, const SignalInfo &expected) { CheckSignalMetadata(actual, expected); QCOMPARE(actual.byteOrder, expected.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola); QCOMPARE(actual.valueType, expected.isSigned ? ValueType::Signed : ValueType::Unsigned); } template void CheckFrame(const Frame &actual, const FrameInfo &expected) { QCOMPARE(actual.name, expected.name); QCOMPARE(actual.canId, expected.canId); QCOMPARE(actual.isExtended, expected.isExtended); QCOMPARE(actual.dlc, expected.dlc); QCOMPARE(actual.pgn, expected.pgn); QCOMPARE(actual.hasPgn, expected.hasPgn); QCOMPARE(actual.transmitter, expected.transmitter); QCOMPARE(actual.comment, expected.comment); QCOMPARE(actual.signals.size(), expected.signals.size()); for (std::size_t i = 0; i < expected.signals.size(); ++i) CheckSignal(actual.signals[i], expected.signals[i]); } void CheckTree(const TreeNode &root, const DbcDatabase &expected) { QCOMPARE(root.GetType(), NodeType::Root); QVERIFY(root.GetParent() == nullptr); QVERIFY(root.GetFrame() == nullptr); QVERIFY(root.GetSignal() == nullptr); QCOMPARE(root.GetChildCount(), expected.frames.size()); for (std::size_t i = 0; i < expected.frames.size(); ++i) { const TreeNode *frame = root.GetChild(i); QVERIFY(frame != nullptr); QCOMPARE(frame->GetType(), NodeType::Frame); QCOMPARE(frame->GetParent(), &root); QCOMPARE(frame->GetRow(), i); QCOMPARE(frame->GetName(), expected.frames[i].name); QVERIFY(frame->GetFrame() != nullptr); QVERIFY(frame->GetSignal() == nullptr); CheckFrame(*frame->GetFrame(), expected.frames[i]); QCOMPARE(frame->GetChildCount(), expected.frames[i].signals.size()); for (std::size_t j = 0; j < frame->GetChildCount(); ++j) { const TreeNode *signal = frame->GetChild(j); QVERIFY(signal != nullptr); QCOMPARE(signal->GetType(), NodeType::Signal); QCOMPARE(signal->GetParent(), frame); QCOMPARE(signal->GetRow(), j); QCOMPARE(signal->GetName(), expected.frames[i].signals[j].name); QVERIFY(signal->GetSignal() != nullptr); QVERIFY(signal->GetFrame() == nullptr); QCOMPARE(signal->GetChildCount(), std::size_t(0)); CheckSignal(*signal->GetSignal(), expected.frames[i].signals[j]); } } QVERIFY(root.GetChild(expected.frames.size()) == nullptr); } void CheckUnknown(const DecodeDatabase &runtime, bool extended) { const FrameKey key(291U, extended); QVERIFY(runtime.FindFrame(key) == nullptr); QVERIFY(runtime.FindSignal(SignalKey(key, "State")) == nullptr); QVERIFY(DbcDecoder().FindFrame(runtime, key.canId, key.isExtended) == nullptr); RawCanFrame raw; raw.canId = key.canId; raw.isExtended = extended; raw.data = {3U, 4U}; const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw); QVERIFY(!decoded.valid); QCOMPARE(decoded.status, FrameDecodeStatus::UnknownFrame); QVERIFY(decoded.definition == nullptr); QVERIFY(decoded.signals.empty()); QVERIFY(decoded.key == key); } }// namespace class PipelineTest : public QObject { Q_OBJECT private Q_SLOTS: void metadataLookupDecodeAndTree(); void emptyDatabase_data(); void emptyDatabase(); void databaseReplacement(); }; void PipelineTest::metadataLookupDecodeAndTree() { const DbcParseResult parsed = ParseFixture(fixture); QVERIFY(!parsed.HasErrors()); QVERIFY(parsed.diagnostics.empty()); const DbcDatabase expected = ExpectedMetadata(); QCOMPARE(parsed.database.frames.size(), expected.frames.size()); const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database); QCOMPARE(runtime.GetFrames().size(), expected.frames.size()); const std::unique_ptr tree = DbcTreeBuilder().Build(parsed.database); QVERIFY(tree != nullptr); CheckTree(*tree, expected); for (std::size_t i = 0; i < expected.frames.size(); ++i) { CheckFrame(parsed.database.frames[i], expected.frames[i]); CheckFrame(runtime.GetFrames()[i], expected.frames[i]); const FrameKey key(expected.frames[i].canId, expected.frames[i].isExtended); const std::shared_ptr found = runtime.FindFrame(key); QVERIFY(found != nullptr); QCOMPARE(found.get(), &runtime.GetFrames()[i]); QCOMPARE(DbcDecoder().FindFrame(runtime, key.canId, key.isExtended).get(), found.get()); for (std::size_t j = 0; j < found->signals.size(); ++j) { const std::shared_ptr signal = runtime.FindSignal(SignalKey(key, found->signals[j].name)); QVERIFY(signal != nullptr); QCOMPARE(signal.get(), &found->signals[j]); } } QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, true), "Mode")) == nullptr); QVERIFY(runtime.FindFrame(FrameKey(292U, false)) == nullptr); RawCanFrame raw; raw.canId = 291U; raw.data = {3U, 4U}; const DecodedFrameValue standard = DbcDecoder().Decode(runtime, raw); QVERIFY(standard.valid); QCOMPARE(standard.status, FrameDecodeStatus::Valid); QVERIFY(standard.key == FrameKey(291U, false)); QCOMPARE(standard.definition.get(), runtime.FindFrame(standard.key).get()); QCOMPARE(standard.signals.size(), std::size_t(2)); for (std::size_t i = 0; i < standard.signals.size(); ++i) { const DecodedSignalValue &signal = standard.signals[i]; QVERIFY(signal.valid); QCOMPARE(signal.status, SignalDecodeStatus::Valid); QVERIFY(signal.key == SignalKey(standard.key, i == 0 ? "Mode" : "State")); QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get()); QCOMPARE(std::get(signal.rawValue), i == 0 ? 3U : 4U); QCOMPARE(signal.physicalValue, i == 0 ? 3.0 : 9.0); QVERIFY(signal.GetValueDescription() != nullptr); QCOMPARE(*signal.GetValueDescription(), std::string(i == 0 ? "Selected" : "Standard four")); } raw.isExtended = true; raw.data = {0xFEU}; const DecodedFrameValue extended = DbcDecoder().Decode(runtime, raw); QVERIFY(extended.valid); QCOMPARE(extended.status, FrameDecodeStatus::Valid); QVERIFY(extended.key == FrameKey(291U, true)); QCOMPARE(extended.definition.get(), runtime.FindFrame(extended.key).get()); QCOMPARE(extended.signals.size(), std::size_t(1)); const DecodedSignalValue &signal = extended.signals[0]; QVERIFY(signal.valid); QCOMPARE(signal.status, SignalDecodeStatus::Valid); QVERIFY(signal.key == SignalKey(extended.key, "State")); QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get()); QCOMPARE(std::get(signal.rawValue), std::int64_t(-2)); QCOMPARE(signal.physicalValue, -4.0); QVERIFY(signal.GetValueDescription() != nullptr); QCOMPARE(*signal.GetValueDescription(), std::string("Extended minus two")); raw.canId = 0x18F00442U; raw.data.clear(); const DecodedFrameValue j1939 = DbcDecoder().Decode(runtime, raw); QVERIFY(j1939.valid); QCOMPARE(j1939.status, FrameDecodeStatus::Valid); QVERIFY(j1939.key == FrameKey(0x18F00442U, true)); QCOMPARE(j1939.definition.get(), runtime.FindFrame(j1939.key).get()); QVERIFY(j1939.signals.empty()); } void PipelineTest::emptyDatabase_data() { QTest::addColumn("text"); QTest::newRow("empty-file") << QString(); QTest::newRow("headers-only") << QString::fromUtf8("VERSION \"empty\"\nNS_ :\nBS_:\nBU_: Sender\n"); } void PipelineTest::emptyDatabase() { QFETCH(QString, text); const DbcParseResult parsed = ParseFixture(text.toStdString()); QVERIFY(!parsed.HasErrors()); QVERIFY(parsed.diagnostics.empty()); QVERIFY(parsed.database.frames.empty()); const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database); QVERIFY(runtime.GetFrames().empty()); CheckUnknown(runtime, false); CheckUnknown(runtime, true); const std::unique_ptr tree = DbcTreeBuilder().Build(parsed.database); QVERIFY(tree != nullptr); CheckTree(*tree, DbcDatabase()); } void PipelineTest::databaseReplacement() { DbcParseResult parsed = ParseFixture(fixture); QVERIFY(!parsed.HasErrors()); DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database); std::unique_ptr tree = DbcTreeBuilder().Build(parsed.database); RawCanFrame raw; raw.canId = 291U; raw.data = {3U, 4U}; const DecodedFrameValue saved = DbcDecoder().Decode(runtime, raw); QVERIFY(saved.valid); QCOMPARE(saved.signals.size(), std::size_t(2)); QVERIFY(tree != nullptr); std::unique_ptr savedTree = std::move(tree); parsed = ParseFixture(R"DBC(BO_ 291 Replacement: 1 NewSender SG_ NewState : 0|8@1+ (3,-1) [-1|764] "V" NewReceiver CM_ BO_ 291 "Replacement frame"; CM_ SG_ 291 NewState "Replacement signal"; VAL_ 291 NewState 4 "New four"; )DBC"); QVERIFY(!parsed.HasErrors()); QVERIFY(parsed.diagnostics.empty()); FrameInfo replacement; replacement.name = "Replacement"; replacement.canId = 291U; replacement.dlc = 1U; replacement.transmitter = "NewSender"; replacement.comment = "Replacement frame"; SignalInfo state; state.name = "NewState"; state.length = 8U; state.factor = 3; state.offset = -1; state.minimum = -1; state.maximum = 764; state.unit = "V"; state.receivers = {"NewReceiver"}; state.comment = "Replacement signal"; state.valueDescriptions = {{std::uint64_t(4), "New four"}}; replacement.signals = {state}; const DbcDatabase expected{{replacement}}; QCOMPARE(parsed.database.frames.size(), std::size_t(1)); CheckFrame(parsed.database.frames[0], replacement); runtime = DbcDecodeBuilder().Build(parsed.database); tree = DbcTreeBuilder().Build(parsed.database); QVERIFY(tree != nullptr); QCOMPARE(runtime.GetFrames().size(), std::size_t(1)); CheckFrame(runtime.GetFrames()[0], replacement); CheckTree(*tree, expected); CheckUnknown(runtime, true); QVERIFY(runtime.FindFrame(FrameKey(0x18F00442U, true)) == nullptr); QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "State")) == nullptr); QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "Mode")) == nullptr); raw.data = {4U}; const DecodedFrameValue current = DbcDecoder().Decode(runtime, raw); QVERIFY(current.valid); QCOMPARE(current.status, FrameDecodeStatus::Valid); QCOMPARE(current.signals.size(), std::size_t(1)); QVERIFY(current.signals[0].valid); QVERIFY(current.signals[0].key == SignalKey(FrameKey(291U, false), "NewState")); QCOMPARE(current.signals[0].definition.get(), runtime.FindSignal(current.signals[0].key).get()); QCOMPARE(std::get(current.signals[0].rawValue), std::uint64_t(4)); QCOMPARE(current.signals[0].physicalValue, 11.0); QVERIFY(current.signals[0].GetValueDescription() != nullptr); QCOMPARE(*current.signals[0].GetValueDescription(), std::string("New four")); parsed = ParseFixture(""); QVERIFY(!parsed.HasErrors()); QVERIFY(parsed.diagnostics.empty()); runtime = DbcDecodeBuilder().Build(parsed.database); tree = DbcTreeBuilder().Build(parsed.database); QVERIFY(tree != nullptr); QVERIFY(runtime.GetFrames().empty()); CheckUnknown(runtime, false); CheckUnknown(runtime, true); CheckTree(*tree, DbcDatabase()); QVERIFY(runtime.FindSignal(current.signals[0].key) == nullptr); CheckFrame(*current.definition, replacement); CheckFrame(*saved.definition, ExpectedMetadata().frames[0]); CheckTree(*savedTree, ExpectedMetadata()); QCOMPARE(saved.signals[1].physicalValue, 9.0); QVERIFY(saved.signals[1].GetValueDescription() != nullptr); QCOMPARE(*saved.signals[1].GetValueDescription(), std::string("Standard four")); } int RunPipelineTests(int argc, char **argv) { PipelineTest test; return QTest::qExec(&test, argc, argv); } #include "dbc_pipeline_test.moc"