134 lines
5.7 KiB
C++
134 lines
5.7 KiB
C++
#include "dbc_parser.h"
|
|
#include "dbc_statement_handler.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.");
|
|
}
|
|
};
|
|
|
|
class CountingHandler : public DbcStatementHandler {
|
|
public:
|
|
inline static int instances = 0;
|
|
inline static int calls = 0;
|
|
CountingHandler() { ++instances; }
|
|
void Handle (const std::string &, DbcParseContext &context) const override {
|
|
++calls;
|
|
context.database.frames.emplace_back();
|
|
}
|
|
};
|
|
|
|
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");
|
|
}
|
|
DbcDatabase dispatchDatabase;
|
|
DbcParseContext context {dispatchDatabase, nullptr};
|
|
DbcStatementDispatcher dispatcher;
|
|
dispatcher.RegisterType<CountingHandler> ("TEST_");
|
|
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");
|
|
const DbcParser parser;
|
|
fixture.Write ("BO_ 1 First: 8 ECU\n");
|
|
check (parser.ParseFile (fixture.path.string()).frames.size() == 1U, "First parse");
|
|
fixture.Write ("SG_ Speed : invalid\n");
|
|
try {
|
|
parser.ParseFile (fixture.path.string());
|
|
check (false, "Independent parse context");
|
|
} catch (const std::runtime_error &error) {
|
|
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());
|
|
check (recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
|
|
"Fresh parse after error");
|
|
if (failures != 0) return 1;
|
|
std::cout << "All " << count << " parser cases passed.\n";
|
|
return 0;
|
|
}
|