Got it!! 99% there. I'll update my GitHub samples repository for easier replication in the next few days:
Here's rundown on how to add unit tests using VSCode + STM32 + Unity Unit Testing Framework on Windows OS; as our products use vanilla C. Remember, this tests business logic only! Not your board-specific code as STMicro does not provide an emulator like Microchip or Espressif at this time.
Overview
The following is using a "Common" folder for Unity as we have projects for both the Bootloader and Application (OpCode) in the same repository.
The secret sauce is in downloading a proper platform-specific compiler with CMake! You can't rely on CMake that is bundled with STM32 extension pack, as VS Code won't discover the tests. Frankly, I don't like duplicating tooling on my machines.. plays hell with your PATH definitions.
- Off-Site Unit Testing (platform specific as it runs on your OS)
- Platform specific compiler (to make EXE not ELF)
Known Issues:
- "Unity" shows up in the "Testing" list even though there are no unit tests there.
- Sometimes the compiler builds platform specific code into Unit Test app and builds fail (even though it's not referenced ANYWHERE!)
- Build error messages are VERY POOR! It just says, failed, with little trace as to what/where. (I've been spoiled by other unit testing system.)
Tools Needed:
- VS Code + STM32 Extension
- Unity Unit Testing Framework
- MinGW-w64 (via MSys2)
- pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain
- pacman -S --needed mingw-w64-ucrt-x86_64-ninja
- pacman -S --needed mingw-w64-ucrt-x86_64-cmake
- Set Environment Variable to MinGW's "xxx\bin\" folder
Folder Structure:
source/
source/Bootloader/
source/App/
--[ App.code-workspace
--[ CMakeLists.txt
--[ CMakePresets.json
source/App/cmake/
--[ gcc-x86_64-windows.cmake
source/App/Tests/
--[ CMakeLists.txt
--[ SampleTests.c
--[ SampleTests.h
source/Common/Unity/
--[ CMakeLists.txt
--[ unity_internals.h
--[ unity.c
--[ unity.h
App/App.code-workspace:
{
"folders": [
{
"path": "."
},
{
"path": "../Common/Unity"
}
],
...
App/CMakePresets.json:
{
"version": 3,
"configurePresets": [
{
"name": "Unit-Tests",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/UnitTests",
"toolchainFile": "${sourceDir}/cmake/gcc-x86_64-windows.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BUILD_TESTS": "ON",
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
}
}
...
App/CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_PROJECT_NAME MyProject-App)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
project(${CMAKE_PROJECT_NAME})
enable_language(C ASM)
if(NOT BUILD_TESTS)
# Normal on-Target build configurations
elseif(BUILD_TESTS)
message("---------------------------------->")
message("-[ Building with CTest version ${CMAKE_VERSION}")
message("---------------------------------->")
add_library(source_code STATIC
"Core/Src/app/SomeFile.c"
# Another biz-logic file to test
)
# Add include paths for the source code library
target_include_directories(source_code PUBLIC
"Core/Inc"
)
include(CTest)
# Add Libraries: add_subdirectory(SourceDir BinaryDir)
add_subdirectory("../Common/Unity" "${CMAKE_CURRENT_BINARY_DIR}/Unity")
# Add Tests
add_subdirectory("Tests")
else()
message(FATAL_ERROR "Invalid build configuration. Please set BUILD_TESTS to ON or OFF.")
endif()
App/cmake/gcc-x86_64-windows.cmake:
set(CMAKE_C_COMPILER_ID GNU)
set(TOOLCHAIN_PREFIX "x86_64-w64-mingw32-")
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc.exe)
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++.exe)
set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}g++.exe)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy.exe)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size.exe)
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".exe")
set(CMAKE_EXECUTABLE_SUFFIX_C ".exe")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".exe")
Common/Unity/CMakeLists.txt:
# CMakeLists for Unity library
# Create a static library from the Unity source files
add_library(UnityLib STATIC unity.c)
target_include_directories(UnityLib PUBLIC ${CMAKE_CURRENT_LIST_DIR})
App/Tests/CMakeLists.txt:
# Register "Test Suite A" .c files with CMake as test executables
add_executable(suite_1_app
"SampleTest.c"
)
# souce_code - Defined in root or business logic's folder "CMakeLists.txt"
# UnityLib - Defined in "Common/Unity/CMakeLists.txt"
target_link_libraries(suite_1_app
source_code
UnityLib
)
# Name of Test Group: test_suite1_name
# Files to test: suite_1_app
add_test(TestSuite1 suite_1_app)
App/Tests/SampleTest.c:
#include "SampleTest.h"
#include "../../Common/Unity/unity.h"
void test_fubar(void);
int add(int a, int b);
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_fubar);
return UNITY_END();
}
void setUp(void)
{
}
void tearDown(void)
{
}
void test_fubar(void)
{
int result = add(2, 3);
TEST_ASSERT_EQUAL(5, result);
}
int add(int a, int b)
{
return a + b;
}
App/Tests/SampleTest.h:
#ifndef _SAMPLETEST_H_
#define _SAMPLETEST_H_
/** @brief Include necessary headers here. */
void Fubar();
#endif /* _SAMPLETEST_H_ */