This commit is contained in:
deeaitch committed 2026-10-05 21:41:19 -04:00
1 parent 0067cb4ddd
commit f17ddb6aad
104 files changed
+1928 -889

No files matched your search

+28 -24
View File
@@ -1,25 +1,40 @@
# Decoder regression tests
# QtTest regression tests
Run from the repository root with a C++17 compiler:
Open the repository's root `dbc.pro` in Qt Creator. It contains `core` (a static
C++17 library) and `tests` (the `dbc_tests` QtTest executable). Core is built first;
tests link its archive rather than compiling its implementation again.
Qt Core and Qt Test development packages are required for tests only.
Build from the repository root with Qt 6 (use `qmake` for Qt 5):
```sh
g++ -std=c++17 -Wall -Wextra -Wpedantic -I. \
tests/dbc_decoder_test.cpp dbc_decoder.cpp -o /tmp/dbc_decoder_test
/tmp/dbc_decoder_test
mkdir build
cd build
qmake6 ../dbc.pro
make -j
make check
```
Alternatively, build the standalone qmake project out of source:
Or run `./tests/dbc_tests` directly. Both parser and decoder suites run, and the
executable returns nonzero when either suite fails. Decoder cases appear as
individual QtTest data rows; parser checks are grouped into named test slots.
Checks also run in release builds.
To build tests separately against an existing core build:
```sh
mkdir -p /tmp/dbc_decoder_test_build
cd /tmp/dbc_decoder_test_build
qmake /absolute/path/to/dbc/tests/dbc_decoder_test.pro
mkdir /tmp/dbc-tests-build
cd /tmp/dbc-tests-build
qmake6 /absolute/path/to/dbc/tests/tests.pro \
DBC_CORE_BUILD_DIR=/absolute/path/to/build/core
make
./dbc_decoder_test
make check
```
The executable returns a nonzero exit code if any case fails. Checks remain
active in release builds and do not depend on `assert` or an external test framework.
`parser_tests.pri` and `decoder_tests.pri` include the test sources.
`../core/core.pri` supplies the library include path and linkage.
## Decoder coverage
Coverage includes Motorola bit numbering within and across bytes, unaligned
signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary values
@@ -27,18 +42,7 @@ signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary val
payloads, and Intel regression cases. Frame validity means the definition was
found; insufficient payloads are checked through signal validity.
## Parser regression tests
Run from the repository root:
```sh
g++ -std=c++17 -Wall -Wextra -Wpedantic -Werror -I. \
tests/dbc_parser_test.cpp dbc_parser.cpp dbc_statement_handler.cpp -o /tmp/dbc_parser_test
/tmp/dbc_parser_test
```
For qmake, use `tests/dbc_parser_test.pro` in a separate build directory,
following the decoder instructions above, then run `./dbc_parser_test`.
## Parser coverage
Parser tests cover standard and extended CAN IDs, whitespace, payload storage
boundaries, missing fields, malformed and overflowing numbers, reserved ID bits,
+64 -51
View File
@@ -1,6 +1,6 @@
#include "dbc_decoder.h"
#include <iostream>
#include <QtTest>
#include <limits>
#include <sstream>
@@ -18,7 +18,18 @@ struct DecodeCase {
double physicalValue;
};
int main() {
Q_DECLARE_METATYPE(DecodeCase)
class DbcDecoderTest : public QObject {
Q_OBJECT
private Q_SLOTS:
void decode_data();
void decode();
void boundaries();
};
void DbcDecoderTest::decode_data() {
QTest::addColumn<DecodeCase>("test");
const DecodeCase cases[] = {
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
@@ -38,47 +49,49 @@ int main() {
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
int failures = 0;
for (const DecodeCase &test: cases) {
DecodeSignal signal;
signal.name = test.name;
signal.byteOrder = test.byteOrder;
signal.startBit = test.startBit;
signal.length = test.length;
signal.valueType = test.valueType;
signal.factor = test.factor;
signal.offset = test.offset;
for (const auto &test: cases)
QTest::newRow(test.name) << test;
}
DecodeFrame definition;
definition.canId = 0x123U;
definition.dlc = static_cast<std::uint8_t>(test.data.size());
definition.signals.push_back(signal);
DecodeDatabase database;
database.frames.push_back(definition);
database.frameIndexByKey.emplace(FrameKey(definition.canId, false), 0U);
void DbcDecoderTest::decode() {
QFETCH(DecodeCase, test);
DecodeSignal signal;
signal.name = test.name;
signal.byteOrder = test.byteOrder;
signal.startBit = test.startBit;
signal.length = test.length;
signal.valueType = test.valueType;
signal.factor = test.factor;
signal.offset = test.offset;
RawCanFrame frame;
frame.canId = definition.canId;
frame.data = test.data;
const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
const bool passed = result.valid &&
(result.definition == &database.frames[0]) &&
(result.signals.size() == 1U) &&
(result.signals[0].definition == &database.frames[0].signals[0]) &&
(result.signals[0].valid == test.valid) &&
(!test.valid || (((test.valueType == ValueType::Signed)
? (std::get_if<std::int64_t>(&result.signals[0].rawValue) &&
std::get<std::int64_t>(result.signals[0].rawValue) == test.rawValue)
: (std::get_if<std::uint64_t>(&result.signals[0].rawValue) &&
std::get<std::uint64_t>(result.signals[0].rawValue) ==
static_cast<std::uint64_t>(test.rawValue))) &&
(result.signals[0].physicalValue == test.physicalValue)));
if (!passed) {
std::cerr << "FAIL: " << test.name << '\n';
++failures;
}
}
std::size_t boundaryCases = 0U;
DecodeFrame definition;
definition.canId = 0x123U;
definition.dlc = static_cast<std::uint8_t>(test.data.size());
definition.signals.push_back(signal);
DecodeDatabase database;
database.frames.push_back(definition);
database.frameIndexByKey.emplace(FrameKey(definition.canId, false), 0U);
RawCanFrame frame;
frame.canId = definition.canId;
frame.data = test.data;
const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
const bool passed = result.valid &&
(result.definition == &database.frames[0]) &&
(result.signals.size() == 1U) &&
(result.signals[0].definition == &database.frames[0].signals[0]) &&
(result.signals[0].valid == test.valid) &&
(!test.valid || (((test.valueType == ValueType::Signed)
? (std::get_if<std::int64_t>(&result.signals[0].rawValue) &&
std::get<std::int64_t>(result.signals[0].rawValue) == test.rawValue)
: (std::get_if<std::uint64_t>(&result.signals[0].rawValue) &&
std::get<std::uint64_t>(result.signals[0].rawValue) ==
static_cast<std::uint64_t>(test.rawValue))) &&
(result.signals[0].physicalValue == test.physicalValue)));
QVERIFY2(passed, test.name);
}
void DbcDecoderTest::boundaries() {
for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
@@ -126,17 +139,17 @@ int main() {
expectedText << bits;
else
expectedText << signedExpected;
if (!decoded.valid || decoded.rawValue != expected ||
decoded.physicalValue != physical || actualText.str() != expectedText.str()) {
std::cerr << "FAIL: 64-bit boundary " << bits << '\n';
++failures;
}
++boundaryCases;
QVERIFY2(decoded.valid && decoded.rawValue == expected &&
decoded.physicalValue == physical && actualText.str() == expectedText.str(),
expectedText.str().c_str());
}
}
}
if (failures != 0)
return 1;
std::cout << "All " << sizeof(cases) / sizeof(cases[0]) + boundaryCases << " decoder cases passed.\n";
return 0;
}
int RunDecoderTests(int argc, char **argv) {
DbcDecoderTest tests;
return QTest::qExec(&tests, argc, argv);
}
#include "dbc_decoder_test.moc"
-7
View File
@@ -1,7 +0,0 @@
TEMPLATE = app
TARGET = dbc_decoder_test
CONFIG += console c++17
CONFIG -= app_bundle qt
INCLUDEPATH += ..
SOURCES += dbc_decoder_test.cpp ../dbc_decoder.cpp
HEADERS += ../dbc_decoder.h ../decode_database.h
+118 -87
View File
@@ -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"
-7
View File
@@ -1,7 +0,0 @@
TEMPLATE = app
TARGET = dbc_parser_test
CONFIG += console c++17
CONFIG -= app_bundle qt
INCLUDEPATH += ..
SOURCES += dbc_parser_test.cpp ../dbc_parser.cpp ../dbc_statement_handler.cpp
HEADERS += ../dbc_statement_handler.h ../typefactory.h ../dbc_parser.h ../dbc_database.h ../frame_info.h ../signal_info.h
+1
View File
@@ -0,0 +1 @@
SOURCES += $$PWD/dbc_decoder_test.cpp
+12
View File
@@ -0,0 +1,12 @@
#include <QCoreApplication>
#include <QtTest>
int RunParserTests(int argc, char **argv);
int RunDecoderTests(int argc, char **argv);
int main(int argc, char **argv) {
QCoreApplication application(argc, argv);
const int parserResult = RunParserTests(argc, argv);
const int decoderResult = RunDecoderTests(argc, argv);
return parserResult | decoderResult;
}
+1
View File
@@ -0,0 +1 @@
SOURCES += $$PWD/dbc_parser_test.cpp
+11
View File
@@ -0,0 +1,11 @@
TEMPLATE = app
TARGET = dbc_tests
QT += core testlib
QT -= gui
CONFIG += console testcase c++17 no_keywords
CONFIG -= app_bundle
include(../core/core.pri)
include(parser_tests.pri)
include(decoder_tests.pri)
SOURCES += main.cpp
CONFIG -= debug_and_release debug_and_release_target