15 signal.
name =
"Value";
17 frame.signals.push_back(signal);
21 DbcDatabase ParsedDefinitions(
const std::vector<DecodeFrame> &frames) {
39 copy.multiplexing = signal.multiplexing;
40 target.signals.push_back(copy);
42 database.
frames.push_back(target);
48class DbcValidationTest :
public QObject {
51 void invalidMetadata_data();
52 void invalidMetadata();
53 void numericParameters_data();
54 void numericParameters();
55 void validBoundaries();
56 void multiplexingBoundaries();
57 void independentSnapshot();
58 void failedReplacement();
61void DbcValidationTest::invalidMetadata_data() {
62 QTest::addColumn<int>(
"kind");
63 QTest::newRow(
"standard-id") << 0;
64 QTest::newRow(
"extended-id") << 1;
65 QTest::newRow(
"encoded-id") << 2;
66 QTest::newRow(
"duplicate-frame") << 3;
67 QTest::newRow(
"duplicate-signal") << 4;
68 QTest::newRow(
"zero-length") << 5;
69 QTest::newRow(
"65-bits") << 6;
70 QTest::newRow(
"length-overflow") << 7;
71 QTest::newRow(
"intel-start-overflow") << 8;
72 QTest::newRow(
"motorola-start-overflow") << 9;
73 QTest::newRow(
"intel-outside-payload") << 10;
74 QTest::newRow(
"motorola-outside-payload") << 11;
75 QTest::newRow(
"zero-payload") << 12;
76 QTest::newRow(
"inverted-range") << 13;
77 QTest::newRow(
"byte-order") << 14;
78 QTest::newRow(
"value-type") << 15;
79 QTest::newRow(
"multiplex-role") << 16;
80 QTest::newRow(
"missing-selector") << 17;
81 QTest::newRow(
"signed-selector") << 18;
82 QTest::newRow(
"two-selectors") << 19;
83 QTest::newRow(
"branch-range") << 20;
84 QTest::newRow(
"unconditional-overlap") << 21;
85 QTest::newRow(
"mixed-order-overlap") << 22;
86 QTest::newRow(
"same-branch-overlap") << 23;
87 QTest::newRow(
"selector-branch-overlap") << 24;
88 QTest::newRow(
"maximum-can-id") << 25;
91void DbcValidationTest::invalidMetadata() {
93 std::vector<DecodeFrame> frames = {ValidFrame()};
100 frame.
canId = 0x800U;
104 frame.
canId = 0x20000000U;
108 frame.
canId = 0x80000123U;
111 frames.push_back(frame);
114 frame.signals.push_back(signal);
123 signal.
length = std::numeric_limits<std::uint32_t>::max();
126 signal.
startBit = std::numeric_limits<std::uint32_t>::max();
130 signal.
startBit = std::numeric_limits<std::uint32_t>::max();
153 signal.multiplexing.role =
static_cast<MultiplexRole
>(99);
156 signal.multiplexing.role = MultiplexRole::Branch;
159 signal.multiplexing.role = MultiplexRole::Selector;
163 signal.multiplexing.role = MultiplexRole::Selector;
164 other.multiplexing.role = MultiplexRole::Selector;
166 frame.signals.push_back(other);
171 signal.multiplexing.role = MultiplexRole::Selector;
172 other.multiplexing.role = MultiplexRole::Branch;
173 other.
startBit = kind == 24 ? 0U : 8U;
174 other.multiplexing.value = kind == 20 ? 256U : 1U;
175 frame.signals.push_back(other);
177 other.
name =
"Third";
178 frame.signals.push_back(other);
182 frame.signals.push_back(other);
187 frame.signals.push_back(other);
190 frame.
canId = std::numeric_limits<std::uint32_t>::max();
193 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase(frames));
196 if (kind != 14 && kind != 15)
197 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions(frames)));
200void DbcValidationTest::numericParameters_data() {
201 QTest::addColumn<int>(
"field");
202 QTest::addColumn<double>(
"value");
203 const double values[] = {std::numeric_limits<double>::quiet_NaN(),
204 std::numeric_limits<double>::infinity(),
205 -std::numeric_limits<double>::infinity()};
206 for (
int field = 0; field < 4; ++field) {
207 for (
int index = 0; index < 3; ++index) {
208 const QByteArray name = QByteArray::number(field) +
"-" + QByteArray::number(index);
209 QTest::newRow(name.constData()) << field << values[index];
214void DbcValidationTest::numericParameters() {
216 QFETCH(
double, value);
233 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase({frame}));
234 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions({frame})));
237void DbcValidationTest::validBoundaries() {
238 for (
ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
239 for (std::uint8_t dlc: {std::uint8_t(8U), std::uint8_t(64U), std::uint8_t(255U)}) {
241 standard.
canId = 0x7FFU;
245 signal.
startBit = (std::uint32_t(dlc) - 8U) * 8U + (order == ByteOrder::Intel ? 0U : 7U);
248 signal.
offset = std::numeric_limits<double>::max();
249 signal.
minimum = -std::numeric_limits<double>::max();
250 signal.
maximum = std::numeric_limits<double>::max();
254 maximum.
canId = 0x1FFFFFFFU;
255 const std::vector<DecodeFrame> frames = {standard, extended, maximum};
259 QVERIFY(direct.FindFrame(frame.GetKey()));
260 QVERIFY(built.FindSignal(
SignalKey(frame.GetKey(),
"Value")));
270void DbcValidationTest::multiplexingBoundaries() {
275 selector.multiplexing.role = MultiplexRole::Selector;
277 first.
name =
"First";
280 first.multiplexing.role = MultiplexRole::Branch;
281 first.multiplexing.value = std::numeric_limits<std::uint64_t>::max();
283 second.
name =
"Second";
286 second.multiplexing.value = 0U;
287 frame.signals.push_back(first);
288 frame.signals.push_back(second);
293 raw.
data.assign(9U, 255U);
296 QCOMPARE(result.signals[1].
status, SignalDecodeStatus::Valid);
297 QCOMPARE(result.signals[2].
status, SignalDecodeStatus::Inactive);
301void DbcValidationTest::independentSnapshot() {
302 std::vector<DecodeFrame> frames = {ValidFrame()};
307 const FrameKey key = alias.GetKey();
310 signalAlias.
name =
"Changed";
311 signalAlias.
startBit = std::numeric_limits<std::uint32_t>::max();
312 descriptionAlias =
"Changed";
313 const std::shared_ptr<const DecodeFrame> saved = database.FindFrame(key);
315 QCOMPARE(saved->canId, key.canId);
316 QVERIFY(!database.FindFrame(
FrameKey(456U,
false)));
317 const std::shared_ptr<const DecodeSignal> signal = database.FindSignal(
SignalKey(key,
"Value"));
319 QCOMPARE(signal->startBit, 0U);
320 QCOMPARE(signal->valueDescriptions.begin()->second, std::string(
"One"));
323void DbcValidationTest::failedReplacement() {
326 const std::shared_ptr<const DecodeFrame> original = database.FindFrame(valid.GetKey());
328 invalid.
canId = 0x800U;
329 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database =
DecodeDatabase({invalid}));
330 QCOMPARE(database.FindFrame(valid.GetKey()).get(), original.get());
332 invalid.signals[0].factor = std::numeric_limits<double>::infinity();
333 QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
335 QCOMPARE(database.FindFrame(valid.GetKey()).get(), original.get());
336 QCOMPARE(database.GetFrames().size(), std::size_t(1U));
338 QVERIFY(database.GetFrames().empty());
339 QVERIFY(!database.FindFrame(valid.GetKey()));
343int RunValidationTests(
int argc,
char **argv) {
344 DbcValidationTest tests;
345 return QTest::qExec(&tests, argc, argv);
348#include "dbc_validation_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
Runtime CAN decoder using prebuilt decode database.
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
ByteOrder
Signal byte order used for runtime decoding.
ValueType
Signal numeric type.
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Runtime-ready frame definition.
Runtime-ready signal definition.
ValueDescriptions valueDescriptions
Describes one CAN frame from a DBC file.
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
std::vector< std::uint8_t > data
Describes one signal inside a DBC frame.