DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
dbc_statement_test.cpp
2#include "dbc_decoder.h"
3#include "dbc_parser.h"
4#include <QTemporaryFile>
5#include <QtTest>
6#include <iomanip>
7#include <sstream>
8#include <stdexcept>
9
10namespace {
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();
15 }
16
17 std::string Quote(const std::string &text) {
18 std::ostringstream stream;
19 stream << std::quoted(text);
20 return stream.str();
21 }
22
23 std::string RepeatedRanges() {
24 std::string ranges = "1-1";
25 for (unsigned index = 1U; index < 10000U; ++index)
26 ranges += ",1-1";
27 return ranges;
28 }
29
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";
34}// namespace
35
36class StatementParserTest : public QObject {
37 Q_OBJECT
38private Q_SLOTS:
39 void longValueDescriptions_data();
40 void longValueDescriptions();
41 void longMultiplexRanges_data();
42 void longMultiplexRanges();
43 void invalidLongStatements_data();
44 void invalidLongStatements();
45};
46
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;
52 std::string escaped;
53 for (unsigned index = 0U; index < 12500U; ++index)
54 escaped += "\"\\; ";
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;
58}
59
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);
67 statement += ";\n";
68 const std::string frame = "BO_ 1 Frame: 4 ECU\nSG_ Value : 0|32@1+ (1,0) [0|1e10] \"\" ECU\n";
69 QTemporaryFile file;
70 const std::string path = WriteFixture(file, beforeDefinitions ? statement + frame : frame + statement);
71 const DbcParser parser;
72 const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
73 QVERIFY(!result.HasErrors());
74 QVERIFY(result.diagnostics.empty());
75 const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
76 for (const DbcDatabase &database: databases) {
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);
81 const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
82 RawCanFrame raw;
83 raw.canId = 1U;
84 const unsigned last = count - 1U;
85 raw.data = {static_cast<std::uint8_t>(last), static_cast<std::uint8_t>(last >> 8U), 0U, 0U};
86 const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw);
87 QCOMPARE(decoded.status, FrameDecodeStatus::Valid);
88 const std::string *label = decoded.signals.at(0).GetValueDescription();
89 QVERIFY(label);
90 QCOMPARE(*label, description);
91 }
92}
93
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;
102}
103
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";
109 QTemporaryFile file;
110 const std::string path = WriteFixture(file, beforeDefinitions ? statement + definitions : definitions + statement);
111 const DbcParser parser;
112 const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
113 QVERIFY(!result.HasErrors());
114 QVERIFY(result.diagnostics.empty());
115 const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
116 for (const DbcDatabase &database: databases) {
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));
120 for (const MultiplexRange &range: mux.ranges) {
121 QCOMPARE(range.minimum, std::uint64_t(1));
122 QCOMPARE(range.maximum, std::uint64_t(1));
123 }
124 const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
125 RawCanFrame raw;
126 raw.canId = 1U;
127 raw.data = {1U, 42U};
128 const DecodedFrameValue active = DbcDecoder().Decode(runtime, raw);
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));
132 raw.data[0] = 2U;
133 const DecodedFrameValue inactive = DbcDecoder().Decode(runtime, raw);
134 QCOMPARE(inactive.signals.at(1).status, SignalDecodeStatus::Inactive);
135 }
136}
137
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;
164}
165
166void StatementParserTest::invalidLongStatements() {
167 QFETCH(std::string, statement);
168 QFETCH(DbcDiagnosticCode, code);
169 QTemporaryFile file;
170 const std::string path = WriteFixture(file, "\n" + statement + "\n" + definitions);
171 const DbcParser parser;
172 const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
173 QVERIFY(result.HasErrors());
174 QVERIFY(result.database.frames.empty());
175 QCOMPARE(result.diagnostics.size(), std::size_t(1));
176 const DbcDiagnostic &error = result.diagnostics[0];
177 QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
178 QCOMPARE(error.code, code);
179 QCOMPARE(error.filePath, path);
180 QCOMPARE(error.lineNumber, std::size_t(2));
181 try {
182 parser.ParseFile(path);
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);
186 }
187}
188
189int RunStatementParserTests(int argc, char **argv) {
190 StatementParserTest tests;
191 return QTest::qExec(&tests, argc, argv);
192}
193
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.
Definition dbc_decoder.h:87
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Minimal DBC parser.
Definition dbc_parser.h:45
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.
std::string message
DbcDiagnosticSeverity severity
DbcDiagnosticCode code
std::string filePath
std::size_t lineNumber
Parsed database and diagnostics; the database is empty on error.
std::vector< DbcDiagnostic > diagnostics
bool HasErrors() const
Return whether any diagnostic has error severity.
DbcDatabase database
Fully decoded frame.
Definition dbc_decoder.h:74
FrameDecodeStatus status
Definition dbc_decoder.h:79
Inclusive raw selector range, without expanding individual values.
Basic markers and optional explicit extended multiplexing condition.
std::vector< MultiplexRange > ranges
std::string selector
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:25
std::vector< std::uint8_t > data
Definition dbc_decoder.h:28
std::uint32_t canId
Definition dbc_decoder.h:26