This commit is contained in:
deeaitch committed 2026-10-14 10:32:44 -04:00
1 parent 0b05152bcf
commit d87c100398
9 files changed
+265 -65

No files matched your search

+5
View File
@@ -75,6 +75,11 @@ On Linux, reading a directory exercises the distinct `FileReadError` path.
Parser tests cover standard and extended CAN IDs, whitespace, payload storage
boundaries, missing fields, malformed and overflowing numbers, reserved ID bits,
error paths and line numbers, and integration with signals and comments.
Comment regressions cover tabs, escaped quotes/backslashes, multiline text with
blank lines and indentation, CRLF, missing final newlines, comments preceding
frame/signal definitions, last-wins and empty overwrites, normalized ID boundaries,
standard/extended separation, malformed syntax/IDs, and missing targets. Errors
verify both structured diagnostics and the throwing API at the starting line.
Duplicate definitions are rejected at their source lines, including equivalent
extended ID encodings and repeated signal names within a frame. Standard and
extended frames may share a numeric ID, and separate frames may reuse signal names.
+1 -4
View File
@@ -312,10 +312,7 @@ void CoverageTest::comments_data() {
QTest::newRow("tabs") << QString::fromUtf8("") << QString::fromUtf8("CM_\tBO_ 1 \"Frame\";\nCM_\tSG_ 1 State \"Signal\";\n") << QString::fromUtf8("Frame") << QString::fromUtf8("Signal");
QTest::newRow("empty-overwrite") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"Old\";\nCM_ BO_ 1 \"\";\nCM_ SG_ 1 State \"Old\";\nCM_ SG_ 1 State \"\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
QTest::newRow("last-wins") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"First\";\nCM_ BO_ 1 \"Last\";\nCM_ SG_ 1 State \"First\";\nCM_ SG_ 1 State \"Last\";\n") << QString::fromUtf8("Last") << QString::fromUtf8("Last");
QTest::newRow("missing-targets") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 2 \"Unknown\";\nCM_ SG_ 2 State \"Unknown\";\nCM_ SG_ 1 Missing \"Unknown\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
// Current documented limitation: comments resolve only already parsed targets.
QTest::newRow("before-definitions") << QString::fromUtf8("CM_ BO_ 1 \"Early\";\nCM_ SG_ 1 State \"Early\";\n") << QString::fromUtf8("") << QString::fromUtf8("") << QString::fromUtf8("");
QTest::newRow("missing-quotes") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 Missing;\nCM_ SG_ 1 State \"Unclosed;\n") << QString::fromUtf8("") << QString::fromUtf8("");
QTest::newRow("before-definitions") << QString::fromUtf8("CM_ BO_ 1 \"Early\";\nCM_ SG_ 1 State \"Early\";\n") << QString::fromUtf8("") << QString::fromUtf8("Early") << QString::fromUtf8("Early");
}
void CoverageTest::comments() {
+100
View File
@@ -75,6 +75,9 @@ private Q_SLOTS:
void duplicateFrames();
void duplicateSignals();
void distinctDefinitions();
void comments();
void invalidComments_data();
void invalidComments();
void integration();
void invalidSignals();
void validSignals();
@@ -489,6 +492,103 @@ void DbcParserTest::distinctDefinitions() {
QCOMPARE(extended.definition->comment, std::string("Extended"));
}
void DbcParserTest::comments() {
Fixture fixture;
fixture.Write(
"CM_\tBO_\t1\t\"First\";\r\n"
"CM_\tSG_\t1\tState\t\" Line one \r\n"
"\r\n"
"\tBO_ is text; \\\"quoted\\\" and \\\\slash\"\t;\r\n"
"CM_ BO_ 2147483649 \"Extended\";\r\n"
"CM_ SG_ 2147483649 State \"Extended signal\";\r\n"
"BO_ 1 Standard: 1 ECU\r\n"
"CM_ BO_ 1 \"Last\";\r\n"
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\r\n"
"BO_ 2147483649 Extended: 1 ECU\r\n"
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU");
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
QVERIFY(!result.HasErrors());
QVERIFY(result.diagnostics.empty());
QCOMPARE(result.database.frames.size(), std::size_t(2));
QCOMPARE(result.database.frames[0].comment, std::string("Last"));
QCOMPARE(result.database.frames[0].signals[0].comment,
std::string(" Line one \n\n\tBO_ is text; \"quoted\" and \\slash"));
QCOMPARE(result.database.frames[1].comment, std::string("Extended"));
QCOMPARE(result.database.frames[1].signals[0].comment, std::string("Extended signal"));
const std::string ids[][2] = {
{"0", "0"},
{"2047", "2047"},
{"2048", "2147485696"},
{"536870911", "2684354559"},
{"2684354559", "536870911"}};
for (const std::string(&id)[2]: ids) {
fixture.Write("CM_ BO_ " + id[0] + " \"Frame\";\nCM_ SG_ " + id[0] +
" State \"Signal\";\nBO_ " + id[1] + " Frame: 1 ECU\n"
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
QCOMPARE(database.frames[0].comment, std::string("Frame"));
QCOMPARE(database.frames[0].signals[0].comment, std::string("Signal"));
}
fixture.Write("CM_ BO_ 1 \"Line one\nLine two\";\n"
"BO_ 1 Frame: 0 ECU\nUNKNOWN_ value\nBO_ bad Frame: 0 ECU\n");
const DbcParseResult following = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
QVERIFY(following.HasErrors());
QCOMPARE(following.diagnostics.size(), std::size_t(2));
QCOMPARE(following.diagnostics[0].lineNumber, std::size_t(4));
QCOMPARE(following.diagnostics[1].lineNumber, std::size_t(5));
}
void DbcParserTest::invalidComments_data() {
QTest::addColumn<QString>("statement");
const char *ids[] = {"-1", "+1", "1x", "0x1", "4294967296", "536870912",
"1073741824", "2684354560", "4294967295"};
for (const char *id: ids) {
const std::string frameRow = std::string("frame-id-") + id;
const std::string signalRow = std::string("signal-id-") + id;
QTest::newRow(frameRow.c_str()) << QString::fromStdString("CM_ BO_ " + std::string(id) + " \"Text\";");
QTest::newRow(signalRow.c_str()) << QString::fromStdString("CM_ SG_ " + std::string(id) + " State \"Text\";");
}
QTest::newRow("missing-id") << QStringLiteral("CM_ BO_");
QTest::newRow("missing-signal") << QStringLiteral("CM_ SG_ 1 \"Text\";");
QTest::newRow("missing-quote") << QStringLiteral("CM_ BO_ 1 Text;");
QTest::newRow("unclosed") << QStringLiteral("CM_ SG_ 1 State \"Text\n More");
QTest::newRow("invalid-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\q\";");
QTest::newRow("dangling-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\");
QTest::newRow("missing-semicolon") << QStringLiteral("CM_ SG_ 1 State \"Text\"");
QTest::newRow("trailing-text") << QStringLiteral("CM_ BO_ 1 \"Text\"; garbage");
QTest::newRow("extra-header") << QStringLiteral("CM_ BO_ 1 extra \"Text\";");
QTest::newRow("extra-string") << QStringLiteral("CM_ BO_ 1 \"Text\" \"More\";");
QTest::newRow("missing-frame") << QStringLiteral("CM_ BO_ 2 \"Text\";");
QTest::newRow("missing-signal-frame") << QStringLiteral("CM_ SG_ 2 State \"Text\";");
QTest::newRow("missing-signal-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\";");
QTest::newRow("different-frame-type") << QStringLiteral("CM_ BO_ 2147483649 \"Text\";");
QTest::newRow("missing-multiline-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\nMore\";");
}
void DbcParserTest::invalidComments() {
QFETCH(QString, statement);
Fixture fixture;
fixture.Write("BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n\n" +
statement.toStdString());
const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
QVERIFY(result.HasErrors());
QVERIFY(result.database.frames.empty());
QCOMPARE(result.diagnostics.size(), std::size_t(1));
const DbcDiagnostic &error = result.diagnostics[0];
QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
QCOMPARE(error.code, DbcDiagnosticCode::InvalidStatement);
QCOMPARE(error.filePath, fixture.path.string());
QCOMPARE(error.lineNumber, std::size_t(4));
QVERIFY(!error.message.empty());
try {
DbcParser().ParseFile(fixture.path.string());
QFAIL("Malformed or unresolved comment must throw.");
} catch (const std::runtime_error &exception) {
QVERIFY(std::string(exception.what()).find(fixture.path.string() + ":4: ") == 0U);
}
}
void DbcParserTest::integration() {
Fixture fixture;
fixture.Write(