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();
156 signal.
multiplexing.role =
static_cast<MultiplexRole
>(99);
170 frame.signals.push_back(other);
177 other.
startBit = kind == 24 ? 0U : 8U;
179 frame.signals.push_back(other);
181 other.
name =
"Third";
182 frame.signals.push_back(other);
186 frame.signals.push_back(other);
191 frame.signals.push_back(other);
194 frame.
canId = std::numeric_limits<std::uint32_t>::max();
197 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase(frames));
200 if (kind != 14 && kind != 15)
201 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions(frames)));
204void DbcValidationTest::numericParameters_data() {
205 QTest::addColumn<int>(
"field");
206 QTest::addColumn<double>(
"value");
207 const double values[] = {std::numeric_limits<double>::quiet_NaN(),
208 std::numeric_limits<double>::infinity(),
209 -std::numeric_limits<double>::infinity()};
210 for (
int field = 0; field < 4; ++field) {
211 for (
int index = 0; index < 3; ++index) {
212 const QByteArray name = QByteArray::number(field) +
"-" + QByteArray::number(index);
213 QTest::newRow(name.constData()) << field << values[index];
218void DbcValidationTest::numericParameters() {
220 QFETCH(
double, value);
237 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase({frame}));
238 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(ParsedDefinitions({frame})));
241void DbcValidationTest::validBoundaries() {
242 for (
ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
243 for (std::uint8_t dlc: {std::uint8_t(8U), std::uint8_t(64U), std::uint8_t(255U)}) {
245 standard.
canId = 0x7FFU;
249 signal.
startBit = (std::uint32_t(dlc) - 8U) * 8U + (order == ByteOrder::Intel ? 0U : 7U);
252 signal.
offset = std::numeric_limits<double>::max();
253 signal.
minimum = -std::numeric_limits<double>::max();
254 signal.
maximum = std::numeric_limits<double>::max();
258 maximum.
canId = 0x1FFFFFFFU;
259 const std::vector<DecodeFrame> frames = {standard, extended, maximum};
263 QVERIFY(direct.FindFrame(frame.
GetKey()));
274void DbcValidationTest::layoutBoundaries() {
276 for (
const std::uint8_t dlc: {std::uint8_t(0U), std::uint8_t(1U), std::uint8_t(8U),
277 std::uint8_t(64U), std::uint8_t(255U)}) {
278 const std::uint64_t payloadBits = std::uint64_t(dlc) * 8U;
279 for (
const bool littleEndian: {
true,
false}) {
280 for (std::uint32_t start = 0U; start < payloadBits + 8U; ++start) {
281 std::uint64_t bit = start;
283 QVERIFY(!IsValidSignalLayout(start, 0U, littleEndian, dlc));
284 for (std::uint32_t length = 1U; length <= 64U; ++length) {
285 fits = fits && bit < payloadBits;
286 QCOMPARE(IsValidSignalLayout(start, length, littleEndian, dlc), fits);
290 bit = bit % 8U == 0U ? bit + 15U : bit - 1U;
292 QVERIFY(!IsValidSignalLayout(start, 65U, littleEndian, dlc));
294 const std::uint32_t maximum = std::numeric_limits<std::uint32_t>::max();
295 QVERIFY(!IsValidSignalLayout(maximum, 1U, littleEndian, dlc));
296 QVERIFY(!IsValidSignalLayout(maximum, maximum, littleEndian, dlc));
297 QVERIFY(!IsValidSignalLayout(0U, maximum, littleEndian, dlc));
302void DbcValidationTest::builderSnapshot() {
303 DbcDatabase source = ParsedDefinitions({ValidFrame()});
304 source.
frames[0].signals[0].valueDescriptions.emplace(std::uint64_t(1U),
"One");
307 source.
frames[0].canId = 0x800U;
308 source.
frames[0].signals[0].name =
"Changed";
309 source.
frames[0].signals[0].valueDescriptions.begin()->second =
"Changed";
311 const std::shared_ptr<const DecodeSignal> signal = database.
FindSignal(
SignalKey(key,
"Value"));
313 QCOMPARE(signal->valueDescriptions.begin()->second, std::string(
"One"));
318void DbcValidationTest::multiplexingBoundaries() {
325 first.
name =
"First";
331 second.
name =
"Second";
335 frame.signals.push_back(first);
336 frame.signals.push_back(second);
341 raw.
data.assign(9U, 255U);
344 QCOMPARE(result.signals[1].
status, SignalDecodeStatus::Valid);
345 QCOMPARE(result.signals[2].
status, SignalDecodeStatus::Inactive);
349void DbcValidationTest::independentSnapshot() {
350 std::vector<DecodeFrame> frames = {ValidFrame()};
358 signalAlias.
name =
"Changed";
359 signalAlias.
startBit = std::numeric_limits<std::uint32_t>::max();
360 descriptionAlias =
"Changed";
361 const std::shared_ptr<const DecodeFrame> saved = database.
FindFrame(key);
363 QCOMPARE(saved->canId, key.
canId);
365 const std::shared_ptr<const DecodeSignal> signal = database.
FindSignal(
SignalKey(key,
"Value"));
367 QCOMPARE(signal->startBit, 0U);
368 QCOMPARE(signal->valueDescriptions.begin()->second, std::string(
"One"));
371void DbcValidationTest::failedReplacement() {
374 const std::shared_ptr<const DecodeFrame> original = database.
FindFrame(valid.
GetKey());
376 invalid.
canId = 0x800U;
377 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database =
DecodeDatabase({invalid}));
380 invalid.signals[0].factor = std::numeric_limits<double>::infinity();
381 QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
384 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database =
DecodeDatabase({valid, valid}));
387 invalid.signals.push_back(invalid.signals[0]);
388 QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
392 QCOMPARE(database.
GetFrames().size(), std::size_t(1U));
399int RunValidationTests(
int argc,
char **argv) {
400 DbcValidationTest tests;
401 return QTest::qExec(&tests, argc, argv);
404#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