4#include <QTemporaryFile>
11 std::string WriteFixture(QTemporaryFile &file,
const std::string &text) {
12 if (!file.open() || file.write(text.data(),
static_cast<qint64
>(text.size())) !=
static_cast<qint64
>(text.size()) || !file.flush())
13 throw std::runtime_error(
"Cannot write statement fixture");
14 return file.fileName().toStdString();
17 std::string Quote(
const std::string &text) {
18 std::ostringstream stream;
19 stream << std::quoted(text);
23 std::string RepeatedRanges() {
24 std::string ranges =
"1-1";
25 for (
unsigned index = 1U; index < 10000U; ++index)
30 const std::string definitions =
31 "BO_ 1 Frame: 2 ECU\n"
32 "SG_ Mode M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
33 "SG_ Value m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
36class StatementParserTest :
public QObject {
39 void longValueDescriptions_data();
40 void longValueDescriptions();
41 void longMultiplexRanges_data();
42 void longMultiplexRanges();
43 void invalidLongStatements_data();
44 void invalidLongStatements();
47void StatementParserTest::longValueDescriptions_data() {
48 QTest::addColumn<std::string>(
"description");
49 QTest::addColumn<unsigned>(
"count");
50 QTest::addColumn<bool>(
"beforeDefinitions");
51 QTest::newRow(
"50000-character-description") << std::string(50000U,
'x') << 1U <<
false;
53 for (
unsigned index = 0U; index < 12500U; ++index)
55 QTest::newRow(
"50000-escaped-characters") << escaped << 1U <<
true;
56 QTest::newRow(
"10000-pairs") << std::string(
"Description") << 10000U <<
true;
57 QTest::newRow(
"50000-spaces") << std::string(50000U,
' ') << 1U <<
false;
60void StatementParserTest::longValueDescriptions() {
61 QFETCH(std::string, description);
62 QFETCH(
unsigned, count);
63 QFETCH(
bool, beforeDefinitions);
64 std::string statement =
"VAL_ 1 Value";
65 for (
unsigned index = 0U; index < count; ++index)
66 statement +=
" " + std::to_string(index) +
" " + Quote(description);
68 const std::string frame =
"BO_ 1 Frame: 4 ECU\nSG_ Value : 0|32@1+ (1,0) [0|1e10] \"\" ECU\n";
70 const std::string path = WriteFixture(file, beforeDefinitions ? statement + frame : frame + statement);
75 const std::vector<DbcDatabase> databases = {result.
database, parser.
ParseFile(path)};
77 const ValueDescriptions &values = database.
frames.at(0).signals.at(0).valueDescriptions;
78 QCOMPARE(values.size(),
static_cast<std::size_t
>(count));
79 for (
unsigned index = 0U; index < count; ++index)
80 QCOMPARE(values.at(std::uint64_t(index)), description);
84 const unsigned last = count - 1U;
85 raw.
data = {
static_cast<std::uint8_t
>(last),
static_cast<std::uint8_t
>(last >> 8U), 0U, 0U};
87 QCOMPARE(decoded.
status, FrameDecodeStatus::Valid);
88 const std::string *label = decoded.signals.at(0).GetValueDescription();
90 QCOMPARE(*label, description);
94void StatementParserTest::longMultiplexRanges_data() {
95 QTest::addColumn<std::string>(
"ranges");
96 QTest::addColumn<unsigned>(
"count");
97 QTest::addColumn<bool>(
"beforeDefinitions");
98 QTest::newRow(
"10000-ranges") << RepeatedRanges() << 10000U <<
false;
99 const std::string paddedOne = std::string(50000U,
'0') +
"1";
100 QTest::newRow(
"long-bounds") << paddedOne +
"-" + paddedOne << 1U <<
true;
101 QTest::newRow(
"long-whitespace") << std::string(50000U,
' ') +
"1\t-\t1" + std::string(50000U,
' ') << 1U <<
false;
104void StatementParserTest::longMultiplexRanges() {
105 QFETCH(std::string, ranges);
106 QFETCH(
unsigned, count);
107 QFETCH(
bool, beforeDefinitions);
108 const std::string statement =
"SG_MUL_VAL_ 1 Value Mode " + ranges +
";\n";
110 const std::string path = WriteFixture(file, beforeDefinitions ? statement + definitions : definitions + statement);
115 const std::vector<DbcDatabase> databases = {result.
database, parser.
ParseFile(path)};
117 const Multiplexing &mux = database.frames.at(0).signals.at(1).multiplexing;
118 QCOMPARE(mux.
selector, std::string(
"Mode"));
119 QCOMPARE(mux.
ranges.size(),
static_cast<std::size_t
>(count));
121 QCOMPARE(range.minimum, std::uint64_t(1));
122 QCOMPARE(range.maximum, std::uint64_t(1));
127 raw.
data = {1U, 42U};
129 QCOMPARE(active.
status, FrameDecodeStatus::Valid);
130 QVERIFY(active.signals.at(1).
valid);
131 QCOMPARE(std::get<std::uint64_t>(active.signals[1].rawValue), std::uint64_t(42));
134 QCOMPARE(inactive.signals.at(1).
status, SignalDecodeStatus::Inactive);
138void StatementParserTest::invalidLongStatements_data() {
139 QTest::addColumn<std::string>(
"statement");
140 QTest::addColumn<DbcDiagnosticCode>(
"code");
141 const std::string text(50000U,
'x');
142 const std::string value =
"VAL_ 1 Value 0 \"" + text;
143 QTest::newRow(
"val-missing-quote") << value +
";" << DbcDiagnosticCode::InvalidValueDescription;
144 QTest::newRow(
"val-dangling-escape") << value +
"\\" << DbcDiagnosticCode::InvalidValueDescription;
145 QTest::newRow(
"val-invalid-escape") << value +
"\\q\";" << DbcDiagnosticCode::InvalidValueDescription;
146 QTest::newRow(
"val-missing-semicolon") << value +
"\"" << DbcDiagnosticCode::InvalidValueDescription;
147 QTest::newRow(
"val-trailing-text") << value +
"\"; junk" << DbcDiagnosticCode::InvalidValueDescription;
148 QTest::newRow(
"val-missing-pair-separator") << value +
"\"1 \"On\";" << DbcDiagnosticCode::InvalidValueDescription;
149 QTest::newRow(
"val-missing-number-separator") <<
"VAL_ 1 Value 0\"" + text +
"\";" << DbcDiagnosticCode::InvalidValueDescription;
150 QTest::newRow(
"val-duplicate") << value +
"\" 00 \"Duplicate\";" << DbcDiagnosticCode::InvalidValueDescription;
151 QTest::newRow(
"val-overflow") <<
"VAL_ 1 Value " + std::string(50000U,
'9') +
" \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
152 QTest::newRow(
"val-long-missing-reference") <<
"VAL_ 1 " + text +
" 0 \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
153 const std::string mux =
"SG_MUL_VAL_ 1 Value Mode " + RepeatedRanges();
154 QTest::newRow(
"mux-missing-semicolon") << mux << DbcDiagnosticCode::InvalidMultiplexing;
155 QTest::newRow(
"mux-trailing-comma") << mux +
",;" << DbcDiagnosticCode::InvalidMultiplexing;
156 QTest::newRow(
"mux-empty-range") << mux +
",,1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
157 QTest::newRow(
"mux-reversed-range") << mux +
",2-1;" << DbcDiagnosticCode::InvalidMultiplexing;
158 QTest::newRow(
"mux-signed-bound") << mux +
",+1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
159 QTest::newRow(
"mux-suffix") << mux +
"x;" << DbcDiagnosticCode::InvalidMultiplexing;
160 QTest::newRow(
"mux-overflow") << mux +
",0-18446744073709551616;" << DbcDiagnosticCode::InvalidMultiplexing;
161 QTest::newRow(
"mux-long-overflow") <<
"SG_MUL_VAL_ 1 Value Mode 0-" + std::string(50000U,
'9') +
";" << DbcDiagnosticCode::InvalidMultiplexing;
162 QTest::newRow(
"mux-trailing-text") << mux +
"; junk" << DbcDiagnosticCode::InvalidMultiplexing;
163 QTest::newRow(
"mux-long-missing-reference") <<
"SG_MUL_VAL_ 1 " + text +
" Mode 1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
166void StatementParserTest::invalidLongStatements() {
167 QFETCH(std::string, statement);
168 QFETCH(DbcDiagnosticCode, code);
170 const std::string path = WriteFixture(file,
"\n" + statement +
"\n" + definitions);
175 QCOMPARE(result.
diagnostics.size(), std::size_t(1));
177 QCOMPARE(error.
severity, DbcDiagnosticSeverity::Error);
178 QCOMPARE(error.
code, code);
183 QFAIL(
"Malformed statements must throw through ParseFile");
184 }
catch (
const std::runtime_error &exception) {
185 QCOMPARE(std::string(exception.what()), path +
":2: " + error.
message);
189int RunStatementParserTests(
int argc,
char **argv) {
190 StatementParserTest tests;
191 return QTest::qExec(&tests, argc, argv);
194#include "dbc_statement_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
Runtime CAN decoder using prebuilt decode database.
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
Parse with warnings and a structured first error instead of throwing.
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
Runtime decode database builder.
CAN frame decoding API and result types.
DBC file parser and interpretation options.
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Diagnostic message and physical source location.
DbcDiagnosticSeverity severity
Parsed database and diagnostics; the database is empty on error.
std::vector< DbcDiagnostic > diagnostics
bool HasErrors() const
Return whether any diagnostic has error severity.
Inclusive raw selector range, without expanding individual values.
Basic markers and optional explicit extended multiplexing condition.
std::vector< MultiplexRange > ranges
Raw CAN frame used for runtime or trace decoding.
std::vector< std::uint8_t > data