Fixed SIGSEGV
This commit is contained in:
1 parent
f03882d3f4
commit
5b0019d50d
50 files changed
+1565
-821
No files matched your search
+25
-2
@@ -40,14 +40,15 @@ make
|
||||
make check
|
||||
```
|
||||
|
||||
`parser_tests.pri` and `decoder_tests.pri` include the original test sources;
|
||||
`parser_tests.pri` includes `dbc_parser_test.cpp` and `dbc_statement_test.cpp`;
|
||||
`decoder_tests.pri` includes the decoder test source;
|
||||
`tests.pro` also includes `dbc_coverage_test.cpp`, `dbc_lifetime_test.cpp`,
|
||||
`dbc_validation_test.cpp`, and `dbc_pipeline_test.cpp`.
|
||||
`../core/core.pri` supplies the library include path and linkage.
|
||||
|
||||
## Local regression checks
|
||||
|
||||
`run_core_ci.sh` builds the core archive, runs all six QtTest suites and the
|
||||
`run_core_ci.sh` builds the core archive, runs all seven QtTest suites and the
|
||||
qmake tree target with `make check`, then compiles and runs the standalone C++17
|
||||
tree/builder test without Qt. A failed command returns a nonzero status. The
|
||||
`sanitize` mode applies ASan/UBSan flags to core, tests, and the standalone
|
||||
@@ -295,6 +296,28 @@ Attribute changes were verified with `qmake6 ../dbc.pro`, `make -j4`, and
|
||||
passed. The standalone C++17 tree test also passed. The existing named
|
||||
comma-decimal C locale test skipped because the locale is not installed.
|
||||
|
||||
## Long value descriptions and multiplex ranges
|
||||
|
||||
`dbc_statement_test.cpp` adds a separate `StatementParserTest` suite to the
|
||||
existing parser qmake source list and the main test runner. Both parser APIs are
|
||||
checked with 50,000-character descriptions (including quotes, backslashes,
|
||||
semicolons, and spaces), 10,000 `VAL_` pairs, 10,000 multiplex ranges, and long
|
||||
numeric bounds and whitespace. Valid metadata must survive parsing without
|
||||
truncation, support value-description lookup after decoding, and activate or
|
||||
skip multiplex branches correctly. Statements may precede or follow definitions.
|
||||
|
||||
Malformed long statements cover incomplete quotes/escapes, invalid escapes,
|
||||
missing separators and semicolons, duplicate keys, numeric overflow, empty and
|
||||
reversed ranges, trailing text, and unresolved long names. The diagnostic API
|
||||
must return an empty database with the correct category, path, and source line;
|
||||
the throwing API must report that same location.
|
||||
|
||||
Verified with Qt 6.8.2 in separate Release and ASan/UBSan builds with
|
||||
`CONFIG+=dbc_qt`: each passed 2,449 core QtTest checks (including 29 checks in the
|
||||
new suite), seven model checks, and the qmake tree test. One named-locale case
|
||||
skipped because the locale is unavailable. Sanitizer errors were fatal; local
|
||||
leak checking used `detect_leaks=0` because of the documented ptrace restriction.
|
||||
|
||||
## Unsupported signal value types
|
||||
|
||||
`unsupportedSignalValueTypes` verifies that both parser APIs reject `SIG_VALTYPE_`
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "dbc_decode_builder.h"
|
||||
#include "dbc_decoder.h"
|
||||
#include "dbc_parser.h"
|
||||
#include <QTemporaryFile>
|
||||
#include <QtTest>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
std::string WriteFixture(QTemporaryFile &file, const std::string &text) {
|
||||
if (!file.open() || file.write(text.data(), static_cast<qint64>(text.size())) != static_cast<qint64>(text.size()) || !file.flush())
|
||||
throw std::runtime_error("Cannot write statement fixture");
|
||||
return file.fileName().toStdString();
|
||||
}
|
||||
|
||||
std::string Quote(const std::string &text) {
|
||||
std::ostringstream stream;
|
||||
stream << std::quoted(text);
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string RepeatedRanges() {
|
||||
std::string ranges = "1-1";
|
||||
for (unsigned index = 1U; index < 10000U; ++index)
|
||||
ranges += ",1-1";
|
||||
return ranges;
|
||||
}
|
||||
|
||||
const std::string definitions =
|
||||
"BO_ 1 Frame: 2 ECU\n"
|
||||
"SG_ Mode M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"SG_ Value m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
|
||||
}// namespace
|
||||
|
||||
class StatementParserTest : public QObject {
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void longValueDescriptions_data();
|
||||
void longValueDescriptions();
|
||||
void longMultiplexRanges_data();
|
||||
void longMultiplexRanges();
|
||||
void invalidLongStatements_data();
|
||||
void invalidLongStatements();
|
||||
};
|
||||
|
||||
void StatementParserTest::longValueDescriptions_data() {
|
||||
QTest::addColumn<std::string>("description");
|
||||
QTest::addColumn<unsigned>("count");
|
||||
QTest::addColumn<bool>("beforeDefinitions");
|
||||
QTest::newRow("50000-character-description") << std::string(50000U, 'x') << 1U << false;
|
||||
std::string escaped;
|
||||
for (unsigned index = 0U; index < 12500U; ++index)
|
||||
escaped += "\"\\; ";
|
||||
QTest::newRow("50000-escaped-characters") << escaped << 1U << true;
|
||||
QTest::newRow("10000-pairs") << std::string("Description") << 10000U << true;
|
||||
QTest::newRow("50000-spaces") << std::string(50000U, ' ') << 1U << false;
|
||||
}
|
||||
|
||||
void StatementParserTest::longValueDescriptions() {
|
||||
QFETCH(std::string, description);
|
||||
QFETCH(unsigned, count);
|
||||
QFETCH(bool, beforeDefinitions);
|
||||
std::string statement = "VAL_ 1 Value";
|
||||
for (unsigned index = 0U; index < count; ++index)
|
||||
statement += " " + std::to_string(index) + " " + Quote(description);
|
||||
statement += ";\n";
|
||||
const std::string frame = "BO_ 1 Frame: 4 ECU\nSG_ Value : 0|32@1+ (1,0) [0|1e10] \"\" ECU\n";
|
||||
QTemporaryFile file;
|
||||
const std::string path = WriteFixture(file, beforeDefinitions ? statement + frame : frame + statement);
|
||||
const DbcParser parser;
|
||||
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
|
||||
QVERIFY(!result.HasErrors());
|
||||
QVERIFY(result.diagnostics.empty());
|
||||
const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
|
||||
for (const DbcDatabase &database: databases) {
|
||||
const ValueDescriptions &values = database.frames.at(0).signals.at(0).valueDescriptions;
|
||||
QCOMPARE(values.size(), static_cast<std::size_t>(count));
|
||||
for (unsigned index = 0U; index < count; ++index)
|
||||
QCOMPARE(values.at(std::uint64_t(index)), description);
|
||||
const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 1U;
|
||||
const unsigned last = count - 1U;
|
||||
raw.data = {static_cast<std::uint8_t>(last), static_cast<std::uint8_t>(last >> 8U), 0U, 0U};
|
||||
const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw);
|
||||
QCOMPARE(decoded.status, FrameDecodeStatus::Valid);
|
||||
const std::string *label = decoded.signals.at(0).GetValueDescription();
|
||||
QVERIFY(label);
|
||||
QCOMPARE(*label, description);
|
||||
}
|
||||
}
|
||||
|
||||
void StatementParserTest::longMultiplexRanges_data() {
|
||||
QTest::addColumn<std::string>("ranges");
|
||||
QTest::addColumn<unsigned>("count");
|
||||
QTest::addColumn<bool>("beforeDefinitions");
|
||||
QTest::newRow("10000-ranges") << RepeatedRanges() << 10000U << false;
|
||||
const std::string paddedOne = std::string(50000U, '0') + "1";
|
||||
QTest::newRow("long-bounds") << paddedOne + "-" + paddedOne << 1U << true;
|
||||
QTest::newRow("long-whitespace") << std::string(50000U, ' ') + "1\t-\t1" + std::string(50000U, ' ') << 1U << false;
|
||||
}
|
||||
|
||||
void StatementParserTest::longMultiplexRanges() {
|
||||
QFETCH(std::string, ranges);
|
||||
QFETCH(unsigned, count);
|
||||
QFETCH(bool, beforeDefinitions);
|
||||
const std::string statement = "SG_MUL_VAL_ 1 Value Mode " + ranges + ";\n";
|
||||
QTemporaryFile file;
|
||||
const std::string path = WriteFixture(file, beforeDefinitions ? statement + definitions : definitions + statement);
|
||||
const DbcParser parser;
|
||||
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
|
||||
QVERIFY(!result.HasErrors());
|
||||
QVERIFY(result.diagnostics.empty());
|
||||
const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
|
||||
for (const DbcDatabase &database: databases) {
|
||||
const Multiplexing &mux = database.frames.at(0).signals.at(1).multiplexing;
|
||||
QCOMPARE(mux.selector, std::string("Mode"));
|
||||
QCOMPARE(mux.ranges.size(), static_cast<std::size_t>(count));
|
||||
for (const MultiplexRange &range: mux.ranges) {
|
||||
QCOMPARE(range.minimum, std::uint64_t(1));
|
||||
QCOMPARE(range.maximum, std::uint64_t(1));
|
||||
}
|
||||
const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 1U;
|
||||
raw.data = {1U, 42U};
|
||||
const DecodedFrameValue active = DbcDecoder().Decode(runtime, raw);
|
||||
QCOMPARE(active.status, FrameDecodeStatus::Valid);
|
||||
QVERIFY(active.signals.at(1).valid);
|
||||
QCOMPARE(std::get<std::uint64_t>(active.signals[1].rawValue), std::uint64_t(42));
|
||||
raw.data[0] = 2U;
|
||||
const DecodedFrameValue inactive = DbcDecoder().Decode(runtime, raw);
|
||||
QCOMPARE(inactive.signals.at(1).status, SignalDecodeStatus::Inactive);
|
||||
}
|
||||
}
|
||||
|
||||
void StatementParserTest::invalidLongStatements_data() {
|
||||
QTest::addColumn<std::string>("statement");
|
||||
QTest::addColumn<DbcDiagnosticCode>("code");
|
||||
const std::string text(50000U, 'x');
|
||||
const std::string value = "VAL_ 1 Value 0 \"" + text;
|
||||
QTest::newRow("val-missing-quote") << value + ";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-dangling-escape") << value + "\\" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-invalid-escape") << value + "\\q\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-missing-semicolon") << value + "\"" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-trailing-text") << value + "\"; junk" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-missing-pair-separator") << value + "\"1 \"On\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-missing-number-separator") << "VAL_ 1 Value 0\"" + text + "\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-duplicate") << value + "\" 00 \"Duplicate\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-overflow") << "VAL_ 1 Value " + std::string(50000U, '9') + " \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
QTest::newRow("val-long-missing-reference") << "VAL_ 1 " + text + " 0 \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
|
||||
const std::string mux = "SG_MUL_VAL_ 1 Value Mode " + RepeatedRanges();
|
||||
QTest::newRow("mux-missing-semicolon") << mux << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-trailing-comma") << mux + ",;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-empty-range") << mux + ",,1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-reversed-range") << mux + ",2-1;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-signed-bound") << mux + ",+1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-suffix") << mux + "x;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-overflow") << mux + ",0-18446744073709551616;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-long-overflow") << "SG_MUL_VAL_ 1 Value Mode 0-" + std::string(50000U, '9') + ";" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-trailing-text") << mux + "; junk" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
QTest::newRow("mux-long-missing-reference") << "SG_MUL_VAL_ 1 " + text + " Mode 1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
|
||||
}
|
||||
|
||||
void StatementParserTest::invalidLongStatements() {
|
||||
QFETCH(std::string, statement);
|
||||
QFETCH(DbcDiagnosticCode, code);
|
||||
QTemporaryFile file;
|
||||
const std::string path = WriteFixture(file, "\n" + statement + "\n" + definitions);
|
||||
const DbcParser parser;
|
||||
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
|
||||
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, code);
|
||||
QCOMPARE(error.filePath, path);
|
||||
QCOMPARE(error.lineNumber, std::size_t(2));
|
||||
try {
|
||||
parser.ParseFile(path);
|
||||
QFAIL("Malformed statements must throw through ParseFile");
|
||||
} catch (const std::runtime_error &exception) {
|
||||
QCOMPARE(std::string(exception.what()), path + ":2: " + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
int RunStatementParserTests(int argc, char **argv) {
|
||||
StatementParserTest tests;
|
||||
return QTest::qExec(&tests, argc, argv);
|
||||
}
|
||||
|
||||
#include "dbc_statement_test.moc"
|
||||
+3
-1
@@ -2,6 +2,7 @@
|
||||
#include <QtTest>
|
||||
|
||||
int RunParserTests(int argc, char **argv);
|
||||
int RunStatementParserTests(int argc, char **argv);
|
||||
int RunDecoderTests(int argc, char **argv);
|
||||
int RunCoverageTests(int argc, char **argv);
|
||||
|
||||
@@ -14,10 +15,11 @@ int RunPipelineTests(int argc, char **argv);
|
||||
int main(int argc, char **argv) {
|
||||
QCoreApplication application(argc, argv);
|
||||
const int parserResult = RunParserTests(argc, argv);
|
||||
const int statementResult = RunStatementParserTests(argc, argv);
|
||||
const int decoderResult = RunDecoderTests(argc, argv);
|
||||
const int coverageResult = RunCoverageTests(argc, argv);
|
||||
const int lifetimeResult = RunLifetimeTests(argc, argv);
|
||||
const int validationResult = RunValidationTests(argc, argv);
|
||||
const int pipelineResult = RunPipelineTests(argc, argv);
|
||||
return pipelineResult | validationResult | parserResult | decoderResult | coverageResult | lifetimeResult;
|
||||
return statementResult | pipelineResult | validationResult | parserResult | decoderResult | coverageResult | lifetimeResult;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
SOURCES += $$PWD/dbc_parser_test.cpp
|
||||
SOURCES += $$PWD/dbc_parser_test.cpp $$PWD/dbc_statement_test.cpp
|
||||
Reference in new issue
Block a user