Add Kernel Unit Tests Environment (#494)

* Implement Kernel Unit Test Framework with CMock

* Add Readme.md to unit tests

* Add Posix build checker to git actions

* Add Ruby requirement

* Fix file header checks

* Fix header checks

* Add color output to test runs
pull/498/head
alfred gedeon 4 years ago committed by GitHub
parent a78cb45d12
commit 9b9011917a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -63,4 +63,3 @@ jobs:
- name: Posix Network Build Checker
run: |
bash workspace/.github/scripts/posix_network_build_checker.sh workspace

@ -1,74 +0,0 @@
cmake_minimum_required ( VERSION 3.13.0 )
project ( "FreeRTOS Unit Tests"
VERSION 1.0.0
LANGUAGES C )
# Allow the project to be organized into folders.
set_property( GLOBAL PROPERTY USE_FOLDERS ON )
# Use C90.
set( CMAKE_C_STANDARD 90 )
set( CMAKE_C_STANDARD_REQUIRED ON )
# Do not allow in-source build.
if( ${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR} )
message( FATAL_ERROR "In-source build is not allowed. Please build in a separate directory, such as ${PROJECT_SOURCE_DIR}/build." )
endif()
# Set global path variables.
get_filename_component(__MODULE_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE)
set(MODULE_ROOT_DIR ${__MODULE_ROOT_DIR} CACHE INTERNAL "FreeRTOS root.")
# Configure options to always show in CMake GUI.
option( BUILD_CLONE_SUBMODULES
"Set this to ON to automatically clone any required Git submodules. When OFF, submodules must be manually cloned."
ON )
# Set output directories.
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
# Define a CMock resource path.
set( CMOCK_DIR ${MODULE_ROOT_DIR}/FreeRTOS/Test/CMock/CMock CACHE INTERNAL "CMock library source directory." )
# Include CMock build configuration.
include( cmock_build.cmake )
# Check if the CMock source directory exists, and if not present, clone the submodule
# if BUILD_CLONE_SUBMODULES configuration is enabled.
if( NOT EXISTS ${CMOCK_DIR}/src )
# Attempt to clone CMock.
if( ${BUILD_CLONE_SUBMODULES} )
clone_cmock()
else()
message( FATAL_ERROR "The required submodule CMock does not exist. Either clone it manually, or set BUILD_CLONE_SUBMODULES to 1 to automatically clone it during build." )
endif()
endif()
# Add unit test and coverage configuration.
# Use CTest utility for managing test runs. This has to be added BEFORE
# defining test targets with add_test()
enable_testing()
# Add build targets for CMock and Unit, required for unit testing.
add_cmock_targets()
# Add function to enable CMock based tests and coverage.
include( ${MODULE_ROOT_DIR}/tools/cmock/create_test.cmake )
# Include build configuration for unit tests.
include( queue/queue.cmake )
# List of unit tests
set( unit_test_list
queue_utest
)
# Add a target for running coverage on tests.
add_custom_target( coverage
COMMAND ${CMAKE_COMMAND} -P ${MODULE_ROOT_DIR}/tools/cmock/coverage.cmake
DEPENDS cmock unity ${unit_test_list}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

@ -0,0 +1,70 @@
# Change to match installed location
export CC=/usr/local/bin/gcc
export LD=/usr/local/bin/ld
# Add units here when adding a new unit test directory with the same name
UNITS := queue
include makefile.in
.PHONY: all run doc clean directories $(UNITS) coverage
all: doc coverage
execs: $(UNITS) directories
$(UNITS): directories ${LIB_DIR}/libcmock.so ${LIB_DIR}/libunity.so
$(MAKE) -C $@
doc: directories
$(MAKE) -C doc all
directories:
-mkdir $(BUILD_DIR)
-mkdir $(GENERATED_DIR)
-mkdir $(COVERAGE_DIR)
-mkdir $(BIN_DIR)
-mkdir $(DOC_DIR)
-mkdir $(LIB_DIR)
clean:
rm -rf build
help:
@echo -e 'Usage: $$ make <unit>\n '
@echo -e ' where <unit> is one of: $(UNITS) doc all run run_formatted run_col run_col_formatted coverage'
$(LIB_DIR)/libcmock.so : ${CMOCK_SRC_DIR}/cmock.c \
${CMOCK_SRC_DIR}/cmock.h \
${LIB_DIR}/libunity.so \
Makefile
${CC} -o $@ -shared -fPIC $< ${INCLUDE_DIR}
$(LIB_DIR)/libunity.so : ${UNITY_SRC_DIR}/unity.c \
${CMOCK_SRC_DIR}/cmock.h \
Makefile directories
${CC} -o $@ -shared -fPIC $<
run : $(UNITS) directories
for f in $(BIN_DIR)/*; do \
$${f}; done
run_col : $(UNITS) directories
for f in $(BIN_DIR)/*; do \
ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb -e "report('`$${f}`')"; done
run_formatted : $(UNITS) directories
for f in $(BIN_DIR)/*; do \
$${f} > $(BUILD_DIR)/output; \
ruby $(UNITY_BIN_DIR)/parse_output.rb $(BUILD_DIR)/output ; \
done
run_col_formatted : $(UNITS) directories
for f in $(BIN_DIR)/*; do \
$${f} > $(BUILD_DIR)/output; \
ruby -r $(UNITY_BIN_DIR)/colour_reporter.rb -e "report('$$(ruby $(UNITY_BIN_DIR)/parse_output.rb $(BUILD_DIR)/output)')"; \
done
coverage : run_col
lcov --base-directory . --directory . -c --rc lcov_branch_coverage=1 --rc genhtml_branch_coverage=1 -o $(BUILD_DIR)/cmock_test.info
genhtml $(BUILD_DIR)/cmock_test.info --branch-coverage --output-directory $(COVERAGE_DIR)

@ -0,0 +1,54 @@
# FreeRTOS Kernel Unit Tests
## Prerequisites as tested
GCC
```
gcc: gcc (GCC) 9.2.0
```
LCOV
```
lcov: LCOV version 1.14-6-g40580cd
```
Make
```
GNU Make 3.82
```
Ruby
```
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
```
Doxygen (optional)
```
1.8.5
```
## How to run
```
$ make help
Usage: $ make <unit>
where <unit> is one of: queue doc all run run_formatted run_col
run_col_formatted coverage
```
Explanation
```
$ make queue
```
Would build the kernel queue unit tests and put the executable in build/bin
```
$ make doc
```
Would generate the doxygen documentation in build/doc
```
$ make run | run_formatted | run_col | run_col_formatted
```
Would build all unit tests and runs them one after the other with different
options between normal and formatted and colored for easily spotting errors
```
$ make coverage
```
Would build all unit tests, runs them one after the other, then generates html code
coverage and places them in build/coverage with initial file index.html

@ -1,59 +0,0 @@
# Macro utility to clone the CMock submodule.
macro( clone_cmock )
find_package( Git REQUIRED )
message( "Cloning submodule CMock." )
execute_process( COMMAND rm -rf ${CMOCK_DIR}
COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive ${MODULE_ROOT_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE CMOCK_CLONE_RESULT )
if( NOT ${CMOCK_CLONE_RESULT} STREQUAL "0" )
message( FATAL_ERROR "Failed to clone CMock submodule." )
endif()
endmacro()
# Macro utility to add library targets for Unity and CMock to build configuration.
macro( add_cmock_targets )
# Build Configuration for CMock and Unity libraries.
list( APPEND CMOCK_INCLUDE_DIRS
"${CMOCK_DIR}/vendor/unity/src/"
"${CMOCK_DIR}/vendor/unity/extras/fixture/src"
"${CMOCK_DIR}/vendor/unity/extras/memory/src"
"${CMOCK_DIR}/src"
)
add_library(cmock STATIC
"${CMOCK_DIR}/src/cmock.c"
)
set_target_properties(cmock PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
COMPILE_FLAGS "-Og"
)
target_include_directories(cmock PUBLIC
${CMOCK_DIR}/src
${CMOCK_DIR}/vendor/unity/src/
${CMOCK_DIR}/examples
${CMOCK_INCLUDE_DIRS}
)
add_library(unity STATIC
"${CMOCK_DIR}/vendor/unity/src/unity.c"
"${CMOCK_DIR}/vendor/unity/extras/fixture/src/unity_fixture.c"
"${CMOCK_DIR}/vendor/unity/extras/memory/src/unity_memory.c"
)
set_target_properties(unity PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
)
target_include_directories(unity PUBLIC
${CMOCK_INCLUDE_DIRS}
)
target_link_libraries(cmock unity)
endmacro()

@ -0,0 +1,7 @@
DOXY_ARGS := "INPUT=../ \n FILE_PATTERNS=*.c *.h\n RECURSIVE=YES\n"
DOXY_ARGS += "EXCLUDE=../CMock/ ../config/ ../scripts/ ../build/\n"
DOXY_ARGS += "PROJECT_NAME=FreeRTOS Unit Tests\nGENERATE_LATEX=NO\n"
DOXY_ARGS += "OUTPUT_DIRECTORY=$(DOC_DIR)"
all:
echo -e $(DOXY_ARGS) | doxygen -

@ -0,0 +1,51 @@
# Various directory locations
UT_ROOT_DIR_REL :=.
UT_ROOT_DIR := $(abspath $(UT_ROOT_DIR_REL))
BUILD_DIR := $(UT_ROOT_DIR)/build
DOC_DIR := $(BUILD_DIR)/doc
COVERAGE_DIR := $(BUILD_DIR)/coverage
BIN_DIR := $(BUILD_DIR)/bin
GENERATED_DIR := $(BUILD_DIR)/generated
LIB_DIR := $(BUILD_DIR)/lib
FREERTOS_DIR_REL := ../../../FreeRTOS
FREERTOS_DIR := $(abspath $(FREERTOS_DIR_REL))
KERNEL_DIR_REL := ../../../FreeRTOS/Source
KERNEL_DIR := $(abspath $(KERNEL_DIR_REL))
CMOCK_DIR := $(UT_ROOT_DIR)/CMock
CMOCK_SRC_DIR := $(CMOCK_DIR)/src
UNITY_DIR := $(CMOCK_DIR)/vendor/unity
UNITY_SRC_DIR := $(UNITY_DIR)/src
UNITY_INC_DIR := $(UNITY_DIR)/src
UNITY_BIN_DIR := $(UNITY_DIR)/auto
CMOCK_EXEC_DIR := $(CMOCK_DIR)/lib
# Include directory location
INCLUDE_DIR := -I$(KERNEL_DIR)/include -I. -I$(UT_ROOT_DIR)/config
INCLUDE_DIR += -I$(UNITY_INC_DIR)
INCLUDE_DIR += -I$(CMOCK_SRC_DIR)
CPPFLAGS :=
CFLAGS := $(INCLUDE_DIR) -O0 -ggdb -pthread --std=c99
LDFLAGS := -L$(LIB_DIR) -lunity -lcmock -Wl,-rpath,$(LIB_DIR) -pthread -lgcov
export BUILD_DIR
export DOC_DIR
export GENERATED_DIR
export COVERAGE_DIR
export BIN_DIR
export UNITS
export CFLAGS
export LDFLAGS
export CPPFLAGS
export CMOCK_EXEC_DIR
export KERNEL_DIR
export UNITY_BIN_DIR
export LIB_DIR

@ -0,0 +1,63 @@
# Change according to what your unit test directory is.
# For example if testing queue.c your directory should be called queue
# and the project name should be queue
# if testing list.c your directory should be called list
# and the project name should be list
PROJECT := queue
# List the dependency files you wish to mock
MOCK_FILES_FP := $(KERNEL_DIR)/include/task.h
MOCK_FILES_FP += $(KERNEL_DIR)/include/list.h
# List the options the compilation would need
CPPFLAGS += -DportUSING_MPU_WRAPPERS=0
# Try not to edit beyond this line
MOCK_FILES := $(notdir $(MOCK_FILES_FP))
MOCK_OBJ := $(addprefix mock_,$(MOCK_FILES:.h=.o))
MOCK_SRC := $(addprefix mock_,$(MOCK_FILES:.h=.c))
EXEC := $(PROJECT)_utest
PROJECT_DIR := $(abspath .)
SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
PROJ_LIB_DIR := $(SCRATCH_DIR)/lib
MOCK_OBJ_LIST := $(addprefix $(PROJ_LIB_DIR)/,$(MOCK_OBJ))
MOCKS_DIR := $(SCRATCH_DIR)/mocks
MOCK_SRC_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_SRC))
CFLAGS += -I$(MOCKS_DIR)
COVERAGE_OPTS := -fprofile-arcs -ftest-coverage -fprofile-generate
$(MOCKS_DIR)/mock_%.c : directories Makefile
cd $(SCRATCH_DIR) && \
ruby $(CMOCK_EXEC_DIR)/cmock.rb -o$(PROJECT_DIR)/$(PROJECT).yml \
$(MOCK_FILES_FP)
$(PROJ_LIB_DIR)/mock_%.o : $(MOCKS_DIR)/mock_%.c
$(CC) -c $< -fPIC $(CFLAGS) -o $@
$(BIN_DIR)/$(EXEC) : $(SCRATCH_DIR)/test_runner.o \
$(SCRATCH_DIR)/$(PROJECT).o \
$(SCRATCH_DIR)/$(PROJECT)_utest.o \
$(MOCK_OBJ_LIST) | Makefile
$(CC) $+ $(LDFLAGS) -o $@
$(SCRATCH_DIR)/test_runner.o : $(SCRATCH_DIR)/test_runner.c
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
$(SCRATCH_DIR)/$(PROJECT)_utest.o : $(PROJECT_DIR)/$(PROJECT)_utest.c
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
$(SCRATCH_DIR)/$(PROJECT).o : $(KERNEL_DIR)/$(PROJECT).c
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) $(COVERAGE_OPTS) -o $@
$(SCRATCH_DIR)/test_runner.c : Makefile $(MOCK_OBJ_LIST)
ruby $(UNITY_BIN_DIR)/generate_test_runner.rb $(EXEC).c \
$(PROJECT_DIR)/$(PROJECT).yml $@
.PHONY: directories
directories :
-mkdir -p $(SCRATCH_DIR)
-mkdir -p $(MOCKS_DIR)
-mkdir -p $(PROJ_LIB_DIR)
# prevent deletion by chain of implicit rules
NO_DELETE: $(MOCK_SRC_LIST)

@ -1,118 +0,0 @@
# ============================= Queue unit tests ===========================
project( "queue" )
set(project_name "queue")
set(utest_name "${project_name}_utest")
set(utest_source "${project_name}_utest.c")
set(utest_yml "${project_name}.yml")
# ===================== Create your mock here (edit) ========================
# clear the original variables
set( mock_list "" )
set( mock_include_list "" )
set( mock_define_list "" )
set( real_source_files "" )
set( real_include_directories "" )
set( test_include_directories "" )
set( utest_link_list "" )
set( utest_dep_list "" )
set(mock_dir "wrapper_mocks")
# list of files to preprocess
list(APPEND preprocessed_mock_list
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include/task.h"
#"${MODULE_ROOT_DIR}/FreeRTOS/Source/include/portable.h"
)
set(preprocess_commands "-I ${MODULE_ROOT_DIR}/FreeRTOS/Source/Include -I ${MODULE_ROOT_DIR}/FreeRTOS/Test/CMock/config" )
# list the files to mock here
list(APPEND mock_list
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include/task.h"
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include/list.h"
#"${MODULE_ROOT_DIR}/FreeRTOS/Source/include/portable.h"
)
# list the directories your mocks need
list(APPEND mock_include_list
"config"
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include"
#"${CMAKE_CURRENT_LIST_DIR}/${mock_dir}"
)
#list the definitions of your mocks to control what to be included
list(APPEND mock_define_list
portUSING_MPU_WRAPPERS=0
)
# ================= Create the library under test here (edit) ==================
# list the files you would like to test here
list(APPEND real_source_files
"${MODULE_ROOT_DIR}/FreeRTOS/Source/queue.c"
)
# list the directories the module under test includes
list(APPEND real_include_directories
"config"
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include"
#"${CMAKE_CURRENT_BINARY_DIR}/${mock_dir}"
)
# ===================== Create UnitTest Code here (edit) =====================
# list the directories your test needs to include
list(APPEND test_include_directories
"config"
"${MODULE_ROOT_DIR}/FreeRTOS/Source/include"
"${CMAKE_CURRENT_BINARY_DIR}/${mock_dir}"
)
# ============================= (end edit) ===================================
set(mock_name "${project_name}_mock")
set(real_name "${project_name}_real")
set(pre_mock_name "pre_${mock_name}")
create_mock_list("${mock_name}"
"${mock_list}"
"${CMAKE_CURRENT_LIST_DIR}/${utest_yml}"
"${mock_include_list}"
"${mock_define_list}"
"${mock_dir}"
)
separate_arguments(unix_flags UNIX_COMMAND "${preprocess_commands}")
message(STATUS "Unix Flags: ${unix_flags}")
#preprocess_mock_list(${mock_name}
# ${preprocessed_mock_list}
# "${unix_flags}"
# )
#add_dependencies(${mock_name} ${pre_mock_name} )
create_real_library(${real_name}
"${real_source_files}"
"${real_include_directories}"
"${mock_name}"
)
list(APPEND utest_link_list
-l${mock_name}
lib${real_name}.a
)
list(APPEND utest_dep_list
${real_name}
)
create_test(${utest_name}
"${CMAKE_CURRENT_LIST_DIR}/${utest_source}"
"${utest_link_list}"
"${utest_dep_list}"
"${test_include_directories}"
"${CMAKE_CURRENT_LIST_DIR}/${utest_yml}"
"${mock_dir}"
)

@ -1,6 +1,5 @@
:cmock:
:mock_prefix: mock_
:mock_path: wrapper_mocks
:when_no_prototypes: :warn
:treat_externs: :include
:enforce_strict_ordering: TRUE

@ -1,4 +1,5 @@
/*
* FreeRTOS V202012.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
@ -18,9 +19,11 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*! @file queue_utest.c */
/* C runtime includes. */
#include <stdlib.h>
@ -36,6 +39,7 @@
/* Mock includes. */
#include "mock_task.h"
#include "mock_list.h"
/* ============================ GLOBAL VARIABLES =========================== */
static uint16_t usMallocFreeCalls = 0;
@ -44,12 +48,13 @@ static uint16_t usMallocFreeCalls = 0;
void * pvPortMalloc( size_t xSize )
{
return malloc(xSize);
return malloc( xSize );
}
void vPortFree( void * pv )
{
return free(pv);
return free( pv );
}
/*******************************************************************************
* Unity fixtures
******************************************************************************/
@ -57,7 +62,7 @@ void setUp( void )
{
}
/* called before each testcase */
/*! called before each testcase */
void tearDown( void )
{
TEST_ASSERT_EQUAL_INT_MESSAGE( 0, usMallocFreeCalls,
@ -66,12 +71,12 @@ void tearDown( void )
usMallocFreeCalls = 0;
}
/* called at the beginning of the whole suite */
/*! called at the beginning of the whole suite */
void suiteSetUp()
{
}
/* called at the end of the whole suite */
/*! called at the end of the whole suite */
int suiteTearDown( int numFailures )
{
return numFailures;
@ -83,6 +88,8 @@ int suiteTearDown( int numFailures )
*/
void test_xQueueCreate_Success( void )
{
QueueHandle_t xQueue = xQueueCreate(1 , 1);
vListInitialise_Ignore();
QueueHandle_t xQueue = xQueueCreate( 1, 1 );
TEST_ASSERT_NOT_EQUAL( NULL, xQueue );
}

@ -1,9 +0,0 @@
#!/bin/bash
echo "Running tests..."
SOURCE_DIR=FreeRTOS/Test/CMock
BUILD_DIR=FreeRTOS/Test/CMock/build
cmake -DBUILD_CLONE_SUBMODULES=0 -S ${SOURCE_DIR} -B ${BUILD_DIR} && make -C ${BUILD_DIR} && ${BUILD_DIR}/bin/tests/queue_utest
TEST_RESULT=$?
echo "Done"
exit ${TEST_RESULT}

@ -1,7 +0,0 @@
#!/bin/bash
echo "Setting up test environment..."
git -C FreeRTOS submodule update --init --recursive
RC=$?
echo "Done"
exit $RC
Loading…
Cancel
Save