find_package(Unity REQUIRED) find_package(CMock REQUIRED) set(MOCK_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/mocks") file(MAKE_DIRECTORY "${MOCK_GEN_DIR}") # Generate a CMock mock from a header and attach it to a target. # Usage: cmock_generate_mock( ) # CMock names generated files Mock.h/.c (capital M, no separator) function(cmock_generate_mock target header) if (NOT RUBY_EXECUTABLE) message(FATAL_ERROR "Ruby is required for CMock generation") endif() get_filename_component(name "${header}" NAME_WE) get_filename_component(header_dir "${header}" DIRECTORY) set(mock_src "${MOCK_GEN_DIR}/Mock${name}.c") set(mock_hdr "${MOCK_GEN_DIR}/Mock${name}.h") add_custom_command( OUTPUT "${mock_src}" "${mock_hdr}" COMMAND "${RUBY_EXECUTABLE}" "${CMOCK_SCRIPT}" "-o" "${CMAKE_CURRENT_SOURCE_DIR}/cmock.yml" "--mock_path=${MOCK_GEN_DIR}" "${header}" DEPENDS "${header}" COMMENT "CMock: generating Mock${name}" VERBATIM ) target_sources("${target}" PRIVATE "${mock_src}") target_include_directories("${target}" PRIVATE "${MOCK_GEN_DIR}" "${header_dir}") endfunction() set(TEST_TARGETS "") # CRSF tests — pure functions (CRC, parse, build), no mock needed add_executable(test_crsf test_crsf.c) target_include_directories(test_crsf PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_crsf PRIVATE celcrsf Unity::Unity) target_compile_features(test_crsf PRIVATE c_std_23) add_test(NAME test_crsf COMMAND test_crsf) list(APPEND TEST_TARGETS test_crsf) # CRSF stream tests add_executable(test_crsf_stream test_crsf_stream.c) target_include_directories(test_crsf_stream PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_crsf_stream PRIVATE celcrsf Unity::Unity) target_compile_features(test_crsf_stream PRIVATE c_std_23) add_test(NAME test_crsf_stream COMMAND test_crsf_stream) list(APPEND TEST_TARGETS test_crsf_stream) # CRSF telemetry tests add_executable(test_crsf_telemetry test_crsf_telemetry.c) target_include_directories(test_crsf_telemetry PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_crsf_telemetry PRIVATE celcrsf Unity::Unity) target_compile_features(test_crsf_telemetry PRIVATE c_std_23) add_test(NAME test_crsf_telemetry COMMAND test_crsf_telemetry) list(APPEND TEST_TARGETS test_crsf_telemetry) # CRSF param tests — mocks serial for write/ping/read add_executable(test_crsf_param test_crsf_param.c) target_include_directories(test_crsf_param PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_crsf_param PRIVATE celcrsf celserial Unity::Unity CMock::CMock) target_compile_features(test_crsf_param PRIVATE c_std_23) cmock_generate_mock(test_crsf_param "${CMAKE_SOURCE_DIR}/celrs/platform/serial_internal.h") add_test(NAME test_crsf_param COMMAND test_crsf_param) list(APPEND TEST_TARGETS test_crsf_param) # Serial tests — mocks the platform backend (serial_internal.h) add_executable(test_serial test_serial.c) target_include_directories(test_serial PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_serial PRIVATE celserial Unity::Unity CMock::CMock) target_compile_features(test_serial PRIVATE c_std_23) cmock_generate_mock(test_serial "${CMAKE_SOURCE_DIR}/celrs/platform/serial_internal.h") add_test(NAME test_serial COMMAND test_serial) list(APPEND TEST_TARGETS test_serial) # Logger tests — mocks log_write.h so output calls are intercepted add_executable(test_logger test_logger.c) target_include_directories(test_logger PRIVATE "${CMAKE_SOURCE_DIR}") target_link_libraries(test_logger PRIVATE cellogger Unity::Unity CMock::CMock) target_compile_features(test_logger PRIVATE c_std_23) cmock_generate_mock(test_logger "${CMAKE_SOURCE_DIR}/celrs/log_write.h") add_test(NAME test_logger COMMAND test_logger) list(APPEND TEST_TARGETS test_logger) # 'check' builds all suites and runs CTest with full Unity output. # USES_TERMINAL keeps ANSI colors alive through Ninja's output buffering. add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --test-dir "${CMAKE_BINARY_DIR}" --output-on-failure --progress WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" USES_TERMINAL DEPENDS ${TEST_TARGETS} ) if (ENABLE_COVERAGE) set(COVERAGE_DIR "${CMAKE_BINARY_DIR}/coverage") add_custom_target(coverage COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure COMMAND ${CMAKE_COMMAND} -E make_directory "${COVERAGE_DIR}" COMMAND ${GCOVR_EXE} --gcov-executable "${GCOV_EXECUTABLE}" --root "${CMAKE_SOURCE_DIR}" --filter "${CMAKE_SOURCE_DIR}/celrs/" --exclude "${CMAKE_SOURCE_DIR}/tests/" --exclude ".*Mock.*" --exclude ".*unity.*" --exclude ".*cmock.*" --html-details "${COVERAGE_DIR}/index.html" --txt --print-summary "${CMAKE_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" USES_TERMINAL DEPENDS ${TEST_TARGETS} COMMENT "Coverage report: ${COVERAGE_DIR}/index.html" ) endif()