Closes #25
This commit is contained in:
1 parent
e555ebb571
commit
849ce31e2f
17 files changed
+675
-35
No files matched your search
+18
-1
@@ -66,7 +66,7 @@ line numbers for skipped syntax and immediate/deferred errors; warnings precedin
|
||||
an error are retained, and failed parses return an empty database. Common headers
|
||||
and namespace symbol lists stay quiet. Tests also check file-open errors, fresh
|
||||
parse sessions, and compatibility with the legacy throwing API.
|
||||
The diagnostic audit also covers 15 unsupported-statement variants, all namespace
|
||||
The diagnostic audit also covers 13 unsupported-statement variants, all namespace
|
||||
termination forms, CRLF and missing final newlines, relative paths with spaces,
|
||||
first-error stopping, deferred diagnostic ordering, duplicate definitions/values,
|
||||
selector ranges, overlaps, and preservation of supported decoding and metadata.
|
||||
@@ -220,3 +220,20 @@ Empty and headers-only fixtures produce an empty runtime database and a valid
|
||||
empty tree root. Replacement fixtures change definitions, metadata and decoded
|
||||
values, remove old lookup keys, and then clear both outputs through an empty
|
||||
fixture. Saved decoded results and trees retain their original metadata.
|
||||
|
||||
## Attribute coverage
|
||||
|
||||
Parser slots `attributes`, `invalidAttributes`, and `attributeWarnings` cover
|
||||
all five scopes and types, deferred definitions/defaults/targets, explicit
|
||||
values overriding defaults, repeated assignments, escaped strings, typed
|
||||
range/scope errors, duplicate definitions/defaults, unknown attributes and
|
||||
missing targets. They check source diagnostics and the throwing API, distinct
|
||||
standard/extended targets, frame cycle times and enum labels, unsupported
|
||||
formats and ID conflicts, unchanged J1939 opt-in, and runtime/tree propagation.
|
||||
Existing unsupported-statement checks now use relation forms, which remain
|
||||
unsupported, instead of supported attribute forms.
|
||||
|
||||
Attribute changes were verified with `qmake6 ../dbc.pro`, `make -j4`, and
|
||||
`make check` in `build/`: all six QtTest suites and the qmake tree target
|
||||
passed. The standalone C++17 tree test also passed. The existing named
|
||||
comma-decimal C locale test skipped because the locale is not installed.
|
||||
+133
-4
@@ -3,6 +3,7 @@
|
||||
#include "dbc_parser.h"
|
||||
#include "dbc_statement_handler.h"
|
||||
#include "dbc_tree_builder.h"
|
||||
#include <algorithm>
|
||||
#include <clocale>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
@@ -87,6 +88,9 @@ private:
|
||||
}
|
||||
}
|
||||
private Q_SLOTS:
|
||||
void attributes();
|
||||
void invalidAttributes();
|
||||
void attributeWarnings();
|
||||
void diagnostics();
|
||||
void diagnosticErrors();
|
||||
void diagnosticUnsupportedStatements();
|
||||
@@ -113,6 +117,133 @@ private Q_SLOTS:
|
||||
void invalidValueDescriptions();
|
||||
};
|
||||
|
||||
void DbcParserTest::attributes() {
|
||||
Fixture fixture;
|
||||
fixture.Write(
|
||||
"BA_ \"GenMsgCycleTime\" BO_ 1 25;\n"
|
||||
"BA_ \"VFrameFormat\" BO_ 2147483649 1;\n"
|
||||
"BA_ \"Label\" SG_ 1 Value \"a\\\"b\\\\c\";\n"
|
||||
"BA_ \"NodeLabel\" BU_ ECU \"node\";\n"
|
||||
"BA_ \"Environment\" EV_ Env 2;\n"
|
||||
"BA_DEF_DEF_ \"GenMsgCycleTime\" 100;\n"
|
||||
"BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN\";\n"
|
||||
"BA_DEF_DEF_ \"Label\" \"default\";\n"
|
||||
"BA_DEF_DEF_ \"BusLabel\" \"CAN\";\n"
|
||||
"BA_DEF_DEF_ \"Ratio\" 1.5e-1;\n"
|
||||
"BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 10000;\n"
|
||||
"BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"StandardCAN_FD\";\n"
|
||||
"BA_DEF_ SG_ \"Label\" STRING;\n"
|
||||
"BA_DEF_ \"BusLabel\" STRING;\n"
|
||||
"BA_DEF_ BO_ \"Ratio\" FLOAT -1 1;\n"
|
||||
"BA_DEF_ BU_ \"NodeLabel\" STRING;\n"
|
||||
"BA_DEF_ EV_ \"Environment\" HEX 0 255;\n"
|
||||
"BO_ 1 Standard: 1 ECU\n"
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"BO_ 2147483649 Extended: 1 ECU\n"
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
|
||||
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
|
||||
QVERIFY(!result.HasErrors());
|
||||
QVERIFY(result.diagnostics.empty());
|
||||
const DbcDatabase &database = result.database;
|
||||
QCOMPARE(database.attributeDefinitions.size(), std::size_t(7));
|
||||
QCOMPARE(database.attributeAssignments.size(), std::size_t(5));
|
||||
QCOMPARE(std::get<std::string>(database.attributes.at("BusLabel")), std::string("CAN"));
|
||||
QCOMPARE(database.frames[0].cycleTimeMs.value(), 25U);
|
||||
QCOMPARE(database.frames[1].cycleTimeMs.value(), 100U);
|
||||
QCOMPARE(database.frames[1].frameFormat.value(), std::string("ExtendedCAN"));
|
||||
QVERIFY(!database.frames[1].hasPgn);
|
||||
QCOMPARE(std::get<double>(database.frames[0].attributes.at("Ratio")), 0.15);
|
||||
QCOMPARE(std::get<std::string>(database.frames[0].signals[0].attributes.at("Label")), std::string("a\"b\\c"));
|
||||
QCOMPARE(std::get<std::string>(database.frames[1].signals[0].attributes.at("Label")), std::string("default"));
|
||||
const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
|
||||
QCOMPARE(runtime.GetFrames()[0].cycleTimeMs.value(), 25U);
|
||||
QCOMPARE(runtime.GetFrames()[1].frameFormat.value(), std::string("ExtendedCAN"));
|
||||
QCOMPARE(runtime.GetFrames()[0].signals[0].attributes, database.frames[0].signals[0].attributes);
|
||||
const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(database);
|
||||
QCOMPARE(tree->GetChild(0)->GetFrame()->cycleTimeMs.value(), 25U);
|
||||
QCOMPARE(tree->GetChild(0)->GetChild(0)->GetSignal()->attributes, database.frames[0].signals[0].attributes);
|
||||
fixture.Write("BA_DEF_ BO_ \"Period\" INT 0 10;\nBA_DEF_DEF_ \"Period\" 1;\n"
|
||||
"BA_ \"Period\" BO_ 1 2;\nBA_ \"Period\" BO_ 1 3;\nBO_ 1 F: 0 ECU\n");
|
||||
const DbcDatabase repeated = DbcParser().ParseFile(fixture.path.string());
|
||||
QCOMPARE(std::get<std::int64_t>(repeated.frames[0].attributes.at("Period")), std::int64_t(3));
|
||||
|
||||
fixture.Write("BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967295;\n"
|
||||
"BA_DEF_DEF_ \"GenMsgCycleTime\" 0;\n"
|
||||
"BA_ \"GenMsgCycleTime\" BO_ 1 4294967295;\n"
|
||||
"BA_DEF_ \"Wide\" INT -9223372036854775808 9223372036854775807;\n"
|
||||
"BA_DEF_DEF_ \"Wide\" -9223372036854775808;\n"
|
||||
"BA_DEF_ SG_ \"State\" ENUM \"off\",\"on\";\n"
|
||||
"BA_DEF_DEF_ \"State\" 1;\n"
|
||||
"BO_ 1 F: 0 ECU\nBO_ 2 Empty: 0 ECU\n");
|
||||
const DbcDatabase boundaries = DbcParser().ParseFile(fixture.path.string());
|
||||
QCOMPARE(boundaries.frames[0].cycleTimeMs.value(), std::numeric_limits<std::uint32_t>::max());
|
||||
QCOMPARE(boundaries.frames[1].cycleTimeMs.value(), 0U);
|
||||
QVERIFY(!boundaries.frames[0].frameFormat);
|
||||
QCOMPARE(std::get<std::int64_t>(boundaries.attributes.at("Wide")), std::numeric_limits<std::int64_t>::min());
|
||||
QCOMPARE(std::get<std::int64_t>(*boundaries.attributeDefinitions.at("State").defaultValue), std::int64_t(1));
|
||||
}
|
||||
|
||||
void DbcParserTest::invalidAttributes() {
|
||||
Fixture fixture;
|
||||
const std::vector<std::string> invalid = {
|
||||
"BA_DEF_ BO_ \"A\" INT 2 1;",
|
||||
"BA_DEF_ BO_ \"A\" FLOAT nan 1;",
|
||||
"BA_DEF_ BO_ \"A\" FLOAT 0 inf;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 9223372036854775808;",
|
||||
"BA_DEF_ BO_ \"A\" STRING; trailing",
|
||||
"BA_DEF_ BO_ \"A\" ENUM;",
|
||||
"BA_DEF_ BO_ \"A\" ENUM \"one\",;",
|
||||
"BA_DEF_ BO_ \"A\" ENUM \"one\",\"one\";",
|
||||
"BA_DEF_ BO_ \"VFrameFormat\" STRING;",
|
||||
"BA_DEF_ SG_ \"GenMsgCycleTime\" INT 0 10;",
|
||||
"BA_ \"Unknown\" BO_ 1 0;",
|
||||
"BA_DEF_DEF_ \"Unknown\" 0;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 2;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 \"0\";",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 S 0;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 2 0;",
|
||||
"BA_DEF_ SG_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 Missing 0;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_ BO_ \"A\" INT 0 1;",
|
||||
"BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_DEF_ \"A\" 0;\nBA_DEF_DEF_ \"A\" 1;",
|
||||
"BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"bad\\q\";",
|
||||
"BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"unterminated;",
|
||||
"BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_DEF_DEF_ \"A\" \"missing\";",
|
||||
"BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_ \"A\" BO_ 1 1;",
|
||||
"BA_DEF_ BO_ \"GenMsgCycleTime\" INT -1 4294967296;\nBA_DEF_DEF_ \"GenMsgCycleTime\" -1;",
|
||||
"BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967296;\nBA_ \"GenMsgCycleTime\" BO_ 1 4294967296;"};
|
||||
for (const std::string &statement: invalid) {
|
||||
fixture.Write("BO_ 1 F: 0 ECU\n" + statement + '\n');
|
||||
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
|
||||
QVERIFY2(result.HasErrors(), statement.c_str());
|
||||
QVERIFY(result.database.frames.empty());
|
||||
QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
|
||||
QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(2 + std::count(statement.begin(), statement.end(), '\n')));
|
||||
QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
|
||||
}
|
||||
}
|
||||
|
||||
void DbcParserTest::attributeWarnings() {
|
||||
Fixture fixture;
|
||||
fixture.Write("BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"J1939PG\",\"StandardCAN_FD\";\n"
|
||||
"BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN_FD\";\n"
|
||||
"BA_ \"VFrameFormat\" BO_ 1 2;\n"
|
||||
"BA_ \"VFrameFormat\" BO_ 2147483649 0;\n"
|
||||
"BO_ 1 F: 0 ECU\nBO_ 2147483649 E: 0 ECU\nBO_ 2 Default: 0 ECU\n");
|
||||
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
|
||||
QVERIFY(!result.HasErrors());
|
||||
QCOMPARE(result.diagnostics.size(), std::size_t(3));
|
||||
const std::size_t lines[] = {3U, 4U, 2U};
|
||||
for (std::size_t index = 0; index < 3U; ++index) {
|
||||
QCOMPARE(result.diagnostics[index].code, DbcDiagnosticCode::UnsupportedAttribute);
|
||||
QCOMPARE(result.diagnostics[index].severity, DbcDiagnosticSeverity::Warning);
|
||||
QCOMPARE(result.diagnostics[index].lineNumber, lines[index]);
|
||||
QCOMPARE(result.diagnostics[index].filePath, fixture.path.string());
|
||||
}
|
||||
QCOMPARE(result.database.frames[0].frameFormat.value(), std::string("J1939PG"));
|
||||
QVERIFY(!result.database.frames[0].hasPgn);
|
||||
QVERIFY(result.database.frames[1].isExtended);
|
||||
}
|
||||
|
||||
void DbcParserTest::diagnostics() {
|
||||
Fixture fixture;
|
||||
fixture.Write("VERSION \"test\"\n"
|
||||
@@ -126,7 +257,7 @@ void DbcParserTest::diagnostics() {
|
||||
"BO_ 1 Frame: 1 ECU\n"
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"SIG_VALTYPE_ 1 Value : 1;\n"
|
||||
"BA_ \"SignalType\" SG_ 1 Value 1;\n"
|
||||
"BA_REL_ \"SignalType\" SG_ 1 Value 1;\n"
|
||||
"VENDOR_EXTENSION_ data;\n");
|
||||
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
|
||||
CHECK(!result.HasErrors() && result.database.frames.size() == 1U,
|
||||
@@ -264,9 +395,7 @@ void DbcParserTest::diagnosticUnsupportedStatements() {
|
||||
"CM_ SG_ 1 Value \"Signal comment\";\nVAL_ 1 Value 1 \"On\";\n";
|
||||
const std::vector<std::string> statements = {
|
||||
"SIG_VALTYPE_ 1 Value : 1;", "SIG_VALTYPE_ 1 Value : 2;",
|
||||
"BA_ \"SignalType\" SG_ 1 Value 1;",
|
||||
"BA_DEF_ SG_ \"SignalType\" INT 0 1;",
|
||||
"BA_DEF_DEF_ \"SignalType\" 0;",
|
||||
"BA_REL_ \"SignalType\" SG_ 1 Value 1;",
|
||||
"BA_DEF_REL_ BU_SG_REL_ \"Relation\" INT 0 1;",
|
||||
"BA_REL_ \"Relation\" BU_SG_REL_ ECU SG_ 1 Value 1;",
|
||||
"SGTYPE_ Type : 8@1+ (1,0) [0|255] \"\" 0, Table;",
|
||||
|
||||
Reference in new issue
Block a user