FreeRTOS-Kernel/FreeRTOS/Test/CMock/testdir.mk
Paul Bartell 92aca6e910
Collect initial coverage data so that untagged functions are still included in the lcov report (#537)
Collect initial coverage data with lcov --initial and add this to coverage data to the combined coverage from each test binary.
This ensures that all functions in the target file(s) are included in coverage statistics, even if those functions are not tagged in a _utest.c file.
Note: Functions which are excluded by the preprocessor will not have initial coverage data generated for them.
2021-03-23 18:41:14 -07:00

211 lines
7.4 KiB
Makefile

# Indent with spaces
.RECIPEPREFIX := $(.RECIPEPREFIX) $(.RECIPEPREFIX)
# define the name for this test's output files
ifdef SUITE
EXEC_PREFIX := $(PROJECT)_$(SUITE)
else
EXEC_PREFIX := $(PROJECT)
endif
# Define directory paths
ifdef SUITE
PROJECT_DIR := $(abspath ../)
else
PROJECT_DIR := $(abspath .)
endif
SUITE_DIR := $(abspath .)
ifdef SUITE
SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)/$(SUITE)
else
SCRATCH_DIR := $(GENERATED_DIR)/$(PROJECT)
endif
MOCKS_DIR := $(SCRATCH_DIR)/mocks
PROJ_DIR := $(SCRATCH_DIR)/proj
# Define mock details
MOCK_FILES := $(notdir $(MOCK_FILES_FP))
MOCK_HDR := $(addprefix mock_,$(MOCK_FILES))
MOCK_SRC := $(addprefix mock_,$(MOCK_FILES:.h=.c))
MOCK_OBJ := $(addprefix mock_,$(MOCK_FILES:.h=.o))
MOCK_HDR_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_HDR))
MOCK_SRC_LIST := $(addprefix $(MOCKS_DIR)/,$(MOCK_SRC))
MOCK_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(MOCK_OBJ))
CFLAGS += -I$(MOCKS_DIR)
# Kernel files under test
PROJ_SRC_LIST := $(addprefix $(KERNEL_DIR)/,$(PROJECT_SRC))
PROJ_PP_LIST := $(addprefix $(PROJ_DIR)/,$(PROJECT_SRC:.c=.i))
PROJ_OBJ_LIST := $(addprefix $(PROJ_DIR)/,$(PROJECT_SRC:.c=.o))
PROJ_GCDA_LIST := $(PROJ_OBJ_LIST:.o=.gcda)
# Unit test files
SUITE_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:.c=.o))
RUNNER_SRC_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:_utest.c=_utest_runner.c))
RUNNER_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:_utest.c=_utest_runner.o))
# Support files
SF_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/sf_,$(SUITE_SUPPORT_SRC:.c=.o))
DEPS_OBJ_LIST := $(addprefix $(SCRATCH_DIR)/dep_,$(PROJECT_DEPS_SRC:.c=.o))
EXECS := $(addprefix $(EXEC_PREFIX)_,$(SUITE_UT_SRC:.c=))
EXEC_LIST := $(addprefix $(BIN_DIR)/,$(EXECS))
LCOV_LIST := $(addsuffix .info,$(addprefix $(SCRATCH_DIR)/,$(SUITE_UT_SRC:.c=)))
COVINFO_INITIAL := $(SCRATCH_DIR)/$(EXEC_PREFIX)_initial.info
COVINFO_COMBINE := $(SCRATCH_DIR)/$(EXEC_PREFIX)_combined.info
COVINFO := $(abspath $(SCRATCH_DIR)/..)/$(EXEC_PREFIX).info
LIBS_LIST := $(foreach lib, $(LIBS), $(LIB_DIR)/$(lib).so)
# Coverage related options
GCC_COV_OPTS := -fprofile-arcs -ftest-coverage -fprofile-generate
GCOV_OPTS := --unconditional-branches --branch-probabilities
COV_REPORT_DIR := $(SCRATCH_DIR)/coverage
.PHONY: all clean run gcov bin lcov lcovhtml libs
# Prevent deletion of intermediate files
NO_DELETE : $(MOCK_HDR_LIST) $(MOCK_SRC_LIST) $(MOCK_OBJ_LIST) \
$(DEPS_OBJ_LIST) $(SF_OBJ_LIST) $(EXEC_LIST) \
$(PROJ_PP_LIST) $(PROJ_OBJ_LIST) $(PROJ_GCDA_LIST) \
$(SUITE_OBJ_LIST) $(RUNNER_SRC_LIST) $(RUNNER_OBJ_LIST) \
$(COVINFO) $(LCOV_LIST)
# Cases that run test binaries cannot be run in parallel.
.NOTPARALLEL : $(COVINFO) $(LCOV_LIST) $(PROJ_GCDA_LIST)
.DEFAULT_GOAL := run
# Generate gcov files by default
run : gcov
gcov : $(PROJ_GCDA_LIST)
clean:
rm -rf $(SCRATCH_DIR)
rm -rf $(EXEC_LIST)
rm -rf $(COVINFO)
$(LIBS_LIST) :
make -C $(UT_ROOT_DIR) libs
define run-test
$(1)
endef
# Run and append to gcov data files
$(PROJ_GCDA_LIST) : $(EXEC_LIST)
rm -f $(PROJ_DIR)/*.gcda
mkdir -p $(BIN_DIR)
# run each test case
$(foreach bin,$^,$(call run-test,$(bin)))
# Run and generate lcov
lcov: $(COVINFO)
lcovhtml : $(COVINFO)
mkdir -p $(COV_REPORT_DIR)
genhtml $(COVINFO) $(LCOV_OPTS) --output-directory $(COV_REPORT_DIR)
bin: $(EXEC_LIST)
# Generate _mock.c / .h files
$(MOCK_HDR_LIST) $(MOCK_SRC_LIST) : $(PROJECT_DIR)/$(PROJECT).yml $(MOCK_FILES_FP)
mkdir -p $(SCRATCH_DIR) $(MOCKS_DIR)
cd $(SCRATCH_DIR) && \
ruby $(CMOCK_EXEC_DIR)/cmock.rb -o$(PROJECT_DIR)/$(PROJECT).yml \
$(MOCK_FILES_FP)
# Generate callgraph for coverage filtering
$(PROJ_DIR)/callgraph.json : $(PROJ_SRC_LIST)
mkdir -p $(PROJ_DIR)
python3 $(UT_ROOT_DIR)/tools/callgraph.py --out $@ $^
# preprocess proj files to expand macros for coverage
$(PROJ_DIR)/%.i : $(KERNEL_DIR)/%.c
mkdir -p $(PROJ_DIR)
$(CC) -E $< $(CPPFLAGS) $(CFLAGS) -o $@
# compile the project objects with coverage instrumented
$(PROJ_DIR)/%.o : $(PROJ_DIR)/%.i
$(CC) -c $< $(CPPFLAGS) $(INCLUDE_DIR) $(GCC_COV_OPTS) -o $@
# Build mock objects
$(SCRATCH_DIR)/mock_%.o : $(MOCKS_DIR)/mock_%.c
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
# compile unit tests
$(SCRATCH_DIR)/%_utest.o : $(SUITE_DIR)/%_utest.c $(MOCK_HDR_LIST) $(LIBS_LIST)
mkdir -p $(SCRATCH_DIR)
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
# compile support files
$(SCRATCH_DIR)/sf_%.o : $(PROJECT_DIR)/%.c $(MOCK_HDR_LIST)
mkdir -p $(SCRATCH_DIR)
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
# compile c files that are needed by PROJ but not mocked
$(SCRATCH_DIR)/dep_%.o : $(KERNEL_DIR)/%.c
mkdir -p $(SCRATCH_DIR)
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
# generate a test runner for each test file
$(SCRATCH_DIR)/%_utest_runner.c : $(SUITE_DIR)/%_utest.c
mkdir -p $(SCRATCH_DIR)
ruby $(UNITY_BIN_DIR)/generate_test_runner.rb\
$(PROJECT_DIR)/$(PROJECT).yml $< $@
# compile test runner
$(SCRATCH_DIR)/%_utest_runner.o : $(SCRATCH_DIR)/%_utest_runner.c
mkdir -p $(SCRATCH_DIR)
$(CC) -c $< $(CPPFLAGS) $(CFLAGS) -o $@
# Link the _utest binary
$(EXEC_LIST) : $(BIN_DIR)/$(EXEC_PREFIX)_%_utest : $(SCRATCH_DIR)/%_utest.o \
$(SCRATCH_DIR)/%_utest_runner.o \
$(SF_OBJ_LIST) $(MOCK_OBJ_LIST) \
$(PROJ_OBJ_LIST) $(LIBS_LIST) \
$(DEPS_OBJ_LIST)
mkdir -p $(BIN_DIR)
$(CC) $< $(subst .o,_runner.o,$<) $(SF_OBJ_LIST) $(DEPS_OBJ_LIST) \
$(MOCK_OBJ_LIST) $(PROJ_OBJ_LIST) $(LDFLAGS) -o $@
# Generate baseline inital coverage data from .gcno file
$(SCRATCH_DIR)/$(EXEC_PREFIX)_initial.info : $(PROJ_OBJ_LIST)
lcov $(LCOV_OPTS) --capture --initial --directory $(PROJ_DIR) -o $@
# Run the test runner and genrate a filtered gcov.json.gz file
$(SCRATCH_DIR)/%_utest.info : $(BIN_DIR)/$(EXEC_PREFIX)_%_utest \
$(PROJ_DIR)/callgraph.json
# Remove any existing coverage data
rm -f $(PROJ_DIR)/*.gcda
# run the testrunner
$<
# Gather coverage into a json.gz file
gcov $(GCOV_OPTS) $(foreach src,$(PROJECT_SRC),$(PROJ_DIR)/$(src:.c=.gcda)) \
--json-format --stdout | gzip > $(subst .info,.json.gz,$@)
# Filter coverage based on tags in unit test file
$(TOOLS_DIR)/filtercov.py --in $(subst .info,.json.gz,$@) \
--map $(PROJ_DIR)/callgraph.json \
--test $(SUITE_DIR)/$*_utest.c \
--format lcov \
--out $@
-lcov $(LCOV_OPTS) --summary $@
# Remove temporary files
rm -f $(subst .info,.json.gz,$@)
rm -f $(PROJ_GCDA_LIST)
# Combine lcov from each test bin into one lcov info file for the suite
$(COVINFO_COMBINE) : $(LCOV_LIST)
lcov $(LCOV_OPTS) -o $@ $(foreach cov,$(LCOV_LIST),--add-tracefile $(cov) )
# Add baseline / initial coverage generated by gcc to point out untagged functions
$(COVINFO) : $(COVINFO_COMBINE) $(COVINFO_INITIAL)
lcov $(LCOV_OPTS) -o $@ --add-tracefile $(COVINFO_INITIAL) --add-tracefile $(COVINFO_COMBINE)