This commit is contained in:
deeaitch committed 2026-10-07 18:32:51 -04:00
1 parent a533d9aa00
commit a0938cb29c
14 files changed
+375 -13

No files matched your search

+6
View File
@@ -72,3 +72,9 @@ Enable `CONFIG+=dbc_qt` in the root qmake invocation to build `dbc_qt` and
QAbstractItemModelTester, verify navigation across columns, invalid/foreign
indexes, metadata and exact VAL_ integers, and persistent-index invalidation
when reloading or clearing the tree. No GUI/display server is needed.
Basic multiplexing coverage includes selector declarations after branches,
Intel/Motorola branch overlap, signed active values and VAL_ lookup, unconditional
signals, inactive branches, missing selector/branch payloads, exact UINT64_MAX
selector matching, malformed runtime selectors, source locations, and rejection
of invalid markers, selector references/ranges, overlaps, and SG_MUL_VAL_.
+124 -1
View File
@@ -63,6 +63,8 @@ private:
}
}
private Q_SLOTS:
void multiplexing();
void multiplexingBoundaries();
void validFrames();
void invalidFrames();
void integration();
@@ -153,6 +155,127 @@ void DbcParserTest::integration() {
}
}
void DbcParserTest::multiplexing() {
Fixture fixture;
const std::string selector = "SG_ Mode M : 0|8@1+ (10,3) [0|255] \"\" ECU\n";
const std::string first = "SG_ First m1 : 8|8@1- (2,0) [-128|127] \"V\" ECU\n";
const std::string second = "SG_ Second m2 : 15|8@0+ (1,0) [0|255] \"\" ECU\n";
const std::string common = "SG_ Common : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
fixture.Write("BO_ 1 Frame: 3 ECU\n" + first + second + common + selector +
"VAL_ 1 First -1 \"Negative\";\n");
const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
CHECK(parsed.frames[0].signals[3].multiplexing.role == MultiplexRole::Selector,
"Selector declared after branches");
const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
CHECK(tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing.value == 1U,
"Tree preserves multiplexing");
RawCanFrame raw;
raw.canId = 1U;
raw.data = {1U, 255U, 7U};
DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].valid && std::get<std::int64_t>(result.signals[0].rawValue) == -1 &&
result.signals[0].physicalValue == -2 && *result.signals[0].GetValueDescription() == "Negative",
"Active signed branch and VAL_ use exact raw selector");
CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
result.signals[1].GetValueDescription() == nullptr && result.signals[2].valid,
"Inactive branch and unconditional signal");
raw.data[0] = 2U;
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
std::get<std::uint64_t>(result.signals[1].rawValue) == 255U,
"Motorola branch becomes active");
raw.data = {3U};
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
result.signals[1].status == SignalDecodeStatus::Inactive &&
result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
"Unmapped selector leaves branches inactive even with missing bytes");
raw.data = {1U};
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
"Active branch requires payload");
raw.data.clear();
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
"Unavailable selector does not imply inactive branch");
const std::vector<std::string> invalid = {
first,
selector + "SG_ Other M : 16|8@1+ (1,0) [0|255] \"\" ECU\n",
"SG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
selector + "SG_ TooLarge m256 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
selector + first + "SG_ Same m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n",
selector + "SG_ Collision m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
selector + first + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
"SG_MUL_VAL_ 1 First Mode 1-2;\n"
};
for (const std::string &signals : invalid) {
fixture.Write("BO_ 1 Frame: 3 ECU\n" + signals);
bool rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &error) {
rejected = std::string(error.what()).find(fixture.path.string() + ":") == 0U;
}
CHECK(rejected, "Invalid multiplexing rejected with location");
}
for (const std::string marker : {"m", "m-1", "m+1", "m1M", "m18446744073709551616", "M m1"})
RejectSignal(fixture, "SG_ Bad " + marker + " : 8|8@1+ (1,0) [0|255] \"\" ECU");
}
void DbcParserTest::multiplexingBoundaries() {
Fixture fixture;
fixture.Write("BO_ 1 Frame: 9 ECU\n"
"SG_ Branch m18446744073709551615 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ Mode M : 7|64@0+ (0,5) [0|1] \"\" ECU\n");
const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
CHECK(runtime.frames[0].signals[0].multiplexing.value == std::numeric_limits<std::uint64_t>::max(),
"Exact uint64 branch metadata");
RawCanFrame raw;
raw.canId = 1U;
raw.data = std::vector<std::uint8_t>(9U, 255U);
DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].valid && result.signals[1].valid &&
std::get<std::uint64_t>(result.signals[1].rawValue) == std::numeric_limits<std::uint64_t>::max(),
"Motorola uint64 selector compares raw value before zero scaling");
raw.data[7] = 254U;
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
"Adjacent large integers remain distinct");
runtime.frames[0].signals[1].length = 0U;
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");
runtime.frames[0].signals.pop_back();
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition,
"Missing runtime selector");
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");
bool rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &error) {
rejected = std::string(error.what()).find(fixture.path.string() + ":3: ") == 0U;
}
CHECK(rejected, "Deferred validation reports branch source line");
fixture.Write("BO_ 1 Frame: 2 ECU\n"
"SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n");
rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &) {
rejected = true;
}
CHECK(rejected, "Ordinary overlapping signals rejected");
}
void DbcParserTest::invalidSignals() {
Fixture fixture;
const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255");
@@ -172,7 +295,7 @@ void DbcParserTest::invalidSignals() {
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
}
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
for (const std::string name: {"", "Two Names", "Value m1M"}) {
std::string line = ordinary;
line.replace(4U, 5U, name);
RejectSignal(fixture, line);