diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3bc437c..20dcf3f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,13 +17,18 @@ jobs: - uses: actions/checkout@v3 - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} .. - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -- -j + run: | + cd build + make - name: Test - working-directory: ${{github.workspace}}/build - # Didn't configure to use ctest. Opted for a custom target to run instead - run: cmake --build ${{github.workspace}}/build --target test -- -j + run: | + cd build + ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 8992cfc..40b1f8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.10) project(dbc) option(DEBUG "use debug flag" NO) +option(ENABLE_TESTS "Enable Unittests" ON) # specify the C++ standard set(CMAKE_CXX_STANDARD 11) @@ -31,7 +32,11 @@ list(APPEND SOURCE ${PROJECT_SOURCE_DIR}/src/utils.cpp include_directories(src) include_directories(include) -add_subdirectory(test) +if(ENABLE_TESTS) + include(CTest) + add_subdirectory(test) +endif() + add_subdirectory(doc) add_library(${PROJECT_NAME} STATIC ${SOURCE}) diff --git a/src/dbc.cpp b/src/dbc.cpp index c92cc3e..bb21845 100644 --- a/src/dbc.cpp +++ b/src/dbc.cpp @@ -4,6 +4,28 @@ #include +namespace { + +const auto floatPattern = "(-?\\d+\\.?(\\d+)?)"; // Can be negative + +const auto signalIdentifierPattern = "(SG_)"; +const auto namePattern = "(\\w+)"; +const auto bitStartPattern = "(\\d+)"; // Cannot be negative +const auto lengthPattern = "(\\d+)"; // Cannot be negative +const auto byteOrderPattern = "([0-1])"; +const auto signPattern = "(\\+|\\-)"; +const auto scalePattern = "(\\d+\\.?(\\d+)?)"; // Non negative float +const auto offsetPattern = floatPattern; +const auto offsetScalePattern = std::string("\\(") + scalePattern + "\\," + offsetPattern + "\\)"; +const auto minPattern = floatPattern; +const auto maxPattern = floatPattern; +const auto minMaxPattern = std::string("\\[") + minPattern + "\\|" + maxPattern + "\\]"; +const auto unitPattern = "\"(.*)\""; // Random string +const auto receiverPattern = "([\\w\\,]+|Vector__XXX)*"; +const auto whiteSpace = "\\s"; + +} // anonymous namespace + namespace libdbc { DbcParser::DbcParser() : version(""), nodes(), @@ -11,7 +33,27 @@ namespace libdbc { name_space_re("^(NS_)\\s\\:"), node_re("^(BU_:)\\s((?:[\\w]+?\\s?)*)"), message_re("^(BO_)\\s(\\d+)\\s(\\w+)\\:\\s(\\d+)\\s(\\w+|Vector__XXX)"), // NOTE: No multiplex support yet - signal_re("\\s(SG_)\\s(\\w+)\\s\\:\\s(\\d+)\\|(\\d+)\\@(\\d+)(\\+|\\-)\\s\\((\\d+\\.?(\\d+)?)\\,(\\d+\\.?(\\d+)?)\\)\\s\\[(-?\\d+\\.?(\\d+)?)\\|(-?\\d+\\.?(\\d+)?)\\]\\s\"(\\w*)\"\\s([\\w\\,]+|Vector__XXX)*") { + signal_re(std::string(whiteSpace) + + signalIdentifierPattern + + whiteSpace + + namePattern + + whiteSpace + + "\\:" + + whiteSpace + + bitStartPattern + + "\\|" + + lengthPattern + + "\\@" + + byteOrderPattern + + signPattern + + whiteSpace + + offsetScalePattern + + whiteSpace + + minMaxPattern + + whiteSpace + + unitPattern + + whiteSpace + + receiverPattern) { } diff --git a/src/signal.cpp b/src/signal.cpp index 971cfd0..1124fc3 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -2,7 +2,7 @@ namespace libdbc { Signal::Signal(std::string name, bool is_multiplexed, uint32_t start_bit, uint32_t size, bool is_bigendian, bool is_signed, double factor, double offset, double min, double max, std::string unit, std::vector receivers) : - name(name), is_multiplexed(is_multiplexed), start_bit(start_bit), size(size), is_bigendian(is_bigendian), is_signed(is_signed), offset(offset), min(min), max(max), unit(unit), receivers(receivers) {} + name(name), is_multiplexed(is_multiplexed), start_bit(start_bit), size(size), is_bigendian(is_bigendian), is_signed(is_signed), factor(factor), offset(offset), min(min), max(max), unit(unit), receivers(receivers) {} bool Signal::operator==(const Signal& rhs) const { return (this->name == rhs.name) && (this->is_multiplexed == rhs.is_multiplexed) && diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d7d18f9..b76812e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -project(tests VERSION 0.1.0) +enable_testing() # Download and build Catch2 test framework Include(FetchContent) @@ -8,15 +8,17 @@ FetchContent_Declare( GIT_TAG v3.2.1 ) FetchContent_MakeAvailable(Catch2) +include(Catch) list(APPEND TEST_SOURCES test_dbc.cpp test_utils.cpp) -add_executable(tests ${TEST_SOURCES} ${SOURCE}) -target_link_libraries(tests PRIVATE Catch2::Catch2WithMain) - -add_custom_target(test - COMMAND ${PROJECT_NAME} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - DEPENDS ${PROJECT_NAME}) \ No newline at end of file +add_executable(tests ${TEST_SOURCES}) +target_compile_definitions(tests PRIVATE TESTDBCFILES_PATH="${CMAKE_CURRENT_SOURCE_DIR}/dbcs") +target_link_libraries(tests PRIVATE dbc Catch2::Catch2WithMain) +target_sources(tests INTERFACE FILE_SET HEADERS + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES defines.hpp) +catch_discover_tests(tests) diff --git a/test/defines.hpp b/test/defines.hpp index 7095570..0855be5 100644 --- a/test/defines.hpp +++ b/test/defines.hpp @@ -1,11 +1,22 @@ #include // Correctly formated files -static const std::string COMPLEX_DBC_FILE = "./dbcs/Complex.dbc"; -static const std::string SIMPLE_DBC_FILE = "./dbcs/Simple.dbc"; +static const std::string COMPLEX_DBC_FILE = std::string(TESTDBCFILES_PATH) + "/Complex.dbc"; +static const std::string SIMPLE_DBC_FILE = std::string(TESTDBCFILES_PATH) + "/Simple.dbc"; // Files with Errors -static const std::string MISSING_NEW_SYMBOLS_DBC_FILE = "./dbcs/MissingNewSymbols.dbc"; -static const std::string MISSING_VERSION_DBC_FILE = "./dbcs/MissingVersion.dbc"; -static const std::string MISSING_BIT_TIMING_DBC_FILE = "./dbcs/MissingBitTiming.dbc"; -static const std::string TEXT_FILE = "./dbcs/TextFile.txt"; +static const std::string MISSING_NEW_SYMBOLS_DBC_FILE = std::string(TESTDBCFILES_PATH) + "/MissingNewSymbols.dbc"; +static const std::string MISSING_VERSION_DBC_FILE = std::string(TESTDBCFILES_PATH) + "/MissingVersion.dbc"; +static const std::string MISSING_BIT_TIMING_DBC_FILE = std::string(TESTDBCFILES_PATH) + "/MissingBitTiming.dbc"; +static const std::string TEXT_FILE = std::string(TESTDBCFILES_PATH) + "/TextFile.txt"; + +static const std::string PRIMITIVE_DBC = +R"(VERSION "1.0.0" + +NS_ : + +BS_: + +BU_: DBG DRIVER IO MOTOR SENSOR + +)"; diff --git a/test/test_dbc.cpp b/test/test_dbc.cpp index 9393488..4ad0c8e 100644 --- a/test/test_dbc.cpp +++ b/test/test_dbc.cpp @@ -2,6 +2,17 @@ #include "defines.hpp" #include +void create_tmp_dbc_with(const char* filename, const char* content) +{ + auto* file = std::fopen(filename, "w"); + CHECK(file); + + std::fputs(PRIMITIVE_DBC.c_str(), file); + std::fputs(content, file); + std::fclose(file); +} + + TEST_CASE("Testing dbc file loading error issues", "[fileio][error]") { auto parser = std::unique_ptr(new libdbc::DbcParser()); @@ -49,4 +60,68 @@ TEST_CASE("Testing dbc file loading", "[fileio]") { REQUIRE(parser->get_messages().front().signals == msg.signals); } -} \ No newline at end of file +} + +TEST_CASE("Testing negative values") { + const auto* filename = std::tmpnam(NULL); + + create_tmp_dbc_with(filename, R"(BO_ 234 MSG1: 8 Vector__XXX + SG_ Sig1 : 55|16@0- (0.1,0) [-3276.8|-3276.7] "C" Vector__XXX + SG_ Sig2 : 39|16@0- (0.1,0) [-3276.8|-3276.7] "C" Vector__XXX + SG_ Sig3 : 23|16@0- (10,0) [-3276.8|-3276.7] "C" Vector__XXX + SG_ Sig4 : 7|16@0- (1,-10) [0|32767] "" Vector__XXX)"); + + auto parser = libdbc::DbcParser(); + parser.parse_file(std::string(filename)); + + REQUIRE(parser.get_messages().size() == 1); + REQUIRE(parser.get_messages().at(0).signals.size() == 4); + + SECTION("Evaluating first message") { + const auto signal = parser.get_messages().at(0).signals.at(0); + REQUIRE(signal.factor == 0.1); + REQUIRE(signal.offset == 0); + REQUIRE(signal.min == -3276.8); + REQUIRE(signal.max == -3276.7); + } + SECTION("Evaluating second message") { + const auto signal = parser.get_messages().at(0).signals.at(1); + REQUIRE(signal.factor == 0.1); + REQUIRE(signal.offset == 0); + REQUIRE(signal.min == -3276.8); + REQUIRE(signal.max == -3276.7); + } + SECTION("Evaluating third message"){ + const auto signal = parser.get_messages().at(0).signals.at(2); + REQUIRE(signal.factor == 10); + REQUIRE(signal.offset == 0); + REQUIRE(signal.min == -3276.8); + REQUIRE(signal.max == -3276.7); + } + SECTION("Evaluating fourth message"){ + const auto signal = parser.get_messages().at(0).signals.at(3); + REQUIRE(signal.factor == 1); + REQUIRE(signal.offset == -10); + REQUIRE(signal.min == 0); + REQUIRE(signal.max == 32767); + } +} + + +TEST_CASE("Special characters in unit") { + const auto* filename = std::tmpnam(NULL); + + create_tmp_dbc_with(filename, R"(BO_ 234 MSG1: 8 Vector__XXX + SG_ Speed : 0|8@1+ (1,0) [0|204] "Km/h" DEVICE1,DEVICE2,DEVICE3)"); + + + auto parser = libdbc::DbcParser(); + parser.parse_file(std::string(filename)); + + REQUIRE(parser.get_messages().size() == 1); + REQUIRE(parser.get_messages().at(0).signals.size() == 1); + SECTION("Checking that signal with special characters as unit is parsed correctly") { + const auto signal = parser.get_messages().at(0).signals.at(0); + REQUIRE(signal.unit.compare("Km/h") == 0); + } +}