4#include "signal_layout.h"
16 signal.
name =
"Value";
18 frame.signals.push_back(signal);
22 DbcDatabase ParsedDefinitions(
const std::vector<DecodeFrame> &frames) {
41 target.signals.push_back(copy);
43 database.
frames.push_back(target);
49class DbcValidationTest :
public QObject {
52 void invalidMetadata_data();
53 void invalidMetadata();
54 void numericParameters_data();
55 void numericParameters();
56 void validBoundaries();
57 void layoutBoundaries();
58 void builderSnapshot();
59 void multiplexingBoundaries();
60 void independentSnapshot();
61 void failedReplacement();
64void DbcValidationTest::invalidMetadata_data() {
65 QTest::addColumn<int>(
"kind");
66 QTest::newRow(
"standard-id") << 0;
67 QTest::newRow(
"extended-id") << 1;
68 QTest::newRow(
"encoded-id") << 2;
69 QTest::newRow(
"duplicate-frame") << 3;
70 QTest::newRow(
"duplicate-signal") << 4;
71 QTest::newRow(
"zero-length") << 5;
72 QTest::newRow(
"65-bits") << 6;
73 QTest::newRow(
"length-overflow") << 7;
74 QTest::newRow(
"intel-start-overflow") << 8;
75 QTest::newRow(
"motorola-start-overflow") << 9;
76 QTest::newRow(
"intel-outside-payload") << 10;
77 QTest::newRow(
"motorola-outside-payload") << 11;
78 QTest::newRow(
"zero-payload") << 12;
79 QTest::newRow(
"inverted-range") << 13;
80 QTest::newRow(
"byte-order") << 14;
81 QTest::newRow(
"value-type") << 15;
82 QTest::newRow(
"multiplex-role") << 16;
83 QTest::newRow(
"missing-selector") << 17;
84 QTest::newRow(
"signed-selector") << 18;
85 QTest::newRow(
"two-selectors") << 19;
86 QTest::newRow(
"branch-range") << 20;
87 QTest::newRow(
"unconditional-overlap") << 21;
88 QTest::newRow(
"mixed-order-overlap") << 22;
89 QTest::newRow(
"same-branch-overlap") << 23;
90 QTest::newRow(
"selector-branch-overlap") << 24;
91 QTest::newRow(
"maximum-can-id") << 25;
94void DbcValidationTest::invalidMetadata() {
96 std::vector<DecodeFrame> frames = {ValidFrame()};
100 other.
name =
"Other";
103 frame.
canId = 0x800U;
107 frame.
canId = 0x20000000U;
111 frame.
canId = 0x80000123U;
114 frames.push_back(frame);
117 frame.signals.push_back(signal);
126 signal.
length = std::numeric_limits<std::uint32_t>::max();
129 signal.
startBit = std::numeric_limits<std::uint32_t>::max();
133 signal.
startBit = std::numeric_limits<std::uint32_t>::max();
169 frame.signals.push_back(other);
176 other.
startBit = kind == 24 ? 0U : 8U;
178 frame.signals.push_back(other);
180 other.
name =
"Third";
181 frame.signals.push_back(other);
185 frame.signals.push_back(other);
190 frame.signals.push_back(other);
193 frame.
canId = std::numeric_limits<std::uint32_t>::max();
196 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase(frames));
199 if (kind != 14 && kind != 15)
200 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions(frames)));
203void DbcValidationTest::numericParameters_data() {
204 QTest::addColumn<int>(
"field");
205 QTest::addColumn<double>(
"value");
206 const double values[] = {std::numeric_limits<double>::quiet_NaN(),
207 std::numeric_limits<double>::infinity(),
208 -std::numeric_limits<double>::infinity()};
209 for (
int field = 0; field < 4; ++field) {
210 for (
int index = 0; index < 3; ++index) {
211 const QByteArray name = QByteArray::number(field) +
"-" + QByteArray::number(index);
212 QTest::newRow(name.constData()) << field << values[index];
217void DbcValidationTest::numericParameters() {
219 QFETCH(
double, value);
236 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase({frame}));
237 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions({frame})));
240void DbcValidationTest::validBoundaries() {
241 for (
ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
242 for (std::uint8_t dlc: {std::uint8_t(8U), std::uint8_t(64U), std::uint8_t(255U)}) {
244 standard.
canId = 0x7FFU;
248 signal.
startBit = (std::uint32_t(dlc) - 8U) * 8U + (order == ByteOrder::Intel ? 0U : 7U);
251 signal.
offset = std::numeric_limits<double>::max();
252 signal.
minimum = -std::numeric_limits<double>::max();
253 signal.
maximum = std::numeric_limits<double>::max();
257 maximum.
canId = 0x1FFFFFFFU;
258 const std::vector<DecodeFrame> frames = {standard, extended, maximum};
262 QVERIFY(direct.FindFrame(frame.
GetKey()));
273void DbcValidationTest::layoutBoundaries() {
275 for (
const std::uint8_t dlc: {std::uint8_t(0U), std::uint8_t(1U), std::uint8_t(8U),
276 std::uint8_t(64U), std::uint8_t(255U)}) {
277 const std::uint64_t payloadBits = std::uint64_t(dlc) * 8U;
278 for (
const bool littleEndian: {
true,
false}) {
279 for (std::uint32_t start = 0U; start < payloadBits + 8U; ++start) {
280 std::uint64_t bit = start;
282 QVERIFY(!IsValidSignalLayout(start, 0U, littleEndian, dlc));
283 for (std::uint32_t length = 1U; length <= 64U; ++length) {
284 fits = fits && bit < payloadBits;
285 QCOMPARE(IsValidSignalLayout(start, length, littleEndian, dlc), fits);
289 bit = bit % 8U == 0U ? bit + 15U : bit - 1U;
291 QVERIFY(!IsValidSignalLayout(start, 65U, littleEndian, dlc));
293 const std::uint32_t maximum = std::numeric_limits<std::uint32_t>::max();
294 QVERIFY(!IsValidSignalLayout(maximum, 1U, littleEndian, dlc));
295 QVERIFY(!IsValidSignalLayout(maximum, maximum, littleEndian, dlc));
296 QVERIFY(!IsValidSignalLayout(0U, maximum, littleEndian, dlc));
301void DbcValidationTest::builderSnapshot() {
302 DbcDatabase source = ParsedDefinitions({ValidFrame()});
303 source.
frames[0].signals[0].valueDescriptions.emplace(std::uint64_t(1U),
"One");
306 source.
frames[0].canId = 0x800U;
307 source.
frames[0].signals[0].name =
"Changed";
308 source.
frames[0].signals[0].valueDescriptions.begin()->second =
"Changed";
310 const std::shared_ptr<const DecodeSignal> signal = database.
FindSignal(
SignalKey(key,
"Value"));
312 QCOMPARE(signal->valueDescriptions.begin()->second, std::string(
"One"));
317void DbcValidationTest::multiplexingBoundaries() {
324 first.
name =
"First";
330 second.
name =
"Second";
334 frame.signals.push_back(first);
335 frame.signals.push_back(second);
340 raw.
data.assign(9U, 255U);
343 QCOMPARE(result.signals[1].
status, SignalDecodeStatus::Valid);
344 QCOMPARE(result.signals[2].
status, SignalDecodeStatus::Inactive);
348void DbcValidationTest::independentSnapshot() {
349 std::vector<DecodeFrame> frames = {ValidFrame()};
357 signalAlias.
name =
"Changed";
358 signalAlias.
startBit = std::numeric_limits<std::uint32_t>::max();
359 descriptionAlias =
"Changed";
360 const std::shared_ptr<const DecodeFrame> saved = database.
FindFrame(key);
362 QCOMPARE(saved->canId, key.
canId);
364 const std::shared_ptr<const DecodeSignal> signal = database.
FindSignal(
SignalKey(key,
"Value"));
366 QCOMPARE(signal->startBit, 0U);
367 QCOMPARE(signal->valueDescriptions.begin()->second, std::string(
"One"));
370void DbcValidationTest::failedReplacement() {
373 const std::shared_ptr<const DecodeFrame> original = database.
FindFrame(valid.
GetKey());
375 invalid.
canId = 0x800U;
376 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database =
DecodeDatabase({invalid}));
379 invalid.signals[0].factor = std::numeric_limits<double>::infinity();
380 QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
383 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database =
DecodeDatabase({valid, valid}));
386 invalid.signals.push_back(invalid.signals[0]);
387 QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
391 QCOMPARE(database.
GetFrames().size(), std::size_t(1U));
398int RunValidationTests(
int argc,
char **argv) {
399 DbcValidationTest tests;
400 return QTest::qExec(&tests, argc, argv);
403#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...
std::shared_ptr< const DecodeSignal > FindSignal(const SignalKey &key) const
Return an owning signal handle, or an empty handle for a missing key.
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
const std::vector< DecodeFrame > & GetFrames() const
Runtime decode database builder.
CAN frame decoding API and result types.
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.
FrameKey GetKey() const
Return the stable normalized frame identifier.
Runtime-ready signal definition.
ValueDescriptions valueDescriptions
Multiplexing multiplexing
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.
Multiplexing multiplexing