├── cpputest-for-freertos-lib ├── .gitignore ├── src │ ├── cpputest_main.cpp │ ├── cpputest_for_freertos_fake_queue.hpp │ ├── cpputest_for_freertos_assert.cpp │ ├── cpputest_for_freertos_semaphore.cpp │ ├── cpputest_for_freertos_queue_set.cpp │ ├── cpputest_for_freertos_task.cpp │ ├── cpputest_for_freertos_mutex.cpp │ ├── cpputest_for_freertos_queue.cpp │ └── cpputest_for_freertos_timers.cpp ├── tests │ ├── CMakeLists.txt │ ├── cpputest_for_freertos_assert_tests.cpp │ ├── cpputest_for_freertos_task_tests.cpp │ ├── cpputest_for_freertos_semaphore_tests.cpp │ ├── cpputest_for_freertos_mutex_tests.cpp │ ├── cpputest_for_freertos_queue_tests.cpp │ ├── cpputest_for_freertos_timers_tests.cpp │ └── cpputest_for_freertos_queue_set_tests.cpp ├── cmake │ └── cpputestCMake.cmake ├── include │ ├── cpputest_for_freertos_task.hpp │ ├── cpputest_for_freertos_assert.hpp │ ├── cpputest_for_freertos_mutex.hpp │ ├── cpputest_for_freertos_memory.hpp │ ├── cpputest_for_freertos_lib.hpp │ └── cpputest_for_freertos_timers.hpp ├── CMakeLists.txt └── port │ └── include │ └── portmacro.h ├── example ├── apps │ ├── CMakeLists.txt │ └── demoPcApp │ │ ├── externals │ │ └── .gitignore │ │ ├── CMakeLists.txt │ │ ├── main.cpp │ │ └── FreeRTOSConfig.h ├── drivers │ ├── CMakeLists.txt │ ├── hwLockCtrl │ │ ├── CMakeLists.txt │ │ ├── src │ │ │ └── hwLockCtrl.c │ │ └── include │ │ │ └── hwLockCtrl.h │ └── buttonReader │ │ ├── CMakeLists.txt │ │ ├── src │ │ └── buttonReader.c │ │ └── include │ │ └── buttonReader.h ├── services │ ├── CMakeLists.txt │ ├── buttonService │ │ ├── CMakeLists.txt │ │ ├── test │ │ │ ├── CMakeLists.txt │ │ │ └── buttonServiceTests.cpp │ │ ├── include │ │ │ └── buttonService.h │ │ └── src │ │ │ └── buttonService.c │ ├── hwLockCtrlService │ │ ├── CMakeLists.txt │ │ ├── test │ │ │ ├── CMakeLists.txt │ │ │ └── hwLockCtrlServiceTests.cpp │ │ ├── include │ │ │ └── hwLockCtrlService.h │ │ └── src │ │ │ └── hwLockCtrlService.c │ └── include │ │ └── cmsExecutionOption.h ├── test │ └── mocks │ │ ├── buttonReader │ │ ├── mockButtonReader.hpp │ │ └── mockButtonReader.cpp │ │ └── hwLockCtrl │ │ └── mockHwLockCtrl.cpp └── CMakeLists.txt ├── CMakeLists.txt ├── .gitignore ├── LICENSE.txt ├── .github └── workflows │ └── cmake.yml └── README.md /cpputest-for-freertos-lib/.gitignore: -------------------------------------------------------------------------------- 1 | externals -------------------------------------------------------------------------------- /example/apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(demoPcApp) 2 | -------------------------------------------------------------------------------- /example/apps/demoPcApp/externals/.gitignore: -------------------------------------------------------------------------------- 1 | FreeRTOS-Kernel 2 | 3 | -------------------------------------------------------------------------------- /example/drivers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(hwLockCtrl) 2 | add_subdirectory(buttonReader) 3 | -------------------------------------------------------------------------------- /example/services/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${FREERTOS_KERNEL_PATH}/include) 2 | add_subdirectory(hwLockCtrlService) 3 | add_subdirectory(buttonService) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(cpputest-for-freertos-lib VERSION 1.1.0) 3 | 4 | add_subdirectory(cpputest-for-freertos-lib) 5 | add_subdirectory(example) 6 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_main.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/CommandLineTestRunner.h" 2 | 3 | int main(int ac, char** av) 4 | { 5 | return CommandLineTestRunner::RunAllTests(ac, av); 6 | } -------------------------------------------------------------------------------- /example/drivers/hwLockCtrl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | add_library(hwLockCtrl include/hwLockCtrl.h src/hwLockCtrl.c) 3 | target_include_directories(hwLockCtrl PUBLIC include) 4 | -------------------------------------------------------------------------------- /example/drivers/buttonReader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | add_library(buttonReader include/buttonReader.h src/buttonReader.c) 3 | target_include_directories(buttonReader PUBLIC include) 4 | -------------------------------------------------------------------------------- /example/test/mocks/buttonReader/mockButtonReader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_BUTTON_READER_HPP 2 | #define MOCK_BUTTON_READER_HPP 3 | 4 | namespace cms::test::mock { 5 | void ButtonReaderDoIsr(); 6 | } 7 | 8 | #endif //MOCK_BUTTON_READER_HPP 9 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(MOCKS_TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks CACHE INTERNAL "") 3 | set(DRIVERS_TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/drivers CACHE INTERNAL "") 4 | 5 | include_directories(services/include) 6 | add_subdirectory(services) 7 | add_subdirectory(drivers) 8 | add_subdirectory(apps) -------------------------------------------------------------------------------- /example/services/buttonService/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | add_subdirectory(test) 3 | add_library(buttonService include/buttonService.h src/buttonService.c) 4 | target_link_libraries(buttonService buttonReader freertos_kernel freertos_config) 5 | target_include_directories(buttonService PUBLIC include) -------------------------------------------------------------------------------- /example/services/hwLockCtrlService/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | add_subdirectory(test) 3 | add_library(hwLockCtrlService include/hwLockCtrlService.h src/hwLockCtrlService.c) 4 | target_link_libraries(hwLockCtrlService hwLockCtrl freertos_kernel freertos_config) 5 | target_include_directories(hwLockCtrlService PUBLIC include) 6 | -------------------------------------------------------------------------------- /example/services/include/cmsExecutionOption.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Matthew Eshleman on 4/9/21. 3 | // 4 | 5 | #ifndef CMS_EXECUTION_OPTION_H 6 | #define CMS_EXECUTION_OPTION_H 7 | 8 | 9 | typedef enum ExecutionOption 10 | { 11 | EXECUTION_OPTION_NORMAL, 12 | EXECUTION_OPTION_UNIT_TEST 13 | } ExecutionOptionT; 14 | 15 | #endif //CMS_EXECUTION_OPTION_H 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .idea 35 | 36 | cmake-build-* 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The software supporting this CppUTest port enabling 2 | unit testing of FreeRTOS-based code is licensed under a 3 | dual-licensing model, with the default license being GPL. 4 | 5 | Most of the files present in this repository are only needed for 6 | host PC based test execution. However, a few of the examples 7 | and files may find their way into a product's build. 8 | 9 | To request a commercial license see contact information 10 | below. 11 | 12 | Contact Information: 13 | ==================== 14 | Matthew Eshleman 15 | - https://covemountainsoftware.com 16 | - mailto:info@covemountainsoftware.com 17 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(TEST_APP_NAME cpputest-for-freertos-lib-tests) 3 | set(TEST_SOURCES 4 | cpputest_for_freertos_assert_tests.cpp 5 | cpputest_for_freertos_timers_tests.cpp 6 | cpputest_for_freertos_queue_tests.cpp 7 | cpputest_for_freertos_queue_set_tests.cpp 8 | cpputest_for_freertos_task_tests.cpp 9 | cpputest_for_freertos_semaphore_tests.cpp 10 | cpputest_for_freertos_mutex_tests.cpp 11 | ) 12 | 13 | # this include expects TEST_SOURCES and TEST_APP_NAME to be 14 | # defined, and creates the cpputest based test executable target 15 | include(${CMS_CMAKE_DIR}/cpputestCMake.cmake) 16 | 17 | target_link_libraries(${TEST_APP_NAME} cpputest-for-freertos-lib ${CPPUTEST_LDFLAGS}) -------------------------------------------------------------------------------- /example/services/hwLockCtrlService/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # prep for cpputest based build 2 | set(TEST_APP_NAME HwLockCtrlServiceTests) 3 | 4 | include_directories(${DRIVERS_TOP_DIR}/hwLockCtrl/include) 5 | 6 | #note: we are building and linking with the MOCK LockCtrl module, instead 7 | # of the actual LockCtrl driver. We must also pull in 8 | # sources for cpputest execution and QP and our fake port of QP 9 | set(TEST_SOURCES 10 | hwLockCtrlServiceTests.cpp 11 | ../src/hwLockCtrlService.c 12 | ${MOCKS_TOP_DIR}/hwLockCtrl/mockHwLockCtrl.cpp) 13 | 14 | # this include expects TEST_SOURCES and TEST_APP_NAME to be 15 | # defined, and creates the cpputest based test executable target 16 | include(${CMS_CMAKE_DIR}/cpputestCMake.cmake) 17 | 18 | target_link_libraries(${TEST_APP_NAME} cpputest-for-freertos-lib ${CPPUTEST_LDFLAGS}) -------------------------------------------------------------------------------- /example/services/buttonService/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # prep for cpputest based build 2 | set(TEST_APP_NAME ButtonServiceTests) 3 | 4 | include_directories(${DRIVERS_TOP_DIR}/buttonReader/include) 5 | include_directories(${MOCKS_TOP_DIR}/buttonReader) 6 | 7 | #note: we are building and linking with the MOCK LockCtrl module, instead 8 | # of the actual LockCtrl driver. We must also pull in 9 | # sources for cpputest execution and QP and our fake port of QP 10 | set(TEST_SOURCES 11 | buttonServiceTests.cpp 12 | ../src/buttonService.c 13 | ${MOCKS_TOP_DIR}/buttonReader/mockButtonReader.cpp) 14 | 15 | # this include expects TEST_SOURCES and TEST_APP_NAME to be 16 | # defined, and creates the cpputest based test executable target 17 | include(${CMS_CMAKE_DIR}/cpputestCMake.cmake) 18 | 19 | target_link_libraries(${TEST_APP_NAME} cpputest-for-freertos-lib ${CPPUTEST_LDFLAGS}) -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_fake_queue.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_FAKE_QUEUE_HPP 3 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_FAKE_QUEUE_HPP 4 | 5 | #include 6 | #include 7 | #include "FreeRTOS.h" 8 | 9 | typedef struct QueueDefinition 10 | { 11 | UBaseType_t queueLength = {}; 12 | UBaseType_t itemSize = {}; 13 | uint8_t queueType = {}; 14 | std::deque> queue = {}; 15 | uint64_t recursiveCallCount = {}; 16 | const char * registryName = nullptr; 17 | struct QueueDefinition * queueSetContainer = nullptr; 18 | } FakeQueue; 19 | 20 | namespace cms { 21 | BaseType_t InternalQueueReceive(FakeQueue *queue, void * const buffer); 22 | BaseType_t InternalQueueReceive(FakeQueue *queue); 23 | } //namespace cms 24 | 25 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_FAKE_QUEUE_HPP 26 | -------------------------------------------------------------------------------- /example/apps/demoPcApp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # setup the FreeRTOS config interface, i.e. where the build will find the FreeRTOSConfig.h 3 | # header file. 4 | add_library(freertos_config INTERFACE) 5 | target_include_directories(freertos_config INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 6 | set(FREERTOS_HEAP "3" CACHE STRING "" FORCE) 7 | set(FREERTOS_PORT "GCC_POSIX" CACHE STRING "" FORCE) 8 | 9 | FetchContent_Declare(FreeRTOS-Kernel-DemoPcApp 10 | GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS-Kernel.git 11 | GIT_TAG dbf70559b27d39c1fdb68dfb9a32140b6a6777a0 #v11.1.0 12 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/externals/FreeRTOS-Kernel 13 | ) 14 | message("Demo App: Fetching FreeRTOS Kernel git repository") 15 | FetchContent_MakeAvailable(FreeRTOS-Kernel-DemoPcApp) 16 | 17 | add_executable(demoPcApp main.cpp) 18 | include_directories(${FREERTOS_KERNEL_PATH}/include) 19 | target_link_libraries(demoPcApp hwLockCtrlService buttonService freertos_kernel freertos_config) 20 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/cmake/cpputestCMake.cmake: -------------------------------------------------------------------------------- 1 | if(DEFINED ENV{CPPUTEST_HOME}) 2 | message(STATUS "Using CppUTest home: $ENV{CPPUTEST_HOME}") 3 | set(CPPUTEST_INCLUDE_DIRS $ENV{CPPUTEST_HOME}/include) 4 | set(CPPUTEST_LIBRARIES $ENV{CPPUTEST_HOME}/lib) 5 | set(CPPUTEST_LDFLAGS CppUTest CppUTestExt) 6 | else() 7 | find_package(PkgConfig REQUIRED) 8 | pkg_search_module(CPPUTEST REQUIRED cpputest>=3.8) 9 | message(STATUS "Found CppUTest version ${CPPUTEST_VERSION}") 10 | endif() 11 | 12 | if(CPPUTEST_VERSION_MAJOR LESS 4) 13 | # likely 3.8 14 | add_compile_definitions(CMS_CPPUTEST_LEGACY) 15 | else() 16 | # 4.0 version or newer 17 | add_compile_definitions(CMS_CPPUTEST_V4) 18 | endif() 19 | 20 | include_directories(${CPPUTEST_INCLUDE_DIRS}) 21 | link_directories(${CPPUTEST_LIBRARIES}) 22 | 23 | add_executable(${TEST_APP_NAME} ${TEST_SOURCES}) 24 | target_link_libraries(${TEST_APP_NAME} ${APP_LIB_NAME} ${CPPUTEST_LDFLAGS}) 25 | 26 | # (5) Run the test once the build is done 27 | add_custom_command(TARGET ${TEST_APP_NAME} COMMAND ./${TEST_APP_NAME} POST_BUILD) -------------------------------------------------------------------------------- /example/drivers/hwLockCtrl/src/hwLockCtrl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This concrete implementation of the LockCtrl "C" style interface 3 | * is for demo purposes only. The focus of this project 4 | * is how to test state machines and active objects, hence 5 | * this fake hardware driver module. 6 | */ 7 | #include "hwLockCtrl.h" 8 | #include 9 | 10 | bool HwLockCtrlInit() 11 | { 12 | printf("%s() executed\n", __FUNCTION__); 13 | return true; 14 | } 15 | 16 | bool HwLockCtrlLock() 17 | { 18 | printf("%s() executed\n", __FUNCTION__); 19 | return true; 20 | } 21 | 22 | bool HwLockCtrlUnlock() 23 | { 24 | printf("%s() executed\n", __FUNCTION__); 25 | return true; 26 | } 27 | 28 | bool HwLockCtrlSelfTest(HwLockCtrlSelfTestResultT* outResult) 29 | { 30 | printf("%s() executed\n", __FUNCTION__); 31 | if (outResult) 32 | { 33 | *outResult = HW_LOCK_CTRL_SELF_TEST_PASSED; 34 | return true; 35 | } 36 | else 37 | { 38 | printf("%s() executed with nullptr arg\n", __FUNCTION__); 39 | return false; 40 | } 41 | } 42 | 43 | int32_t HwLockCtrlReadCurrent() 44 | { 45 | printf("%s() executed\n", __FUNCTION__); 46 | return 50; 47 | } -------------------------------------------------------------------------------- /example/drivers/buttonReader/src/buttonReader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This concrete implementation of the Button Reader "C" style interface 3 | * is for demo purposes only. The focus of this project 4 | * is how to test FreeRTOS threads, hence 5 | * this fake driver module. 6 | */ 7 | #include "buttonReader.h" 8 | #include 9 | 10 | static ButtonReaderIsrCallback s_isrCallback; 11 | static void* s_isrContext; 12 | 13 | bool ButtonReaderInit() 14 | { 15 | printf("%s() executed\n", __FUNCTION__); 16 | s_isrCallback = NULL; 17 | s_isrContext = NULL; 18 | return true; 19 | } 20 | 21 | bool ButtonReaderRead(uint16_t* pressed, uint16_t* released) 22 | { 23 | static bool toggle = false; 24 | printf("%s() executed\n", __FUNCTION__); 25 | 26 | if ((pressed == NULL) || (released == NULL)) 27 | { 28 | return false; 29 | } 30 | 31 | if (toggle) 32 | { 33 | *pressed = 2; 34 | *released = 0; 35 | } 36 | else 37 | { 38 | *pressed = 0; 39 | *released = 2; 40 | } 41 | 42 | toggle = !toggle; 43 | 44 | return true; 45 | } 46 | 47 | bool ButtonReaderRegisterIsrCallback(ButtonReaderIsrCallback callback, void* context) 48 | { 49 | printf("%s() executed\n", __FUNCTION__); 50 | 51 | s_isrCallback = callback; 52 | s_isrContext = context; 53 | return true; 54 | } 55 | -------------------------------------------------------------------------------- /example/test/mocks/buttonReader/mockButtonReader.cpp: -------------------------------------------------------------------------------- 1 | #include "buttonReader.h" 2 | #include "mockButtonReader.hpp" 3 | #include "CppUTestExt/MockSupport.h" 4 | 5 | static constexpr const char* MOCK_NAME = "ButtonReader"; 6 | static ButtonReaderIsrCallback s_lastCallback = nullptr; 7 | static void* s_lastContext = nullptr; 8 | 9 | bool ButtonReaderInit() 10 | { 11 | s_lastCallback = nullptr; 12 | s_lastContext = nullptr; 13 | mock(MOCK_NAME).actualCall("Init"); 14 | return mock(MOCK_NAME).returnBoolValueOrDefault(true); 15 | } 16 | 17 | bool ButtonReaderRead(uint16_t* pressed, uint16_t* released) 18 | { 19 | mock(MOCK_NAME) 20 | .actualCall("Read") 21 | .withOutputParameter("pressed", pressed) 22 | .withOutputParameter("released", released); 23 | return mock(MOCK_NAME).returnBoolValueOrDefault(true); 24 | } 25 | 26 | bool ButtonReaderRegisterIsrCallback(ButtonReaderIsrCallback callback, void* context) 27 | { 28 | //skip the mock, make this behavior a 'fake' instead 29 | s_lastCallback = callback; 30 | s_lastContext = context; 31 | return true; 32 | } 33 | 34 | namespace cms::test::mock { 35 | 36 | void ButtonReaderDoIsr() 37 | { 38 | if (s_lastCallback == nullptr) 39 | { 40 | return; 41 | } 42 | 43 | s_lastCallback(s_lastContext); 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-22.04 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | submodules: recursive 24 | - name: Install cpputest 25 | run: sudo apt install -y cpputest 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_assert_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of support methods to help with unit testing for FreeRTOS configASSERT. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | #include "CppUTest/TestHarness.h" 24 | #include "cpputest_for_freertos_assert.hpp" 25 | 26 | TEST_GROUP(AssertTests) 27 | { 28 | void setup() final 29 | { 30 | } 31 | 32 | void teardown() final 33 | { 34 | mock().clear(); 35 | } 36 | }; 37 | 38 | TEST(AssertTests, configASSERT_results_in_expected_mock_hit_and_proper_test_exit) 39 | { 40 | cms::test::AssertOutputDisable(); 41 | cms::test::MockExpectAssert(); 42 | configASSERT(true == false); 43 | mock().checkExpectations(); 44 | } 45 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_task.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS task 2 | /// related methods. 3 | /// @ingroup 4 | /// @cond 5 | ///*************************************************************************** 6 | /// 7 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 8 | /// 9 | /// This program is open source software: you can redistribute it and/or 10 | /// modify it under the terms of the GNU General Public License as published 11 | /// by the Free Software Foundation, either version 3 of the License, or 12 | /// (at your option) any later version. 13 | /// 14 | /// Alternatively, upon written permission from Matthew Eshleman, this program 15 | /// may be distributed and modified under the terms of a Commercial 16 | /// License. For further details, see the Contact Information below. 17 | /// 18 | /// Contact Information: 19 | /// Matthew Eshleman 20 | /// https://covemountainsoftware.com 21 | /// info@covemountainsoftware.com 22 | ///*************************************************************************** 23 | /// @endcond 24 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TASK_HPP 25 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TASK_HPP 26 | 27 | namespace cms { 28 | namespace test { 29 | 30 | /** 31 | * Init, prepare for task related usage, primarily for time 32 | * related tracking. 33 | */ 34 | void TaskInit(); 35 | 36 | /** 37 | * Clean up/destroy the fake CppUTest for task details, primarily 38 | * related to time. 39 | */ 40 | void TaskDestroy(); 41 | } 42 | } 43 | 44 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TASK_HPP 45 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_assert.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS configASSERT. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_ASSERT_HPP 25 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_ASSERT_HPP 26 | 27 | #include "FreeRTOS.h" 28 | #include "CppUTestExt/MockSupport.h" 29 | 30 | namespace cms { 31 | namespace test { 32 | 33 | void AssertOutputEnable(); 34 | void AssertOutputDisable(); 35 | 36 | static constexpr const char* ASSERT_MOCK_NAME = "ASSERT"; 37 | static constexpr const char* ON_ASSERT_FUNC_NAME = "cmsAssertCalled"; 38 | 39 | inline void MockExpectAssert() 40 | { 41 | mock(ASSERT_MOCK_NAME) 42 | .expectOneCall(ON_ASSERT_FUNC_NAME) 43 | .ignoreOtherParameters(); 44 | } 45 | } // namespace test 46 | } // namespace cms 47 | 48 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_ASSERT_HPP 49 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_mutex.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS mutexes. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MUTEX_HPP 25 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MUTEX_HPP 26 | 27 | namespace cms { 28 | namespace test { 29 | 30 | /** 31 | * Initialize the mutex state tracking, 32 | * such that this unit test, when Teardown is called, 33 | * will confirm that all mutexes are unlocked. 34 | */ 35 | void MutexTrackingInit(); 36 | 37 | /** 38 | * Check the state of any active mutexes. 39 | * If a mutex is found to be locked, then that is considered 40 | * a test failure. i.e. confirm that all mutex usage during 41 | * a unit test locks and then unlocks. 42 | */ 43 | void MutexTrackingTeardown(); 44 | 45 | /** 46 | * Check if any active mutexes are in a locked 47 | * state. 48 | * @return 49 | */ 50 | bool IsAnyMutexLocked(); 51 | 52 | } //namespace 53 | }//namespace 54 | 55 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MUTEX_HPP 56 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_memory.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS, memory allocation 2 | /// related support, such as unique_ptr types for allocated queues, etc. 3 | /// @ingroup 4 | /// @cond 5 | ///*************************************************************************** 6 | /// 7 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 8 | /// 9 | /// This program is open source software: you can redistribute it and/or 10 | /// modify it under the terms of the GNU General Public License as published 11 | /// by the Free Software Foundation, either version 3 of the License, or 12 | /// (at your option) any later version. 13 | /// 14 | /// Alternatively, upon written permission from Matthew Eshleman, this program 15 | /// may be distributed and modified under the terms of a Commercial 16 | /// License. For further details, see the Contact Information below. 17 | /// 18 | /// Contact Information: 19 | /// Matthew Eshleman 20 | /// https://covemountainsoftware.com 21 | /// info@covemountainsoftware.com 22 | ///*************************************************************************** 23 | /// @endcond 24 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MEMORY_HPP 25 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MEMORY_HPP 26 | 27 | #include "FreeRTOS.h" 28 | #include "queue.h" 29 | #include 30 | #include 31 | 32 | namespace cms { 33 | namespace test { 34 | 35 | struct FreeRTOSQueueDeleter 36 | { 37 | void operator()(struct QueueDefinition * handle) 38 | { 39 | if (handle != nullptr) 40 | { 41 | vQueueDelete(handle); 42 | } 43 | } 44 | }; 45 | 46 | using unique_queue = std::unique_ptr; 47 | using unique_sema = std::unique_ptr; 48 | 49 | } //namespace test 50 | } //namespace cms 51 | 52 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_MEMORY_HPP 53 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_lib.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Top level init/teardown methods for CppUTest for FreeRTOS library. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_HPP 25 | #define CPPUTEST_FOR_FREERTOS_LIB_HPP 26 | 27 | #include "cpputest_for_freertos_assert.hpp" 28 | #include "cpputest_for_freertos_task.hpp" 29 | #include "cpputest_for_freertos_timers.hpp" 30 | #include "cpputest_for_freertos_mutex.hpp" 31 | 32 | namespace cms { 33 | namespace test { 34 | /** 35 | * call this in your unit test setup() method to initialize 36 | * all available CppUTest for FreeRTOS modules. 37 | */ 38 | void LibInitAll() { 39 | TaskInit(); 40 | AssertOutputEnable(); 41 | TimersInit(); 42 | MutexTrackingInit(); 43 | } 44 | 45 | /** 46 | * call this in your unit test teardown() method to correctly 47 | * destroy/teardown all available CppUTest for FreeRTOS modules. 48 | */ 49 | void LibTeardownAll() { 50 | MutexTrackingTeardown(); 51 | TimersDestroy(); 52 | TaskDestroy(); 53 | } 54 | } // namespace test 55 | } //namespace cms 56 | 57 | #endif //CPPUTEST_FOR_FREERTOS_LIB_HPP 58 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/include/cpputest_for_freertos_timers.hpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS timers. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TIMERS_HPP 24 | #define CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TIMERS_HPP 25 | 26 | #include 27 | 28 | namespace cms { 29 | namespace test { 30 | 31 | /** 32 | * Initialize the functional but fake CppUTest for FreeRTOS timers. 33 | */ 34 | void TimersInit(); 35 | 36 | /** 37 | * Clean up/destroy the fake CppUTest for FreeRTOS timers. 38 | */ 39 | void TimersDestroy(); 40 | 41 | /** 42 | * check if the current unit test has initialized the timer 43 | * subsystem or not. 44 | * @return true: yes, TimersInit() was called, so timers are active. 45 | */ 46 | bool TimersIsActive(); 47 | 48 | /** 49 | * Move Time Forward. 50 | * @param duration 51 | */ 52 | void MoveTimeForward(std::chrono::nanoseconds duration); 53 | 54 | /** 55 | * Get the current time, as duration since Init was called. 56 | * @return 57 | */ 58 | std::chrono::nanoseconds GetCurrentInternalTime(); 59 | 60 | } //namespace 61 | }//namespace 62 | 63 | #endif //CPPUTEST_FOR_FREERTOS_LIB_CPPUTEST_FOR_FREERTOS_TIMERS_HPP 64 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_C_STANDARD 11) 7 | add_compile_options(-Wall -Wextra -Werror) 8 | 9 | set(CMS_EXTERNALS_TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/externals) 10 | set(CMS_CMAKE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake CACHE INTERNAL "") 11 | 12 | if(NOT DEFINED CMS_FREERTOS_KERNEL_TOP_DIR) 13 | set(CMS_FREERTOS_KERNEL_TOP_DIR ${CMS_EXTERNALS_TOP_DIR}/FreeRTOS-Kernel) 14 | FetchContent_Declare(FreeRTOS-Kernel 15 | GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS-Kernel.git 16 | GIT_TAG dbf70559b27d39c1fdb68dfb9a32140b6a6777a0 #v11.1.0 17 | SOURCE_DIR ${CMS_FREERTOS_KERNEL_TOP_DIR} 18 | CONFIGURE_COMMAND "" 19 | BUILD_COMMAND "" 20 | ) 21 | message("Fetching FreeRTOS Kernel git repository") 22 | FetchContent_GetProperties(FreeRTOS-Kernel) 23 | if(NOT FreeRTOS-Kernel_POPULATED) 24 | FetchContent_Populate(FreeRTOS-Kernel) 25 | endif() 26 | endif(NOT DEFINED CMS_FREERTOS_KERNEL_TOP_DIR) 27 | 28 | FetchContent_Declare(fake-timers 29 | GIT_REPOSITORY https://github.com/covemountainsoftware/fake-timers.git 30 | GIT_TAG 0dd7edd4bfac80d8bede10e6ff7bc651df110ab4 #latest 31 | SOURCE_DIR ${CMS_EXTERNALS_TOP_DIR}/fake-timers 32 | ) 33 | message("Fetching fake-timers git repository") 34 | FetchContent_MakeAvailable(fake-timers) 35 | 36 | include_directories(include) 37 | 38 | set(FREERTOS_KERNEL_PATH ${CMS_FREERTOS_KERNEL_TOP_DIR} CACHE INTERNAL "") 39 | 40 | add_library(cpputest-for-freertos-lib 41 | src/cpputest_for_freertos_task.cpp 42 | src/cpputest_for_freertos_queue.cpp 43 | src/cpputest_for_freertos_queue_set.cpp 44 | src/cpputest_for_freertos_assert.cpp 45 | src/cpputest_for_freertos_timers.cpp 46 | src/cpputest_main.cpp 47 | src/cpputest_for_freertos_semaphore.cpp 48 | src/cpputest_for_freertos_mutex.cpp 49 | include/cpputest_for_freertos_lib.hpp 50 | ) 51 | 52 | add_subdirectory(tests) 53 | 54 | target_include_directories(cpputest-for-freertos-lib PUBLIC include port/include externals/FreeRTOS-Kernel/include) 55 | target_link_libraries(cpputest-for-freertos-lib fake-timers-lib) 56 | -------------------------------------------------------------------------------- /example/test/mocks/hwLockCtrl/mockHwLockCtrl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2019-2020> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include "hwLockCtrl.h" 26 | #include "CppUTestExt/MockSupport.h" 27 | 28 | static constexpr const char* MOCK_NAME = "HwLockCtrl"; 29 | 30 | bool HwLockCtrlInit() 31 | { 32 | mock(MOCK_NAME).actualCall("Init"); 33 | return static_cast(mock(MOCK_NAME).returnIntValueOrDefault(true)); //use IntValue due to bug in CppUTest bool handling. 34 | } 35 | 36 | bool HwLockCtrlLock() 37 | { 38 | mock(MOCK_NAME).actualCall("Lock"); 39 | return static_cast(mock(MOCK_NAME).returnIntValueOrDefault(true)); 40 | } 41 | 42 | bool HwLockCtrlUnlock() 43 | { 44 | mock(MOCK_NAME).actualCall("Unlock"); 45 | return static_cast(mock(MOCK_NAME).returnIntValueOrDefault(true)); 46 | } 47 | 48 | bool HwLockCtrlSelfTest(HwLockCtrlSelfTestResultT* outResult) 49 | { 50 | mock(MOCK_NAME).actualCall("SelfTest").withOutputParameter("outResult", outResult); 51 | return static_cast(mock(MOCK_NAME).returnIntValueOrDefault(true)); 52 | } 53 | 54 | int32_t HwLockCtrlReadCurrent() 55 | { 56 | mock(MOCK_NAME).actualCall("ReadCurrent"); 57 | return mock(MOCK_NAME).returnIntValueOrDefault(50); 58 | } 59 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_assert.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Support methods to help with unit testing for FreeRTOS configASSERT. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #include 25 | #include "cpputest_for_freertos_assert.hpp" 26 | 27 | static bool m_printAssert = true; 28 | 29 | void cms::test::AssertOutputEnable() 30 | { 31 | m_printAssert = true; 32 | } 33 | 34 | void cms::test::AssertOutputDisable() 35 | { 36 | m_printAssert = false; 37 | } 38 | 39 | extern "C" void cmsAssertCalled( const char * file, unsigned long line ) 40 | { 41 | if (m_printAssert) 42 | { 43 | fprintf(stdout, "\n%s(%s:%lu)\n", __FUNCTION__ , file, line); 44 | } 45 | 46 | // The TEST_EXIT macro used below is throwing an exception. 47 | // If any code being tested is C++ and marked noexcept, then 48 | // sadly the following applies: 49 | // 50 | // Per https://en.cppreference.com/w/cpp/language/noexcept_spec: 51 | // "Non-throwing functions are permitted to call potentially-throwing 52 | // functions. Whenever an exception is thrown and the search for a 53 | // handler encounters the outermost block of a non-throwing function, 54 | // the function std::terminate ... is called ..." 55 | // 56 | mock(cms::test::ASSERT_MOCK_NAME) 57 | .actualCall(cms::test::ON_ASSERT_FUNC_NAME) 58 | .withParameter("file", file) 59 | .withParameter("line", line); 60 | 61 | TEST_EXIT; 62 | 63 | //help compiler, which doesn't realize that TEST_EXIT is a no-return 64 | for (;;) {} 65 | } 66 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_semaphore.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides an implementation of a fake FreeRTOS semaphore 2 | /// which, like FreeRTOS, is under the hood a queue. 3 | /// cpputest-for-freertos-lib assumes that a queue should be 4 | /// functional, i.e. not a mock. No blocking is implemented. 5 | /// 6 | /// @ingroup 7 | /// @cond 8 | ///*************************************************************************** 9 | /// 10 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 11 | /// 12 | /// This program is open source software: you can redistribute it and/or 13 | /// modify it under the terms of the GNU General Public License as published 14 | /// by the Free Software Foundation, either version 3 of the License, or 15 | /// (at your option) any later version. 16 | /// 17 | /// Alternatively, upon written permission from Matthew Eshleman, this program 18 | /// may be distributed and modified under the terms of a Commercial 19 | /// License. For further details, see the Contact Information below. 20 | /// 21 | /// Contact Information: 22 | /// Matthew Eshleman 23 | /// https://covemountainsoftware.com 24 | /// info@covemountainsoftware.com 25 | ///*************************************************************************** 26 | /// @endcond 27 | 28 | #include "cpputest_for_freertos_fake_queue.hpp" 29 | #include 30 | #include "queue.h" 31 | #include "semphr.h" 32 | 33 | extern "C" BaseType_t xQueueSemaphoreTake(QueueHandle_t queue, TickType_t ticks) 34 | { 35 | (void) ticks; //ignore in cpputest fake semaphore 36 | configASSERT(queue != nullptr); 37 | configASSERT(queue->queueType != queueQUEUE_TYPE_RECURSIVE_MUTEX); 38 | 39 | return cms::InternalQueueReceive(queue); 40 | } 41 | 42 | 43 | extern "C" BaseType_t xQueueGiveFromISR(QueueHandle_t queue, 44 | BaseType_t * const pxHigherPriorityTaskWoken) 45 | { 46 | configASSERT(queue != nullptr); 47 | configASSERT(queue->queueType != queueQUEUE_TYPE_RECURSIVE_MUTEX); 48 | 49 | (void)pxHigherPriorityTaskWoken; 50 | return xSemaphoreGive(queue); 51 | } 52 | 53 | extern "C" QueueHandle_t xQueueCreateCountingSemaphore(const UBaseType_t maxCount, 54 | const UBaseType_t initialCount) 55 | { 56 | QueueHandle_t sema = xQueueCreate(maxCount, 0); 57 | if (sema == nullptr) 58 | { 59 | return nullptr; 60 | } 61 | 62 | for (UBaseType_t i = 0; i < initialCount; ++i) 63 | { 64 | xSemaphoreGive(sema); 65 | } 66 | 67 | return sema; 68 | } 69 | -------------------------------------------------------------------------------- /example/drivers/buttonReader/include/buttonReader.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2024> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @brief public header for the Button Reader driver. 27 | * This driver provides methods to read 28 | * the status of up to 16 buttons. 29 | * Additionally, it provides an ISR callback. 30 | * 31 | */ 32 | #ifndef BUTTON_READER_H 33 | #define BUTTON_READER_H 34 | 35 | #include 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | /** 43 | * @brief initializes the driver. 44 | * @return true - initialization completed successfully. 45 | * false - some error. 46 | */ 47 | bool ButtonReaderInit(); 48 | 49 | /** 50 | * Read the pressed/released status. Guaranteed atomic. 51 | * @param pressed: which buttons pressed since last read. 52 | * @param released: which buttons released since last read. 53 | * @return true: read ok. otherwise error. 54 | */ 55 | bool ButtonReaderRead(uint16_t* pressed, uint16_t* released); 56 | 57 | /* 58 | * ISR Function Callback type. 59 | */ 60 | typedef void (*ButtonReaderIsrCallback)(void* context); 61 | 62 | /** 63 | * Register a callback to be executed when the button change ISR is hit. 64 | * @param callback 65 | * @param context 66 | * @return 67 | */ 68 | bool ButtonReaderRegisterIsrCallback(ButtonReaderIsrCallback callback, void* context); 69 | 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | 74 | #endif //BUTTON_READER_H 75 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_queue_set.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides an implementation of a fake FreeRTOS queue set 2 | /// cpputest-for-freertos-lib assumes that a queue set should be 3 | /// functional, i.e. not a mock. 4 | /// 5 | /// @ingroup 6 | /// @cond 7 | ///*************************************************************************** 8 | /// 9 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 10 | /// 11 | /// This program is open source software: you can redistribute it and/or 12 | /// modify it under the terms of the GNU General Public License as published 13 | /// by the Free Software Foundation, either version 3 of the License, or 14 | /// (at your option) any later version. 15 | /// 16 | /// Alternatively, upon written permission from Matthew Eshleman, this program 17 | /// may be distributed and modified under the terms of a Commercial 18 | /// License. For further details, see the Contact Information below. 19 | /// 20 | /// Contact Information: 21 | /// Matthew Eshleman 22 | /// https://covemountainsoftware.com 23 | /// info@covemountainsoftware.com 24 | ///*************************************************************************** 25 | /// @endcond 26 | 27 | #include "cpputest_for_freertos_fake_queue.hpp" 28 | #include "FreeRTOS.h" 29 | #include "queue.h" 30 | 31 | extern "C" QueueSetHandle_t xQueueCreateSet(const UBaseType_t eventQueueLength) 32 | { 33 | return xQueueGenericCreate(eventQueueLength, sizeof(QueueHandle_t), queueQUEUE_TYPE_SET); 34 | } 35 | 36 | extern "C" BaseType_t xQueueAddToSet(QueueSetMemberHandle_t itemToAdd, QueueSetHandle_t set) 37 | { 38 | auto fakeItemToAdd = static_cast(itemToAdd); 39 | auto fakeSet = static_cast(set); 40 | 41 | configASSERT(fakeItemToAdd != nullptr); 42 | configASSERT(fakeSet != nullptr); 43 | 44 | if ((fakeItemToAdd->queueSetContainer != nullptr) || 45 | (!fakeItemToAdd->queue.empty())) 46 | { 47 | return pdFAIL; 48 | } 49 | else 50 | { 51 | fakeItemToAdd->queueSetContainer = fakeSet; 52 | return pdPASS; 53 | } 54 | } 55 | 56 | extern "C" BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t itemToRemove, QueueSetHandle_t set) 57 | { 58 | auto fakeItemToRemove = static_cast(itemToRemove); 59 | 60 | configASSERT(fakeItemToRemove != nullptr); 61 | configASSERT(set != nullptr); 62 | 63 | if ((fakeItemToRemove->queueSetContainer != set) || 64 | (!fakeItemToRemove->queue.empty())) 65 | { 66 | return pdFAIL; 67 | } 68 | else 69 | { 70 | fakeItemToRemove->queueSetContainer = nullptr; 71 | return pdPASS; 72 | } 73 | } 74 | 75 | extern "C" QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t queueSet, const TickType_t ticksToWait) 76 | { 77 | QueueSetMemberHandle_t rtnItem = nullptr; 78 | 79 | auto result = xQueueReceive(queueSet, &rtnItem, ticksToWait ); 80 | if (result == pdTRUE) 81 | { 82 | return rtnItem; 83 | } 84 | 85 | return nullptr; 86 | } -------------------------------------------------------------------------------- /example/drivers/hwLockCtrl/include/hwLockCtrl.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2019-2020> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @brief public header for the Hw Lock Ctrl driver. 27 | * This driver controls a physical electronic 28 | * controlled hardware lock. 29 | * 30 | * The driver API is fully synchronous. 31 | */ 32 | #ifndef ACTIVEOBJECTUNITTESTINGDEMO_HWLOCKCTRL_H 33 | #define ACTIVEOBJECTUNITTESTINGDEMO_HWLOCKCTRL_H 34 | 35 | #include 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | /** 43 | * @brief HwLockCtrlSelfTestResult enumerates 44 | * possible self test results. 45 | */ 46 | typedef enum HwLockCtrlSelfTestResult 47 | { 48 | HW_LOCK_CTRL_SELF_TEST_PASSED, 49 | HW_LOCK_CTRL_SELF_TEST_FAILED_POWER, 50 | HW_LOCK_CTRL_SELF_TEST_FAILED_MOTOR, 51 | } HwLockCtrlSelfTestResultT; 52 | 53 | /** 54 | * @brief HwLockCtrlInit initializes the driver. Lock state is undefined. 55 | * @return true - initialization completed successfully. 56 | * false - some error. 57 | */ 58 | bool HwLockCtrlInit(); 59 | 60 | /** 61 | * @brief HwLockCtrlLock locks the lock. 62 | * @return true - lock operation completed successfully 63 | * false - some error. 64 | */ 65 | bool HwLockCtrlLock(); 66 | 67 | /** 68 | * @brief HwLockCtrlUnlock unlocks the lock. 69 | * @return true - unlock operation completed successfully 70 | * false - some error. 71 | */ 72 | bool HwLockCtrlUnlock(); 73 | 74 | /** 75 | * @brief HwLockCtrlSelfTest executes a self test. When completed, the Lock is always LOCKED. 76 | * @arg outResult: [out] output the self test results 77 | * @return true - self test completed and results are available in 'outResult' 78 | * false - self test failed to execute. 79 | */ 80 | bool HwLockCtrlSelfTest(HwLockCtrlSelfTestResultT* outResult); 81 | 82 | /** 83 | * Reads current draw by the lock. 84 | * @return current draw in mA. Negative number if internal error. 85 | */ 86 | int32_t HwLockCtrlReadCurrent(); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif //ACTIVEOBJECTUNITTESTINGDEMO_HWLOCKCTRL_H 93 | -------------------------------------------------------------------------------- /example/services/buttonService/include/buttonService.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2024> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #ifndef BUTTON_SERVICE_H 26 | #define BUTTON_SERVICE_H 27 | 28 | #include 29 | #include "cmsExecutionOption.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | /** 36 | * Buttons supported on this product 37 | */ 38 | typedef enum ButtonFlag { 39 | BUTTON_ON_OFF = 0x1, 40 | BUTTON_FUNC_A = 0x2, 41 | } ButtonFlagT; 42 | 43 | typedef enum ButtonState { 44 | BUTTON_PRESSED, 45 | BUTTON_RELEASED, 46 | } ButtonStateT; 47 | 48 | /** 49 | * Initialize the Button Service. Does not start the thread, just prepares. 50 | */ 51 | void ButtonService_Init(); 52 | 53 | /** 54 | * Destroy the ButtonService. Typically only called in unit testing environment. 55 | */ 56 | void ButtonService_Destroy(); 57 | 58 | /** 59 | * Start the button service/thread. Init() must have been called first. 60 | * @param option 61 | */ 62 | void ButtonService_Start(ExecutionOptionT option); 63 | 64 | /** 65 | * typedef for the button service callback 66 | */ 67 | typedef void (*ButtonService_ButtonChangeCallback)(ButtonFlagT flags, ButtonStateT state, void *context); 68 | 69 | /** 70 | * Register callback to be executed from the button service 71 | * thread context. Guaranteed to NOT be called from an ISR context. 72 | * @param callback 73 | * @param context 74 | */ 75 | void ButtonService_RegisterButtonChangeCallback(ButtonService_ButtonChangeCallback callback, void *context); 76 | 77 | /****************************************************************************/ 78 | /***** Backdoor functionality provided for unit testing access only ********/ 79 | /****************************************************************************/ 80 | 81 | /** 82 | * provided for unit testing access only. For the button service, 83 | * will check if semaphore token is available or not (i.e. 84 | * the button ISR has occurred). 85 | * 86 | * @param option EXECUTION_OPTION_NORMAL - internal, normal thread use 87 | * EXECUTION_OPTION_UNIT_TEST - for unit testing 88 | * 89 | * @return true: an event was processed 90 | * false: no events, nothing processed. 91 | */ 92 | bool ButtonService_ProcessOneEvent(ExecutionOptionT option); 93 | 94 | /** 95 | * Fake/simulate the ISR that the button service relies upon 96 | */ 97 | void ButtonService_SimulateIsr(); 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | 103 | #endif //BUTTON_SERVICE_H 104 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_task_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of CppUTest FreeRTOS task methods. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | #include "FreeRTOS.h" 24 | #include "task.h" 25 | #include "cpputest_for_freertos_timers.hpp" 26 | #include "cpputest_for_freertos_task.hpp" 27 | #include "CppUTest/TestHarness.h" 28 | 29 | TEST_GROUP(TaskTests) 30 | { 31 | void setup() final 32 | { 33 | cms::test::TaskInit(); 34 | } 35 | 36 | void teardown() final 37 | { 38 | cms::test::TaskDestroy(); 39 | } 40 | }; 41 | 42 | TEST(TaskTests, task_delay_method_is_available_and_tracks_time_when_not_using_timers) 43 | { 44 | auto count1 = xTaskGetTickCount(); 45 | vTaskDelay(100); 46 | auto count2 = xTaskGetTickCount(); 47 | CHECK_EQUAL(100, count2 - count1); 48 | } 49 | 50 | TEST(TaskTests, task_delay_method_will_move_timers_time_forward_if_timers_are_active) 51 | { 52 | cms::test::TimersInit(); 53 | auto count1 = xTaskGetTickCount(); 54 | vTaskDelay(100); 55 | auto count2 = xTaskGetTickCount(); 56 | CHECK_EQUAL(100, count2 - count1); 57 | auto fromTimers = cms::test::GetCurrentInternalTime(); 58 | 59 | CHECK_TRUE(std::chrono::milliseconds(pdTICKS_TO_MS(100)) == fromTimers); 60 | cms::test::TimersDestroy(); 61 | } 62 | 63 | TEST(TaskTests, task_delay_until_method_is_available) 64 | { 65 | auto lastWakeTime = xTaskGetTickCount(); 66 | auto count1 = lastWakeTime; 67 | vTaskDelayUntil(&lastWakeTime, 10); 68 | auto count2 = xTaskGetTickCount(); 69 | CHECK_EQUAL(10, count2 - count1); 70 | } 71 | 72 | TEST(TaskTests, task_delay_until_method_returns_false_if_next_is_already_happened) 73 | { 74 | auto lastWakeTime = xTaskGetTickCount(); 75 | 76 | //simulate a task switch which means the next delay until deadline has passed 77 | vTaskDelay(11); 78 | 79 | auto didSleep = xTaskDelayUntil(&lastWakeTime, 10); 80 | CHECK_EQUAL(pdFALSE, didSleep); 81 | } 82 | 83 | TEST(TaskTests, task_delay_until_method_returns_true_if_sleep_was_needed) 84 | { 85 | auto lastWakeTime = xTaskGetTickCount(); 86 | auto count1 = lastWakeTime; 87 | 88 | //simulate a brief task switch, but the next deadline is still in the future 89 | vTaskDelay(3); 90 | 91 | auto didSleep = xTaskDelayUntil(&lastWakeTime, 10); 92 | CHECK_EQUAL(pdTRUE, didSleep); 93 | 94 | auto count2 = xTaskGetTickCount(); 95 | CHECK_EQUAL(10, count2 - count1); 96 | } 97 | 98 | static void staticTaskCode(void * parameters) 99 | { 100 | (void)parameters; 101 | //for testing, does nothing 102 | } 103 | 104 | TEST(TaskTests, task_create_static_is_available) 105 | { 106 | static StaticTask_t staticTaskBuffer; 107 | static std::array staticStack; 108 | 109 | auto taskHandle = xTaskCreateStatic( 110 | staticTaskCode, /* Function that implements the task. */ 111 | "TEST", /* Text name for the task. */ 112 | staticStack.size(), /* Number of indexes in the xStack array. */ 113 | ( void * ) 1, /* Parameter passed into the task. */ 114 | tskIDLE_PRIORITY,/* Priority at which the task is created. */ 115 | staticStack.data(), /* Array to use as the task's stack. */ 116 | &staticTaskBuffer ); /* Variable to hold the task's data structure. */ 117 | 118 | CHECK_TRUE(taskHandle != nullptr); 119 | } -------------------------------------------------------------------------------- /example/apps/demoPcApp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "FreeRTOS.h" 6 | #include "task.h" 7 | #include "hwLockCtrlService.h" 8 | #include "buttonService.h" 9 | 10 | static HLCS_LockStateT s_lastState = HLCS_LOCK_STATE_UNKNOWN; 11 | 12 | std::string GetLockState() 13 | { 14 | switch (s_lastState) 15 | { 16 | case HLCS_LOCK_STATE_UNLOCKED: 17 | return "Unlocked"; 18 | case HLCS_LOCK_STATE_LOCKED: 19 | return "Locked"; 20 | default: 21 | return "Unknown"; 22 | } 23 | } 24 | 25 | void LockStateChangeCallback(HLCS_LockStateT state) 26 | { 27 | //NOTE: this callback is being executed 28 | // in the thread context of the HwLockCtrlService. 29 | s_lastState = state; 30 | std::cout << "Lock state changed: " << GetLockState() << std::endl; 31 | } 32 | 33 | void SelfTestResultCallback(HLCS_SelfTestResultT result) 34 | { 35 | //NOTE: this callback is being executed 36 | // in the thread context of the HwLockCtrlService. 37 | 38 | const char* resultStr = (result == HLCS_SELF_TEST_RESULT_PASS) ? "Pass" : "Fail"; 39 | std::cout << "Self Test result: " << resultStr << std::endl; 40 | } 41 | 42 | enum class DesiredAction 43 | { 44 | EXIT, 45 | LOCK, 46 | UNLOCK, 47 | SELF_TEST 48 | }; 49 | 50 | DesiredAction GetUserAction() 51 | { 52 | std::string action; 53 | 54 | std::cout << "Lock state is: " << GetLockState() << std::endl; 55 | std::cout << "0: Unlock the lock" << std::endl; 56 | std::cout << "1: Lock the lock" << std::endl; 57 | std::cout << "2: Self Test the lock" << std::endl; 58 | std::cout << "3: Button ISR" << std::endl; 59 | std::cout << "9: Exit" << std::endl 60 | << std::endl; 61 | 62 | while (true) { 63 | int ch = getchar(); 64 | switch (ch) { 65 | case '0': 66 | return DesiredAction::UNLOCK; 67 | case '1': 68 | return DesiredAction::LOCK; 69 | case '2': 70 | return DesiredAction::SELF_TEST; 71 | case '3': 72 | ButtonService_SimulateIsr(); 73 | continue; 74 | case '9': 75 | return DesiredAction::EXIT; 76 | default: 77 | vTaskDelay(10); 78 | continue; 79 | } 80 | } 81 | } 82 | 83 | void UserInputTask(void*) 84 | { 85 | while (true) 86 | { 87 | switch (GetUserAction()) 88 | { 89 | case DesiredAction::LOCK: 90 | HLCS_RequestLockedAsync(); 91 | break; 92 | case DesiredAction::UNLOCK: 93 | HLCS_RequestUnlockedAsync(); 94 | break; 95 | case DesiredAction::SELF_TEST: 96 | HLCS_RequestSelfTestAsync(); 97 | break; 98 | default: 99 | HLCS_Destroy(); 100 | exit(0); 101 | } 102 | } 103 | } 104 | 105 | void ButtonChangeCallback(ButtonFlagT flags, ButtonStateT state, void* context) 106 | { 107 | (void)context; 108 | printf("%s(flags = 0x%x, state = %d)\n", 109 | __FUNCTION__ , flags, state); 110 | } 111 | 112 | int main() 113 | { 114 | HLCS_Init(); 115 | HLCS_RegisterChangeStateCallback(LockStateChangeCallback); 116 | HLCS_RegisterSelfTestResultCallback(SelfTestResultCallback); 117 | HLCS_Start(EXECUTION_OPTION_NORMAL); 118 | 119 | ButtonService_Init(); 120 | ButtonService_RegisterButtonChangeCallback(ButtonChangeCallback, nullptr); 121 | ButtonService_Start(EXECUTION_OPTION_NORMAL); 122 | 123 | TaskHandle_t input_thread = nullptr; 124 | BaseType_t ok = xTaskCreate(UserInputTask, "Input", 2000, 125 | nullptr, tskIDLE_PRIORITY+2, &input_thread); 126 | assert(ok == pdPASS); 127 | 128 | //kick off FreeRTOS 129 | vTaskStartScheduler(); 130 | } 131 | 132 | void vApplicationStackOverflowHook( TaskHandle_t xTask, 133 | char * pcTaskName ) 134 | { 135 | /* Check pcTaskName for the name of the offending task, 136 | * or pxCurrentTCB if pcTaskName has itself been corrupted. */ 137 | ( void ) xTask; 138 | fprintf(stderr, "!! --> Overflow: %s\n", pcTaskName); 139 | } 140 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_task.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides a non-functional FreeRTOS task API 2 | /// cpputest-for-freertos-lib assumes that unit tests 3 | /// do not actually want threads. Instead, the unit 4 | /// tests should test the code executed in a thread via 5 | /// some sort of accessor. Preferably an active object 6 | /// as demonstrated in the provided example. 7 | /// 8 | /// @ingroup 9 | /// @cond 10 | ///*************************************************************************** 11 | /// 12 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 13 | /// 14 | /// This program is open source software: you can redistribute it and/or 15 | /// modify it under the terms of the GNU General Public License as published 16 | /// by the Free Software Foundation, either version 3 of the License, or 17 | /// (at your option) any later version. 18 | /// 19 | /// Alternatively, upon written permission from Matthew Eshleman, this program 20 | /// may be distributed and modified under the terms of a Commercial 21 | /// License. For further details, see the Contact Information below. 22 | /// 23 | /// Contact Information: 24 | /// Matthew Eshleman 25 | /// https://covemountainsoftware.com 26 | /// info@covemountainsoftware.com 27 | ///*************************************************************************** 28 | /// @endcond 29 | #include "FreeRTOS.h" 30 | #include "task.h" 31 | #include "cpputest_for_freertos_timers.hpp" 32 | 33 | namespace cms { 34 | namespace test { 35 | 36 | static TickType_t s_tickCount = 0; 37 | 38 | void TaskInit() 39 | { 40 | s_tickCount = 0; 41 | } 42 | 43 | void TaskDestroy() 44 | { 45 | s_tickCount = 0; 46 | } 47 | 48 | } //namespace test 49 | } //namespace cms 50 | 51 | //dummy task control block 52 | struct tskTaskControlBlock 53 | { 54 | int dummy; 55 | }; 56 | 57 | extern "C" BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, 58 | const char * const pcName, 59 | const configSTACK_DEPTH_TYPE uxStackDepth, 60 | void * const pvParameters, 61 | UBaseType_t uxPriority, 62 | TaskHandle_t * const pxCreatedTask ) 63 | { 64 | (void)pxTaskCode; 65 | (void)pcName; 66 | (void)uxStackDepth; 67 | (void)pvParameters; 68 | (void)uxPriority; 69 | (void)pxCreatedTask; 70 | return pdPASS; 71 | } 72 | 73 | extern "C" TaskHandle_t xTaskCreateStatic( 74 | TaskFunction_t pxTaskCode, 75 | const char * const pcName, 76 | const configSTACK_DEPTH_TYPE uxStackDepth, 77 | void * const pvParameters, 78 | UBaseType_t uxPriority, 79 | StackType_t * const puxStackBuffer, 80 | StaticTask_t * const pxTaskBuffer ) 81 | { 82 | (void)pxTaskCode; 83 | (void)pcName; 84 | (void)uxStackDepth; 85 | (void)pvParameters; 86 | (void)uxPriority; 87 | (void)puxStackBuffer; 88 | (void)pxTaskBuffer; 89 | 90 | static struct tskTaskControlBlock dummy = {}; 91 | return &dummy; 92 | } 93 | 94 | extern "C" void vTaskDelete( TaskHandle_t xTaskToDelete ) 95 | { 96 | (void)xTaskToDelete; 97 | } 98 | 99 | extern "C" void vTaskDelay(const TickType_t ticks) 100 | { 101 | if (cms::test::TimersIsActive()) 102 | { 103 | auto duration = std::chrono::milliseconds {pdTICKS_TO_MS(ticks)}; 104 | cms::test::MoveTimeForward(duration); 105 | } 106 | else 107 | { 108 | cms::test::s_tickCount += ticks; 109 | } 110 | } 111 | 112 | extern "C" TickType_t xTaskGetTickCount(void) 113 | { 114 | if (cms::test::TimersIsActive()) 115 | { 116 | auto current = cms::test::GetCurrentInternalTime(); 117 | auto milliseconds = std::chrono::duration_cast(current); 118 | return pdMS_TO_TICKS(milliseconds.count()); 119 | } 120 | else 121 | { 122 | return cms::test::s_tickCount; 123 | } 124 | } 125 | 126 | extern "C" BaseType_t xTaskDelayUntil(TickType_t * const previous, const TickType_t increment) 127 | { 128 | configASSERT(previous != nullptr); 129 | auto current = xTaskGetTickCount(); 130 | auto next = *previous + increment; 131 | if (next <= current) 132 | { 133 | return pdFALSE; 134 | } 135 | 136 | vTaskDelay(next - current); 137 | return pdTRUE; 138 | } 139 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_mutex.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides an implementation of a fake FreeRTOS semaphore 2 | /// which, like FreeRTOS, is under the hood a queue. 3 | /// cpputest-for-freertos-lib assumes that a queue should be 4 | /// functional, i.e. not a mock. No blocking is implemented. 5 | /// 6 | /// @ingroup 7 | /// @cond 8 | ///*************************************************************************** 9 | /// 10 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 11 | /// 12 | /// This program is open source software: you can redistribute it and/or 13 | /// modify it under the terms of the GNU General Public License as published 14 | /// by the Free Software Foundation, either version 3 of the License, or 15 | /// (at your option) any later version. 16 | /// 17 | /// Alternatively, upon written permission from Matthew Eshleman, this program 18 | /// may be distributed and modified under the terms of a Commercial 19 | /// License. For further details, see the Contact Information below. 20 | /// 21 | /// Contact Information: 22 | /// Matthew Eshleman 23 | /// https://covemountainsoftware.com 24 | /// info@covemountainsoftware.com 25 | ///*************************************************************************** 26 | /// @endcond 27 | 28 | #include 29 | #include 30 | #include "cpputest_for_freertos_fake_queue.hpp" 31 | #include "cpputest_for_freertos_mutex.hpp" 32 | #include "queue.h" 33 | #include "semphr.h" 34 | 35 | //must be last 36 | #include "CppUTest/TestHarness.h" 37 | 38 | namespace cms { 39 | namespace test { 40 | 41 | static std::list* s_mutexes = nullptr; 42 | 43 | void MutexTrackingInit() 44 | { 45 | configASSERT(s_mutexes == nullptr); 46 | s_mutexes = new std::list; 47 | } 48 | 49 | void MutexTrackingTeardown() 50 | { 51 | if (s_mutexes == nullptr) 52 | return; 53 | 54 | bool isAnyLocked = IsAnyMutexLocked(); 55 | 56 | s_mutexes->clear(); 57 | delete s_mutexes; 58 | s_mutexes = nullptr; 59 | 60 | if (isAnyLocked) 61 | { 62 | FAIL_TEST("A mutex is still active and locked. Test expects all mutexes to be unlocked."); 63 | } 64 | } 65 | 66 | bool IsAnyMutexLocked() 67 | { 68 | if (s_mutexes == nullptr) 69 | return false; 70 | 71 | return std::any_of(s_mutexes->begin(), s_mutexes->end(), [](QueueDefinition* mutex) 72 | { 73 | return uxSemaphoreGetCount(mutex) == 0; 74 | }); 75 | } 76 | 77 | void MutexAboutToDelete(QueueHandle_t mutex) 78 | { 79 | if (s_mutexes == nullptr) 80 | return; 81 | 82 | s_mutexes->remove(mutex); 83 | } 84 | } //namespace 85 | }//namespace 86 | 87 | extern "C" QueueHandle_t xQueueCreateMutex(const uint8_t queueType) 88 | { 89 | (void)queueType; 90 | auto mutex = xQueueGenericCreate(1, 0, queueType); 91 | switch (queueType) { 92 | case queueQUEUE_TYPE_MUTEX: 93 | //wasn't documented, but in experiment, the standard mutex is created unlocked 94 | //(i.e) with one token available 95 | xSemaphoreGive(mutex); 96 | break; 97 | case queueQUEUE_TYPE_RECURSIVE_MUTEX: 98 | xSemaphoreGive(mutex); 99 | mutex->recursiveCallCount = 0; 100 | break; 101 | default: 102 | configASSERT(true == false); 103 | } 104 | configASSERT(mutex != nullptr); 105 | if (cms::test::s_mutexes != nullptr) 106 | { 107 | cms::test::s_mutexes->push_back(mutex); 108 | } 109 | return mutex; 110 | } 111 | 112 | extern "C" BaseType_t xQueueTakeMutexRecursive(QueueHandle_t mutex, 113 | TickType_t ticks) 114 | { 115 | (void) ticks; //not used 116 | 117 | configASSERT(mutex != nullptr); 118 | configASSERT(mutex->queueType == queueQUEUE_TYPE_RECURSIVE_MUTEX); 119 | 120 | if (1 == uxSemaphoreGetCount(mutex)) 121 | { 122 | auto rtn = cms::InternalQueueReceive(mutex); 123 | if (rtn != pdTRUE) 124 | { 125 | return rtn; 126 | } 127 | } 128 | 129 | mutex->recursiveCallCount++; 130 | return pdTRUE; 131 | 132 | } 133 | 134 | extern "C" BaseType_t xQueueGiveMutexRecursive(QueueHandle_t mutex) 135 | { 136 | configASSERT(mutex != nullptr); 137 | configASSERT(mutex->queueType == queueQUEUE_TYPE_RECURSIVE_MUTEX); 138 | 139 | if (0 == uxSemaphoreGetCount(mutex) && mutex->recursiveCallCount > 0) 140 | { 141 | mutex->recursiveCallCount--; 142 | if (mutex->recursiveCallCount == 0) 143 | { 144 | return xSemaphoreGive(mutex); 145 | } 146 | 147 | return pdTRUE; 148 | } 149 | 150 | return pdFALSE; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_semaphore_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of CppUTest for FreeRTOS semaphore implementation 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #include "FreeRTOS.h" 25 | #include "semphr.h" 26 | #include "CppUTest/TestHarness.h" 27 | 28 | TEST_GROUP(SemaphoreTests) 29 | { 30 | SemaphoreHandle_t mSemaUnderTest; 31 | 32 | void setup() final 33 | { 34 | } 35 | 36 | void teardown() final 37 | { 38 | if (mSemaUnderTest != nullptr) 39 | { 40 | vSemaphoreDelete(mSemaUnderTest); 41 | mSemaUnderTest = nullptr; 42 | } 43 | } 44 | 45 | void CreateBinarySemaphore() 46 | { 47 | mSemaUnderTest = xSemaphoreCreateBinary(); 48 | CHECK_TRUE(mSemaUnderTest != nullptr); 49 | } 50 | 51 | void CreateCountingSemaphore(uint32_t count = 10, uint32_t initial = 0) 52 | { 53 | mSemaUnderTest = xSemaphoreCreateCounting(count, initial); 54 | CHECK_TRUE(mSemaUnderTest != nullptr); 55 | } 56 | }; 57 | 58 | TEST(SemaphoreTests, can_create_a_binary_semaphore) 59 | { 60 | CreateBinarySemaphore(); 61 | } 62 | 63 | TEST(SemaphoreTests, can_read_binary_semaphore_count) 64 | { 65 | CreateBinarySemaphore(); 66 | 67 | auto count = uxSemaphoreGetCount(mSemaUnderTest); 68 | CHECK_EQUAL(0, count); 69 | } 70 | 71 | TEST(SemaphoreTests, can_read_binary_semaphore_count_after_give) 72 | { 73 | CreateBinarySemaphore(); 74 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 75 | 76 | xSemaphoreGive(mSemaUnderTest); 77 | CHECK_EQUAL(1, uxSemaphoreGetCount(mSemaUnderTest)); 78 | } 79 | 80 | TEST(SemaphoreTests, binary_semaphore_count_is_one_after_multiple_gives) 81 | { 82 | CreateBinarySemaphore(); 83 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 84 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 85 | CHECK_EQUAL(pdFALSE, xSemaphoreGive(mSemaUnderTest)); 86 | CHECK_EQUAL(pdFALSE, xSemaphoreGive(mSemaUnderTest)); 87 | CHECK_EQUAL(1, uxSemaphoreGetCount(mSemaUnderTest)); 88 | } 89 | 90 | TEST(SemaphoreTests, binary_semaphore_count_is_zero_after_give_then_take) 91 | { 92 | CreateBinarySemaphore(); 93 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 94 | 95 | xSemaphoreGive(mSemaUnderTest); 96 | CHECK_EQUAL(1, uxSemaphoreGetCount(mSemaUnderTest)); 97 | 98 | CHECK_EQUAL(pdTRUE, xSemaphoreTake(mSemaUnderTest, 1000)); 99 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 100 | } 101 | 102 | TEST(SemaphoreTests, binary_semaphore_take_rtn_false_when_not_yet_signaled) 103 | { 104 | CreateBinarySemaphore(); 105 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 106 | CHECK_EQUAL(pdFALSE, xSemaphoreTake(mSemaUnderTest, 1000)); 107 | } 108 | 109 | TEST(SemaphoreTests, can_create_a_counting_semaphore) 110 | { 111 | CreateCountingSemaphore(); 112 | } 113 | 114 | TEST(SemaphoreTests, counting_semaphore_honors_initial_count) 115 | { 116 | CreateCountingSemaphore(10, 3); 117 | CHECK_EQUAL(3, uxSemaphoreGetCount(mSemaUnderTest)); 118 | } 119 | 120 | TEST(SemaphoreTests, counting_semaphore_honors_initial_count_when_zero) 121 | { 122 | CreateCountingSemaphore(10, 0); 123 | CHECK_EQUAL(0, uxSemaphoreGetCount(mSemaUnderTest)); 124 | } 125 | 126 | TEST(SemaphoreTests, counting_semaphore_rtns_false_when_exceed_give_max) 127 | { 128 | CreateCountingSemaphore(3, 0); 129 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 130 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 131 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 132 | CHECK_EQUAL(pdFALSE, xSemaphoreGive(mSemaUnderTest)); 133 | } 134 | 135 | TEST(SemaphoreTests, counting_semaphore_give_take_match) 136 | { 137 | CreateCountingSemaphore(3, 0); 138 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 139 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 140 | CHECK_EQUAL(pdTRUE, xSemaphoreGive(mSemaUnderTest)); 141 | CHECK_EQUAL(pdTRUE, xSemaphoreTake(mSemaUnderTest, 1000)); 142 | CHECK_EQUAL(pdTRUE, xSemaphoreTake(mSemaUnderTest, 1000)); 143 | CHECK_EQUAL(pdTRUE, xSemaphoreTake(mSemaUnderTest, 1000)); 144 | CHECK_EQUAL(pdFALSE, xSemaphoreTake(mSemaUnderTest, 1000)); 145 | } 146 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_mutex_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of CppUTest for FreeRTOS mutex implementation 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #include "FreeRTOS.h" 25 | #include "semphr.h" 26 | #include "cpputest_for_freertos_mutex.hpp" 27 | #include "CppUTest/TestHarness.h" 28 | 29 | 30 | TEST_GROUP(MutexTests) 31 | { 32 | SemaphoreHandle_t mMutexUnderTest; 33 | 34 | void setup() final 35 | { 36 | } 37 | 38 | void teardown() final 39 | { 40 | if (mMutexUnderTest != nullptr) 41 | { 42 | vSemaphoreDelete(mMutexUnderTest); 43 | mMutexUnderTest = nullptr; 44 | } 45 | } 46 | 47 | void CreateMutex() 48 | { 49 | mMutexUnderTest = xSemaphoreCreateMutex(); 50 | CHECK_TRUE(nullptr != mMutexUnderTest); 51 | } 52 | 53 | void CreateRecursiveMutex() 54 | { 55 | mMutexUnderTest = xSemaphoreCreateRecursiveMutex(); 56 | CHECK_TRUE(nullptr != mMutexUnderTest); 57 | } 58 | }; 59 | 60 | TEST(MutexTests, can_create_a_mutex_and_is_not_locked) 61 | { 62 | CreateMutex(); 63 | CHECK_EQUAL(1, uxSemaphoreGetCount(mMutexUnderTest)); 64 | } 65 | 66 | TEST(MutexTests, can_lock_and_unlock_a_mutex) 67 | { 68 | CreateMutex(); 69 | xSemaphoreTake(mMutexUnderTest, 1000); 70 | CHECK_EQUAL(0, uxSemaphoreGetCount(mMutexUnderTest)); 71 | xSemaphoreGive(mMutexUnderTest); 72 | CHECK_EQUAL(1, uxSemaphoreGetCount(mMutexUnderTest)); 73 | } 74 | 75 | TEST(MutexTests, library_can_detect_locked_mutexes) 76 | { 77 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 78 | cms::test::MutexTrackingInit(); 79 | CreateMutex(); 80 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 81 | 82 | xSemaphoreTake(mMutexUnderTest, 1000); 83 | CHECK_TRUE(cms::test::IsAnyMutexLocked()); 84 | 85 | xSemaphoreGive(mMutexUnderTest); 86 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 87 | cms::test::MutexTrackingTeardown(); 88 | } 89 | 90 | TEST(MutexTests, library_does_not_detect_locked_mutex_that_was_deleted) 91 | { 92 | cms::test::MutexTrackingInit(); 93 | CreateMutex(); 94 | xSemaphoreTake(mMutexUnderTest, 1000); 95 | CHECK_TRUE(cms::test::IsAnyMutexLocked()); 96 | 97 | vSemaphoreDelete(mMutexUnderTest); 98 | mMutexUnderTest = nullptr; 99 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 100 | cms::test::MutexTrackingTeardown(); 101 | } 102 | 103 | TEST(MutexTests, can_create_a_recursive_mutex) 104 | { 105 | CreateRecursiveMutex(); 106 | CHECK_EQUAL(1, uxSemaphoreGetCount(mMutexUnderTest)); 107 | } 108 | 109 | TEST(MutexTests, can_lock_a_recursive_mutex_multiple_times) 110 | { 111 | CreateRecursiveMutex(); 112 | CHECK_EQUAL(1, uxSemaphoreGetCount(mMutexUnderTest)); 113 | CHECK_EQUAL(pdTRUE, xSemaphoreTakeRecursive(mMutexUnderTest, 1000)); 114 | CHECK_EQUAL(pdTRUE, xSemaphoreTakeRecursive(mMutexUnderTest, 1000)); 115 | CHECK_EQUAL(pdTRUE, xSemaphoreTakeRecursive(mMutexUnderTest, 1000)); 116 | CHECK_EQUAL(0, uxSemaphoreGetCount(mMutexUnderTest)); 117 | 118 | //now confirm give recursive while here 119 | CHECK_EQUAL(pdTRUE, xSemaphoreGiveRecursive(mMutexUnderTest)); 120 | CHECK_EQUAL(0, uxSemaphoreGetCount(mMutexUnderTest)); 121 | CHECK_EQUAL(pdTRUE, xSemaphoreGiveRecursive(mMutexUnderTest)); 122 | CHECK_EQUAL(0, uxSemaphoreGetCount(mMutexUnderTest)); 123 | CHECK_EQUAL(pdTRUE, xSemaphoreGiveRecursive(mMutexUnderTest)); 124 | CHECK_EQUAL(1, uxSemaphoreGetCount(mMutexUnderTest)); 125 | } 126 | 127 | TEST(MutexTests, library_can_detect_locked_recursive_mutexes) 128 | { 129 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 130 | cms::test::MutexTrackingInit(); 131 | CreateRecursiveMutex(); 132 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 133 | 134 | xSemaphoreTakeRecursive(mMutexUnderTest, 1000); 135 | CHECK_TRUE(cms::test::IsAnyMutexLocked()); 136 | 137 | xSemaphoreGiveRecursive(mMutexUnderTest); 138 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 139 | cms::test::MutexTrackingTeardown(); 140 | } 141 | 142 | TEST(MutexTests, library_does_not_detect_locked_recursive_mutex_that_was_deleted) 143 | { 144 | cms::test::MutexTrackingInit(); 145 | CreateRecursiveMutex(); 146 | xSemaphoreTakeRecursive(mMutexUnderTest, 1000); 147 | CHECK_TRUE(cms::test::IsAnyMutexLocked()); 148 | 149 | vSemaphoreDelete(mMutexUnderTest); 150 | mMutexUnderTest = nullptr; 151 | CHECK_FALSE(cms::test::IsAnyMutexLocked()); 152 | cms::test::MutexTrackingTeardown(); 153 | } -------------------------------------------------------------------------------- /example/services/buttonService/src/buttonService.c: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2024> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include "buttonService.h" 28 | #include "buttonReader.h" 29 | #include "FreeRTOS.h" 30 | #include "task.h" 31 | #include "semphr.h" //freertos semaphores 32 | 33 | static void ButtonService_Task(void*); 34 | static void ButtonService_Initialize(); 35 | static void ButtonService_IsrCallback(void* context); 36 | static void Emit(uint16_t bits, ButtonStateT state); 37 | 38 | static TaskHandle_t s_thread = NULL; 39 | static SemaphoreHandle_t s_eventSema = NULL; 40 | static atomic_bool s_exitThread = false; 41 | static ButtonService_ButtonChangeCallback s_callback = NULL; 42 | static void* s_context = NULL; 43 | 44 | static const uint16_t ON_OFF_BIT = 0x02; 45 | static const uint16_t FUNC_A_BIT = 0x08; 46 | 47 | void ButtonService_Init() 48 | { 49 | configASSERT(s_thread == NULL); 50 | configASSERT(s_eventSema == NULL); 51 | configASSERT(s_callback == NULL); 52 | configASSERT(s_context == NULL); 53 | configASSERT(s_exitThread == false); 54 | 55 | s_eventSema = xSemaphoreCreateBinary(); 56 | configASSERT(s_eventSema != 0); 57 | 58 | //thread is created in Start() 59 | } 60 | 61 | void ButtonService_Destroy() 62 | { 63 | if (s_eventSema != NULL) 64 | { 65 | s_exitThread = true; 66 | xSemaphoreGive(s_eventSema); 67 | vTaskDelete(s_thread); 68 | vSemaphoreDelete(s_eventSema); 69 | } 70 | 71 | s_exitThread = false; 72 | s_eventSema = NULL; 73 | s_thread = NULL; 74 | s_callback = NULL; 75 | s_context = NULL; 76 | } 77 | 78 | void ButtonService_Start(ExecutionOptionT option) 79 | { 80 | configASSERT(s_thread == NULL); 81 | if (EXECUTION_OPTION_NORMAL == option) 82 | { 83 | BaseType_t ok = xTaskCreate(ButtonService_Task, "ButtonService", 2000, NULL, tskIDLE_PRIORITY+3, &s_thread); 84 | configASSERT(ok == pdPASS); 85 | } 86 | else 87 | { 88 | ButtonService_Initialize(); 89 | } 90 | } 91 | 92 | void ButtonService_RegisterButtonChangeCallback(ButtonService_ButtonChangeCallback callback, void* context) 93 | { 94 | s_callback = callback; 95 | s_context = context; 96 | } 97 | 98 | bool ButtonService_ProcessOneEvent(ExecutionOptionT option) 99 | { 100 | if ((EXECUTION_OPTION_UNIT_TEST == option) && 101 | (0 == uxSemaphoreGetCount(s_eventSema))) 102 | { 103 | return false; 104 | } 105 | 106 | BaseType_t rtn = xSemaphoreTake(s_eventSema, portMAX_DELAY); 107 | if (rtn == pdFALSE) 108 | { 109 | return false; 110 | } 111 | 112 | uint16_t pressed = 0; 113 | uint16_t released = 0; 114 | bool ok = ButtonReaderRead(&pressed, &released); 115 | configASSERT(ok); 116 | 117 | if (s_callback != NULL) 118 | { 119 | if (pressed != 0) 120 | { 121 | Emit(pressed, BUTTON_PRESSED); 122 | } 123 | 124 | if (released != 0) 125 | { 126 | Emit(released, BUTTON_RELEASED); 127 | } 128 | } 129 | 130 | return true; 131 | } 132 | 133 | void ButtonService_Initialize() 134 | { 135 | ButtonReaderInit(); 136 | ButtonReaderRegisterIsrCallback(ButtonService_IsrCallback, NULL); 137 | } 138 | 139 | void ButtonService_Task(void* params) 140 | { 141 | (void) params; 142 | 143 | ButtonService_Initialize(); 144 | while (!s_exitThread) 145 | { 146 | ButtonService_ProcessOneEvent(EXECUTION_OPTION_NORMAL); 147 | } 148 | } 149 | 150 | void ButtonService_IsrCallback(void* context) 151 | { 152 | (void)context; 153 | BaseType_t higherPriority = pdFALSE; 154 | xSemaphoreGiveFromISR(s_eventSema, &higherPriority); 155 | portYIELD_FROM_ISR(higherPriority); 156 | } 157 | 158 | void ButtonService_SimulateIsr() 159 | { 160 | ButtonService_IsrCallback(NULL); 161 | } 162 | 163 | void Emit(uint16_t bits, ButtonStateT state) 164 | { 165 | ButtonFlagT flags = 0; 166 | if (bits & ON_OFF_BIT) 167 | { 168 | flags |= BUTTON_ON_OFF; 169 | } 170 | 171 | if (bits & FUNC_A_BIT) 172 | { 173 | flags |= BUTTON_FUNC_A; 174 | } 175 | 176 | if ((s_callback != NULL) && (flags != 0)) 177 | { 178 | s_callback(flags, state, s_context); 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /example/services/hwLockCtrlService/include/hwLockCtrlService.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2021> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @brief the HwLockCtrlService (HLCS) provides for higher level hardware lock control behavior. 27 | * For example, this service will automatically return the hardware lock to its last 28 | * state after completing a self test request. 29 | * 30 | * @note: This service "is a" active object, and was created for demonstration purposes, to 31 | * show the simplest active object pattern in a faux RTOS environment. 32 | * 33 | * @note: this file represents the public facing C API for this particular active object 34 | */ 35 | 36 | #ifndef HW_LOCK_CTRL_SERVICE_H 37 | #define HW_LOCK_CTRL_SERVICE_H 38 | 39 | #include 40 | #include "cmsExecutionOption.h" 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | typedef enum HLCS_LockState 47 | { 48 | HLCS_LOCK_STATE_UNKNOWN, 49 | HLCS_LOCK_STATE_LOCKED, 50 | HLCS_LOCK_STATE_UNLOCKED 51 | } HLCS_LockStateT; 52 | 53 | typedef enum HLCS_SelfTestResult 54 | { 55 | HLCS_SELF_TEST_RESULT_PASS, 56 | HLCS_SELF_TEST_RESULT_FAIL 57 | } HLCS_SelfTestResultT; 58 | 59 | /** 60 | * @brief HLCS_Init() will initialize the module and associated RTOS 61 | * components. The module will be idle and not actually started. 62 | */ 63 | void HLCS_Init(); 64 | 65 | /** 66 | * @brief HLCS_Destroy() will stop (kill) the module and its internal thread 67 | * 68 | * @note: typical C module firmware designs may not include this type of 69 | * public API. However, this is needed for clean unit testing and 70 | * ultimately might prove useful in production code as well. 71 | */ 72 | void HLCS_Destroy(); 73 | 74 | /** 75 | * @brief HLCS_Start() will start behavior. Init() must have been called. 76 | */ 77 | void HLCS_Start(ExecutionOptionT option); 78 | 79 | /** 80 | * @brief HLCS_GetState() provides a thread safe synchronous API to 81 | * determine the current state of this module. 82 | * @return the current lock state 83 | */ 84 | HLCS_LockStateT HLCS_GetState(); 85 | 86 | typedef void (*HLCS_ChangeStateCallback)(HLCS_LockStateT state); 87 | /** 88 | * @brief HLCS_RegisterChangeStateCallback() provides a method to enable 89 | * a single external observer of this module's state. 90 | * @note: The callback will be executed in another thread context. 91 | * The provided callback should be "fast" with minimal blocking. 92 | */ 93 | void HLCS_RegisterChangeStateCallback(HLCS_ChangeStateCallback callback); 94 | 95 | typedef void (*HLCS_SelfTestResultCallback)(HLCS_SelfTestResultT result); 96 | /** 97 | * @brief HLCS_RegisterSelfTestResultCallback() provides a method to enable 98 | * a single external observer of this module's self test behavior. 99 | * @note: The callback will be executed in another thread context. 100 | * The provided callback should be "fast" with minimal blocking. 101 | */ 102 | void HLCS_RegisterSelfTestResultCallback(HLCS_SelfTestResultCallback callback); 103 | 104 | /** 105 | * @brief HLCS_RequestLockedAsync() issue an asynchronous request to this module 106 | * to lock the hardware lock. 107 | */ 108 | void HLCS_RequestLockedAsync(); 109 | 110 | /** 111 | * @brief HLCS_RequestUnlockedAsync() issue an asynchronous request to this module 112 | * to unlock the hardware lock. 113 | */ 114 | void HLCS_RequestUnlockedAsync(); 115 | 116 | /** 117 | * @brief HLCS_RequestUnlockedAsync() issue an asynchronous request to this module 118 | * to perform a self test on the hardware lock. 119 | */ 120 | void HLCS_RequestSelfTestAsync(); 121 | 122 | /****************************************************************************/ 123 | /***** Backdoor functionality provided for unit testing access only ********/ 124 | /****************************************************************************/ 125 | 126 | /** 127 | * @brief HLCS_ProcessOneEvent() - provided for unit testing 128 | * access only. 129 | * 130 | * @param option EXECUTION_OPTION_NORMAL - internal, normal thread use 131 | * EXECUTION_OPTION_UNIT_TEST - for unit testing 132 | * 133 | * @return true: a message was processed. 134 | * false: internal queue was empty, nothing processed. 135 | */ 136 | bool HLCS_ProcessOneEvent(ExecutionOptionT option); 137 | 138 | #ifdef __cplusplus 139 | } 140 | #endif 141 | 142 | #endif //HW_LOCK_CTRL_SERVICE_H 143 | -------------------------------------------------------------------------------- /example/services/buttonService/test/buttonServiceTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2024> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include "buttonService.h" 26 | #include "cpputest_for_freertos_lib.hpp" 27 | #include "mockButtonReader.hpp" 28 | #include "CppUTest/TestHarness.h" 29 | #include "CppUTestExt/MockSupport.h" 30 | 31 | //a bit of internal knowledge the test needs to know about the hardware design 32 | static const uint16_t ON_OFF_BIT = 0x02; 33 | static const uint16_t FUNC_A_BIT = 0x08; 34 | 35 | TEST_GROUP(ButtonServiceTests) { 36 | 37 | uint8_t testContextObject = 1; 38 | 39 | void setup() final { 40 | cms::test::LibInitAll(); 41 | mock("ButtonReader").expectOneCall("Init"); 42 | ButtonService_Init(); 43 | ButtonService_Start(EXECUTION_OPTION_UNIT_TEST); 44 | 45 | ButtonService_RegisterButtonChangeCallback([](ButtonFlagT flags, ButtonStateT state, void *context){ 46 | mock("TEST").actualCall("Callback") 47 | .withParameter("flags", flags) 48 | .withParameter("state", state) 49 | .withParameter("context", context); 50 | }, &testContextObject); 51 | mock().checkExpectations(); 52 | mock().clear(); 53 | } 54 | 55 | void teardown() final { 56 | ButtonService_Destroy(); //ensure we are stopped/clean/destroyed. 57 | mock().clear(); 58 | cms::test::LibTeardownAll(); 59 | } 60 | 61 | static void GiveProcessingTime() { 62 | //use our unit testing backdoor to service 63 | //the active object's internal semaphore. This avoids threading issues 64 | //with unit tests, creating 100% predictable unit tests. 65 | while (ButtonService_ProcessOneEvent(EXECUTION_OPTION_UNIT_TEST)) {} 66 | } 67 | 68 | static void DoButtonIsr(uint16_t pressedBits, uint16_t releasedBits) 69 | { 70 | mock("ButtonReader") 71 | .expectOneCall("Read") 72 | .withOutputParameterReturning("pressed", &pressedBits, sizeof(pressedBits)) 73 | .withOutputParameterReturning("released", &releasedBits, sizeof(releasedBits)); 74 | cms::test::mock::ButtonReaderDoIsr(); 75 | GiveProcessingTime(); 76 | } 77 | }; 78 | 79 | TEST(ButtonServiceTests, given_startup_when_created_then_does_not_crash) 80 | { 81 | //setup() is called by cpputest, which inits AND starts our unit under test. 82 | } 83 | 84 | TEST(ButtonServiceTests, given_button_isr_then_reads_button_status) 85 | { 86 | mock("ButtonReader").expectOneCall("Read").ignoreOtherParameters(); 87 | cms::test::mock::ButtonReaderDoIsr(); 88 | GiveProcessingTime(); 89 | mock().checkExpectations(); 90 | } 91 | 92 | TEST(ButtonServiceTests, given_button_isr_and_registered_callback_then_reads_button_status_and_executes_service_callback) 93 | { 94 | mock("TEST").expectOneCall("Callback") 95 | .withParameter("flags", BUTTON_ON_OFF) 96 | .withParameter("state", BUTTON_PRESSED) 97 | .ignoreOtherParameters(); 98 | 99 | DoButtonIsr(ON_OFF_BIT, 0); 100 | 101 | mock().checkExpectations(); 102 | } 103 | 104 | TEST(ButtonServiceTests, given_button_isr_and_registered_callback_then_callback_provides_correct_context) 105 | { 106 | mock("TEST").expectOneCall("Callback") 107 | .withParameter("context", (void*)&testContextObject) 108 | .ignoreOtherParameters(); 109 | 110 | DoButtonIsr(ON_OFF_BIT, 0); 111 | 112 | mock().checkExpectations(); 113 | } 114 | 115 | TEST(ButtonServiceTests, given_button_released_and_then_callback_is_released_only) 116 | { 117 | mock("TEST").expectOneCall("Callback") 118 | .withParameter("state", BUTTON_RELEASED) 119 | .ignoreOtherParameters(); 120 | 121 | DoButtonIsr(0, ON_OFF_BIT); 122 | mock().checkExpectations(); 123 | } 124 | 125 | TEST(ButtonServiceTests, given_button_released_and_pressed_then_two_callbacks) 126 | { 127 | mock("TEST").expectOneCall("Callback") 128 | .withParameter("state", BUTTON_PRESSED) 129 | .withParameter("flags", BUTTON_FUNC_A) 130 | .ignoreOtherParameters(); 131 | 132 | mock("TEST").expectOneCall("Callback") 133 | .withParameter("state", BUTTON_RELEASED) 134 | .withParameter("flags", BUTTON_ON_OFF) 135 | .ignoreOtherParameters(); 136 | 137 | DoButtonIsr(FUNC_A_BIT, ON_OFF_BIT); 138 | mock().checkExpectations(); 139 | } 140 | 141 | TEST(ButtonServiceTests, given_garbage_button_read_then_callback_is_not_executed) 142 | { 143 | mock("TEST").expectNoCall("Callback"); 144 | DoButtonIsr(1, 1); 145 | mock().checkExpectations(); 146 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CppUTest for FreeRTOS 2 | 3 | Build and Test status: ![Build and Tests](https://github.com/covemountainsoftware/cpputest-for-freertos/actions/workflows/cmake.yml/badge.svg) 4 | 5 | Copyright Matthew Eshleman. 6 | 7 | To learn more about Matthew, see here: https://covemountainsoftware.com/services/consulting/ 8 | 9 | # Introduction 10 | 11 | CppUTest for FreeRTOS is a host PC based unit testing library supporting 12 | the unit testing of well-formed FreeRTOS based code. Fundamentally it provides 13 | various FreeRTOS functions as non-functional linking only replacements OR 'fakes' 14 | that provide equivalent functionality (especially see timers). 15 | 16 | What do I mean by well-formed? Well-formed in this context means a thread or 17 | module with only a single thread blocking mechanism (queue or semaphore, for example) 18 | which in turn, when unblocked, activates the thread's behavior. 19 | A well-known example would be an [active object](https://covemountainsoftware.com/2021/04/20/what-is-an-active-object/). 20 | 21 | Frankly, a firmware's threads should be well-formed. Like many others, 22 | I consider the mixing of RTOS blocking and locking mechanism's to be 23 | a recipe for a bad-cake, if not disaster. 24 | 25 | # Environment 26 | 27 | This project was developed and proven in Ubuntu 22.04. In theory any 28 | build or host operating system environment supported by CppUTest 4.0 will 29 | be compatible with this code. 30 | 31 | ## Prerequisites 32 | 33 | * CMake and associated build tools were used to develop 34 | and prove out this project. 35 | * CppUTest (version 4.0) 36 | * This project requires support for C++14 and C11. 37 | 38 | ## Continuous Integration 39 | 40 | This project uses GitHub Actions to build and execute all 41 | unit tests found in this project. This is an example of one of the 42 | key benefits of host-based testing of embedded software. 43 | 44 | See the configuration at: `.github/workflows/cmake.yml` 45 | 46 | # Examples 47 | 48 | ## Button Service 49 | 50 | The button service is effectively an active object style FreeRTOS service (thread) 51 | which blocks exclusively on a semaphore. It is provided as an example of 52 | how to use this library to unit test a thread that pends on a semaphore. 53 | 54 | See `example/services/buttonService/test` for the associated unit tests. 55 | 56 | ## Hw Lock Ctrl Service 57 | 58 | The Hw Lock Ctrl Service is a traditional active object FreeRTOS service (thread) 59 | which blocks exclusively on a queue. It is provided as an example of 60 | how to use this library to unit test an active object thread. 61 | 62 | See `example/services/hwLockCtrlService/test` for the associated unit tests. 63 | 64 | ## demoPcApp 65 | 66 | A POSIX port FreeRTOS demonstration app interacting with the example services in a 67 | real FreeRTOS environment. 68 | 69 | See `example\apps\demoPcApp`. 70 | 71 | # FreeRTOS Support 72 | 73 | ## Tasks 74 | 75 | Non-functional FreeRTOS task related methods are provided for linking purposes only. 76 | The library assumes that the unit under test will avoid actual threading 77 | behavior while unit testing. i.e. test the code executed by a thread, NOT 78 | threading behavior itself. 79 | 80 | ## Queues 81 | 82 | The library provides fake but functional FreeRTOS compatible queues. The queues 83 | do not block in any manner. 84 | 85 | ## Queue Sets 86 | 87 | The library provides fake but functional FreeRTOS compatible queue sets. 88 | The queues sets do not block in any manner and operate as expected with 89 | queues and semaphores. NOTE: provided for projects that may have a 90 | real need for this FreeRTOS feature. However, FreeRTOS is not encouraging 91 | this feature and neither would I encourage the use of this feature. Too 92 | many caveats and potential maintenance issues. 93 | 94 | ## Timers 95 | 96 | The library provides fake but functional FreeRTOS compatible software timers. 97 | Accessor methods are provided allowing unit tests to "move time forward," 98 | triggering FreeRTOS timers to fire as expected. 99 | 100 | ## ASSERT 101 | 102 | The library provides a configured "configASSERT" macro for asserts compatible 103 | with the FreeRTOS assert method. The assert is mocked and helper methods 104 | are provided to enable unit testing of assert behavior. 105 | 106 | ## Delay 107 | 108 | Basic vTaskDelay provided, which will coordinate with the fake timers when used. 109 | Keeps APIs such xTaskGetTickCount coordinated with fake timers or alternate tick count 110 | when timers are not being used. 111 | 112 | ## Semaphores 113 | 114 | Available. The provided fake semaphores do not block, just like the queues. 115 | See the example ButtonService and its unit tests for a FreeRTOS thread 116 | exclusively blocking on a semaphore, triggered from an ISR. 117 | 118 | ## Mutexes 119 | 120 | Available. The provided fake mutexes do not block, just like the semaphores and queues. 121 | Additionally, when a unit test is completed, the library confirms that all mutex 122 | have been either deleted OR are unlocked. i.e. ensure that a particular test does 123 | not leave any mutexes in a locked state accidentally. 124 | 125 | ## Direct to task notifications 126 | 127 | TODO. 128 | 129 | ## Stream buffers 130 | 131 | TODO. 132 | 133 | ## Message buffers 134 | 135 | TODO. 136 | 137 | ## Event groups 138 | 139 | TODO. 140 | 141 | # License 142 | 143 | All code in this project found in the `cms` namespace follows a dual-license approach. 144 | Please see LICENSE.txt for details. 145 | 146 | All licenses for external source code and libraries relied upon by this project 147 | remain fully owned by their respective owners. 148 | 149 | # References 150 | 151 | This project was inspired by a generic example, see this blog post: 152 | https://covemountainsoftware.com/2020/04/17/unit-testing-active-objects-and-state-machines/ 153 | 154 | Additionally, please see that post's associated GitHub repo: 155 | https://github.com/covemountainsoftware/activeObjectUnitTestingDemo 156 | 157 | Other references: 158 | * Sutter, Herb. Prefer Using Active Objects Instead of Naked Threads. Dr. Dobbs, June 2010. 159 | * Grenning, James. Test Driven Development for Embedded C. 160 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/port/include/portmacro.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPPUTEST_FOR_FREERTOS_LIB_PORTMACRO_H 3 | #define CPPUTEST_FOR_FREERTOS_LIB_PORTMACRO_H 4 | 5 | /*----------------------------------------------------------- 6 | * Port specific definitions. 7 | * 8 | * The settings in this file configure FreeRTOS correctly for the 9 | * given hardware and compiler. 10 | * 11 | * These settings should not be altered. 12 | *----------------------------------------------------------- 13 | */ 14 | 15 | /* Type definitions. */ 16 | #define portCHAR char 17 | #define portFLOAT float 18 | #define portDOUBLE double 19 | #define portLONG long 20 | #define portSHORT int 21 | #define portSTACK_TYPE uint8_t 22 | #define portBASE_TYPE char 23 | 24 | #define portSTACK_GROWTH ( -1 ) 25 | #define portBYTE_ALIGNMENT 4 26 | #define portPOINTER_SIZE_TYPE size_t 27 | typedef portSTACK_TYPE StackType_t; 28 | typedef signed char BaseType_t; 29 | typedef unsigned char UBaseType_t; 30 | 31 | #if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) 32 | typedef uint16_t TickType_t; 33 | #define portMAX_DELAY ( TickType_t ) 0xffffU 34 | #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS ) 35 | typedef uint32_t TickType_t; 36 | #define portMAX_DELAY ( TickType_t ) 0xffffffffU 37 | #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS ) 38 | typedef uint64_t TickType_t; 39 | #define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffU 40 | #else 41 | #error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width. 42 | #endif 43 | 44 | /* Architecture specific optimisations. */ 45 | #ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION 46 | #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 47 | #endif 48 | 49 | #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 50 | 51 | /* Check the configuration. */ 52 | #if ( configMAX_PRIORITIES > 32 ) 53 | #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. 54 | #endif 55 | 56 | /* Store/clear the ready priorities in a bit map. */ 57 | #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) 58 | #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) 59 | 60 | /*-----------------------------------------------------------*/ 61 | 62 | #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) \ 63 | do { \ 64 | uxTopPriority = 0; \ 65 | } while( 0 ) 66 | 67 | #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ 68 | 69 | /* Disable the interrupts */ 70 | #define portDISABLE_INTERRUPTS() do {} while( 0 ) 71 | 72 | /* Enable the interrupts */ 73 | #define portENABLE_INTERRUPTS() do {} while( 0 ) 74 | 75 | #if ( configNUMBER_OF_CORES == 1 ) 76 | /* preserve current interrupt state and then disable interrupts */ 77 | #define portENTER_CRITICAL() do {} while( 0 ) 78 | 79 | /* restore previously preserved interrupt state */ 80 | #define portEXIT_CRITICAL() do {} while( 0 ) 81 | #else 82 | 83 | /* The port can maintain the critical nesting count in TCB or maintain the critical 84 | * nesting count in the port. */ 85 | #define portCRITICAL_NESTING_IN_TCB 1 86 | 87 | /* vTaskEnterCritical and vTaskExitCritical should be used in the implementation 88 | * of portENTER/EXIT_CRITICAL if the number of cores is more than 1 in the system. */ 89 | #define portENTER_CRITICAL vTaskEnterCritical 90 | #define portEXIT_CRITICAL vTaskExitCritical 91 | 92 | /* vTaskEnterCriticalFromISR and vTaskExitCriticalFromISR should be used in the 93 | * implementation of portENTER/EXIT_CRITICAL_FROM_ISR if the number of cores is 94 | * more than 1 in the system. */ 95 | #define portENTER_CRITICAL_FROM_ISR vTaskEnterCriticalFromISR 96 | #define portEXIT_CRITICAL_FROM_ISR vTaskExitCriticalFromISR 97 | 98 | #endif /* if ( configNUMBER_OF_CORES == 1 ) */ 99 | 100 | #define portYIELD() do {} while(0) 101 | #define portYIELD_FROM_ISR(x) do {} while(0) 102 | 103 | /* Task function macros as described on the FreeRTOS.org WEB site. */ 104 | #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters ) 105 | #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters ) 106 | 107 | #if ( configNUMBER_OF_CORES > 1 ) 108 | /* Return the core ID on which the code is running. */ 109 | #define portGET_CORE_ID() 0 110 | 111 | /* Set the interrupt mask. */ 112 | #define portSET_INTERRUPT_MASK() 0 113 | 114 | /* Clear the interrupt mask. */ 115 | #define portCLEAR_INTERRUPT_MASK( x ) ( ( void ) ( x ) ) 116 | 117 | /* Request the core ID x to yield. */ 118 | #define portYIELD_CORE( x ) do {} while( 0 ) 119 | 120 | /* Acquire the TASK lock. TASK lock is a recursive lock. 121 | * It should be able to be locked by the same core multiple times. */ 122 | #define portGET_TASK_LOCK() do {} while( 0 ) 123 | 124 | /* Release the TASK lock. If a TASK lock is locked by the same core multiple times, 125 | * it should be released as many times as it is locked. */ 126 | #define portRELEASE_TASK_LOCK() do {} while( 0 ) 127 | 128 | /* Acquire the ISR lock. ISR lock is a recursive lock. 129 | * It should be able to be locked by the same core multiple times. */ 130 | #define portGET_ISR_LOCK() do {} while( 0 ) 131 | 132 | /* Release the ISR lock. If a ISR lock is locked by the same core multiple times, \ 133 | * it should be released as many times as it is locked. */ 134 | #define portRELEASE_ISR_LOCK() do {} while( 0 ) 135 | 136 | #endif /* if ( configNUMBER_OF_CORES > 1 ) */ 137 | 138 | #endif //CPPUTEST_FOR_FREERTOS_LIB_PORTMACRO_H 139 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_queue.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides an implementation of a fake FreeRTOS queue 2 | /// cpputest-for-freertos-lib assumes that a queue should be 3 | /// functional, i.e. not a mock. 4 | /// 5 | /// @ingroup 6 | /// @cond 7 | ///*************************************************************************** 8 | /// 9 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 10 | /// 11 | /// This program is open source software: you can redistribute it and/or 12 | /// modify it under the terms of the GNU General Public License as published 13 | /// by the Free Software Foundation, either version 3 of the License, or 14 | /// (at your option) any later version. 15 | /// 16 | /// Alternatively, upon written permission from Matthew Eshleman, this program 17 | /// may be distributed and modified under the terms of a Commercial 18 | /// License. For further details, see the Contact Information below. 19 | /// 20 | /// Contact Information: 21 | /// Matthew Eshleman 22 | /// https://covemountainsoftware.com 23 | /// info@covemountainsoftware.com 24 | ///*************************************************************************** 25 | /// @endcond 26 | 27 | #include "cpputest_for_freertos_fake_queue.hpp" 28 | #include 29 | #include "FreeRTOS.h" 30 | #include "queue.h" 31 | 32 | extern "C" QueueHandle_t xQueueGenericCreate(const UBaseType_t queueLength, 33 | const UBaseType_t itemSize, 34 | const uint8_t queueType) 35 | { 36 | auto queue = new FakeQueue(); 37 | queue->queueLength = queueLength; 38 | queue->itemSize = itemSize; 39 | queue->queueType = queueType; 40 | return queue; 41 | } 42 | 43 | namespace cms{ 44 | namespace test { 45 | extern void MutexAboutToDelete(QueueHandle_t mutex); 46 | } 47 | } 48 | extern "C" void vQueueDelete(QueueHandle_t queue) 49 | { 50 | configASSERT(queue != nullptr); 51 | if (queue->queueType == queueQUEUE_TYPE_MUTEX) 52 | { 53 | cms::test::MutexAboutToDelete(queue); 54 | } 55 | delete queue; 56 | } 57 | 58 | extern "C" UBaseType_t uxQueueMessagesWaiting(const QueueHandle_t queue) 59 | { 60 | configASSERT(queue != nullptr); 61 | return queue->queue.size(); 62 | } 63 | 64 | extern "C" UBaseType_t uxQueueSpacesAvailable(const QueueHandle_t queue) 65 | { 66 | configASSERT(queue != nullptr); 67 | return queue->queueLength - uxQueueMessagesWaiting(queue); 68 | } 69 | 70 | BaseType_t cms::InternalQueueReceive(FakeQueue * queue) 71 | { 72 | configASSERT(queue != nullptr); 73 | 74 | uint64_t dummy; 75 | return cms::InternalQueueReceive(queue, &dummy); 76 | } 77 | 78 | BaseType_t cms::InternalQueueReceive(FakeQueue * queue, void * const buffer) 79 | { 80 | configASSERT(queue != nullptr); 81 | 82 | if (!queue->queue.empty()) 83 | { 84 | auto front = queue->queue.front(); 85 | memcpy(buffer, front.data(), queue->itemSize); 86 | queue->queue.pop_front(); 87 | return pdTRUE; 88 | } 89 | else 90 | { 91 | return pdFALSE; 92 | } 93 | } 94 | 95 | extern "C" BaseType_t xQueueReceive(QueueHandle_t queue, void * const buffer, TickType_t ticks) 96 | { 97 | configASSERT(queue != nullptr); 98 | configASSERT(buffer != nullptr); 99 | 100 | (void)ticks; //in our unit testing fake, never honor ticks to wait. 101 | return cms::InternalQueueReceive(queue, buffer); 102 | } 103 | 104 | extern "C" BaseType_t xQueueGenericSend(QueueHandle_t queue, 105 | const void * const itemToQueue, 106 | TickType_t ticks, 107 | const BaseType_t copyPosition) 108 | { 109 | (void)ticks; 110 | configASSERT(queue != nullptr); 111 | configASSERT(!((itemToQueue == nullptr) && (queue->itemSize != 0U))); 112 | configASSERT(!((copyPosition == queueOVERWRITE) && (queue->queueLength != 1))); 113 | 114 | if ((copyPosition != queueOVERWRITE) && 115 | (queue->queue.size() >= queue->queueLength)) 116 | { 117 | return errQUEUE_FULL; 118 | } 119 | 120 | std::vector msg; 121 | msg.resize(queue->itemSize); 122 | memcpy(msg.data(), itemToQueue, queue->itemSize); 123 | 124 | if (copyPosition == queueSEND_TO_BACK) 125 | { 126 | queue->queue.push_back(msg); 127 | } 128 | else if (copyPosition == queueSEND_TO_FRONT) 129 | { 130 | queue->queue.push_front(msg); 131 | } 132 | else if (copyPosition == queueOVERWRITE) 133 | { 134 | queue->queue.pop_front(); 135 | queue->queue.push_front(msg); 136 | } 137 | else 138 | { 139 | configASSERT(true == false); 140 | } 141 | 142 | if (queue->queueSetContainer != nullptr) 143 | { 144 | xQueueGenericSend(queue->queueSetContainer, &queue, ticks, queueSEND_TO_BACK); 145 | } 146 | 147 | return pdTRUE; 148 | } 149 | 150 | extern "C" BaseType_t xQueuePeek(QueueHandle_t queue, void * const buffer, TickType_t ticks) 151 | { 152 | configASSERT(queue != nullptr); 153 | (void)ticks; //in our unit testing fake, never honor ticks to wait. 154 | 155 | if (!queue->queue.empty()) 156 | { 157 | auto front = queue->queue.front(); 158 | memcpy(buffer, front.data(), queue->itemSize); 159 | return pdTRUE; 160 | } 161 | else 162 | { 163 | return pdFALSE; 164 | } 165 | } 166 | 167 | extern "C" QueueHandle_t xQueueGenericCreateStatic(const UBaseType_t queueLength, 168 | const UBaseType_t itemSize, 169 | uint8_t * queueStorage, 170 | StaticQueue_t * staticQueue, 171 | const uint8_t queueType) 172 | { 173 | //for unit testing, just going to ignore the provided static queue and allocate 174 | //dynamically. 175 | (void)queueStorage; 176 | (void)staticQueue; 177 | auto queue = xQueueGenericCreate(queueLength, itemSize, queueType); 178 | return queue; 179 | } 180 | 181 | extern "C" void vQueueAddToRegistry(QueueHandle_t queue, const char * queueName) 182 | { 183 | configASSERT(queue != nullptr); 184 | configASSERT(queueName != nullptr); 185 | queue->registryName = queueName; 186 | } 187 | 188 | extern "C" const char * pcQueueGetName(QueueHandle_t queue) 189 | { 190 | configASSERT(queue != nullptr); 191 | return queue->registryName; 192 | } 193 | 194 | extern "C" void vQueueUnregisterQueue(QueueHandle_t queue) 195 | { 196 | configASSERT(queue != nullptr); 197 | queue->registryName = nullptr; 198 | } -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_queue_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of CppUTest for FreeRTOS queues. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #include "FreeRTOS.h" 25 | #include "queue.h" 26 | #include "CppUTest/TestHarness.h" 27 | 28 | typedef struct TestEvent { 29 | int32_t valueA; 30 | uint64_t valueB; 31 | } TestEventT; 32 | 33 | TEST_GROUP(QueueTests) 34 | { 35 | QueueHandle_t mQueueUnderTest = nullptr; 36 | 37 | void setup() final 38 | { 39 | } 40 | 41 | void teardown() final 42 | { 43 | if (mQueueUnderTest != nullptr) 44 | { 45 | //needed for cpputest memory leak detection 46 | vQueueDelete(mQueueUnderTest); 47 | mQueueUnderTest = nullptr; 48 | } 49 | } 50 | 51 | void CreateUnderTest(UBaseType_t len, UBaseType_t itemSize) 52 | { 53 | mQueueUnderTest = xQueueCreate(len, itemSize); 54 | CHECK_TRUE(mQueueUnderTest != nullptr); 55 | } 56 | }; 57 | 58 | TEST(QueueTests, can_create_a_queue) 59 | { 60 | CreateUnderTest(2, sizeof(TestEventT)); 61 | } 62 | 63 | TEST(QueueTests, can_post_to_queue) 64 | { 65 | CreateUnderTest(2, sizeof(TestEventT)); 66 | TestEventT event; 67 | auto rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 68 | CHECK_EQUAL(pdTRUE, rtn); 69 | } 70 | 71 | TEST(QueueTests, posted_item_can_be_retrieved) 72 | { 73 | CreateUnderTest(2, sizeof(TestEventT)); 74 | TestEventT event = { 22, 44 }; 75 | auto rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 76 | CHECK_EQUAL(pdTRUE, rtn); 77 | 78 | TestEventT retrieved = { 0, 0 }; 79 | rtn = xQueueReceive(mQueueUnderTest, &retrieved, portMAX_DELAY); 80 | CHECK_EQUAL(pdTRUE, rtn); 81 | 82 | CHECK_EQUAL(event.valueA, retrieved.valueA); 83 | CHECK_EQUAL(event.valueB, retrieved.valueB); 84 | } 85 | 86 | TEST(QueueTests, send_to_back_will_fail_if_queue_is_full) 87 | { 88 | CreateUnderTest(1, sizeof(TestEventT)); 89 | TestEventT event = { 22, 44 }; 90 | auto rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 91 | CHECK_EQUAL(pdTRUE, rtn); 92 | 93 | rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 94 | CHECK_EQUAL(errQUEUE_FULL, rtn); 95 | } 96 | 97 | TEST(QueueTests, send_to_front_will_fail_if_queue_is_full) 98 | { 99 | CreateUnderTest(1, sizeof(TestEventT)); 100 | TestEventT event = { 22, 44 }; 101 | auto rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 102 | CHECK_EQUAL(pdTRUE, rtn); 103 | 104 | rtn = xQueueSendToFront(mQueueUnderTest, &event, 1000); 105 | CHECK_EQUAL(errQUEUE_FULL, rtn); 106 | } 107 | 108 | TEST(QueueTests, peek_will_copy_event_as_expected) 109 | { 110 | CreateUnderTest(1, sizeof(TestEventT)); 111 | TestEventT event = { 22, 44 }; 112 | auto rtn = xQueueSendToBack(mQueueUnderTest, &event, 1000); 113 | CHECK_EQUAL(pdTRUE, rtn); 114 | 115 | TestEventT peek = { 0, 0 }; 116 | xQueuePeek(mQueueUnderTest, &peek, 1000); 117 | CHECK_EQUAL(pdTRUE, rtn); 118 | CHECK_EQUAL(event.valueA, peek.valueA); 119 | CHECK_EQUAL(event.valueB, peek.valueB); 120 | 121 | //and we can still fully receive the same event 122 | TestEventT retrieved = { 0, 0 }; 123 | rtn = xQueueReceive(mQueueUnderTest, &retrieved, portMAX_DELAY); 124 | CHECK_EQUAL(pdTRUE, rtn); 125 | 126 | CHECK_EQUAL(event.valueA, retrieved.valueA); 127 | CHECK_EQUAL(event.valueB, retrieved.valueB); 128 | } 129 | 130 | TEST(QueueTests, messages_waiting_works_as_expected) 131 | { 132 | const TestEventT event = { 23, 43 }; 133 | 134 | CreateUnderTest(3, sizeof(TestEventT)); 135 | 136 | auto count = uxQueueMessagesWaiting(mQueueUnderTest); 137 | CHECK_EQUAL(0, count); 138 | 139 | xQueueSendToBack(mQueueUnderTest, &event, 1000); 140 | 141 | count = uxQueueMessagesWaiting(mQueueUnderTest); 142 | CHECK_EQUAL(1, count); 143 | 144 | xQueueSendToBack(mQueueUnderTest, &event, 1000); 145 | 146 | count = uxQueueMessagesWaiting(mQueueUnderTest); 147 | CHECK_EQUAL(2, count); 148 | } 149 | 150 | TEST(QueueTests, spaces_available_works_as_expected) 151 | { 152 | const TestEventT event = { 23, 43 }; 153 | 154 | CreateUnderTest(3, sizeof(TestEventT)); 155 | 156 | auto count = uxQueueSpacesAvailable(mQueueUnderTest); 157 | CHECK_EQUAL(3, count); 158 | 159 | xQueueSendToBack(mQueueUnderTest, &event, 1000); 160 | 161 | count = uxQueueSpacesAvailable(mQueueUnderTest); 162 | CHECK_EQUAL(2, count); 163 | 164 | xQueueSendToBack(mQueueUnderTest, &event, 1000); 165 | 166 | count = uxQueueSpacesAvailable(mQueueUnderTest); 167 | CHECK_EQUAL(1, count); 168 | } 169 | 170 | TEST(QueueTests, can_create_a_static_queue) 171 | { 172 | static StaticQueue_t staticQueue; 173 | uint8_t queueStorageArea[ 2 * sizeof(TestEventT) ]; 174 | mQueueUnderTest = xQueueCreateStatic(2, sizeof(TestEventT), queueStorageArea, &staticQueue); 175 | CHECK_TRUE(mQueueUnderTest != nullptr); 176 | } 177 | 178 | TEST(QueueTests, can_use_queue_overwrite) 179 | { 180 | const TestEventT firstEvent = { 23, 43 }; 181 | const TestEventT overwriteEvent = { 123, 143 }; 182 | 183 | CreateUnderTest(1, sizeof(TestEventT)); 184 | 185 | auto rtn = xQueueSendToBack(mQueueUnderTest, &firstEvent, portMAX_DELAY); 186 | CHECK_EQUAL(pdTRUE, rtn); 187 | 188 | rtn = xQueueOverwrite(mQueueUnderTest, &overwriteEvent); 189 | CHECK_EQUAL(pdPASS, rtn); 190 | 191 | TestEventT retrieved= { 0, 0 }; 192 | rtn = xQueueReceive(mQueueUnderTest, &retrieved, portMAX_DELAY); 193 | CHECK_EQUAL(pdTRUE, rtn); 194 | CHECK_EQUAL(overwriteEvent.valueA, retrieved.valueA); 195 | CHECK_EQUAL(overwriteEvent.valueB, retrieved.valueB); 196 | } 197 | 198 | TEST(QueueTests, can_add_queue_to_registry) 199 | { 200 | static const char * TEST_QUEUE_NAME = "This is a test"; 201 | CreateUnderTest(1, sizeof(TestEventT)); 202 | vQueueAddToRegistry(mQueueUnderTest, TEST_QUEUE_NAME); 203 | 204 | auto result = pcQueueGetName(mQueueUnderTest); 205 | CHECK_EQUAL(TEST_QUEUE_NAME, result); 206 | } 207 | 208 | TEST(QueueTests, can_unregister_queue) 209 | { 210 | static const char * TEST_QUEUE_NAME = "This is another test"; 211 | CreateUnderTest(1, sizeof(TestEventT)); 212 | vQueueAddToRegistry(mQueueUnderTest, TEST_QUEUE_NAME); 213 | auto result = pcQueueGetName(mQueueUnderTest); 214 | CHECK_EQUAL(TEST_QUEUE_NAME, result); 215 | 216 | vQueueUnregisterQueue(mQueueUnderTest); 217 | result = pcQueueGetName(mQueueUnderTest); 218 | CHECK_EQUAL(nullptr, result); 219 | } -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/src/cpputest_for_freertos_timers.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Provides an implementation of fake but functional 2 | /// FreeRTOS software timers. 3 | /// 4 | /// @ingroup 5 | /// @cond 6 | ///*************************************************************************** 7 | /// 8 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 9 | /// 10 | /// This program is open source software: you can redistribute it and/or 11 | /// modify it under the terms of the GNU General Public License as published 12 | /// by the Free Software Foundation, either version 3 of the License, or 13 | /// (at your option) any later version. 14 | /// 15 | /// Alternatively, upon written permission from Matthew Eshleman, this program 16 | /// may be distributed and modified under the terms of a Commercial 17 | /// License. For further details, see the Contact Information below. 18 | /// 19 | /// Contact Information: 20 | /// Matthew Eshleman 21 | /// https://covemountainsoftware.com 22 | /// info@covemountainsoftware.com 23 | ///*************************************************************************** 24 | /// @endcond 25 | 26 | #include "FreeRTOS.h" 27 | #include "timers.h" 28 | #include "FakeTimers.hpp" 29 | 30 | namespace cms { 31 | namespace test { 32 | 33 | static FakeTimers* s_fakeTimers = nullptr; 34 | 35 | void TimersInit() 36 | { 37 | configASSERT(s_fakeTimers == nullptr); 38 | 39 | std::chrono::milliseconds sysTick { configTICK_RATE_HZ * 1/1000 }; 40 | s_fakeTimers = new FakeTimers(sysTick); 41 | } 42 | 43 | void TimersDestroy() 44 | { 45 | configASSERT(s_fakeTimers != nullptr); 46 | delete s_fakeTimers; 47 | s_fakeTimers = nullptr; 48 | } 49 | 50 | bool TimersIsActive() 51 | { 52 | return s_fakeTimers != nullptr; 53 | } 54 | 55 | void MoveTimeForward(std::chrono::nanoseconds duration) 56 | { 57 | configASSERT(s_fakeTimers != nullptr); 58 | s_fakeTimers->MoveTimeForward(duration); 59 | } 60 | 61 | std::chrono::nanoseconds GetCurrentInternalTime() 62 | { 63 | if (TimersIsActive()) 64 | { 65 | return s_fakeTimers->GetCurrentInternalTime(); 66 | } 67 | else 68 | { 69 | return std::chrono::nanoseconds (0); 70 | } 71 | } 72 | 73 | std::chrono::nanoseconds TicksToChrono(TickType_t ticks) 74 | { 75 | std::chrono::milliseconds rtn(pdTICKS_TO_MS(ticks)); 76 | return rtn; 77 | } 78 | 79 | static const uint8_t s_an_object = 1; 80 | 81 | void* HandleToPointer(FakeTimers::Handle handle) 82 | { 83 | return (void*)((&s_an_object) + handle); 84 | } 85 | 86 | FakeTimers::Handle PointerToHandle(void* ptr) 87 | { 88 | return ((uint8_t*)ptr - &s_an_object); 89 | } 90 | 91 | } //namespace test 92 | } //namespace cms 93 | 94 | using namespace cms::test; 95 | 96 | extern "C" TimerHandle_t xTimerCreate( const char * const pcTimerName, 97 | const TickType_t xTimerPeriodInTicks, 98 | const BaseType_t xAutoReload, 99 | void * const pvTimerID, 100 | TimerCallbackFunction_t pxCallbackFunction ) 101 | { 102 | configASSERT(s_fakeTimers != nullptr); 103 | auto behavior = FakeTimers::Behavior::SingleShot; 104 | if (xAutoReload == pdTRUE) 105 | { 106 | behavior = FakeTimers::Behavior::AutoReload; 107 | } 108 | auto handle = s_fakeTimers->TimerCreate(pcTimerName, 109 | TicksToChrono(xTimerPeriodInTicks), 110 | behavior, pvTimerID, 111 | [=](FakeTimers::Handle handle, FakeTimers::Context){ 112 | pxCallbackFunction( 113 | (TimerHandle_t)cms::test::HandleToPointer(handle)); 114 | }); 115 | 116 | return (TimerHandle_t)HandleToPointer(handle); 117 | } 118 | 119 | extern "C" BaseType_t xTimerGenericCommandFromTask( TimerHandle_t xTimer, 120 | const BaseType_t xCommandID, 121 | const TickType_t xOptionalValue, 122 | BaseType_t * const pxHigherPriorityTaskWoken, 123 | const TickType_t xTicksToWait ) 124 | { 125 | configASSERT(s_fakeTimers != nullptr); 126 | configASSERT(xTimer != nullptr); 127 | 128 | (void)pxHigherPriorityTaskWoken; 129 | (void)xTicksToWait; 130 | 131 | switch (xCommandID) { 132 | case tmrCOMMAND_START: { 133 | bool ok = s_fakeTimers->TimerStart(PointerToHandle(xTimer)); 134 | configASSERT(ok); 135 | break; 136 | } 137 | case tmrCOMMAND_DELETE: { 138 | bool ok = s_fakeTimers->TimerDelete(PointerToHandle(xTimer)); 139 | configASSERT(ok); 140 | break; 141 | } 142 | case tmrCOMMAND_STOP: { 143 | bool ok = s_fakeTimers->TimerStop(PointerToHandle(xTimer)); 144 | configASSERT(ok); 145 | break; 146 | } 147 | case tmrCOMMAND_CHANGE_PERIOD: { 148 | bool ok = s_fakeTimers->TimerChangePeriod( 149 | PointerToHandle(xTimer), 150 | std::chrono::milliseconds (pdTICKS_TO_MS(xOptionalValue))); 151 | configASSERT(ok); 152 | break; 153 | } 154 | case tmrCOMMAND_RESET: { 155 | bool ok = s_fakeTimers->TimerReset(PointerToHandle(xTimer)); 156 | configASSERT(ok); 157 | break; 158 | } 159 | default: 160 | configASSERT(true == false); 161 | break; 162 | } 163 | 164 | return pdPASS; 165 | } 166 | 167 | extern "C" BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) 168 | { 169 | configASSERT(s_fakeTimers != nullptr); 170 | bool active = s_fakeTimers->TimerIsActive(PointerToHandle(xTimer)); 171 | if (active) 172 | { 173 | return pdTRUE; 174 | } 175 | else 176 | { 177 | return pdFALSE; 178 | } 179 | } 180 | 181 | extern "C" void * pvTimerGetTimerID( const TimerHandle_t xTimer ) 182 | { 183 | configASSERT(s_fakeTimers != nullptr); 184 | auto context = s_fakeTimers->TimerGetContext(PointerToHandle(xTimer)); 185 | return context; 186 | } 187 | 188 | extern "C" void vTimerSetTimerID( TimerHandle_t xTimer, void * pvNewID ) 189 | { 190 | configASSERT(s_fakeTimers != nullptr); 191 | s_fakeTimers->TimerSetContext(PointerToHandle(xTimer), pvNewID); 192 | } 193 | 194 | extern "C" const char * pcTimerGetName( TimerHandle_t xTimer ) 195 | { 196 | configASSERT(s_fakeTimers != nullptr); 197 | return s_fakeTimers->TimerGetName(PointerToHandle(xTimer)); 198 | } 199 | 200 | extern "C" BaseType_t xTimerGetReloadMode(TimerHandle_t xTimer) 201 | { 202 | configASSERT(s_fakeTimers != nullptr); 203 | auto behavior = s_fakeTimers->TimerGetBehavior(PointerToHandle(xTimer)); 204 | if (behavior == cms::test::FakeTimers::Behavior::AutoReload) 205 | { 206 | return pdTRUE; 207 | } 208 | else 209 | { 210 | return pdFALSE; 211 | } 212 | } 213 | 214 | extern "C" void vTimerSetReloadMode(TimerHandle_t xTimer, const BaseType_t xAutoReload) 215 | { 216 | configASSERT(s_fakeTimers != nullptr); 217 | 218 | if (xAutoReload == pdTRUE) 219 | { 220 | s_fakeTimers->TimerSetBehavior(PointerToHandle(xTimer), FakeTimers::Behavior::AutoReload); 221 | } 222 | else 223 | { 224 | s_fakeTimers->TimerSetBehavior(PointerToHandle(xTimer), FakeTimers::Behavior::SingleShot); 225 | } 226 | } 227 | 228 | extern "C" TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) 229 | { 230 | configASSERT(s_fakeTimers != nullptr); 231 | auto period = s_fakeTimers->TimerGetPeriod(PointerToHandle(xTimer)); 232 | auto milliseconds = std::chrono::duration_cast(period); 233 | return pdMS_TO_TICKS(milliseconds.count()); 234 | } 235 | 236 | extern "C" TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) 237 | { 238 | configASSERT(s_fakeTimers != nullptr); 239 | auto period = s_fakeTimers->TimerGetExpiryTime(PointerToHandle(xTimer)); 240 | auto milliseconds = std::chrono::duration_cast(period); 241 | return pdMS_TO_TICKS(milliseconds.count()); 242 | } -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_timers_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of support methods to help with unit testing for FreeRTOS timers. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | #include "FreeRTOS.h" 24 | #include "timers.h" 25 | #include "cpputest_for_freertos_timers.hpp" 26 | #include "CppUTest/TestHarness.h" 27 | #include "CppUTestExt/MockSupport.h" 28 | 29 | using namespace std::chrono_literals; 30 | 31 | TEST_GROUP(TimersTests) 32 | { 33 | void setup() final 34 | { 35 | cms::test::TimersInit(); 36 | } 37 | 38 | void teardown() final 39 | { 40 | cms::test::TimersDestroy(); 41 | mock().clear(); 42 | } 43 | }; 44 | 45 | TEST(TimersTests, move_time_forward_does_nothing_if_no_timers) 46 | { 47 | cms::test::MoveTimeForward(10s); 48 | mock().checkExpectations(); 49 | } 50 | 51 | TEST(TimersTests, move_time_forward_does_nothing_if_create_timer_but_not_started) 52 | { 53 | auto timer = xTimerCreate("test", 2, pdTRUE, nullptr, [](TimerHandle_t){ 54 | mock("TEST").actualCall("callback"); 55 | }); 56 | (void)timer; 57 | cms::test::MoveTimeForward(10s); 58 | mock().checkExpectations(); 59 | } 60 | 61 | TEST(TimersTests, move_time_forward_fires_singlshot_timer_as_expected) 62 | { 63 | auto timer = xTimerCreate("test", 2, pdFALSE, nullptr, [](TimerHandle_t){ 64 | mock("TEST").actualCall("callback"); 65 | }); 66 | auto rtn = xTimerStart(timer, 1000); 67 | CHECK_EQUAL(rtn, pdPASS); 68 | mock("TEST").expectOneCall("callback"); 69 | cms::test::MoveTimeForward(200s); 70 | mock().checkExpectations(); 71 | } 72 | 73 | TEST(TimersTests, move_time_forward_fires_autoreload_timer_as_expected) 74 | { 75 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 76 | mock("TEST").actualCall("callback"); 77 | }); 78 | auto rtn = xTimerStart(timer, 1000); 79 | CHECK_EQUAL(rtn, pdPASS); 80 | mock("TEST").expectNCalls(10, "callback"); 81 | cms::test::MoveTimeForward(10s); 82 | mock().checkExpectations(); 83 | } 84 | 85 | TEST(TimersTests, move_time_forward_fires_nothing_for_delete_timer) 86 | { 87 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 88 | mock("TEST").actualCall("callback"); 89 | }); 90 | auto rtn = xTimerStart(timer, 1000); 91 | CHECK_EQUAL(rtn, pdPASS); 92 | xTimerDelete(timer, 1000); 93 | mock("TEST").expectNoCall("callback"); 94 | cms::test::MoveTimeForward(10s); 95 | mock().checkExpectations(); 96 | } 97 | 98 | TEST(TimersTests, move_time_forward_fires_nothing_for_start_then_stop_timer) 99 | { 100 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 101 | mock("TEST").actualCall("callback"); 102 | }); 103 | auto rtn = xTimerStart(timer, 1000); 104 | CHECK_EQUAL(rtn, pdPASS); 105 | xTimerStop(timer, 1000); 106 | mock("TEST").expectNoCall("callback"); 107 | cms::test::MoveTimeForward(10s); 108 | mock().checkExpectations(); 109 | } 110 | 111 | TEST(TimersTests, move_time_forward_fires_once_then_nothing_for_start_then_stop_timer) 112 | { 113 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 114 | mock("TEST").actualCall("callback"); 115 | }); 116 | auto rtn = xTimerStart(timer, 1000); 117 | CHECK_EQUAL(rtn, pdPASS); 118 | 119 | mock("TEST").expectOneCall("callback"); 120 | cms::test::MoveTimeForward(1s); 121 | mock().checkExpectations(); 122 | 123 | xTimerStop(timer, 1000); 124 | mock("TEST").expectNoCall("callback"); 125 | cms::test::MoveTimeForward(10s); 126 | mock().checkExpectations(); 127 | } 128 | 129 | TEST(TimersTests, move_time_forward_fires_timer_as_expected_after_period_change) 130 | { 131 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 132 | mock("TEST").actualCall("callback"); 133 | }); 134 | auto rtn = xTimerStart(timer, 1000); 135 | CHECK_EQUAL(rtn, pdPASS); 136 | rtn = xTimerChangePeriod(timer, pdMS_TO_TICKS(2000), 1000); 137 | CHECK_EQUAL(rtn, pdPASS); 138 | mock("TEST").expectNCalls(5, "callback"); 139 | cms::test::MoveTimeForward(10s); 140 | mock().checkExpectations(); 141 | } 142 | 143 | TEST(TimersTests, is_timer_active_works_as_expected) 144 | { 145 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 146 | mock("TEST").actualCall("callback"); 147 | }); 148 | 149 | auto isActive = xTimerIsTimerActive(timer); 150 | CHECK_EQUAL(pdFALSE, isActive); 151 | 152 | //start the timer 153 | xTimerStart(timer, 1000); 154 | isActive = xTimerIsTimerActive(timer); 155 | CHECK_EQUAL(pdTRUE, isActive); 156 | 157 | //stop the timer 158 | xTimerStop(timer, 1000); 159 | isActive = xTimerIsTimerActive(timer); 160 | CHECK_EQUAL(pdFALSE, isActive); 161 | } 162 | 163 | TEST(TimersTests, timer_id_works_as_expected) 164 | { 165 | static uint8_t object = 1; 166 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, &object, [](TimerHandle_t){ 167 | mock("TEST").actualCall("callback"); 168 | }); 169 | 170 | auto getId = pvTimerGetTimerID(timer); 171 | CHECK_EQUAL(&object, getId); 172 | 173 | static uint8_t anotherObject = 2; 174 | vTimerSetTimerID(timer, &anotherObject); 175 | 176 | getId = pvTimerGetTimerID(timer); 177 | CHECK_EQUAL(&anotherObject, getId); 178 | } 179 | 180 | 181 | TEST(TimersTests, move_time_forward_fires_singleshot_timer_after_reset_as_expected) 182 | { 183 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdFALSE, nullptr, [](TimerHandle_t){ 184 | mock("TEST").actualCall("callback"); 185 | }); 186 | auto rtn = xTimerStart(timer, 1000); 187 | CHECK_EQUAL(rtn, pdPASS); 188 | 189 | mock("TEST").expectNoCall("callback"); 190 | cms::test::MoveTimeForward(999ms); 191 | mock().checkExpectations(); 192 | 193 | xTimerReset(timer, 1000); 194 | 195 | mock("TEST").expectOneCall("callback"); 196 | cms::test::MoveTimeForward(1s); 197 | mock().checkExpectations(); 198 | } 199 | 200 | TEST(TimersTests, timer_get_name_works_as_expected) 201 | { 202 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 203 | mock("TEST").actualCall("callback"); 204 | }); 205 | 206 | auto name = pcTimerGetName(timer); 207 | STRCMP_EQUAL("test", name); 208 | } 209 | 210 | TEST(TimersTests, timer_set_reload_mode_works) 211 | { 212 | auto timer = xTimerCreate("test", pdMS_TO_TICKS(1000), pdTRUE, nullptr, [](TimerHandle_t){ 213 | mock("TEST").actualCall("callback"); 214 | }); 215 | auto reload = xTimerGetReloadMode(timer); 216 | CHECK_EQUAL(pdTRUE, reload); 217 | 218 | vTimerSetReloadMode(timer, pdFALSE); 219 | 220 | reload = xTimerGetReloadMode(timer); 221 | CHECK_EQUAL(pdFALSE, reload); 222 | 223 | xTimerStart(timer, 1000); 224 | 225 | mock("TEST").expectOneCall("callback"); 226 | cms::test::MoveTimeForward(100s); 227 | mock().checkExpectations(); 228 | } 229 | 230 | TEST(TimersTests, timer_get_period_works_as_expected) 231 | { 232 | auto timer = xTimerCreate("test", 1111, pdTRUE, nullptr, [](TimerHandle_t){ 233 | mock("TEST").actualCall("callback"); 234 | }); 235 | 236 | auto period = xTimerGetPeriod(timer); 237 | CHECK_EQUAL(1111, period); 238 | } 239 | 240 | TEST(TimersTests, timer_get_expiry_works_as_expected_with_move_time_forward_and_get_tick_count) 241 | { 242 | auto timer = xTimerCreate("test", 1000, pdTRUE, nullptr, [](TimerHandle_t){ 243 | mock("TEST").actualCall("callback"); 244 | }); 245 | 246 | auto rtn = xTimerStart(timer, 1000); 247 | CHECK_EQUAL(rtn, pdPASS); 248 | 249 | auto next = xTimerGetExpiryTime(timer); 250 | CHECK_EQUAL(1000, next); 251 | 252 | cms::test::MoveTimeForward(600ms); 253 | 254 | next = xTimerGetExpiryTime(timer); 255 | CHECK_EQUAL(1000, next); 256 | 257 | auto remaining = next - xTaskGetTickCount(); 258 | CHECK_EQUAL(400, remaining); 259 | } 260 | 261 | -------------------------------------------------------------------------------- /cpputest-for-freertos-lib/tests/cpputest_for_freertos_queue_set_tests.cpp: -------------------------------------------------------------------------------- 1 | /// @brief Tests of CppUTest for FreeRTOS queue sets. 2 | /// @ingroup 3 | /// @cond 4 | ///*************************************************************************** 5 | /// 6 | /// Copyright (C) 2024 Matthew Eshleman. All rights reserved. 7 | /// 8 | /// This program is open source software: you can redistribute it and/or 9 | /// modify it under the terms of the GNU General Public License as published 10 | /// by the Free Software Foundation, either version 3 of the License, or 11 | /// (at your option) any later version. 12 | /// 13 | /// Alternatively, upon written permission from Matthew Eshleman, this program 14 | /// may be distributed and modified under the terms of a Commercial 15 | /// License. For further details, see the Contact Information below. 16 | /// 17 | /// Contact Information: 18 | /// Matthew Eshleman 19 | /// https://covemountainsoftware.com 20 | /// info@covemountainsoftware.com 21 | ///*************************************************************************** 22 | /// @endcond 23 | 24 | #include "FreeRTOS.h" 25 | #include "queue.h" 26 | #include "semphr.h" 27 | #include "cpputest_for_freertos_memory.hpp" 28 | 29 | //must be last 30 | #include "CppUTest/TestHarness.h" 31 | 32 | TEST_GROUP(QueueSetTests) 33 | { 34 | cms::test::unique_queue mUnderTest = nullptr; 35 | 36 | void setup() final 37 | { 38 | } 39 | 40 | void teardown() final 41 | { 42 | } 43 | 44 | void CreateSetUnderTest(UBaseType_t length) 45 | { 46 | auto set = xQueueCreateSet(length); 47 | mUnderTest = cms::test::unique_queue(set); 48 | CHECK_TRUE(mUnderTest != nullptr); 49 | } 50 | 51 | static cms::test::unique_queue CreateQueue(UBaseType_t len, UBaseType_t itemSize) 52 | { 53 | auto rtn = xQueueCreate(len, itemSize); 54 | CHECK_TRUE(rtn != nullptr); 55 | cms::test::unique_queue queue(rtn); 56 | return queue; 57 | } 58 | 59 | cms::test::unique_queue CreateQueueAndAddToUnderTestSet(UBaseType_t len, UBaseType_t itemSize) const 60 | { 61 | auto created = xQueueCreate(len, itemSize); 62 | CHECK_TRUE(created != nullptr); 63 | 64 | auto rtn = xQueueAddToSet(created, mUnderTest.get()); 65 | CHECK_EQUAL(pdPASS, rtn); 66 | 67 | cms::test::unique_queue queue(created); 68 | return queue; 69 | } 70 | }; 71 | 72 | TEST(QueueSetTests, can_create_a_queue_set) 73 | { 74 | CreateSetUnderTest(2); 75 | } 76 | 77 | TEST(QueueSetTests, can_add_a_queue_to_a_set_and_remove_from_set) 78 | { 79 | CreateSetUnderTest(2); 80 | 81 | auto queue = CreateQueue(2, 2); 82 | auto rtn = xQueueAddToSet(queue.get(), mUnderTest.get()); 83 | CHECK_EQUAL(pdPASS, rtn); 84 | 85 | rtn = xQueueRemoveFromSet(queue.get(), mUnderTest.get()); 86 | CHECK_EQUAL(pdPASS, rtn); 87 | } 88 | 89 | TEST(QueueSetTests, adding_a_queue_with_events_to_a_set_fails) 90 | { 91 | const int testValue = 234; 92 | CreateSetUnderTest(2); 93 | auto queue = CreateQueue(2, sizeof(int)); 94 | auto rtn = xQueueSendToBack(queue.get(), &testValue, portMAX_DELAY); 95 | CHECK_EQUAL(pdTRUE, rtn); 96 | 97 | rtn = xQueueAddToSet(queue.get(), mUnderTest.get()); 98 | CHECK_EQUAL(pdFAIL, rtn); 99 | } 100 | 101 | TEST(QueueSetTests, adding_a_queue_already_added_to_a_set_fails) 102 | { 103 | CreateSetUnderTest(2); 104 | auto queue = CreateQueue(2, sizeof(int)); 105 | auto rtn = xQueueAddToSet(queue.get(), mUnderTest.get()); 106 | CHECK_EQUAL(pdPASS, rtn); 107 | 108 | //add it again should fail 109 | rtn = xQueueAddToSet(queue.get(), mUnderTest.get()); 110 | CHECK_EQUAL(pdFAIL, rtn); 111 | } 112 | 113 | TEST(QueueSetTests, can_add_a_queue_to_a_set_and_fails_to_remove_from_another_set) 114 | { 115 | CreateSetUnderTest(2); 116 | cms::test::unique_queue otherSet(xQueueCreateSet(2)); 117 | CHECK_TRUE(otherSet != nullptr); 118 | 119 | auto queue = CreateQueueAndAddToUnderTestSet(2, 2); 120 | 121 | auto rtn = xQueueRemoveFromSet(queue.get(), otherSet.get()); 122 | CHECK_EQUAL(pdFAIL, rtn); 123 | } 124 | 125 | TEST(QueueSetTests, select_from_set_will_not_block_and_return_null_if_no_events) 126 | { 127 | CreateSetUnderTest(2); 128 | auto queue = CreateQueueAndAddToUnderTestSet(2, sizeof(int)); 129 | 130 | auto selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 131 | CHECK_EQUAL(nullptr, selectResult); 132 | } 133 | 134 | TEST(QueueSetTests, select_from_set_will_return_expected_queue_with_event) 135 | { 136 | const uint16_t testValue = 4321; 137 | 138 | CreateSetUnderTest(2); 139 | auto queue = CreateQueueAndAddToUnderTestSet(2, sizeof(testValue)); 140 | 141 | auto rtn = xQueueSendToBack(queue.get(), &testValue, 1000); 142 | CHECK_EQUAL(pdTRUE, rtn); 143 | 144 | auto selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 145 | CHECK_TRUE(selectResult != nullptr); 146 | CHECK_EQUAL(queue.get(), selectResult); 147 | 148 | uint16_t receivedValue = 0; 149 | rtn = xQueueReceive(selectResult, &receivedValue, portMAX_DELAY); 150 | CHECK_EQUAL(pdTRUE, rtn); 151 | CHECK_EQUAL(testValue, receivedValue); 152 | 153 | //confirm select shows null now 154 | selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 155 | CHECK_EQUAL(nullptr, selectResult); 156 | } 157 | 158 | TEST(QueueSetTests, select_from_set_with_multiple_queues_will_return_expected_queues_in_expected_order) 159 | { 160 | const uint16_t testValueQueue1 = 4321; //queue1: 16bit 161 | const int32_t testValueQueue2 = 9876; //queue2: 32bit 162 | 163 | CreateSetUnderTest(4); 164 | auto queue1 = CreateQueueAndAddToUnderTestSet(2, sizeof(testValueQueue1)); 165 | auto queue2 = CreateQueueAndAddToUnderTestSet(2, sizeof(testValueQueue2)); 166 | 167 | //send via queue2, then queue1 168 | auto rtn = xQueueSendToBack(queue2.get(), &testValueQueue2, portMAX_DELAY); 169 | CHECK_EQUAL(pdTRUE, rtn); 170 | rtn = xQueueSendToBack(queue1.get(), &testValueQueue1, portMAX_DELAY); 171 | CHECK_EQUAL(pdTRUE, rtn); 172 | 173 | auto selectResultFirst = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 174 | CHECK_TRUE(selectResultFirst != nullptr); 175 | CHECK_EQUAL(queue2.get(), selectResultFirst); 176 | 177 | auto selectResultSecond = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 178 | CHECK_TRUE(selectResultSecond != nullptr); 179 | CHECK_EQUAL(queue1.get(), selectResultSecond); 180 | 181 | //go ahead and confirm queue2 is as expected 182 | int32_t receivedIntValue = 0; 183 | rtn = xQueueReceive(selectResultFirst, &receivedIntValue, portMAX_DELAY); 184 | CHECK_EQUAL(pdTRUE, rtn); 185 | CHECK_EQUAL(testValueQueue2, receivedIntValue); 186 | 187 | //go ahead and confirm queue1 is as expected 188 | uint16_t receivedShortValue = 0; 189 | rtn = xQueueReceive(selectResultSecond, &receivedShortValue, portMAX_DELAY); 190 | CHECK_EQUAL(pdTRUE, rtn); 191 | CHECK_EQUAL(testValueQueue1, receivedShortValue); 192 | 193 | //confirm select shows null now 194 | auto selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 195 | CHECK_EQUAL(nullptr, selectResult); 196 | } 197 | 198 | TEST(QueueSetTests, select_from_set_will_return_expected_semaphore) 199 | { 200 | CreateSetUnderTest(2); 201 | cms::test::unique_sema sema (xSemaphoreCreateBinary()); 202 | CHECK_TRUE(sema != nullptr); 203 | 204 | auto rtn = xQueueAddToSet(sema.get(), mUnderTest.get()); 205 | CHECK_EQUAL(pdPASS, rtn); 206 | 207 | auto selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 208 | CHECK_EQUAL(nullptr, selectResult); 209 | 210 | xSemaphoreGive(sema.get()); 211 | 212 | selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 213 | CHECK_EQUAL(sema.get(), selectResult); 214 | 215 | auto count = uxSemaphoreGetCount(selectResult); 216 | CHECK_EQUAL(1, count); 217 | } 218 | 219 | TEST(QueueSetTests, select_from_set_with_multiple_semaphores_will_return_expected_semaphore_in_expected_order) 220 | { 221 | CreateSetUnderTest(2); 222 | cms::test::unique_sema sema1 (xSemaphoreCreateBinary()); 223 | CHECK_TRUE(sema1 != nullptr); 224 | cms::test::unique_sema sema2 (xSemaphoreCreateBinary()); 225 | CHECK_TRUE(sema2 != nullptr); 226 | auto rtn = xQueueAddToSet(sema1.get(), mUnderTest.get()); 227 | CHECK_EQUAL(pdPASS, rtn); 228 | rtn = xQueueAddToSet(sema2.get(), mUnderTest.get()); 229 | CHECK_EQUAL(pdPASS, rtn); 230 | 231 | auto selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 232 | CHECK_EQUAL(nullptr, selectResult); 233 | 234 | xSemaphoreGive(sema2.get()); 235 | xSemaphoreGive(sema1.get()); 236 | 237 | selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 238 | CHECK_EQUAL(sema2.get(), selectResult); 239 | auto count = uxSemaphoreGetCount(selectResult); 240 | CHECK_EQUAL(1, count); 241 | 242 | selectResult = xQueueSelectFromSet(mUnderTest.get(), portMAX_DELAY); 243 | CHECK_EQUAL(sema1.get(), selectResult); 244 | count = uxSemaphoreGetCount(selectResult); 245 | CHECK_EQUAL(1, count); 246 | } -------------------------------------------------------------------------------- /example/services/hwLockCtrlService/test/hwLockCtrlServiceTests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2019-2020> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include "hwLockCtrlService.h" 25 | #include "cpputest_for_freertos_lib.hpp" 26 | #include "CppUTest/TestHarness.h" 27 | #include "CppUTestExt/MockSupport.h" 28 | #include "hwLockCtrl.h" 29 | 30 | static constexpr const char* HW_LOCK_CTRL_MOCK = "HwLockCtrl"; 31 | static constexpr const char* CB_MOCK = "TestCb"; 32 | 33 | using namespace std::chrono_literals; 34 | 35 | static void TestLockStateCallback(HLCS_LockStateT state) 36 | { 37 | mock(CB_MOCK).actualCall("LockStateCallback").withIntParameter("state", static_cast(state)); 38 | } 39 | 40 | static void TestSelfTestResultCallback(HLCS_SelfTestResultT result) 41 | { 42 | mock(CB_MOCK).actualCall("SelfTestResultCallback").withIntParameter("result", static_cast(result)); 43 | } 44 | 45 | /** 46 | * @brief This test demonstrates the following key points: 47 | * 1) Does NOT test the thread associated with the active object, 48 | * rather, tests the behavior of the active object via a back door 49 | * to the active object's event queue. 50 | * 2) Tests the internal state machine of the active object without 51 | * internal knowledge of the state machine. Rather, only 52 | * by observing the behavior and associated output/results. 53 | * 3) Create helper methods to drive the internal "state" to known 54 | * starting points. 55 | * 4) Continue to follow software engineering best practices, such 56 | * as adhering to the DRY principle. 57 | */ 58 | TEST_GROUP(HwLockCtrlServiceTests) 59 | { 60 | void setup() final 61 | { 62 | cms::test::LibInitAll(); 63 | HLCS_Init(); 64 | HLCS_RegisterChangeStateCallback(TestLockStateCallback); 65 | HLCS_RegisterSelfTestResultCallback(TestSelfTestResultCallback); 66 | } 67 | 68 | void teardown() final 69 | { 70 | HLCS_Destroy(); //ensure we are stopped/clean/destroyed. 71 | mock().clear(); 72 | cms::test::LibTeardownAll(); 73 | } 74 | 75 | static void GiveProcessingTime() 76 | { 77 | //use our unit testing backdoor to service 78 | //the active object's internal queue. This avoids threading issues 79 | //with unit tests, creating 100% predictable unit tests. 80 | while (HLCS_ProcessOneEvent(EXECUTION_OPTION_UNIT_TEST)) {} 81 | } 82 | 83 | static void StartServiceToLocked() 84 | { 85 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Init"); 86 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Lock"); 87 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_LOCKED)); 88 | HLCS_Start(EXECUTION_OPTION_UNIT_TEST); 89 | GiveProcessingTime(); 90 | mock().checkExpectations(); 91 | CHECK_TRUE(HLCS_LOCK_STATE_LOCKED == HLCS_GetState()); 92 | } 93 | 94 | static void TestUnlock() 95 | { 96 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Unlock"); 97 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_UNLOCKED)); 98 | HLCS_RequestUnlockedAsync(); 99 | GiveProcessingTime(); 100 | mock().checkExpectations(); 101 | CHECK_TRUE(HLCS_LOCK_STATE_UNLOCKED == HLCS_GetState()); 102 | } 103 | 104 | static void StartServiceToUnlocked() 105 | { 106 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Init"); 107 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Lock"); 108 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_LOCKED)); 109 | HLCS_Start(EXECUTION_OPTION_UNIT_TEST); 110 | GiveProcessingTime(); 111 | mock().checkExpectations(); 112 | TestUnlock(); 113 | } 114 | }; 115 | 116 | TEST(HwLockCtrlServiceTests, given_startup_when_created_then_does_not_crash) 117 | { 118 | //setup() is called by cpputest, which creates our unit under test. 119 | } 120 | 121 | TEST(HwLockCtrlServiceTests, given_startup_when_started_then_service_ensures_the_lock_is_locked) 122 | { 123 | //when originally developed, this test contained all the code 124 | //in the below helper method. Since this "setup" was needed by 125 | //other tests, it was extracted into a helper method. 126 | //this pattern of developing tests, discovering the need for common 127 | //setup helper methods, takes place throughout coding of these unit tests. 128 | StartServiceToLocked(); 129 | } 130 | 131 | TEST(HwLockCtrlServiceTests, given_locked_when_another_lock_request_then_service_is_silent) 132 | { 133 | StartServiceToLocked(); 134 | HLCS_RequestLockedAsync(); 135 | GiveProcessingTime(); 136 | mock().checkExpectations(); 137 | } 138 | 139 | TEST(HwLockCtrlServiceTests, given_locked_when_unlock_request_then_service_unlocks_the_driver_and_emits_status_callback) 140 | { 141 | StartServiceToLocked(); 142 | TestUnlock(); 143 | } 144 | 145 | TEST(HwLockCtrlServiceTests, given_unlocked_when_another_unlock_request_then_service_is_silent) 146 | { 147 | StartServiceToUnlocked(); 148 | HLCS_RequestUnlockedAsync(); 149 | GiveProcessingTime(); 150 | mock().checkExpectations(); 151 | } 152 | 153 | TEST(HwLockCtrlServiceTests, given_locked_when_selftest_request_then_service_performs_selftest_emits_results_and_returns_to_locked) 154 | { 155 | StartServiceToLocked(); 156 | 157 | auto passed = HW_LOCK_CTRL_SELF_TEST_PASSED; 158 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("SelfTest").withOutputParameterReturning("outResult", &passed, sizeof(passed)); 159 | mock(CB_MOCK).expectOneCall("SelfTestResultCallback").withIntParameter("result", static_cast(HLCS_SELF_TEST_RESULT_PASS)); 160 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Lock"); 161 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_LOCKED)); 162 | HLCS_RequestSelfTestAsync(); 163 | GiveProcessingTime(); 164 | mock().checkExpectations(); 165 | } 166 | 167 | TEST(HwLockCtrlServiceTests, given_unlocked_when_selftest_request_then_service_performs_selftest_emits_results_and_returns_to_unlocked) 168 | { 169 | StartServiceToUnlocked(); 170 | 171 | auto passed = HW_LOCK_CTRL_SELF_TEST_PASSED; 172 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("SelfTest").withOutputParameterReturning("outResult", &passed, sizeof(passed)); 173 | mock(CB_MOCK).expectOneCall("SelfTestResultCallback").withIntParameter("result", static_cast(HLCS_SELF_TEST_RESULT_PASS)); 174 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Unlock"); 175 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_UNLOCKED)); 176 | HLCS_RequestSelfTestAsync(); 177 | GiveProcessingTime(); 178 | mock().checkExpectations(); 179 | } 180 | 181 | TEST(HwLockCtrlServiceTests,given_locked_when_selftest_request_which_fails_then_service_still_returns_to_locked) 182 | { 183 | StartServiceToLocked(); 184 | 185 | auto passed = HW_LOCK_CTRL_SELF_TEST_FAILED_POWER; 186 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("SelfTest").withOutputParameterReturning("outResult", &passed, sizeof(passed)); 187 | mock(CB_MOCK).expectOneCall("SelfTestResultCallback").withIntParameter("result", static_cast(HLCS_SELF_TEST_RESULT_FAIL)); 188 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Lock"); 189 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_LOCKED)); 190 | HLCS_RequestSelfTestAsync(); 191 | GiveProcessingTime(); 192 | mock().checkExpectations(); 193 | } 194 | 195 | TEST(HwLockCtrlServiceTests, given_unlocked_when_selftest_request_which_fails_then_service_still_returns_to_unlocked) 196 | { 197 | StartServiceToUnlocked(); 198 | 199 | auto passed = HW_LOCK_CTRL_SELF_TEST_FAILED_MOTOR; 200 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("SelfTest").withOutputParameterReturning("outResult", &passed, sizeof(passed)); 201 | mock(CB_MOCK).expectOneCall("SelfTestResultCallback").withIntParameter("result", static_cast(HLCS_SELF_TEST_RESULT_FAIL)); 202 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("Unlock"); 203 | mock(CB_MOCK).expectOneCall("LockStateCallback").withIntParameter("state", static_cast(HLCS_LOCK_STATE_UNLOCKED)); 204 | HLCS_RequestSelfTestAsync(); 205 | GiveProcessingTime(); 206 | mock().checkExpectations(); 207 | } 208 | 209 | TEST(HwLockCtrlServiceTests, given_unlocked_when_5_secs_passes_then_checks_current) { 210 | StartServiceToUnlocked(); 211 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("ReadCurrent").andReturnValue(100); 212 | cms::test::MoveTimeForward(5s); 213 | GiveProcessingTime(); 214 | mock().checkExpectations(); 215 | } 216 | 217 | TEST(HwLockCtrlServiceTests, if_current_exceeds_2amps_will_assert) { 218 | StartServiceToUnlocked(); 219 | mock(HW_LOCK_CTRL_MOCK).expectOneCall("ReadCurrent").andReturnValue(2001); 220 | cms::test::AssertOutputDisable(); 221 | cms::test::MockExpectAssert(); 222 | cms::test::MoveTimeForward(5s); 223 | GiveProcessingTime(); 224 | mock().checkExpectations(); 225 | } 226 | -------------------------------------------------------------------------------- /example/services/hwLockCtrlService/src/hwLockCtrlService.c: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) <2019-2020> 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include "hwLockCtrlService.h" 27 | #include "hwLockCtrl.h" 28 | #include "FreeRTOS.h" 29 | #include "task.h" 30 | #include "queue.h" 31 | #include "timers.h" 32 | 33 | typedef enum Signal 34 | { 35 | SM_ENTER, 36 | SM_EXIT, 37 | SIG_REQUEST_LOCKED, 38 | SIG_REQUEST_UNLOCKED, 39 | SIG_REQUEST_SELF_TEST, 40 | SIG_REQUEST_CURRENT_CHECK, 41 | SIG_REQUEST_THREAD_EXIT 42 | } SignalT; 43 | 44 | typedef struct HLCS_EventType 45 | { 46 | SignalT signal; 47 | } HLCS_EventTypeT; 48 | 49 | //internal prototypes 50 | typedef void* StateRtn; 51 | typedef StateRtn (*HLCS_StateMachineFunc)(const HLCS_EventTypeT * const event); 52 | static void HLCS_PerformSelfTest(); 53 | static void HLCS_NotifyChangedState(HLCS_LockStateT state); 54 | static void HLCS_PushEvent(SignalT sig); 55 | static void HLCS_PushUrgentEvent(SignalT sig); 56 | static void HLCS_SmProcess(const HLCS_EventTypeT * event); 57 | static void HLCS_SmInitialize(); 58 | static void* HLCS_SmInitialPseudoState(const HLCS_EventTypeT* const event); 59 | static void* HLCS_SmLocked(const HLCS_EventTypeT* const event); 60 | static void* HLCS_SmUnlocked(const HLCS_EventTypeT* const event); 61 | static void* HLCS_SmSelfTest(const HLCS_EventTypeT* const event); 62 | static void HLCS_Task(void*); 63 | static void HLCS_CheckCurrentTimerCallback( TimerHandle_t xTimer ); 64 | static void HLCS_DoCurrentCheck(); 65 | 66 | //constants 67 | static const size_t QueueDepth = 10; 68 | static const HLCS_EventTypeT ExitEvent = { .signal = SM_EXIT}; 69 | static const HLCS_EventTypeT EnterEvent = { .signal = SM_ENTER}; 70 | 71 | //module static variables 72 | static _Atomic HLCS_LockStateT s_lockState = HLCS_LOCK_STATE_UNKNOWN; 73 | static atomic_bool s_exitThread = false; 74 | 75 | static TaskHandle_t s_thread = NULL; 76 | static QueueHandle_t s_eventQueue = NULL; 77 | static HLCS_ChangeStateCallback s_stateChangedCallback = NULL; 78 | static HLCS_SelfTestResultCallback s_selfTestResultCallback = NULL; 79 | static HLCS_StateMachineFunc s_currentState = NULL; 80 | static HLCS_StateMachineFunc s_stateHistory = NULL; 81 | static TimerHandle_t s_timer = NULL; 82 | 83 | //internal macros for state machine readability 84 | #define TransitionTo(x) (x) 85 | #define Handled() (s_currentState) 86 | 87 | void HLCS_Init() 88 | { 89 | //ensure Init is being called appropriately 90 | configASSERT(s_lockState == HLCS_LOCK_STATE_UNKNOWN); 91 | configASSERT(s_thread == NULL); 92 | configASSERT(s_eventQueue == NULL); 93 | configASSERT(s_timer == NULL); 94 | configASSERT(s_stateChangedCallback == NULL); 95 | configASSERT(s_selfTestResultCallback == NULL); 96 | configASSERT(s_currentState == NULL); 97 | configASSERT(s_stateHistory == NULL); 98 | configASSERT(s_exitThread == false); 99 | 100 | s_eventQueue = xQueueCreate(QueueDepth, sizeof(HLCS_EventTypeT)); 101 | configASSERT(s_eventQueue != 0); 102 | 103 | s_timer = xTimerCreate("current", pdMS_TO_TICKS(5000), pdTRUE, 104 | NULL, HLCS_CheckCurrentTimerCallback); 105 | configASSERT(s_timer != 0); 106 | 107 | //thread is created in Start() 108 | } 109 | 110 | void HLCS_Destroy() 111 | { 112 | if (s_eventQueue != NULL) 113 | { 114 | s_exitThread = true; 115 | HLCS_PushUrgentEvent(SIG_REQUEST_THREAD_EXIT); 116 | vTaskDelete(s_thread); 117 | vQueueDelete(s_eventQueue); 118 | } 119 | if (s_timer != NULL) 120 | { 121 | xTimerDelete(s_timer, 1000); 122 | s_timer = NULL; 123 | } 124 | s_lockState = HLCS_LOCK_STATE_UNKNOWN; 125 | s_eventQueue = NULL; 126 | s_stateChangedCallback = NULL; 127 | s_selfTestResultCallback = NULL; 128 | s_currentState = NULL; 129 | s_stateHistory = NULL; 130 | s_exitThread = false; 131 | s_thread = NULL; 132 | } 133 | 134 | void HLCS_Start(ExecutionOptionT option) 135 | { 136 | configASSERT(s_currentState == NULL); 137 | configASSERT(s_thread == NULL); 138 | 139 | if (EXECUTION_OPTION_NORMAL == option) 140 | { 141 | BaseType_t ok = xTaskCreate(HLCS_Task, "HLCS", 2000, NULL, tskIDLE_PRIORITY+1, &s_thread); 142 | configASSERT(ok == pdPASS); 143 | } 144 | else 145 | { 146 | HLCS_SmInitialize(); 147 | } 148 | 149 | BaseType_t rtn = xTimerStart(s_timer, 1000); 150 | configASSERT(rtn == pdPASS); 151 | 152 | } 153 | 154 | HLCS_LockStateT HLCS_GetState() 155 | { 156 | return atomic_load(&s_lockState); 157 | } 158 | 159 | void HLCS_RegisterChangeStateCallback(HLCS_ChangeStateCallback callback) 160 | { 161 | s_stateChangedCallback = callback; 162 | } 163 | 164 | void HLCS_RegisterSelfTestResultCallback(HLCS_SelfTestResultCallback callback) 165 | { 166 | s_selfTestResultCallback = callback; 167 | } 168 | 169 | void HLCS_RequestLockedAsync() 170 | { 171 | HLCS_PushEvent(SIG_REQUEST_LOCKED); 172 | } 173 | 174 | void HLCS_RequestUnlockedAsync() 175 | { 176 | HLCS_PushEvent(SIG_REQUEST_UNLOCKED); 177 | } 178 | 179 | void HLCS_RequestSelfTestAsync() 180 | { 181 | HLCS_PushEvent(SIG_REQUEST_SELF_TEST); 182 | } 183 | 184 | bool HLCS_ProcessOneEvent(ExecutionOptionT option) 185 | { 186 | if ((EXECUTION_OPTION_UNIT_TEST == option) && 187 | (0 == uxQueueMessagesWaiting(s_eventQueue))) 188 | { 189 | return false; 190 | } 191 | 192 | HLCS_EventTypeT event; 193 | bool ok = xQueueReceive(s_eventQueue, &event, portMAX_DELAY); 194 | if (!ok) 195 | { 196 | return false; 197 | } 198 | 199 | if (event.signal == SIG_REQUEST_THREAD_EXIT) 200 | { 201 | s_exitThread = true; 202 | return false; 203 | } 204 | 205 | HLCS_SmProcess(&event); 206 | return true; 207 | } 208 | 209 | void HLCS_PushEvent(SignalT sig) 210 | { 211 | HLCS_EventTypeT event = 212 | { 213 | .signal = sig 214 | }; 215 | bool ok = xQueueSendToBack(s_eventQueue, &event, 100); 216 | if (!ok) 217 | { 218 | fprintf(stderr, "HLCS queue send failed for sig %d!\n", sig); 219 | configASSERT(false); 220 | } 221 | } 222 | 223 | void HLCS_PushUrgentEvent(SignalT sig) 224 | { 225 | HLCS_EventTypeT event = 226 | { 227 | .signal = sig 228 | }; 229 | bool ok = xQueueSendToFront(s_eventQueue, &event, 100); 230 | if (!ok) 231 | { 232 | fprintf(stderr, "HLCS queue send failed for sig %d!\n", sig); 233 | configASSERT(false); 234 | } 235 | } 236 | 237 | void HLCS_SmProcess(const HLCS_EventTypeT * event) 238 | { 239 | void* rtn = s_currentState(event); 240 | if (rtn != (void*)s_currentState) 241 | { 242 | s_currentState(&ExitEvent); 243 | s_currentState = rtn; 244 | s_currentState(&EnterEvent); 245 | } 246 | } 247 | 248 | void HLCS_NotifyChangedState(HLCS_LockStateT state) 249 | { 250 | s_lockState = state; 251 | if (s_stateChangedCallback) 252 | { 253 | s_stateChangedCallback(s_lockState); 254 | } 255 | } 256 | 257 | void HLCS_NotifySelfTestResult(HLCS_SelfTestResultT result) 258 | { 259 | if (s_selfTestResultCallback) 260 | { 261 | s_selfTestResultCallback(result); 262 | } 263 | } 264 | 265 | void HLCS_SmInitialize() 266 | { 267 | //get the initial desired state 268 | s_currentState = HLCS_SmInitialPseudoState; 269 | s_currentState = s_currentState(&EnterEvent); 270 | 271 | //now enter the initial desired state 272 | s_currentState(&EnterEvent); 273 | } 274 | 275 | void* HLCS_SmInitialPseudoState(const HLCS_EventTypeT* const event) 276 | { 277 | (void)event; 278 | 279 | HwLockCtrlInit(); 280 | return TransitionTo(HLCS_SmLocked); 281 | } 282 | 283 | void* HLCS_SmLocked(const HLCS_EventTypeT* const event) 284 | { 285 | StateRtn rtn; 286 | switch (event->signal) 287 | { 288 | case SM_ENTER: 289 | HwLockCtrlLock(); 290 | HLCS_NotifyChangedState(HLCS_LOCK_STATE_LOCKED); 291 | rtn = Handled(); 292 | break; 293 | case SM_EXIT: 294 | rtn = Handled(); 295 | s_stateHistory = HLCS_SmLocked; 296 | break; 297 | case SIG_REQUEST_LOCKED: 298 | rtn = Handled(); 299 | break; 300 | case SIG_REQUEST_UNLOCKED: 301 | rtn = TransitionTo(HLCS_SmUnlocked); 302 | break; 303 | case SIG_REQUEST_SELF_TEST: 304 | rtn = TransitionTo(HLCS_SmSelfTest); 305 | break; 306 | case SIG_REQUEST_CURRENT_CHECK: 307 | HLCS_DoCurrentCheck(); 308 | rtn = Handled(); 309 | break; 310 | default: 311 | rtn = Handled(); 312 | break; 313 | } 314 | 315 | return rtn; 316 | } 317 | 318 | void* HLCS_SmUnlocked(const HLCS_EventTypeT* const event) 319 | { 320 | StateRtn rtn; 321 | switch (event->signal) 322 | { 323 | case SM_ENTER: 324 | rtn = Handled(); 325 | HwLockCtrlUnlock(); 326 | HLCS_NotifyChangedState(HLCS_LOCK_STATE_UNLOCKED); 327 | break; 328 | case SM_EXIT: 329 | rtn = Handled(); 330 | s_stateHistory = HLCS_SmUnlocked; 331 | break; 332 | case SIG_REQUEST_LOCKED: 333 | rtn = TransitionTo(HLCS_SmLocked); 334 | break; 335 | case SIG_REQUEST_UNLOCKED: 336 | rtn = Handled(); 337 | break; 338 | case SIG_REQUEST_SELF_TEST: 339 | rtn = TransitionTo(HLCS_SmSelfTest); 340 | break; 341 | case SIG_REQUEST_CURRENT_CHECK: 342 | HLCS_DoCurrentCheck(); 343 | rtn = Handled(); 344 | break; 345 | default: 346 | rtn = Handled(); 347 | break; 348 | } 349 | 350 | return rtn; 351 | } 352 | 353 | void* HLCS_SmSelfTest(const HLCS_EventTypeT* const event) 354 | { 355 | StateRtn rtn; 356 | switch (event->signal) 357 | { 358 | case SM_ENTER: 359 | HLCS_PerformSelfTest(); 360 | rtn = Handled(); 361 | break; 362 | case SIG_REQUEST_LOCKED: 363 | rtn = TransitionTo(HLCS_SmLocked); 364 | break; 365 | case SIG_REQUEST_UNLOCKED: 366 | rtn = TransitionTo(HLCS_SmUnlocked); 367 | break; 368 | 369 | case SIG_REQUEST_SELF_TEST: //purposeful fallthrough 370 | default: 371 | rtn = Handled(); 372 | break; 373 | } 374 | 375 | return rtn; 376 | } 377 | 378 | void HLCS_PerformSelfTest() 379 | { 380 | HwLockCtrlSelfTestResultT result; 381 | bool ok = HwLockCtrlSelfTest(&result); 382 | if (ok && (result == HW_LOCK_CTRL_SELF_TEST_PASSED)) 383 | { 384 | HLCS_NotifySelfTestResult(HLCS_SELF_TEST_RESULT_PASS); 385 | } 386 | else 387 | { 388 | HLCS_NotifySelfTestResult(HLCS_SELF_TEST_RESULT_FAIL); 389 | } 390 | 391 | //remind self to transition back to 392 | //history per this service's requirements 393 | //note the use of "PostUrgent" as per: 394 | // 395 | // https://covemountainsoftware.com/2020/03/08/uml-statechart-handling-errors-when-entering-a-state/ 396 | // 397 | if (s_stateHistory == HLCS_SmUnlocked) 398 | { 399 | HLCS_PushUrgentEvent(SIG_REQUEST_UNLOCKED); 400 | } 401 | else 402 | { 403 | HLCS_PushUrgentEvent(SIG_REQUEST_LOCKED); 404 | } 405 | } 406 | 407 | void HLCS_Task(void* params) 408 | { 409 | (void)params; 410 | HLCS_SmInitialize(); 411 | while (!s_exitThread) 412 | { 413 | HLCS_ProcessOneEvent(EXECUTION_OPTION_NORMAL); 414 | } 415 | } 416 | 417 | void HLCS_CheckCurrentTimerCallback( TimerHandle_t xTimer ) 418 | { 419 | HLCS_PushEvent(SIG_REQUEST_CURRENT_CHECK); 420 | } 421 | 422 | void HLCS_DoCurrentCheck() 423 | { 424 | int32_t milliamps = HwLockCtrlReadCurrent(); 425 | configASSERT(milliamps < 2000); 426 | } -------------------------------------------------------------------------------- /example/apps/demoPcApp/FreeRTOSConfig.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef EXAMPLE_DEMO_PC_APP_LIB_FREE_RTOS_CONFIG_H 3 | #define EXAMPLE_DEMO_PC_APP_LIB_FREE_RTOS_CONFIG_H 4 | 5 | /******************************************************************************/ 6 | /* Hardware description related definitions. **********************************/ 7 | /******************************************************************************/ 8 | 9 | /* In most cases, configCPU_CLOCK_HZ must be set to the frequency of the clock 10 | * that drives the peripheral used to generate the kernels periodic tick interrupt. 11 | * The default value is set to 20MHz and matches the QEMU demo settings. Your 12 | * application will certainly need a different value so set this correctly. 13 | * This is very often, but not always, equal to the main system clock frequency. */ 14 | #define configCPU_CLOCK_HZ ( ( unsigned long ) 20000000 ) 15 | 16 | /* configSYSTICK_CLOCK_HZ is an optional parameter for ARM Cortex-M ports only. 17 | * 18 | * By default ARM Cortex-M ports generate the RTOS tick interrupt from the 19 | * Cortex-M SysTick timer. Most Cortex-M MCUs run the SysTick timer at the same 20 | * frequency as the MCU itself - when that is the case configSYSTICK_CLOCK_HZ is 21 | * not needed and should be left undefined. If the SysTick timer is clocked at a 22 | * different frequency to the MCU core then set configCPU_CLOCK_HZ to the MCU clock 23 | * frequency, as normal, and configSYSTICK_CLOCK_HZ to the SysTick clock 24 | * frequency. Not used if left undefined. 25 | * The default value is undefined (commented out). If you need this value bring it 26 | * back and set it to a suitable value. */ 27 | 28 | /* 29 | #define configSYSTICK_CLOCK_HZ [Platform specific] 30 | */ 31 | 32 | /******************************************************************************/ 33 | /* Scheduling behaviour related definitions. **********************************/ 34 | /******************************************************************************/ 35 | 36 | /* configTICK_RATE_HZ sets frequency of the tick interrupt in Hz, normally 37 | * calculated from the configCPU_CLOCK_HZ value. */ 38 | #define configTICK_RATE_HZ 1000 39 | 40 | /* Set configUSE_PREEMPTION to 1 to use pre-emptive scheduling. Set 41 | * configUSE_PREEMPTION to 0 to use co-operative scheduling. 42 | * See https://www.freertos.org/single-core-amp-smp-rtos-scheduling.html. */ 43 | #define configUSE_PREEMPTION 1 44 | 45 | /* Set configUSE_TIME_SLICING to 1 to have the scheduler switch between Ready 46 | * state tasks of equal priority on every tick interrupt. Set 47 | * configUSE_TIME_SLICING to 0 to prevent the scheduler switching between Ready 48 | * state tasks just because there was a tick interrupt. See 49 | * https://freertos.org/single-core-amp-smp-rtos-scheduling.html. */ 50 | #define configUSE_TIME_SLICING 0 51 | 52 | /* Set configUSE_PORT_OPTIMISED_TASK_SELECTION to 1 to select the next task to 53 | * run using an algorithm optimised to the instruction set of the target hardware - 54 | * normally using a count leading zeros assembly instruction. Set to 0 to select 55 | * the next task to run using a generic C algorithm that works for all FreeRTOS 56 | * ports. Not all FreeRTOS ports have this option. Defaults to 0 if left 57 | * undefined. */ 58 | #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 59 | 60 | /* Set configUSE_TICKLESS_IDLE to 1 to use the low power tickless mode. Set to 61 | * 0 to keep the tick interrupt running at all times. Not all FreeRTOS ports 62 | * support tickless mode. See https://www.freertos.org/low-power-tickless-rtos.html 63 | * Defaults to 0 if left undefined. */ 64 | #define configUSE_TICKLESS_IDLE 0 65 | 66 | /* configMAX_PRIORITIES Sets the number of available task priorities. Tasks can 67 | * be assigned priorities of 0 to (configMAX_PRIORITIES - 1). Zero is the lowest 68 | * priority. */ 69 | #define configMAX_PRIORITIES 5 70 | 71 | /* configMINIMAL_STACK_SIZE defines the size of the stack used by the Idle task 72 | * (in words, not in bytes!). The kernel does not use this constant for any other 73 | * purpose. Demo applications use the constant to make the demos somewhat portable 74 | * across hardware architectures. */ 75 | #define configMINIMAL_STACK_SIZE 128 76 | 77 | /* configMAX_TASK_NAME_LEN sets the maximum length (in characters) of a task's 78 | * human readable name. Includes the NULL terminator. */ 79 | #define configMAX_TASK_NAME_LEN 16 80 | 81 | /* Time is measured in 'ticks' - which is the number of times the tick interrupt 82 | * has executed since the RTOS kernel was started. 83 | * The tick count is held in a variable of type TickType_t. 84 | * 85 | * configTICK_TYPE_WIDTH_IN_BITS controls the type (and therefore bit-width) of TickType_t: 86 | * 87 | * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_16_BITS causes 88 | * TickType_t to be defined (typedef'ed) as an unsigned 16-bit type. 89 | * 90 | * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_32_BITS causes 91 | * TickType_t to be defined (typedef'ed) as an unsigned 32-bit type. 92 | * 93 | * Defining configTICK_TYPE_WIDTH_IN_BITS as TICK_TYPE_WIDTH_64_BITS causes 94 | * TickType_t to be defined (typedef'ed) as an unsigned 64-bit type. */ 95 | #define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_64_BITS 96 | 97 | /* Set configIDLE_SHOULD_YIELD to 1 to have the Idle task yield to an 98 | * application task if there is an Idle priority (priority 0) application task that 99 | * can run. Set to 0 to have the Idle task use all of its timeslice. Default to 1 100 | * if left undefined. */ 101 | #define configIDLE_SHOULD_YIELD 1 102 | 103 | /* Each task has an array of task notifications. 104 | * configTASK_NOTIFICATION_ARRAY_ENTRIES sets the number of indexes in the array. 105 | * See https://www.freertos.org/RTOS-task-notifications.html Defaults to 1 if 106 | * left undefined. */ 107 | #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 108 | 109 | /* configQUEUE_REGISTRY_SIZE sets the maximum number of queues and semaphores 110 | * that can be referenced from the queue registry. Only required when using a 111 | * kernel aware debugger. Defaults to 0 if left undefined. */ 112 | #define configQUEUE_REGISTRY_SIZE 0 113 | 114 | /* Set configENABLE_BACKWARD_COMPATIBILITY to 1 to map function names and 115 | * datatypes from old version of FreeRTOS to their latest equivalent. Defaults to 116 | * 1 if left undefined. */ 117 | #define configENABLE_BACKWARD_COMPATIBILITY 0 118 | 119 | /* Each task has its own array of pointers that can be used as thread local 120 | * storage. configNUM_THREAD_LOCAL_STORAGE_POINTERS set the number of indexes in 121 | * the array. See https://www.freertos.org/thread-local-storage-pointers.html 122 | * Defaults to 0 if left undefined. */ 123 | #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 124 | 125 | /* When configUSE_MINI_LIST_ITEM is set to 0, MiniListItem_t and ListItem_t are 126 | * both the same. When configUSE_MINI_LIST_ITEM is set to 1, MiniListItem_t contains 127 | * 3 fewer fields than ListItem_t which saves some RAM at the cost of violating 128 | * strict aliasing rules which some compilers depend on for optimization. Defaults 129 | * to 1 if left undefined. */ 130 | #define configUSE_MINI_LIST_ITEM 1 131 | 132 | /* Sets the type used by the parameter to xTaskCreate() that specifies the stack 133 | * size of the task being created. The same type is used to return information 134 | * about stack usage in various other API calls. Defaults to size_t if left 135 | * undefined. */ 136 | #define configSTACK_DEPTH_TYPE size_t 137 | 138 | /* configMESSAGE_BUFFER_LENGTH_TYPE sets the type used to store the length of 139 | * each message written to a FreeRTOS message buffer (the length is also written to 140 | * the message buffer. Defaults to size_t if left undefined - but that may waste 141 | * space if messages never go above a length that could be held in a uint8_t. */ 142 | #define configMESSAGE_BUFFER_LENGTH_TYPE size_t 143 | 144 | /* If configHEAP_CLEAR_MEMORY_ON_FREE is set to 1, then blocks of memory allocated 145 | * using pvPortMalloc() will be cleared (i.e. set to zero) when freed using 146 | * vPortFree(). Defaults to 0 if left undefined. */ 147 | #define configHEAP_CLEAR_MEMORY_ON_FREE 1 148 | 149 | /* vTaskList and vTaskGetRunTimeStats APIs take a buffer as a parameter and assume 150 | * that the length of the buffer is configSTATS_BUFFER_MAX_LENGTH. Defaults to 151 | * 0xFFFF if left undefined. 152 | * New applications are recommended to use vTaskListTasks and 153 | * vTaskGetRunTimeStatistics APIs instead and supply the length of the buffer 154 | * explicitly to avoid memory corruption. */ 155 | #define configSTATS_BUFFER_MAX_LENGTH 0xFFFF 156 | 157 | /* Set configUSE_NEWLIB_REENTRANT to 1 to have a newlib reent structure 158 | * allocated for each task. Set to 0 to not support newlib reent structures. 159 | * Default to 0 if left undefined. 160 | * 161 | * Note Newlib support has been included by popular demand, but is not used or 162 | * tested by the FreeRTOS maintainers themselves. FreeRTOS is not responsible for 163 | * resulting newlib operation. User must be familiar with newlib and must provide 164 | * system-wide implementations of the necessary stubs. Note that (at the time of 165 | * writing) the current newlib design implements a system-wide malloc() that must 166 | * be provided with locks. */ 167 | #define configUSE_NEWLIB_REENTRANT 0 168 | 169 | /******************************************************************************/ 170 | /* Software timer related definitions. ****************************************/ 171 | /******************************************************************************/ 172 | 173 | /* Set configUSE_TIMERS to 1 to include software timer functionality in the 174 | * build. Set to 0 to exclude software timer functionality from the build. The 175 | * FreeRTOS/source/timers.c source file must be included in the build if 176 | * configUSE_TIMERS is set to 1. Default to 0 if left undefined. See 177 | * https://www.freertos.org/RTOS-software-timer.html. */ 178 | #define configUSE_TIMERS 1 179 | 180 | /* configTIMER_TASK_PRIORITY sets the priority used by the timer task. Only 181 | * used if configUSE_TIMERS is set to 1. The timer task is a standard FreeRTOS 182 | * task, so its priority is set like any other task. See 183 | * https://www.freertos.org/RTOS-software-timer-service-daemon-task.html Only used 184 | * if configUSE_TIMERS is set to 1. */ 185 | #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) 186 | 187 | /* configTIMER_TASK_STACK_DEPTH sets the size of the stack allocated to the 188 | * timer task (in words, not in bytes!). The timer task is a standard FreeRTOS 189 | * task. See https://www.freertos.org/RTOS-software-timer-service-daemon-task.html 190 | * Only used if configUSE_TIMERS is set to 1. */ 191 | #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE 192 | 193 | /* configTIMER_QUEUE_LENGTH sets the length of the queue (the number of discrete 194 | * items the queue can hold) used to send commands to the timer task. See 195 | * https://www.freertos.org/RTOS-software-timer-service-daemon-task.html Only used 196 | * if configUSE_TIMERS is set to 1. */ 197 | #define configTIMER_QUEUE_LENGTH 10 198 | 199 | /******************************************************************************/ 200 | /* Event Group related definitions. *******************************************/ 201 | /******************************************************************************/ 202 | 203 | /* Set configUSE_EVENT_GROUPS to 1 to include event group functionality in the 204 | * build. Set to 0 to exclude event group functionality from the build. The 205 | * FreeRTOS/source/event_groups.c source file must be included in the build if 206 | * configUSE_EVENT_GROUPS is set to 1. Defaults to 1 if left undefined. */ 207 | 208 | #define configUSE_EVENT_GROUPS 1 209 | 210 | /******************************************************************************/ 211 | /* Stream Buffer related definitions. *****************************************/ 212 | /******************************************************************************/ 213 | 214 | /* Set configUSE_STREAM_BUFFERS to 1 to include stream buffer functionality in 215 | * the build. Set to 0 to exclude event group functionality from the build. The 216 | * FreeRTOS/source/stream_buffer.c source file must be included in the build if 217 | * configUSE_STREAM_BUFFERS is set to 1. Defaults to 1 if left undefined. */ 218 | 219 | #define configUSE_STREAM_BUFFERS 1 220 | 221 | /******************************************************************************/ 222 | /* Memory allocation related definitions. *************************************/ 223 | /******************************************************************************/ 224 | 225 | /* Set configSUPPORT_STATIC_ALLOCATION to 1 to include FreeRTOS API functions 226 | * that create FreeRTOS objects (tasks, queues, etc.) using statically allocated 227 | * memory in the build. Set to 0 to exclude the ability to create statically 228 | * allocated objects from the build. Defaults to 0 if left undefined. See 229 | * https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html. */ 230 | #define configSUPPORT_STATIC_ALLOCATION 1 231 | 232 | /* Set configSUPPORT_DYNAMIC_ALLOCATION to 1 to include FreeRTOS API functions 233 | * that create FreeRTOS objects (tasks, queues, etc.) using dynamically allocated 234 | * memory in the build. Set to 0 to exclude the ability to create dynamically 235 | * allocated objects from the build. Defaults to 1 if left undefined. See 236 | * https://www.freertos.org/Static_Vs_Dynamic_Memory_Allocation.html. */ 237 | #define configSUPPORT_DYNAMIC_ALLOCATION 1 238 | 239 | /* Sets the total size of the FreeRTOS heap, in bytes, when heap_1.c, heap_2.c 240 | * or heap_4.c are included in the build. This value is defaulted to 4096 bytes but 241 | * it must be tailored to each application. Note the heap will appear in the .bss 242 | * section. See https://www.freertos.org/a00111.html. */ 243 | #define configTOTAL_HEAP_SIZE 4096 244 | 245 | /* Set configAPPLICATION_ALLOCATED_HEAP to 1 to have the application allocate 246 | * the array used as the FreeRTOS heap. Set to 0 to have the linker allocate the 247 | * array used as the FreeRTOS heap. Defaults to 0 if left undefined. */ 248 | #define configAPPLICATION_ALLOCATED_HEAP 0 249 | 250 | /* Set configSTACK_ALLOCATION_FROM_SEPARATE_HEAP to 1 to have task stacks 251 | * allocated from somewhere other than the FreeRTOS heap. This is useful if you 252 | * want to ensure stacks are held in fast memory. Set to 0 to have task stacks 253 | * come from the standard FreeRTOS heap. The application writer must provide 254 | * implementations for pvPortMallocStack() and vPortFreeStack() if set to 1. 255 | * Defaults to 0 if left undefined. */ 256 | #define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 0 257 | 258 | /* Set configENABLE_HEAP_PROTECTOR to 1 to enable bounds checking and obfuscation 259 | * to internal heap block pointers in heap_4.c and heap_5.c to help catch pointer 260 | * corruptions. Defaults to 0 if left undefined. */ 261 | #define configENABLE_HEAP_PROTECTOR 0 262 | 263 | /******************************************************************************/ 264 | /* Interrupt nesting behaviour configuration. *********************************/ 265 | /******************************************************************************/ 266 | 267 | /* configKERNEL_INTERRUPT_PRIORITY sets the priority of the tick and context 268 | * switch performing interrupts. Not supported by all FreeRTOS ports. See 269 | * https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific to 270 | * ARM Cortex-M devices. */ 271 | #define configKERNEL_INTERRUPT_PRIORITY 0 272 | 273 | /* configMAX_SYSCALL_INTERRUPT_PRIORITY sets the interrupt priority above which 274 | * FreeRTOS API calls must not be made. Interrupts above this priority are never 275 | * disabled, so never delayed by RTOS activity. The default value is set to the 276 | * highest interrupt priority (0). Not supported by all FreeRTOS ports. 277 | * See https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific to 278 | * ARM Cortex-M devices. */ 279 | #define configMAX_SYSCALL_INTERRUPT_PRIORITY 0 280 | 281 | /* Another name for configMAX_SYSCALL_INTERRUPT_PRIORITY - the name used depends 282 | * on the FreeRTOS port. */ 283 | #define configMAX_API_CALL_INTERRUPT_PRIORITY 0 284 | 285 | /******************************************************************************/ 286 | /* Hook and callback function related definitions. ****************************/ 287 | /******************************************************************************/ 288 | 289 | /* Set the following configUSE_* constants to 1 to include the named hook 290 | * functionality in the build. Set to 0 to exclude the hook functionality from the 291 | * build. The application writer is responsible for providing the hook function 292 | * for any set to 1. See https://www.freertos.org/a00016.html. */ 293 | #define configUSE_IDLE_HOOK 0 294 | #define configUSE_TICK_HOOK 0 295 | #define configUSE_MALLOC_FAILED_HOOK 0 296 | #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 297 | 298 | /* Set configUSE_SB_COMPLETED_CALLBACK to 1 to have send and receive completed 299 | * callbacks for each instance of a stream buffer or message buffer. When the 300 | * option is set to 1, APIs xStreamBufferCreateWithCallback() and 301 | * xStreamBufferCreateStaticWithCallback() (and likewise APIs for message 302 | * buffer) can be used to create a stream buffer or message buffer instance 303 | * with application provided callbacks. Defaults to 0 if left undefined. */ 304 | #define configUSE_SB_COMPLETED_CALLBACK 0 305 | 306 | /* Set configCHECK_FOR_STACK_OVERFLOW to 1 or 2 for FreeRTOS to check for a 307 | * stack overflow at the time of a context switch. Set to 0 to not look for a 308 | * stack overflow. If configCHECK_FOR_STACK_OVERFLOW is 1 then the check only 309 | * looks for the stack pointer being out of bounds when a task's context is saved 310 | * to its stack - this is fast but somewhat ineffective. If 311 | * configCHECK_FOR_STACK_OVERFLOW is 2 then the check looks for a pattern written 312 | * to the end of a task's stack having been overwritten. This is slower, but will 313 | * catch most (but not all) stack overflows. The application writer must provide 314 | * the stack overflow callback when configCHECK_FOR_STACK_OVERFLOW is set to 1. 315 | * See https://www.freertos.org/Stacks-and-stack-overflow-checking.html Defaults 316 | * to 0 if left undefined. */ 317 | #define configCHECK_FOR_STACK_OVERFLOW 2 318 | 319 | /******************************************************************************/ 320 | /* Run time and task stats gathering related definitions. *********************/ 321 | /******************************************************************************/ 322 | 323 | /* Set configGENERATE_RUN_TIME_STATS to 1 to have FreeRTOS collect data on the 324 | * processing time used by each task. Set to 0 to not collect the data. The 325 | * application writer needs to provide a clock source if set to 1. Defaults to 0 326 | * if left undefined. See https://www.freertos.org/rtos-run-time-stats.html. */ 327 | #define configGENERATE_RUN_TIME_STATS 0 328 | 329 | /* Set configUSE_TRACE_FACILITY to include additional task structure members 330 | * are used by trace and visualisation functions and tools. Set to 0 to exclude 331 | * the additional information from the structures. Defaults to 0 if left 332 | * undefined. */ 333 | #define configUSE_TRACE_FACILITY 0 334 | 335 | /* Set to 1 to include the vTaskList() and vTaskGetRunTimeStats() functions in 336 | * the build. Set to 0 to exclude these functions from the build. These two 337 | * functions introduce a dependency on string formatting functions that would 338 | * otherwise not exist - hence they are kept separate. Defaults to 0 if left 339 | * undefined. */ 340 | #define configUSE_STATS_FORMATTING_FUNCTIONS 0 341 | 342 | /******************************************************************************/ 343 | /* Co-routine related definitions. ********************************************/ 344 | /******************************************************************************/ 345 | 346 | /* Set configUSE_CO_ROUTINES to 1 to include co-routine functionality in the 347 | * build, or 0 to omit co-routine functionality from the build. To include 348 | * co-routines, croutine.c must be included in the project. Defaults to 0 if left 349 | * undefined. */ 350 | #define configUSE_CO_ROUTINES 0 351 | 352 | /* configMAX_CO_ROUTINE_PRIORITIES defines the number of priorities available 353 | * to the application co-routines. Any number of co-routines can share the same 354 | * priority. Defaults to 0 if left undefined. */ 355 | #define configMAX_CO_ROUTINE_PRIORITIES 1 356 | 357 | /******************************************************************************/ 358 | /* Debugging assistance. ******************************************************/ 359 | /******************************************************************************/ 360 | 361 | /* configASSERT() has the same semantics as the standard C assert(). It can 362 | * either be defined to take an action when the assertion fails, or not defined 363 | * at all (i.e. comment out or delete the definitions) to completely remove 364 | * assertions. configASSERT() can be defined to anything you want, for example 365 | * you can call a function if an assert fails that passes the filename and line 366 | * number of the failing assert (for example, "vAssertCalled( __FILE__, __LINE__ )" 367 | * or it can simple disable interrupts and sit in a loop to halt all execution 368 | * on the failing line for viewing in a debugger. */ 369 | #define configASSERT( x ) \ 370 | if( ( x ) == 0 ) \ 371 | { \ 372 | taskDISABLE_INTERRUPTS(); \ 373 | for( ; ; ) \ 374 | ; \ 375 | } 376 | 377 | /******************************************************************************/ 378 | /* FreeRTOS MPU specific definitions. *****************************************/ 379 | /******************************************************************************/ 380 | 381 | /* If configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS is set to 1 then 382 | * the application writer can provide functions that execute in privileged mode. 383 | * See: https://www.freertos.org/a00110.html#configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 384 | * Defaults to 0 if left undefined. Only used by the FreeRTOS Cortex-M MPU ports, 385 | * not the standard ARMv7-M Cortex-M port. */ 386 | #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 387 | 388 | /* Set configTOTAL_MPU_REGIONS to the number of MPU regions implemented on your 389 | * target hardware. Normally 8 or 16. Only used by the FreeRTOS Cortex-M MPU 390 | * ports, not the standard ARMv7-M Cortex-M port. Defaults to 8 if left 391 | * undefined. */ 392 | #define configTOTAL_MPU_REGIONS 8 393 | 394 | /* configTEX_S_C_B_FLASH allows application writers to override the default 395 | * values for the for TEX, Shareable (S), Cacheable (C) and Bufferable (B) bits for 396 | * the MPU region covering Flash. Defaults to 0x07UL (which means TEX=000, S=1, 397 | * C=1, B=1) if left undefined. Only used by the FreeRTOS Cortex-M MPU ports, not 398 | * the standard ARMv7-M Cortex-M port. */ 399 | #define configTEX_S_C_B_FLASH 0x07UL 400 | 401 | /* configTEX_S_C_B_SRAM allows application writers to override the default 402 | * values for the for TEX, Shareable (S), Cacheable (C) and Bufferable (B) bits for 403 | * the MPU region covering RAM. Defaults to 0x07UL (which means TEX=000, S=1, C=1, 404 | * B=1) if left undefined. Only used by the FreeRTOS Cortex-M MPU ports, not 405 | * the standard ARMv7-M Cortex-M port. */ 406 | #define configTEX_S_C_B_SRAM 0x07UL 407 | 408 | /* Set configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY to 0 to prevent any privilege 409 | * escalations originating from outside of the kernel code itself. Set to 1 to 410 | * allow application tasks to raise privilege. Defaults to 1 if left undefined. 411 | * Only used by the FreeRTOS Cortex-M MPU ports, not the standard ARMv7-M Cortex-M 412 | * port. */ 413 | #define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1 414 | 415 | /* Set configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS to 1 to allow unprivileged 416 | * tasks enter critical sections (effectively mask interrupts). Set to 0 to 417 | * prevent unprivileged tasks entering critical sections. Defaults to 1 if left 418 | * undefined. Only used by the FreeRTOS Cortex-M MPU ports, not the standard 419 | * ARMv7-M Cortex-M port. */ 420 | #define configALLOW_UNPRIVILEGED_CRITICAL_SECTIONS 0 421 | 422 | /* FreeRTOS Kernel version 10.6.0 introduced a new v2 MPU wrapper, namely 423 | * mpu_wrappers_v2.c. Set configUSE_MPU_WRAPPERS_V1 to 0 to use the new v2 MPU 424 | * wrapper. Set configUSE_MPU_WRAPPERS_V1 to 1 to use the old v1 MPU wrapper 425 | * (mpu_wrappers.c). Defaults to 0 if left undefined. */ 426 | #define configUSE_MPU_WRAPPERS_V1 0 427 | 428 | /* When using the v2 MPU wrapper, set configPROTECTED_KERNEL_OBJECT_POOL_SIZE to 429 | * the total number of kernel objects, which includes tasks, queues, semaphores, 430 | * mutexes, event groups, timers, stream buffers and message buffers, in your 431 | * application. The application will not be able to have more than 432 | * configPROTECTED_KERNEL_OBJECT_POOL_SIZE kernel objects at any point of 433 | * time. */ 434 | #define configPROTECTED_KERNEL_OBJECT_POOL_SIZE 10 435 | 436 | /* When using the v2 MPU wrapper, set configSYSTEM_CALL_STACK_SIZE to the size 437 | * of the system call stack in words. Each task has a statically allocated 438 | * memory buffer of this size which is used as the stack to execute system 439 | * calls. For example, if configSYSTEM_CALL_STACK_SIZE is defined as 128 and 440 | * there are 10 tasks in the application, the total amount of memory used for 441 | * system call stacks is 128 * 10 = 1280 words. */ 442 | #define configSYSTEM_CALL_STACK_SIZE 128 443 | 444 | /* When using the v2 MPU wrapper, set configENABLE_ACCESS_CONTROL_LIST to 1 to 445 | * enable Access Control List (ACL) feature. When ACL is enabled, an 446 | * unprivileged task by default does not have access to any kernel object other 447 | * than itself. The application writer needs to explicitly grant the 448 | * unprivileged task access to the kernel objects it needs using the APIs 449 | * provided for the same. Defaults to 0 if left undefined. */ 450 | #define configENABLE_ACCESS_CONTROL_LIST 1 451 | 452 | /******************************************************************************/ 453 | /* SMP( Symmetric MultiProcessing ) Specific Configuration definitions. *******/ 454 | /******************************************************************************/ 455 | 456 | /* Set configNUMBER_OF_CORES to the number of available processor cores. Defaults 457 | * to 1 if left undefined. */ 458 | 459 | /* 460 | #define configNUMBER_OF_CORES [Num of available cores] 461 | */ 462 | 463 | /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set 464 | * configRUN_MULTIPLE_PRIORITIES to 0 to allow multiple tasks to run 465 | * simultaneously only if they do not have equal priority, thereby maintaining 466 | * the paradigm of a lower priority task never running if a higher priority task 467 | * is able to run. If configRUN_MULTIPLE_PRIORITIES is set to 1, multiple tasks 468 | * with different priorities may run simultaneously - so a higher and lower 469 | * priority task may run on different cores at the same time. */ 470 | #define configRUN_MULTIPLE_PRIORITIES 0 471 | 472 | /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set 473 | * configUSE_CORE_AFFINITY to 1 to enable core affinity feature. When core 474 | * affinity feature is enabled, the vTaskCoreAffinitySet and vTaskCoreAffinityGet 475 | * APIs can be used to set and retrieve which cores a task can run on. If 476 | * configUSE_CORE_AFFINITY is set to 0 then the FreeRTOS scheduler is free to 477 | * run any task on any available core. */ 478 | #define configUSE_CORE_AFFINITY 0 479 | 480 | /* When using SMP with core affinity feature enabled, set 481 | * configTASK_DEFAULT_CORE_AFFINITY to change the default core affinity mask for 482 | * tasks created without an affinity mask specified. Setting the define to 1 would 483 | * make such tasks run on core 0 and setting it to (1 << portGET_CORE_ID()) would 484 | * make such tasks run on the current core. This config value is useful, if 485 | * swapping tasks between cores is not supported (e.g. Tricore) or if legacy code 486 | * should be controlled. Defaults to tskNO_AFFINITY if left undefined. */ 487 | #define configTASK_DEFAULT_CORE_AFFINITY tskNO_AFFINITY 488 | 489 | /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), if 490 | * configUSE_TASK_PREEMPTION_DISABLE is set to 1, individual tasks can be set to 491 | * either pre-emptive or co-operative mode using the vTaskPreemptionDisable and 492 | * vTaskPreemptionEnable APIs. */ 493 | #define configUSE_TASK_PREEMPTION_DISABLE 0 494 | 495 | /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), set 496 | * configUSE_PASSIVE_IDLE_HOOK to 1 to allow the application writer to use 497 | * the passive idle task hook to add background functionality without the overhead 498 | * of a separate task. Defaults to 0 if left undefined. */ 499 | #define configUSE_PASSIVE_IDLE_HOOK 0 500 | 501 | /* When using SMP (i.e. configNUMBER_OF_CORES is greater than one), 502 | * configTIMER_SERVICE_TASK_CORE_AFFINITY allows the application writer to set 503 | * the core affinity of the RTOS Daemon/Timer Service task. Defaults to 504 | * tskNO_AFFINITY if left undefined. */ 505 | #define configTIMER_SERVICE_TASK_CORE_AFFINITY tskNO_AFFINITY 506 | 507 | 508 | /******************************************************************************/ 509 | /* ARMv8-M secure side port related definitions. ******************************/ 510 | /******************************************************************************/ 511 | 512 | /* secureconfigMAX_SECURE_CONTEXTS define the maximum number of tasks that can 513 | * call into the secure side of an ARMv8-M chip. Not used by any other ports. */ 514 | #define secureconfigMAX_SECURE_CONTEXTS 5 515 | 516 | /* Defines the kernel provided implementation of 517 | * vApplicationGetIdleTaskMemory() and vApplicationGetTimerTaskMemory() 518 | * to provide the memory that is used by the Idle task and Timer task respectively. 519 | * The application can provide it's own implementation of 520 | * vApplicationGetIdleTaskMemory() and vApplicationGetTimerTaskMemory() by 521 | * setting configKERNEL_PROVIDED_STATIC_MEMORY to 0 or leaving it undefined. */ 522 | #define configKERNEL_PROVIDED_STATIC_MEMORY 1 523 | 524 | /******************************************************************************/ 525 | /* ARMv8-M port Specific Configuration definitions. ***************************/ 526 | /******************************************************************************/ 527 | 528 | /* Set configENABLE_TRUSTZONE to 1 when running FreeRTOS on the non-secure side 529 | * to enable the TrustZone support in FreeRTOS ARMv8-M ports which allows the 530 | * non-secure FreeRTOS tasks to call the (non-secure callable) functions 531 | * exported from secure side. */ 532 | #define configENABLE_TRUSTZONE 1 533 | 534 | /* If the application writer does not want to use TrustZone, but the hardware does 535 | * not support disabling TrustZone then the entire application (including the FreeRTOS 536 | * scheduler) can run on the secure side without ever branching to the non-secure side. 537 | * To do that, in addition to setting configENABLE_TRUSTZONE to 0, also set 538 | * configRUN_FREERTOS_SECURE_ONLY to 1. */ 539 | #define configRUN_FREERTOS_SECURE_ONLY 1 540 | 541 | /* Set configENABLE_MPU to 1 to enable the Memory Protection Unit (MPU), or 0 542 | * to leave the Memory Protection Unit disabled. */ 543 | #define configENABLE_MPU 1 544 | 545 | /* Set configENABLE_FPU to 1 to enable the Floating Point Unit (FPU), or 0 546 | * to leave the Floating Point Unit disabled. */ 547 | #define configENABLE_FPU 1 548 | 549 | /* Set configENABLE_MVE to 1 to enable the M-Profile Vector Extension (MVE) support, 550 | * or 0 to leave the MVE support disabled. This option is only applicable to Cortex-M55 551 | * and Cortex-M85 ports as M-Profile Vector Extension (MVE) is available only on 552 | * these architectures. configENABLE_MVE must be left undefined, or defined to 0 553 | * for the Cortex-M23,Cortex-M33 and Cortex-M35P ports. */ 554 | #define configENABLE_MVE 1 555 | 556 | /******************************************************************************/ 557 | /* ARMv7-M and ARMv8-M port Specific Configuration definitions. ***************/ 558 | /******************************************************************************/ 559 | 560 | /* Set configCHECK_HANDLER_INSTALLATION to 1 to enable additional asserts to verify 561 | * that the application has correctly installed FreeRTOS interrupt handlers. 562 | * 563 | * An application can install FreeRTOS interrupt handlers in one of the following ways: 564 | * 1. Direct Routing - Install the functions vPortSVCHandler and xPortPendSVHandler 565 | * for SVC call and PendSV interrupts respectively. 566 | * 2. Indirect Routing - Install separate handlers for SVC call and PendSV 567 | * interrupts and route program control from those handlers 568 | * to vPortSVCHandler and xPortPendSVHandler functions. 569 | * The applications that use Indirect Routing must set configCHECK_HANDLER_INSTALLATION to 0. 570 | * 571 | * Defaults to 1 if left undefined. */ 572 | #define configCHECK_HANDLER_INSTALLATION 1 573 | 574 | /******************************************************************************/ 575 | /* Definitions that include or exclude functionality. *************************/ 576 | /******************************************************************************/ 577 | 578 | /* Set the following configUSE_* constants to 1 to include the named feature in 579 | * the build, or 0 to exclude the named feature from the build. */ 580 | #define configUSE_TASK_NOTIFICATIONS 1 581 | #define configUSE_MUTEXES 1 582 | #define configUSE_RECURSIVE_MUTEXES 1 583 | #define configUSE_COUNTING_SEMAPHORES 1 584 | #define configUSE_QUEUE_SETS 0 585 | #define configUSE_APPLICATION_TASK_TAG 0 586 | 587 | /* Set the following INCLUDE_* constants to 1 to incldue the named API function, 588 | * or 0 to exclude the named API function. Most linkers will remove unused 589 | * functions even when the constant is 1. */ 590 | #define INCLUDE_vTaskPrioritySet 1 591 | #define INCLUDE_uxTaskPriorityGet 1 592 | #define INCLUDE_vTaskDelete 1 593 | #define INCLUDE_vTaskSuspend 1 594 | #define INCLUDE_xResumeFromISR 1 595 | #define INCLUDE_vTaskDelayUntil 1 596 | #define INCLUDE_vTaskDelay 1 597 | #define INCLUDE_xTaskGetSchedulerState 1 598 | #define INCLUDE_xTaskGetCurrentTaskHandle 1 599 | #define INCLUDE_uxTaskGetStackHighWaterMark 0 600 | #define INCLUDE_xTaskGetIdleTaskHandle 0 601 | #define INCLUDE_eTaskGetState 0 602 | #define INCLUDE_xEventGroupSetBitFromISR 1 603 | #define INCLUDE_xTimerPendFunctionCall 0 604 | #define INCLUDE_xTaskAbortDelay 0 605 | #define INCLUDE_xTaskGetHandle 0 606 | #define INCLUDE_xTaskResumeFromISR 1 607 | 608 | 609 | #endif //EXAMPLE_DEMO_PC_APP_LIB_FREE_RTOS_CONFIG_H 610 | --------------------------------------------------------------------------------