This commit is contained in:
deeaitch committed 2026-10-06 17:04:13 -04:00
1 parent 65142fdfe3
commit e5d818d9b2
14 files changed
+393 -39

No files matched your search

+4
View File
@@ -50,6 +50,10 @@ error paths and line numbers, and integration with signals and comments.
Signal tests cover complete syntax and numeric validation, scientific notation,
finite physical parameters and ordered ranges, spaces and tabs, receiver lists,
unsupported multiplexing, and Intel/Motorola declared-payload boundaries.
Value-description tests cover full `VAL_` syntax, whitespace, escaped quotes,
empty text, exact integer boundaries, duplicate keys within and across statements,
deferred and normalized frame references, missing targets, error locations,
raw lookup with scaling, unknown/invalid values, and both database builders.
Temporary DBC fixtures are created in the system temporary directory and removed
automatically.
+126 -1
View File
@@ -1,5 +1,9 @@
#include "dbc_decode_builder.h"
#include "dbc_decoder.h"
#include "dbc_parser.h"
#include "dbc_statement_handler.h"
#include "dbc_tree_builder.h"
#include <limits>
#include <QtTest>
#include <chrono>
@@ -66,6 +70,8 @@ private Q_SLOTS:
void validSignals();
void dispatch();
void independentSessions();
void valueDescriptions();
void invalidValueDescriptions();
};
void DbcParserTest::validFrames() {
@@ -256,7 +262,7 @@ void DbcParserTest::dispatch() {
"Factory dispatch and handler reuse");
CHECK(!dispatcher.Dispatch("TEST_OTHER data", context) &&
!dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
!dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) &&
!dispatcher.Dispatch("VAL_TABLE_ States 0 \"Stopped\";", context) &&
CountingHandler::calls == 2,
"Exact and unknown keyword dispatch");
}
@@ -281,6 +287,125 @@ void DbcParserTest::independentSessions() {
"Fresh parse after error");
}
void DbcParserTest::valueDescriptions() {
Fixture fixture;
fixture.Write(
"VAL_\t256\tState\t0\t\"Off\"\t1 \"On\" 2 \"Error with spaces; and \\\"quotes\\\"\";\n"
"BO_ 256 Status: 8 ECU\n"
"SG_ State : 0|8@1+ (2,10) [0|520] \"\" ECU\n"
"VAL_ 256 State 3 \"\";\n"
"VAL_ 2147483904 State -9223372036854775808 \"Minimum\" -1 \"Negative\" 9223372036854775807 \"Maximum\";\n"
"BO_ 2147483904 Extended: 8 ECU\n"
"SG_ State : 0|64@1- (1,0) [-1e20|1e20] \"\" ECU\n"
"BO_ 2048 Unsigned: 8 ECU\n"
"SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n"
"VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n");
const auto parsed = DbcParser().ParseFile(fixture.path.string());
const auto runtime = DbcDecodeBuilder().Build(parsed);
const auto tree = DbcTreeBuilder().Build(parsed);
CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node");
for (std::size_t index = 0; index < parsed.frames.size(); ++index) {
CHECK(tree->GetChild(index)->GetChild(0)->GetSignal()->valueDescriptions ==
parsed.frames[index].signals[0].valueDescriptions &&
runtime.frames[index].signals[0].valueDescriptions ==
parsed.frames[index].signals[0].valueDescriptions,
"All mappings survive both builders");
}
CHECK(tree->GetChild(0)->GetChild(0)->GetSignal()->valueDescriptions ==
runtime.frames[0].signals[0].valueDescriptions,
"Both builders preserve descriptions");
RawCanFrame frame;
frame.canId = 256;
frame.data.resize(8);
const std::string expected[] = {"Off", "On", "Error with spaces; and \"quotes\"", ""};
for (unsigned value = 0; value < 5; ++value) {
frame.data[0] = value;
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.valid && decoded.physicalValue == value * 2 + 10, "Numeric scaling unchanged");
const auto description = decoded.GetValueDescription();
CHECK(value < 4 ? description && *description == expected[value] : !description,
"Raw lookup and unknown value");
}
frame.isExtended = true;
struct Boundary {
std::uint64_t bits;
const char *description;
};
for (const auto &test: {Boundary{0x8000000000000000ULL, "Minimum"},
Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"},
Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) {
for (unsigned byte = 0; byte < 8; ++byte)
frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
"Signed boundaries and distinct extended frame");
}
frame.canId = 2048;
for (const auto &test: {Boundary{0x8000000000000000ULL, "High"},
Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) {
for (unsigned byte = 0; byte < 8; ++byte)
frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
"Unsigned boundaries");
}
CHECK(FindValueDescription(runtime.frames[0].signals[0].valueDescriptions, std::int64_t(1)) != nullptr,
"Nonnegative signed lookup matches unsigned key");
frame.data.clear();
CHECK(!DbcDecoder().Decode(runtime, frame).signals[0].GetValueDescription(), "Invalid decode has no description");
}
void DbcParserTest::invalidValueDescriptions() {
Fixture fixture;
const std::string definitions = "BO_ 1 Frame: 8 ECU\nSG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n";
for (const std::string statement: {
"VAL_", "VAL_ 1 State;", "VAL_ 1 State ;", "VAL_ 1 State 0 \"Off\"",
"VAL_ 1 State 0 \"Off\"; junk", "VAL_ 1 State 0 Off;",
"VAL_ 1 State 0 \"Unclosed;", "VAL_ 1 State 0 \"Off\"1 \"On\";",
"VAL_ 1 State 0 \"Off\" 00 \"Duplicate\";",
"VAL_ 1 State 0 \"Off\" -0 \"Duplicate\";",
"VAL_ 1 State 0 \"Off\";\nVAL_ 1 State 0 \"Duplicate\";",
"VAL_ 2 State 0 \"Missing frame\";", "VAL_ 1 Missing 0 \"Missing signal\";",
"VAL_ -1 State 0 \"Bad ID\";", "VAL_ 536870912 State 0 \"Bad ID\";",
"VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";",
"VAL_ 1 State 0 \"Bad\\q escape\";"}) {
fixture.Write(definitions + statement + '\n');
const auto line = statement.find('\n') == std::string::npos ? 3 : 4;
try {
DbcParser().ParseFile(fixture.path.string());
CHECK(false, statement);
} catch (const std::runtime_error &error) {
CHECK(std::string(error.what()).find(fixture.path.string() + ":" + std::to_string(line) + ": ") == 0,
statement + " " + error.what());
}
}
for (const std::string suffix: {
"BO_ 1 Other: 8 ECU\n", // Ambiguous normalized frame reference.
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n",// Ambiguous signal reference.
""}) {
fixture.Write("VAL_ 1 " + std::string(suffix.empty() ? "Missing" : "State") +
" 0 \"Description\";\n" + definitions + suffix);
try {
DbcParser().ParseFile(fixture.path.string());
CHECK(false, "Deferred missing or ambiguous reference");
} catch (const std::runtime_error &error) {
CHECK(std::string(error.what()).find(fixture.path.string() + ":1: ") == 0,
"Deferred reference retains source location");
}
}
for (const std::string number: {"+1", "1x", "0x10", "1.5", "1e2", "--1",
"-9223372036854775809", "18446744073709551616"}) {
fixture.Write("VAL_ 1 State " + number + " \"Bad\";\n" + definitions);
try {
DbcParser().ParseFile(fixture.path.string());
CHECK(false, number);
} catch (const std::runtime_error &error) {
CHECK(std::string(error.what()).find(fixture.path.string() + ":1: ") == 0, number);
}
}
}
int RunParserTests(int argc, char **argv) {
DbcParserTest tests;
return QTest::qExec(&tests, argc, argv);