Closes #11
This commit is contained in:
1 parent
0067cb4ddd
commit
f17ddb6aad
104 files changed
+1928
-889
No files matched your search
+118
-87
@@ -1,10 +1,12 @@
|
||||
#include "dbc_parser.h"
|
||||
#include "dbc_statement_handler.h"
|
||||
|
||||
#include <QtTest>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define CHECK(condition, name) QVERIFY2((condition), std::string(name).c_str())
|
||||
#include <stdexcept>
|
||||
|
||||
struct Fixture {
|
||||
@@ -41,33 +43,33 @@ std::string SignalLine(const std::string &start, const std::string &length,
|
||||
" (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU";
|
||||
}
|
||||
|
||||
struct TestContext {
|
||||
int failures = 0;
|
||||
std::size_t count = 0U;
|
||||
void Check(bool passed, const std::string &name) {
|
||||
++count;
|
||||
if (!passed) {
|
||||
++failures;
|
||||
std::cerr << "FAIL: " << name << '\n';
|
||||
}
|
||||
}
|
||||
class DbcParserTest : public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
void RejectSignal(const Fixture &fixture, const std::string &line, unsigned payload = 8U) {
|
||||
fixture.Write("BO_ 1 Frame: " + std::to_string(payload) + " ECU\n\n" + line + "\n");
|
||||
try {
|
||||
DbcParser().ParseFile(fixture.path.string());
|
||||
Check(false, "Reject " + line);
|
||||
CHECK(false, "Reject " + line);
|
||||
} catch (const std::runtime_error &error) {
|
||||
const std::string prefix = fixture.path.string() + ":3: ";
|
||||
Check(std::string(error.what()).find(prefix) == 0U &&
|
||||
CHECK(std::string(error.what()).find(prefix) == 0U &&
|
||||
std::string(error.what()).size() > prefix.size(),
|
||||
"Reject " + line);
|
||||
}
|
||||
}
|
||||
private Q_SLOTS:
|
||||
void validFrames();
|
||||
void invalidFrames();
|
||||
void integration();
|
||||
void invalidSignals();
|
||||
void validSignals();
|
||||
void dispatch();
|
||||
void independentSessions();
|
||||
};
|
||||
|
||||
int main() {
|
||||
void DbcParserTest::validFrames() {
|
||||
Fixture fixture;
|
||||
TestContext tests;
|
||||
struct ValidCase {
|
||||
const char *line;
|
||||
std::uint32_t id;
|
||||
@@ -86,14 +88,18 @@ int main() {
|
||||
fixture.Write(std::string(test.line) + '\n');
|
||||
try {
|
||||
const auto database = DbcParser().ParseFile(fixture.path.string());
|
||||
tests.Check(database.frames.size() == 1U && database.frames[0].name == "Frame" &&
|
||||
database.frames[0].canId == test.id &&
|
||||
database.frames[0].isExtended == test.extended &&
|
||||
database.frames[0].dlc == test.length &&
|
||||
database.frames[0].transmitter == "ECU",
|
||||
test.line);
|
||||
} catch (const std::exception &error) { tests.Check(false, error.what()); }
|
||||
CHECK(database.frames.size() == 1U && database.frames[0].name == "Frame" &&
|
||||
database.frames[0].canId == test.id &&
|
||||
database.frames[0].isExtended == test.extended &&
|
||||
database.frames[0].dlc == test.length &&
|
||||
database.frames[0].transmitter == "ECU",
|
||||
test.line);
|
||||
} catch (const std::exception &error) { CHECK(false, error.what()); }
|
||||
}
|
||||
}
|
||||
|
||||
void DbcParserTest::invalidFrames() {
|
||||
Fixture fixture;
|
||||
const char *invalid[] = {
|
||||
"BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
|
||||
"BO_ 1 Frame:", "BO_ 1 Frame: 8", "BO_ 1 Frame: 8 ECU extra",
|
||||
@@ -109,79 +115,91 @@ int main() {
|
||||
fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
|
||||
try {
|
||||
DbcParser().ParseFile(fixture.path.string());
|
||||
tests.Check(false, line);
|
||||
CHECK(false, line);
|
||||
} catch (const std::runtime_error &error) {
|
||||
const std::string prefix = fixture.path.string() + ":3: ";
|
||||
tests.Check(std::string(error.what()).find(prefix) == 0U &&
|
||||
std::string(error.what()).size() > prefix.size(),
|
||||
line);
|
||||
CHECK(std::string(error.what()).find(prefix) == 0U &&
|
||||
std::string(error.what()).size() > prefix.size(),
|
||||
line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DbcParserTest::integration() {
|
||||
Fixture fixture;
|
||||
fixture.Write(
|
||||
"BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
|
||||
" SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
|
||||
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
|
||||
"BO_ 257 Second: 8 ECU\n");
|
||||
const auto database = DbcParser().ParseFile(fixture.path.string());
|
||||
tests.Check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
||||
database.frames[0].comment == "Frame comment" &&
|
||||
database.frames[0].signals[0].comment == "Signal comment",
|
||||
"Integration");
|
||||
CHECK(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
||||
database.frames[0].comment == "Frame comment" &&
|
||||
database.frames[0].signals[0].comment == "Signal comment",
|
||||
"Integration");
|
||||
fixture.Write("\nSG_ Speed : invalid\n");
|
||||
try {
|
||||
DbcParser().ParseFile(fixture.path.string());
|
||||
tests.Check(false, "Signal error location");
|
||||
CHECK(false, "Signal error location");
|
||||
} catch (const std::runtime_error &error) {
|
||||
tests.Check(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
|
||||
"Signal error location");
|
||||
CHECK(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
|
||||
"Signal error location");
|
||||
}
|
||||
const DbcParser signalParser;
|
||||
}
|
||||
|
||||
void DbcParserTest::invalidSignals() {
|
||||
Fixture fixture;
|
||||
const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255");
|
||||
for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5",
|
||||
"4294967296", "999999999999999999999"}) {
|
||||
tests.RejectSignal(fixture, SignalLine(value, "8", "1+", "1", "0", "0", "255"));
|
||||
tests.RejectSignal(fixture, SignalLine("0", value, "1+", "1", "0", "0", "255"));
|
||||
RejectSignal(fixture, SignalLine(value, "8", "1+", "1", "0", "0", "255"));
|
||||
RejectSignal(fixture, SignalLine("0", value, "1+", "1", "0", "0", "255"));
|
||||
}
|
||||
for (const std::string length: {"0", "65"})
|
||||
tests.RejectSignal(fixture, SignalLine("0", length, "1+", "1", "0", "0", "255"));
|
||||
RejectSignal(fixture, SignalLine("0", length, "1+", "1", "0", "0", "255"));
|
||||
for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"})
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", order, "1", "0", "0", "255"));
|
||||
RejectSignal(fixture, SignalLine("0", "8", order, "1", "0", "0", "255"));
|
||||
for (const std::string value: {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
|
||||
RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
|
||||
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
|
||||
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
|
||||
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
|
||||
}
|
||||
tests.RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
|
||||
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
|
||||
for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
|
||||
std::string line = ordinary;
|
||||
line.replace(4U, 5U, name);
|
||||
tests.RejectSignal(fixture, line);
|
||||
RejectSignal(fixture, line);
|
||||
}
|
||||
for (const std::string broken: {
|
||||
"SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
|
||||
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
|
||||
tests.RejectSignal(fixture, broken);
|
||||
tests.RejectSignal(fixture, SignalLine("64", "1", "1+", "1", "0", "0", "1"));
|
||||
tests.RejectSignal(fixture, SignalLine("63", "2", "1+", "1", "0", "0", "3"));
|
||||
tests.RejectSignal(fixture, SignalLine("56", "2", "0+", "1", "0", "0", "3"));
|
||||
tests.RejectSignal(fixture, SignalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
|
||||
tests.RejectSignal(fixture, ordinary, 0U);
|
||||
RejectSignal(fixture, broken);
|
||||
RejectSignal(fixture, SignalLine("64", "1", "1+", "1", "0", "0", "1"));
|
||||
RejectSignal(fixture, SignalLine("63", "2", "1+", "1", "0", "0", "3"));
|
||||
RejectSignal(fixture, SignalLine("56", "2", "0+", "1", "0", "0", "3"));
|
||||
RejectSignal(fixture, SignalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
|
||||
RejectSignal(fixture, ordinary, 0U);
|
||||
for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) {
|
||||
std::string line = ordinary;
|
||||
line.erase(line.find(delimiter), 1U);
|
||||
tests.RejectSignal(fixture, line);
|
||||
RejectSignal(fixture, line);
|
||||
}
|
||||
}
|
||||
|
||||
void DbcParserTest::validSignals() {
|
||||
Fixture fixture;
|
||||
const DbcParser signalParser;
|
||||
fixture.Write("BO_ 1 Frame: 8 ECU\n" +
|
||||
SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
|
||||
SignalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
|
||||
const auto factors = signalParser.ParseFile(fixture.path.string());
|
||||
tests.Check(factors.frames[0].signals.size() == 2U &&
|
||||
factors.frames[0].signals[0].factor == 0.0 &&
|
||||
factors.frames[0].signals[1].factor == -1.0,
|
||||
"Zero and negative factors and equal range boundaries");
|
||||
CHECK(factors.frames[0].signals.size() == 2U &&
|
||||
factors.frames[0].signals[0].factor == 0.0 &&
|
||||
factors.frames[0].signals[1].factor == -1.0,
|
||||
"Zero and negative factors and equal range boundaries");
|
||||
|
||||
struct SignalCase {
|
||||
const char *start;
|
||||
@@ -204,55 +222,68 @@ int main() {
|
||||
try {
|
||||
const auto parsed = signalParser.ParseFile(fixture.path.string());
|
||||
const auto &signal = parsed.frames.at(0).signals.at(0);
|
||||
tests.Check(signal.startBit == std::stoul(test.start) &&
|
||||
signal.length == std::stoul(test.length) &&
|
||||
signal.isLittleEndian == (test.order[0] == '1') &&
|
||||
signal.isSigned == (test.order[1] == '-') &&
|
||||
signal.factor == 5.0 && signal.offset == -0.25 &&
|
||||
signal.minimum == -1.0 && signal.maximum == 200.0,
|
||||
"Valid signal boundary and numeric metadata");
|
||||
} catch (const std::exception &error) { tests.Check(false, error.what()); }
|
||||
CHECK(signal.startBit == std::stoul(test.start) &&
|
||||
signal.length == std::stoul(test.length) &&
|
||||
signal.isLittleEndian == (test.order[0] == '1') &&
|
||||
signal.isSigned == (test.order[1] == '-') &&
|
||||
signal.factor == 5.0 && signal.offset == -0.25 &&
|
||||
signal.minimum == -1.0 && signal.maximum == 200.0,
|
||||
"Valid signal boundary and numeric metadata");
|
||||
} catch (const std::exception &error) { CHECK(false, error.what()); }
|
||||
}
|
||||
fixture.Write("BO_ 1 Frame: 8 ECU\n"
|
||||
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
|
||||
try {
|
||||
const auto parsed = signalParser.ParseFile(fixture.path.string());
|
||||
const auto &signal = parsed.frames.at(0).signals.at(0);
|
||||
tests.Check(signal.name == "Value" && signal.unit == "rpm" &&
|
||||
signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
|
||||
"Signal tabs, spaces, and receiver list");
|
||||
} catch (const std::exception &error) { tests.Check(false, error.what()); }
|
||||
CHECK(signal.name == "Value" && signal.unit == "rpm" &&
|
||||
signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
|
||||
"Signal tabs, spaces, and receiver list");
|
||||
} catch (const std::exception &error) { CHECK(false, error.what()); }
|
||||
}
|
||||
|
||||
void DbcParserTest::dispatch() {
|
||||
CountingHandler::instances = 0;
|
||||
CountingHandler::calls = 0;
|
||||
DbcDatabase dispatchDatabase;
|
||||
DbcParseContext context{dispatchDatabase, nullptr};
|
||||
DbcStatementDispatcher dispatcher;
|
||||
dispatcher.RegisterType<CountingHandler>("TEST_");
|
||||
tests.Check(dispatcher.Dispatch("TEST_ first", context) &&
|
||||
dispatcher.Dispatch("TEST_ second", context) &&
|
||||
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
||||
dispatchDatabase.frames.size() == 2U,
|
||||
"Factory dispatch and handler reuse");
|
||||
tests.Check(!dispatcher.Dispatch("TEST_OTHER data", context) &&
|
||||
!dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
|
||||
!dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
||||
CountingHandler::calls == 2,
|
||||
"Exact and unknown keyword dispatch");
|
||||
CHECK(dispatcher.Dispatch("TEST_ first", context) &&
|
||||
dispatcher.Dispatch("TEST_ second", context) &&
|
||||
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
||||
dispatchDatabase.frames.size() == 2U,
|
||||
"Factory dispatch and handler reuse");
|
||||
CHECK(!dispatcher.Dispatch("TEST_OTHER data", context) &&
|
||||
!dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
|
||||
!dispatcher.Dispatch("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
||||
CountingHandler::calls == 2,
|
||||
"Exact and unknown keyword dispatch");
|
||||
}
|
||||
|
||||
void DbcParserTest::independentSessions() {
|
||||
Fixture fixture;
|
||||
const DbcParser parser;
|
||||
fixture.Write("BO_ 1 First: 8 ECU\n");
|
||||
tests.Check(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
|
||||
CHECK(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
|
||||
fixture.Write("SG_ Speed : invalid\n");
|
||||
try {
|
||||
parser.ParseFile(fixture.path.string());
|
||||
tests.Check(false, "Independent parse context");
|
||||
CHECK(false, "Independent parse context");
|
||||
} catch (const std::runtime_error &error) {
|
||||
tests.Check(std::string(error.what()).find("Signal found before any frame") !=
|
||||
std::string::npos,
|
||||
"Independent parse context");
|
||||
CHECK(std::string(error.what()).find("Signal found before any frame") !=
|
||||
std::string::npos,
|
||||
"Independent parse context");
|
||||
}
|
||||
fixture.Write("BO_ 2 Second: 8 ECU\n");
|
||||
const auto recovered = parser.ParseFile(fixture.path.string());
|
||||
tests.Check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
|
||||
"Fresh parse after error");
|
||||
if (tests.failures != 0) return 1;
|
||||
std::cout << "All " << tests.count << " parser cases passed.\n";
|
||||
return 0;
|
||||
CHECK(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
|
||||
"Fresh parse after error");
|
||||
}
|
||||
|
||||
int RunParserTests(int argc, char **argv) {
|
||||
DbcParserTest tests;
|
||||
return QTest::qExec(&tests, argc, argv);
|
||||
}
|
||||
|
||||
#include "dbc_parser_test.moc"
|
||||
Reference in new issue
Block a user