Closes #3 Validate DBC frame definitions and report error locations

This commit is contained in:
deeaitch committed 2026-10-01 13:23:01 -04:00
1 parent 69074e3321
commit 3bd9339baf
6 files changed
+213 -36

No files matched your search

+19
View File
@@ -26,3 +26,22 @@ signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary val
(including `INT64_MIN`, `INT64_MAX`, and `UINT64_MAX`), numeric output, invalid lengths, insufficient
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 -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 tests cover standard and extended CAN IDs, whitespace, payload storage
boundaries, missing fields, malformed and overflowing numbers, reserved ID bits,
error paths and line numbers, and integration with signals and comments.
Temporary DBC fixtures are created in the system temporary directory and removed
automatically.
+94
View File
@@ -0,0 +1,94 @@
#include "dbc_parser.h"
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <stdexcept>
struct Fixture {
std::filesystem::path path = std::filesystem::temp_directory_path() /
("dbc_parser_test_" + std::to_string (
std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
~Fixture() { std::error_code error; std::filesystem::remove (path, error); }
void Write (const std::string &text) const {
std::ofstream output (path);
output << text;
if (!output)
throw std::runtime_error ("Cannot write test fixture.");
}
};
int main() {
Fixture fixture;
int failures = 0;
std::size_t count = 0U;
const auto check = [&] (bool passed, const std::string &name) {
++count;
if (!passed) { ++failures; std::cerr << "FAIL: " << name << '\n'; }
};
struct ValidCase { const char *line; std::uint32_t id; bool extended; unsigned length; };
const ValidCase valid[] = {
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
{"BO_ 2048 Frame: 64 ECU", 2048U, true, 64U},
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}
};
for (const auto &test : valid) {
fixture.Write (std::string (test.line) + '\n');
try {
const auto database = DbcParser().ParseFile (fixture.path.string());
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()); }
}
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",
"BO_ 1 Two Names: 8 ECU", "BO_ -1 Frame: 8 ECU", "BO_ +1 Frame: 8 ECU",
"BO_ 1x Frame: 8 ECU", "BO_ 0x123 Frame: 8 ECU", "BO_ 1.5 Frame: 8 ECU",
"BO_ 4294967296 Frame: 8 ECU", "BO_ 999999999999999999999 Frame: 8 ECU",
"BO_ 536870912 Frame: 8 ECU", "BO_ 1073741824 Frame: 8 ECU",
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"
};
for (const auto *line : invalid) {
fixture.Write (std::string ("VERSION \"test\"\n\n") + line + '\n');
try {
DbcParser().ParseFile (fixture.path.string());
check (false, line);
} catch (const std::runtime_error &error) {
const std::string prefix = fixture.path.string() + ":3: ";
check (std::string (error.what()).find (prefix) == 0U &&
std::string (error.what()).size() > prefix.size(), line);
}
}
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());
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());
check (false, "Signal error location");
} catch (const std::runtime_error &error) {
check (std::string (error.what()).find (fixture.path.string() + ":2: ") == 0U,
"Signal error location");
}
if (failures != 0) return 1;
std::cout << "All " << count << " parser cases passed.\n";
return 0;
}
+7
View File
@@ -0,0 +1,7 @@
TEMPLATE = app
TARGET = dbc_parser_test
CONFIG += console c++17
CONFIG -= app_bundle qt
INCLUDEPATH += ..
SOURCES += dbc_parser_test.cpp ../dbc_parser.cpp
HEADERS += ../dbc_parser.h ../dbc_database.h ../frame_info.h ../signal_info.h