Closes #16
This commit is contained in:
1 parent
7022c84941
commit
c3d3bd51fb
72 files changed
+3703
-2484
No files matched your search
+27
-8
@@ -24,7 +24,7 @@ make -j
|
||||
make check
|
||||
```
|
||||
|
||||
Or run `./tests/dbc_tests` directly. Parser, decoder, lifetime, and additional coverage
|
||||
Or run `./tests/dbc_tests` directly. Parser, decoder, lifetime, validation, and additional coverage
|
||||
suites run, and the executable returns nonzero when any suite fails. Decoder cases appear as
|
||||
individual QtTest data rows; parser checks are grouped into named test slots.
|
||||
Checks also run in release builds.
|
||||
@@ -41,7 +41,8 @@ make check
|
||||
```
|
||||
|
||||
`parser_tests.pri` and `decoder_tests.pri` include the original test sources;
|
||||
`tests.pro` also includes `dbc_coverage_test.cpp` and `dbc_lifetime_test.cpp`.
|
||||
`tests.pro` also includes `dbc_coverage_test.cpp`, `dbc_lifetime_test.cpp`, and
|
||||
`dbc_validation_test.cpp`.
|
||||
`../core/core.pri` supplies the library include path and linkage.
|
||||
|
||||
## Decoder coverage
|
||||
@@ -52,10 +53,11 @@ signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary val
|
||||
payloads, and Intel regression cases. Frame validity means the definition was
|
||||
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.
|
||||
outside declared DLC, invalid enum metadata and overflowing start bits rejected
|
||||
at construction, and preservation of successful signals on partial payloads.
|
||||
Physical conversion checks cover NaN/Inf parameters rejected at construction,
|
||||
multiplication/addition overflow, finite limits, signed/unsigned raw retention,
|
||||
and raw selector activation after physical conversion failure.
|
||||
|
||||
## Parser coverage
|
||||
|
||||
@@ -116,8 +118,9 @@ of invalid markers, selector references/ranges, overlaps, and SG_MUL_VAL_.
|
||||
|
||||
`dbc_coverage_test.cpp` runs as part of `dbc_tests`. It covers signed boundaries
|
||||
below 64 bits, deterministic extraction comparisons for both byte orders across
|
||||
lengths 1–64, frame lookup failures, exact decode statuses, malformed runtime
|
||||
multiplexing, PGN boundaries, comment handling, file formatting, metadata copying
|
||||
lengths 1–64, frame lookup failures, exact decode statuses, construction rejection
|
||||
of malformed runtime multiplexing, PGN boundaries, comment handling, file
|
||||
formatting, metadata copying
|
||||
and source lifetime, physical conversion, and TypeFactory registration/creation.
|
||||
Direct resolver tests construct ambiguous databases through the public metadata
|
||||
API, verify source locations, and check that ambiguous targets receive no values.
|
||||
@@ -141,3 +144,19 @@ duplicate runtime keys, including manually supplied parsed metadata. Failed relo
|
||||
leaves the active database intact; empty reload clears it. Invalid/unknown results
|
||||
and insufficient payloads retain their documented ownership and status behavior.
|
||||
Inactive branches and unavailable selectors also retain definitions across reload.
|
||||
|
||||
## Database validation coverage
|
||||
|
||||
`dbc_validation_test.cpp` checks direct construction and manually assembled
|
||||
`DbcDatabase` input to `DbcDecodeBuilder`: standard/extended CAN ID limits,
|
||||
encoded/reserved ID bits, duplicate keys, lengths 0/1–64/65/UINT32_MAX,
|
||||
Intel/Motorola payload boundaries and overflowing start bits, invalid enums,
|
||||
NaN and both infinities in every numeric parameter, and reversed physical ranges.
|
||||
Selector tests include missing, duplicate, and signed selectors, branch ranges,
|
||||
UINT64_MAX selection, mixed-order overlaps, and exclusive branches sharing bits.
|
||||
Valid definitions cover the full byte-count storage range, including zero-byte
|
||||
frames without signals and 255-byte frames. Existing extraction tests cover
|
||||
lengths 1–64. Snapshot tests mutate retained frame, signal, and description aliases
|
||||
after rvalue construction, and verify unchanged lookups and metadata. Failed
|
||||
constructor/builder replacement preserves the active snapshot; clear removes its
|
||||
index while saved handles remain alive.
|
||||
@@ -104,6 +104,7 @@ void CoverageTest::frameLookup() {
|
||||
QVERIFY(absent.definition == nullptr);
|
||||
QVERIFY(absent.signals.empty());
|
||||
DecodeSignal signal;
|
||||
signal.length = 8U;
|
||||
DecodeDatabase database = SingleSignal(signal);
|
||||
QVERIFY(decoder.FindFrame(database, 18U, false) == nullptr);
|
||||
QVERIFY(decoder.FindFrame(database, 17U, true) == nullptr);
|
||||
@@ -227,6 +228,10 @@ void CoverageTest::invalidSignalStatus() {
|
||||
signal.startBit = start;
|
||||
signal.length = length;
|
||||
signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
|
||||
if (status == int(SignalDecodeStatus::InvalidDefinition)) {
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) SingleSignal(signal));
|
||||
return;
|
||||
}
|
||||
const DecodeDatabase database = SingleSignal(signal);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
@@ -272,19 +277,7 @@ void CoverageTest::malformedRuntimeMultiplexing() {
|
||||
if (kind == 1) definitions[0].signals[0].valueType = ValueType::Signed;
|
||||
if (kind == 2) definitions[0].signals[0].length = 65U;
|
||||
if (kind == 3) definitions[0].signals[1].multiplexing.value = 2U;
|
||||
const DecodeDatabase database(std::move(definitions));
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
raw.data = {1U, 42U, 99U};
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
|
||||
QCOMPARE(int(result.signals[1].status), int(SignalDecodeStatus::InvalidDefinition));
|
||||
QVERIFY(!result.signals[1].valid);
|
||||
QVERIFY(result.signals[2].valid);
|
||||
QCOMPARE(std::get<std::uint64_t>(result.signals[2].rawValue), std::uint64_t(99));
|
||||
if (kind != 3) {
|
||||
QCOMPARE(int(result.signals[0].status), int(SignalDecodeStatus::InvalidDefinition));
|
||||
if (kind == 0) QCOMPARE(int(result.signals[3].status), int(SignalDecodeStatus::InvalidDefinition));
|
||||
}
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase(definitions));
|
||||
}
|
||||
|
||||
void CoverageTest::pgn_data() {
|
||||
|
||||
+19
-27
@@ -4,6 +4,7 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
struct DecodeCase {
|
||||
const char *name;
|
||||
@@ -77,6 +78,10 @@ void DbcDecoderTest::decode() {
|
||||
definition.canId = 0x123U;
|
||||
definition.dlc = 8U;
|
||||
definition.signals.push_back(signal);
|
||||
if (test.length == 0U || test.length > 64U || test.startBit > 64U) {
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({definition}));
|
||||
return;
|
||||
}
|
||||
const DecodeDatabase database({definition});
|
||||
|
||||
RawCanFrame frame;
|
||||
@@ -253,22 +258,7 @@ void DbcDecoderTest::invalidDefinitions() {
|
||||
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);
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({definition}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,6 +303,10 @@ void DbcDecoderTest::physicalConversion() {
|
||||
definition.signals.push_back(signal);
|
||||
RawCanFrame raw;
|
||||
raw.data = {0x32U};
|
||||
if (!std::isfinite(factor) || !std::isfinite(offset)) {
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({definition}));
|
||||
continue;
|
||||
}
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
QVERIFY(result.valid);
|
||||
QCOMPARE(result.status, finite ? FrameDecodeStatus::Valid : FrameDecodeStatus::SignalErrors);
|
||||
@@ -340,25 +334,25 @@ void DbcDecoderTest::selectorConversionFailure() {
|
||||
definition.dlc = 1U;
|
||||
DecodeSignal selector;
|
||||
selector.name = "Selector";
|
||||
selector.length = 1U;
|
||||
selector.length = 2U;
|
||||
selector.multiplexing.role = MultiplexRole::Selector;
|
||||
selector.factor = std::numeric_limits<double>::infinity();
|
||||
selector.factor = std::numeric_limits<double>::max();
|
||||
DecodeSignal branch;
|
||||
branch.name = "Branch";
|
||||
branch.startBit = 1U;
|
||||
branch.length = 7U;
|
||||
branch.startBit = 2U;
|
||||
branch.length = 6U;
|
||||
branch.multiplexing.role = MultiplexRole::Branch;
|
||||
branch.multiplexing.value = 1U;
|
||||
branch.multiplexing.value = 2U;
|
||||
definition.signals = {branch, selector};
|
||||
RawCanFrame raw;
|
||||
raw.data = {85U};
|
||||
raw.data = {170U};
|
||||
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};
|
||||
QCOMPARE(std::get<std::uint64_t>(result.signals[1].rawValue), std::uint64_t(2U));
|
||||
raw.data = {168U};
|
||||
selector.factor = 1.0;
|
||||
definition.signals = {branch, selector};
|
||||
const DecodedFrameValue inactive = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
|
||||
@@ -378,9 +372,7 @@ void DbcDecoderTest::zeroDlc() {
|
||||
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);
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({definition}));
|
||||
}
|
||||
|
||||
int RunDecoderTests(int argc, char **argv) {
|
||||
|
||||
@@ -175,6 +175,8 @@ void DbcLifetimeTest::duplicateKeys() {
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({frame, frame}));
|
||||
DecodeSignal signal;
|
||||
signal.name = "State";
|
||||
signal.length = 1U;
|
||||
frame.dlc = 1U;
|
||||
frame.signals = {signal, signal};
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({frame}));
|
||||
DbcDatabase parsed;
|
||||
|
||||
@@ -603,16 +603,9 @@ void DbcParserTest::multiplexingBoundaries() {
|
||||
"Adjacent large integers remain distinct");
|
||||
std::vector<DecodeFrame> definitions = runtime.GetFrames();
|
||||
definitions[0].signals[1].length = 0U;
|
||||
runtime = DecodeDatabase(definitions);
|
||||
result = DbcDecoder().Decode(runtime, raw);
|
||||
CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition &&
|
||||
result.signals[1].status == SignalDecodeStatus::InvalidDefinition,
|
||||
"Malformed runtime selector distinguished from payload errors");
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
|
||||
definitions[0].signals.pop_back();
|
||||
runtime = DecodeDatabase(definitions);
|
||||
result = DbcDecoder().Decode(runtime, raw);
|
||||
CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition,
|
||||
"Missing runtime selector");
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
|
||||
fixture.Write("BO_ 1 Frame: 2 ECU\n\n"
|
||||
"SG_ Branch m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n");
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "dbc_decode_builder.h"
|
||||
#include "dbc_decoder.h"
|
||||
#include <QtTest>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
DecodeFrame ValidFrame() {
|
||||
DecodeFrame frame;
|
||||
frame.canId = 0x123U;
|
||||
frame.dlc = 8U;
|
||||
DecodeSignal signal;
|
||||
signal.name = "Value";
|
||||
signal.length = 8U;
|
||||
frame.signals.push_back(signal);
|
||||
return frame;
|
||||
}
|
||||
|
||||
DbcDatabase ParsedDefinitions(const std::vector<DecodeFrame> &frames) {
|
||||
DbcDatabase database;
|
||||
for (const DecodeFrame &frame: frames) {
|
||||
FrameInfo target;
|
||||
target.canId = frame.canId;
|
||||
target.isExtended = frame.isExtended;
|
||||
target.dlc = frame.dlc;
|
||||
for (const DecodeSignal &signal: frame.signals) {
|
||||
SignalInfo copy;
|
||||
copy.name = signal.name;
|
||||
copy.startBit = signal.startBit;
|
||||
copy.length = signal.length;
|
||||
copy.isLittleEndian = signal.byteOrder == ByteOrder::Intel;
|
||||
copy.isSigned = signal.valueType == ValueType::Signed;
|
||||
copy.factor = signal.factor;
|
||||
copy.offset = signal.offset;
|
||||
copy.minimum = signal.minimum;
|
||||
copy.maximum = signal.maximum;
|
||||
copy.multiplexing = signal.multiplexing;
|
||||
target.signals.push_back(copy);
|
||||
}
|
||||
database.frames.push_back(target);
|
||||
}
|
||||
return database;
|
||||
}
|
||||
}// namespace
|
||||
|
||||
class DbcValidationTest : public QObject {
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void invalidMetadata_data();
|
||||
void invalidMetadata();
|
||||
void numericParameters_data();
|
||||
void numericParameters();
|
||||
void validBoundaries();
|
||||
void multiplexingBoundaries();
|
||||
void independentSnapshot();
|
||||
void failedReplacement();
|
||||
};
|
||||
|
||||
void DbcValidationTest::invalidMetadata_data() {
|
||||
QTest::addColumn<int>("kind");
|
||||
QTest::newRow("standard-id") << 0;
|
||||
QTest::newRow("extended-id") << 1;
|
||||
QTest::newRow("encoded-id") << 2;
|
||||
QTest::newRow("duplicate-frame") << 3;
|
||||
QTest::newRow("duplicate-signal") << 4;
|
||||
QTest::newRow("zero-length") << 5;
|
||||
QTest::newRow("65-bits") << 6;
|
||||
QTest::newRow("length-overflow") << 7;
|
||||
QTest::newRow("intel-start-overflow") << 8;
|
||||
QTest::newRow("motorola-start-overflow") << 9;
|
||||
QTest::newRow("intel-outside-payload") << 10;
|
||||
QTest::newRow("motorola-outside-payload") << 11;
|
||||
QTest::newRow("zero-payload") << 12;
|
||||
QTest::newRow("inverted-range") << 13;
|
||||
QTest::newRow("byte-order") << 14;
|
||||
QTest::newRow("value-type") << 15;
|
||||
QTest::newRow("multiplex-role") << 16;
|
||||
QTest::newRow("missing-selector") << 17;
|
||||
QTest::newRow("signed-selector") << 18;
|
||||
QTest::newRow("two-selectors") << 19;
|
||||
QTest::newRow("branch-range") << 20;
|
||||
QTest::newRow("unconditional-overlap") << 21;
|
||||
QTest::newRow("mixed-order-overlap") << 22;
|
||||
QTest::newRow("same-branch-overlap") << 23;
|
||||
QTest::newRow("selector-branch-overlap") << 24;
|
||||
QTest::newRow("maximum-can-id") << 25;
|
||||
}
|
||||
|
||||
void DbcValidationTest::invalidMetadata() {
|
||||
QFETCH(int, kind);
|
||||
std::vector<DecodeFrame> frames = {ValidFrame()};
|
||||
DecodeFrame &frame = frames[0];
|
||||
DecodeSignal &signal = frame.signals[0];
|
||||
DecodeSignal other = signal;
|
||||
other.name = "Other";
|
||||
switch (kind) {
|
||||
case 0:
|
||||
frame.canId = 0x800U;
|
||||
break;
|
||||
case 1:
|
||||
frame.isExtended = true;
|
||||
frame.canId = 0x20000000U;
|
||||
break;
|
||||
case 2:
|
||||
frame.isExtended = true;
|
||||
frame.canId = 0x80000123U;
|
||||
break;
|
||||
case 3:
|
||||
frames.push_back(frame);
|
||||
break;
|
||||
case 4:
|
||||
frame.signals.push_back(signal);
|
||||
break;
|
||||
case 5:
|
||||
signal.length = 0U;
|
||||
break;
|
||||
case 6:
|
||||
signal.length = 65U;
|
||||
break;
|
||||
case 7:
|
||||
signal.length = std::numeric_limits<std::uint32_t>::max();
|
||||
break;
|
||||
case 8:
|
||||
signal.startBit = std::numeric_limits<std::uint32_t>::max();
|
||||
break;
|
||||
case 9:
|
||||
signal.byteOrder = ByteOrder::Motorola;
|
||||
signal.startBit = std::numeric_limits<std::uint32_t>::max();
|
||||
break;
|
||||
case 10:
|
||||
signal.startBit = 57U;
|
||||
break;
|
||||
case 11:
|
||||
signal.byteOrder = ByteOrder::Motorola;
|
||||
signal.startBit = 56U;
|
||||
break;
|
||||
case 12:
|
||||
frame.dlc = 0U;
|
||||
break;
|
||||
case 13:
|
||||
signal.minimum = 2.0;
|
||||
signal.maximum = 1.0;
|
||||
break;
|
||||
case 14:
|
||||
signal.byteOrder = static_cast<ByteOrder>(99);
|
||||
break;
|
||||
case 15:
|
||||
signal.valueType = static_cast<ValueType>(99);
|
||||
break;
|
||||
case 16:
|
||||
signal.multiplexing.role = static_cast<MultiplexRole>(99);
|
||||
break;
|
||||
case 17:
|
||||
signal.multiplexing.role = MultiplexRole::Branch;
|
||||
break;
|
||||
case 18:
|
||||
signal.multiplexing.role = MultiplexRole::Selector;
|
||||
signal.valueType = ValueType::Signed;
|
||||
break;
|
||||
case 19:
|
||||
signal.multiplexing.role = MultiplexRole::Selector;
|
||||
other.multiplexing.role = MultiplexRole::Selector;
|
||||
other.startBit = 8U;
|
||||
frame.signals.push_back(other);
|
||||
break;
|
||||
case 20:
|
||||
case 23:
|
||||
case 24:
|
||||
signal.multiplexing.role = MultiplexRole::Selector;
|
||||
other.multiplexing.role = MultiplexRole::Branch;
|
||||
other.startBit = kind == 24 ? 0U : 8U;
|
||||
other.multiplexing.value = kind == 20 ? 256U : 1U;
|
||||
frame.signals.push_back(other);
|
||||
if (kind == 23) {
|
||||
other.name = "Third";
|
||||
frame.signals.push_back(other);
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
frame.signals.push_back(other);
|
||||
break;
|
||||
case 22:
|
||||
other.byteOrder = ByteOrder::Motorola;
|
||||
other.startBit = 7U;
|
||||
frame.signals.push_back(other);
|
||||
break;
|
||||
case 25:
|
||||
frame.canId = std::numeric_limits<std::uint32_t>::max();
|
||||
break;
|
||||
}
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase(frames));
|
||||
// Parsed metadata uses bools for byte order and signedness, so invalid enum
|
||||
// values exist only in direct runtime definitions.
|
||||
if (kind != 14 && kind != 15)
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DbcDecodeBuilder().Build(ParsedDefinitions(frames)));
|
||||
}
|
||||
|
||||
void DbcValidationTest::numericParameters_data() {
|
||||
QTest::addColumn<int>("field");
|
||||
QTest::addColumn<double>("value");
|
||||
const double values[] = {std::numeric_limits<double>::quiet_NaN(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()};
|
||||
for (int field = 0; field < 4; ++field) {
|
||||
for (int index = 0; index < 3; ++index) {
|
||||
const QByteArray name = QByteArray::number(field) + "-" + QByteArray::number(index);
|
||||
QTest::newRow(name.constData()) << field << values[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DbcValidationTest::numericParameters() {
|
||||
QFETCH(int, field);
|
||||
QFETCH(double, value);
|
||||
DecodeFrame frame = ValidFrame();
|
||||
DecodeSignal &signal = frame.signals[0];
|
||||
switch (field) {
|
||||
case 0:
|
||||
signal.factor = value;
|
||||
break;
|
||||
case 1:
|
||||
signal.offset = value;
|
||||
break;
|
||||
case 2:
|
||||
signal.minimum = value;
|
||||
break;
|
||||
case 3:
|
||||
signal.maximum = value;
|
||||
break;
|
||||
}
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({frame}));
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DbcDecodeBuilder().Build(ParsedDefinitions({frame})));
|
||||
}
|
||||
|
||||
void DbcValidationTest::validBoundaries() {
|
||||
for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
|
||||
for (std::uint8_t dlc: {std::uint8_t(8U), std::uint8_t(64U), std::uint8_t(255U)}) {
|
||||
DecodeFrame standard = ValidFrame();
|
||||
standard.canId = 0x7FFU;
|
||||
standard.dlc = dlc;
|
||||
DecodeSignal &signal = standard.signals[0];
|
||||
signal.byteOrder = order;
|
||||
signal.startBit = (std::uint32_t(dlc) - 8U) * 8U + (order == ByteOrder::Intel ? 0U : 7U);
|
||||
signal.length = 64U;
|
||||
signal.factor = -2.0;
|
||||
signal.offset = std::numeric_limits<double>::max();
|
||||
signal.minimum = -std::numeric_limits<double>::max();
|
||||
signal.maximum = std::numeric_limits<double>::max();
|
||||
DecodeFrame extended = standard;
|
||||
extended.isExtended = true;
|
||||
DecodeFrame maximum = extended;
|
||||
maximum.canId = 0x1FFFFFFFU;
|
||||
const std::vector<DecodeFrame> frames = {standard, extended, maximum};
|
||||
const DecodeDatabase direct(frames);
|
||||
const DecodeDatabase built = DbcDecodeBuilder().Build(ParsedDefinitions(frames));
|
||||
for (const DecodeFrame &frame: frames) {
|
||||
QVERIFY(direct.FindFrame(frame.GetKey()));
|
||||
QVERIFY(built.FindSignal(SignalKey(frame.GetKey(), "Value")));
|
||||
}
|
||||
}
|
||||
}
|
||||
DecodeFrame empty;
|
||||
empty.canId = 0U;
|
||||
QVERIFY(DecodeDatabase({empty}).FindFrame(empty.GetKey()));
|
||||
QVERIFY(DbcDecodeBuilder().Build(DbcDatabase()).GetFrames().empty());
|
||||
}
|
||||
|
||||
void DbcValidationTest::multiplexingBoundaries() {
|
||||
DecodeFrame frame = ValidFrame();
|
||||
frame.dlc = 9U;
|
||||
DecodeSignal &selector = frame.signals[0];
|
||||
selector.length = 64U;
|
||||
selector.multiplexing.role = MultiplexRole::Selector;
|
||||
DecodeSignal first;
|
||||
first.name = "First";
|
||||
first.startBit = 64U;
|
||||
first.length = 8U;
|
||||
first.multiplexing.role = MultiplexRole::Branch;
|
||||
first.multiplexing.value = std::numeric_limits<std::uint64_t>::max();
|
||||
DecodeSignal second = first;
|
||||
second.name = "Second";
|
||||
second.byteOrder = ByteOrder::Motorola;
|
||||
second.startBit = 71U;
|
||||
second.multiplexing.value = 0U;
|
||||
frame.signals.push_back(first);
|
||||
frame.signals.push_back(second);
|
||||
const DecodeDatabase direct({frame});
|
||||
const DecodeDatabase built = DbcDecodeBuilder().Build(ParsedDefinitions({frame}));
|
||||
RawCanFrame raw;
|
||||
raw.canId = frame.canId;
|
||||
raw.data.assign(9U, 255U);
|
||||
for (const DecodeDatabase &database: {direct, built}) {
|
||||
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
|
||||
QCOMPARE(result.signals[1].status, SignalDecodeStatus::Valid);
|
||||
QCOMPARE(result.signals[2].status, SignalDecodeStatus::Inactive);
|
||||
}
|
||||
}
|
||||
|
||||
void DbcValidationTest::independentSnapshot() {
|
||||
std::vector<DecodeFrame> frames = {ValidFrame()};
|
||||
DecodeFrame &alias = frames[0];
|
||||
DecodeSignal &signalAlias = alias.signals[0];
|
||||
signalAlias.valueDescriptions.emplace(std::uint64_t(1U), "One");
|
||||
std::string &descriptionAlias = signalAlias.valueDescriptions.begin()->second;
|
||||
const FrameKey key = alias.GetKey();
|
||||
const DecodeDatabase database(std::move(frames));
|
||||
alias.canId = 456U;
|
||||
signalAlias.name = "Changed";
|
||||
signalAlias.startBit = std::numeric_limits<std::uint32_t>::max();
|
||||
descriptionAlias = "Changed";
|
||||
const std::shared_ptr<const DecodeFrame> saved = database.FindFrame(key);
|
||||
QVERIFY(saved);
|
||||
QCOMPARE(saved->canId, key.canId);
|
||||
QVERIFY(!database.FindFrame(FrameKey(456U, false)));
|
||||
const std::shared_ptr<const DecodeSignal> signal = database.FindSignal(SignalKey(key, "Value"));
|
||||
QVERIFY(signal);
|
||||
QCOMPARE(signal->startBit, 0U);
|
||||
QCOMPARE(signal->valueDescriptions.begin()->second, std::string("One"));
|
||||
}
|
||||
|
||||
void DbcValidationTest::failedReplacement() {
|
||||
const DecodeFrame valid = ValidFrame();
|
||||
DecodeDatabase database({valid});
|
||||
const std::shared_ptr<const DecodeFrame> original = database.FindFrame(valid.GetKey());
|
||||
DecodeFrame invalid = valid;
|
||||
invalid.canId = 0x800U;
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, database = DecodeDatabase({invalid}));
|
||||
QCOMPARE(database.FindFrame(valid.GetKey()).get(), original.get());
|
||||
invalid = valid;
|
||||
invalid.signals[0].factor = std::numeric_limits<double>::infinity();
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument,
|
||||
database = DbcDecodeBuilder().Build(ParsedDefinitions({invalid})));
|
||||
QCOMPARE(database.FindFrame(valid.GetKey()).get(), original.get());
|
||||
QCOMPARE(database.GetFrames().size(), std::size_t(1U));
|
||||
database = DecodeDatabase();
|
||||
QVERIFY(database.GetFrames().empty());
|
||||
QVERIFY(!database.FindFrame(valid.GetKey()));
|
||||
QVERIFY(original);
|
||||
}
|
||||
|
||||
int RunValidationTests(int argc, char **argv) {
|
||||
DbcValidationTest tests;
|
||||
return QTest::qExec(&tests, argc, argv);
|
||||
}
|
||||
|
||||
#include "dbc_validation_test.moc"
|
||||
+4
-1
@@ -7,11 +7,14 @@ int RunCoverageTests(int argc, char **argv);
|
||||
|
||||
int RunLifetimeTests(int argc, char **argv);
|
||||
|
||||
int RunValidationTests(int argc, char **argv);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
QCoreApplication application(argc, argv);
|
||||
const int parserResult = RunParserTests(argc, argv);
|
||||
const int decoderResult = RunDecoderTests(argc, argv);
|
||||
const int coverageResult = RunCoverageTests(argc, argv);
|
||||
const int lifetimeResult = RunLifetimeTests(argc, argv);
|
||||
return parserResult | decoderResult | coverageResult | lifetimeResult;
|
||||
const int validationResult = RunValidationTests(argc, argv);
|
||||
return validationResult | parserResult | decoderResult | coverageResult | lifetimeResult;
|
||||
}
|
||||
+1
-1
@@ -8,5 +8,5 @@ CONFIG -= app_bundle
|
||||
include(../core/core.pri)
|
||||
include(parser_tests.pri)
|
||||
include(decoder_tests.pri)
|
||||
SOURCES += main.cpp dbc_coverage_test.cpp dbc_lifetime_test.cpp
|
||||
SOURCES += main.cpp dbc_coverage_test.cpp dbc_lifetime_test.cpp dbc_validation_test.cpp
|
||||
CONFIG -= debug_and_release debug_and_release_target
|
||||
Reference in new issue
Block a user