Closes #15
This commit is contained in:
1 parent
0708316a37
commit
7022c84941
69 files changed
+2696
-1912
No files matched your search
+6
-1
@@ -50,7 +50,12 @@ Coverage includes Motorola bit numbering within and across bytes, unaligned
|
||||
signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary values
|
||||
(including `INT64_MIN`, `INT64_MAX`, and `UINT64_MAX`), numeric output, invalid lengths, insufficient
|
||||
payloads, and Intel regression cases. Frame validity means the definition was
|
||||
found; insufficient payloads are checked through signal validity.
|
||||
found; frame status reports unknown frames, short payloads, and signal errors.
|
||||
Tests exercise empty/short/exact/long payloads, ignored trailing bytes, signals
|
||||
outside declared DLC, invalid enum metadata and overflowing start bits, and
|
||||
preservation of successful signals. Physical conversion checks cover NaN/Inf
|
||||
parameters, multiplication/addition overflow, finite limits, signed/unsigned raw
|
||||
retention, and raw selector activation after physical conversion failure.
|
||||
|
||||
## Parser coverage
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace {
|
||||
DecodeDatabase SingleSignal(const DecodeSignal &signal) {
|
||||
DecodeFrame frame;
|
||||
frame.canId = 17U;
|
||||
frame.dlc = 16U;
|
||||
frame.signals.push_back(signal);
|
||||
return DecodeDatabase({frame});
|
||||
}
|
||||
@@ -212,7 +213,7 @@ void CoverageTest::invalidSignalStatus_data() {
|
||||
QTest::newRow((prefix + "-65").constData()) << order << start << 65U << 9U << int(SignalDecodeStatus::InvalidDefinition);
|
||||
QTest::newRow((prefix + "-empty").constData()) << order << start << 8U << 0U << int(SignalDecodeStatus::InsufficientPayload);
|
||||
QTest::newRow((prefix + "-short").constData()) << order << start << 16U << 1U << int(SignalDecodeStatus::InsufficientPayload);
|
||||
QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits<unsigned>::max() << 8U << 1U << int(SignalDecodeStatus::InsufficientPayload);
|
||||
QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits<unsigned>::max() << 8U << 1U << int(SignalDecodeStatus::InvalidDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+234
-1
@@ -1,6 +1,7 @@
|
||||
#include "dbc_decoder.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
@@ -26,6 +27,14 @@ private Q_SLOTS:
|
||||
void decode_data();
|
||||
void decode();
|
||||
void boundaries();
|
||||
void payloadPolicy_data();
|
||||
void payloadPolicy();
|
||||
void invalidDefinitions_data();
|
||||
void invalidDefinitions();
|
||||
void physicalConversion_data();
|
||||
void physicalConversion();
|
||||
void selectorConversionFailure();
|
||||
void zeroDlc();
|
||||
};
|
||||
|
||||
void DbcDecoderTest::decode_data() {
|
||||
@@ -66,7 +75,7 @@ void DbcDecoderTest::decode() {
|
||||
|
||||
DecodeFrame definition;
|
||||
definition.canId = 0x123U;
|
||||
definition.dlc = static_cast<std::uint8_t>(test.data.size());
|
||||
definition.dlc = 8U;
|
||||
definition.signals.push_back(signal);
|
||||
const DecodeDatabase database({definition});
|
||||
|
||||
@@ -103,6 +112,7 @@ void DbcDecoderTest::boundaries() {
|
||||
std::uint64_t(0x8000000000000001ULL),
|
||||
std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
|
||||
DecodeFrame definition;
|
||||
definition.dlc = 8U;
|
||||
DecodeSignal signal;
|
||||
signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
|
||||
signal.length = 64U;
|
||||
@@ -150,6 +160,229 @@ void DbcDecoderTest::boundaries() {
|
||||
}
|
||||
}
|
||||
|
||||
void DbcDecoderTest::payloadPolicy_data() {
|
||||
QTest::addColumn<unsigned>("bytes");
|
||||
QTest::addColumn<FrameDecodeStatus>("status");
|
||||
QTest::newRow("empty") << 0U << FrameDecodeStatus::InsufficientPayload;
|
||||
QTest::newRow("partial") << 1U << FrameDecodeStatus::InsufficientPayload;
|
||||
QTest::newRow("short-with-all-signals") << 2U << FrameDecodeStatus::InsufficientPayload;
|
||||
QTest::newRow("exact") << 3U << FrameDecodeStatus::Valid;
|
||||
QTest::newRow("long") << 4U << FrameDecodeStatus::Valid;
|
||||
}
|
||||
|
||||
void DbcDecoderTest::payloadPolicy() {
|
||||
QFETCH(unsigned, bytes);
|
||||
QFETCH(FrameDecodeStatus, status);
|
||||
for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
|
||||
DecodeFrame definition;
|
||||
definition.canId = 17U;
|
||||
definition.dlc = 3U;
|
||||
DecodeSignal signal;
|
||||
signal.name = "First";
|
||||
signal.length = 8U;
|
||||
signal.byteOrder = order;
|
||||
signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
|
||||
definition.signals.push_back(signal);
|
||||
signal.name = "Second";
|
||||
signal.startBit += 8U;
|
||||
definition.signals.push_back(signal);
|
||||
const DecodeDatabase database({definition});
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
raw.data.assign(bytes, 42U);
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
|
||||
QVERIFY(result.valid);
|
||||
QCOMPARE(result.status, status);
|
||||
QCOMPARE(result.signals.size(), std::size_t(2U));
|
||||
for (std::size_t index = 0U; index < result.signals.size(); ++index) {
|
||||
const DecodedSignalValue &value = result.signals[index];
|
||||
QCOMPARE(value.status, bytes > index ? SignalDecodeStatus::Valid : SignalDecodeStatus::InsufficientPayload);
|
||||
QCOMPARE(value.valid, bytes > index);
|
||||
if (value.valid) {
|
||||
QCOMPARE(std::get<std::uint64_t>(value.rawValue), std::uint64_t(42U));
|
||||
QCOMPARE(value.physicalValue, 42.0);
|
||||
}
|
||||
}
|
||||
raw.isExtended = true;
|
||||
const DecodedFrameValue unknown = DbcDecoder().Decode(database, raw);
|
||||
QVERIFY(!unknown.valid && !unknown.definition && unknown.signals.empty());
|
||||
QCOMPARE(unknown.status, FrameDecodeStatus::UnknownFrame);
|
||||
QVERIFY(unknown.key == FrameKey(17U, true));
|
||||
QCOMPARE(DbcDecoder().Decode(DecodeDatabase(), raw).status, FrameDecodeStatus::UnknownFrame);
|
||||
|
||||
definition.signals.clear();
|
||||
raw.isExtended = false;
|
||||
const DecodedFrameValue empty = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QVERIFY(empty.valid && empty.signals.empty());
|
||||
QCOMPARE(empty.status, status);
|
||||
}
|
||||
}
|
||||
|
||||
void DbcDecoderTest::invalidDefinitions_data() {
|
||||
QTest::addColumn<int>("kind");
|
||||
QTest::newRow("zero-length") << 0;
|
||||
QTest::newRow("65-bits") << 1;
|
||||
QTest::newRow("outside-dlc") << 2;
|
||||
QTest::newRow("start-overflow") << 3;
|
||||
QTest::newRow("invalid-byte-order") << 4;
|
||||
QTest::newRow("invalid-value-type") << 5;
|
||||
QTest::newRow("invalid-multiplex-role") << 6;
|
||||
QTest::newRow("missing-selector") << 7;
|
||||
}
|
||||
|
||||
void DbcDecoderTest::invalidDefinitions() {
|
||||
QFETCH(int, kind);
|
||||
for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
|
||||
DecodeFrame definition;
|
||||
definition.dlc = 1U;
|
||||
DecodeSignal good;
|
||||
good.name = "Good";
|
||||
good.length = 4U;
|
||||
good.byteOrder = order;
|
||||
good.startBit = order == ByteOrder::Intel ? 0U : 7U;
|
||||
DecodeSignal bad = good;
|
||||
bad.name = "Bad";
|
||||
switch (kind) {
|
||||
case 0: bad.length = 0U; break;
|
||||
case 1: bad.length = 65U; break;
|
||||
case 2: bad.startBit += 8U; break;
|
||||
case 3: bad.startBit = std::numeric_limits<std::uint32_t>::max(); break;
|
||||
case 4: bad.byteOrder = static_cast<ByteOrder>(99); break;
|
||||
case 5: bad.valueType = static_cast<ValueType>(99); break;
|
||||
case 6: bad.multiplexing.role = static_cast<MultiplexRole>(99); break;
|
||||
case 7: bad.multiplexing.role = MultiplexRole::Branch; break;
|
||||
}
|
||||
definition.signals = {bad, good};
|
||||
const DecodeDatabase database({definition});
|
||||
RawCanFrame raw;
|
||||
raw.data = {0xFFU, 0xFFU}; // Extra bytes must not rescue an invalid definition.
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
|
||||
QVERIFY(result.valid);
|
||||
QCOMPARE(result.status, FrameDecodeStatus::SignalErrors);
|
||||
QCOMPARE(result.signals[0].status, SignalDecodeStatus::InvalidDefinition);
|
||||
QVERIFY(!result.signals[0].valid && !result.signals[0].GetValueDescription());
|
||||
QCOMPARE(result.signals[1].status, SignalDecodeStatus::Valid);
|
||||
QCOMPARE(std::get<std::uint64_t>(result.signals[1].rawValue), std::uint64_t(15U));
|
||||
QCOMPARE(result.signals[1].physicalValue, 15.0);
|
||||
raw.data.clear();
|
||||
const DecodedFrameValue shortResult = DbcDecoder().Decode(database, raw);
|
||||
QCOMPARE(shortResult.status, FrameDecodeStatus::InsufficientPayload);
|
||||
QCOMPARE(shortResult.signals[0].status, SignalDecodeStatus::InvalidDefinition);
|
||||
QCOMPARE(shortResult.signals[1].status, SignalDecodeStatus::InsufficientPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void DbcDecoderTest::physicalConversion_data() {
|
||||
QTest::addColumn<double>("factor");
|
||||
QTest::addColumn<double>("offset");
|
||||
QTest::addColumn<bool>("finite");
|
||||
const double infinity = std::numeric_limits<double>::infinity();
|
||||
const double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
const double maximum = std::numeric_limits<double>::max();
|
||||
QTest::newRow("nan-factor") << nan << 0.0 << false;
|
||||
QTest::newRow("nan-offset") << 1.0 << nan << false;
|
||||
QTest::newRow("infinite-factor") << infinity << 0.0 << false;
|
||||
QTest::newRow("negative-infinite-factor") << -infinity << 0.0 << false;
|
||||
QTest::newRow("infinite-offset") << 1.0 << infinity << false;
|
||||
QTest::newRow("negative-infinite-offset") << 1.0 << -infinity << false;
|
||||
QTest::newRow("multiply-overflow") << maximum << 0.0 << false;
|
||||
QTest::newRow("add-overflow") << maximum / 2.0 << maximum << false;
|
||||
QTest::newRow("finite-limit") << maximum / 2.0 << 0.0 << true;
|
||||
QTest::newRow("zero-factor") << 0.0 << 42.0 << true;
|
||||
}
|
||||
|
||||
void DbcDecoderTest::physicalConversion() {
|
||||
QFETCH(double, factor);
|
||||
QFETCH(double, offset);
|
||||
QFETCH(bool, finite);
|
||||
for (ValueType type: {ValueType::Signed, ValueType::Unsigned}) {
|
||||
DecodeFrame definition;
|
||||
definition.dlc = 1U;
|
||||
DecodeSignal signal;
|
||||
signal.name = "Scaled";
|
||||
signal.length = 4U;
|
||||
signal.valueType = type;
|
||||
signal.factor = factor;
|
||||
signal.offset = offset;
|
||||
signal.valueDescriptions.emplace(std::uint64_t(2U), "Two");
|
||||
definition.signals.push_back(signal);
|
||||
signal.name = "Good";
|
||||
signal.startBit = 4U;
|
||||
signal.factor = 1.0;
|
||||
signal.offset = 0.0;
|
||||
definition.signals.push_back(signal);
|
||||
RawCanFrame raw;
|
||||
raw.data = {0x32U};
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QVERIFY(result.valid);
|
||||
QCOMPARE(result.status, finite ? FrameDecodeStatus::Valid : FrameDecodeStatus::SignalErrors);
|
||||
const DecodedSignalValue &scaled = result.signals[0];
|
||||
QCOMPARE(scaled.valid, finite);
|
||||
QCOMPARE(scaled.status, finite ? SignalDecodeStatus::Valid : SignalDecodeStatus::PhysicalConversionFailed);
|
||||
QCOMPARE(std::isfinite(scaled.physicalValue), finite);
|
||||
if (type == ValueType::Signed)
|
||||
QCOMPARE(std::get<std::int64_t>(scaled.rawValue), std::int64_t(2));
|
||||
else
|
||||
QCOMPARE(std::get<std::uint64_t>(scaled.rawValue), std::uint64_t(2));
|
||||
QVERIFY(result.signals[1].valid);
|
||||
QCOMPARE(result.signals[1].physicalValue, 3.0);
|
||||
if (finite) {
|
||||
QVERIFY(scaled.GetValueDescription());
|
||||
QCOMPARE(*scaled.GetValueDescription(), std::string("Two"));
|
||||
} else {
|
||||
QVERIFY(!scaled.GetValueDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DbcDecoderTest::selectorConversionFailure() {
|
||||
DecodeFrame definition;
|
||||
definition.dlc = 1U;
|
||||
DecodeSignal selector;
|
||||
selector.name = "Selector";
|
||||
selector.length = 1U;
|
||||
selector.multiplexing.role = MultiplexRole::Selector;
|
||||
selector.factor = std::numeric_limits<double>::infinity();
|
||||
DecodeSignal branch;
|
||||
branch.name = "Branch";
|
||||
branch.startBit = 1U;
|
||||
branch.length = 7U;
|
||||
branch.multiplexing.role = MultiplexRole::Branch;
|
||||
branch.multiplexing.value = 1U;
|
||||
definition.signals = {branch, selector};
|
||||
RawCanFrame raw;
|
||||
raw.data = {85U};
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QCOMPARE(result.status, FrameDecodeStatus::SignalErrors);
|
||||
QVERIFY(result.signals[0].valid);
|
||||
QCOMPARE(result.signals[0].physicalValue, 42.0);
|
||||
QCOMPARE(result.signals[1].status, SignalDecodeStatus::PhysicalConversionFailed);
|
||||
QCOMPARE(std::get<std::uint64_t>(result.signals[1].rawValue), std::uint64_t(1U));
|
||||
raw.data = {84U};
|
||||
selector.factor = 1.0;
|
||||
definition.signals = {branch, selector};
|
||||
const DecodedFrameValue inactive = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QCOMPARE(inactive.status, FrameDecodeStatus::Valid);
|
||||
QCOMPARE(inactive.signals[0].status, SignalDecodeStatus::Inactive);
|
||||
}
|
||||
|
||||
void DbcDecoderTest::zeroDlc() {
|
||||
DecodeFrame definition;
|
||||
RawCanFrame raw;
|
||||
for (const std::vector<std::uint8_t> &data: {std::vector<std::uint8_t>(), std::vector<std::uint8_t>{42U}}) {
|
||||
raw.data = data;
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QVERIFY(result.valid && result.signals.empty());
|
||||
QCOMPARE(result.status, FrameDecodeStatus::Valid);
|
||||
}
|
||||
DecodeSignal signal;
|
||||
signal.length = 8U;
|
||||
definition.signals.push_back(signal);
|
||||
const DecodedFrameValue invalid = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QCOMPARE(invalid.status, FrameDecodeStatus::SignalErrors);
|
||||
QCOMPARE(invalid.signals[0].status, SignalDecodeStatus::InvalidDefinition);
|
||||
}
|
||||
|
||||
int RunDecoderTests(int argc, char **argv) {
|
||||
DbcDecoderTest tests;
|
||||
return QTest::qExec(&tests, argc, argv);
|
||||
|
||||
Reference in new issue
Block a user