|
| 1 | +# F-Stack lib/ integration test Makefile (CMocka 1.1.7+) |
| 2 | +# Per docs/unit_test_spec/zh_cn/99-stage6-coverage-boost-review.md |
| 3 | +# FU-CB-DPDKIF-INTEGRATION |
| 4 | +# |
| 5 | +# Unlike tests/unit/, this harness boots a real DPDK EAL (no-huge, no-pci, |
| 6 | +# --vdev=net_null0) so ff_dpdk_init() can run end-to-end and exercise the |
| 7 | +# init_lcore_conf / init_mem_pool / init_dispatch_ring / init_msg_ring / |
| 8 | +# init_port_start / init_clock paths that are unreachable in pure-mock unit |
| 9 | +# tests. |
| 10 | + |
| 11 | +TOPDIR := $(abspath ../..) |
| 12 | +LIB_DIR := $(TOPDIR)/lib |
| 13 | +COMMON_DIR := common |
| 14 | +LIB_OBJS_DIR := lib_objs |
| 15 | + |
| 16 | +# Tooling |
| 17 | +CC ?= gcc |
| 18 | +PKG_CFG ?= pkg-config |
| 19 | +CMOCKA_CFLAGS := $(shell $(PKG_CFG) --cflags cmocka) |
| 20 | +CMOCKA_LIBS := $(shell $(PKG_CFG) --libs cmocka) |
| 21 | +DPDK_CFLAGS := $(shell $(PKG_CFG) --cflags libdpdk 2>/dev/null) |
| 22 | +DPDK_LIBS := $(shell $(PKG_CFG) --libs libdpdk 2>/dev/null) |
| 23 | + |
| 24 | +# Versions guard (R-U-3 mitigation) |
| 25 | +CMOCKA_VER := $(shell $(PKG_CFG) --modversion cmocka 2>/dev/null) |
| 26 | +CMOCKA_OK := $(shell echo "$(CMOCKA_VER)" | awk -F. '{ if ($$1 > 1 || ($$1 == 1 && $$2 > 1) || ($$1 == 1 && $$2 == 1 && $$3 >= 7)) print "ok" }') |
| 27 | +ifneq ($(CMOCKA_OK),ok) |
| 28 | +$(error CMocka >= 1.1.7 required, found '$(CMOCKA_VER)') |
| 29 | +endif |
| 30 | + |
| 31 | +# Host build flags (no FF_KNI / FF_FLOW_ISOLATE / FF_FDIR / FF_USE_PAGE_ARRAY) |
| 32 | +CFLAGS := -O0 -g3 -Wall -Wextra -Wno-unused-parameter \ |
| 33 | + -I$(LIB_DIR) -I$(COMMON_DIR) \ |
| 34 | + $(CMOCKA_CFLAGS) \ |
| 35 | + $(DPDK_CFLAGS) \ |
| 36 | + -DFF_INTEGRATION_TEST=1 |
| 37 | + |
| 38 | +# Fatal-function wraps (mirror unit/Makefile). |
| 39 | +# Note: unlike unit tests we do NOT wrap rte_get_tsc_hz here since the real |
| 40 | +# EAL is initialized and rte_get_tsc_hz returns a sane value post-init. |
| 41 | +BASE_WRAPS := -Wl,--wrap=rte_exit -Wl,--wrap=rte_panic |
| 42 | + |
| 43 | +LDFLAGS_BASE := $(CMOCKA_LIBS) |
| 44 | + |
| 45 | +# Tests list |
| 46 | +ALL_TESTS := test_ff_dpdk_if_integration |
| 47 | + |
| 48 | +# Common stub objects |
| 49 | +COMMON_OBJS := $(COMMON_DIR)/rte_stub.o |
| 50 | + |
| 51 | +# Runtime LD path for DPDK shared libs |
| 52 | +DPDK_RUNPATH := /usr/local/lib64 |
| 53 | +RUN_ENV := LD_LIBRARY_PATH=$(DPDK_RUNPATH):$$LD_LIBRARY_PATH |
| 54 | + |
| 55 | +.PHONY: all test check clean coverage coverage_clean help |
| 56 | +all: $(ALL_TESTS) |
| 57 | + |
| 58 | +help: |
| 59 | + @echo "F-Stack lib/ integration-test build targets:" |
| 60 | + @echo " make all - build all integration test binaries" |
| 61 | + @echo " make test - run all integration tests" |
| 62 | + @echo " make check - run integration tests under valgrind" |
| 63 | + @echo " make coverage - build with gcov + run + emit lcov HTML report" |
| 64 | + @echo " make clean - clean build artifacts (uses rm_tmp_file.sh wrapper)" |
| 65 | + @echo " make coverage_clean - remove gcov/.gcda/.gcno + coverage_report/" |
| 66 | + |
| 67 | +test: all |
| 68 | + @for t in $(ALL_TESTS); do \ |
| 69 | + echo "==> running $$t"; $(RUN_ENV) ./$$t || exit 1; \ |
| 70 | + done |
| 71 | + @echo "ALL INTEGRATION TESTS PASS ($(words $(ALL_TESTS)) binaries)" |
| 72 | + |
| 73 | +# ----- check (valgrind memcheck on integration tests) ------------------- |
| 74 | +VALGRIND ?= valgrind |
| 75 | +VALGRIND_SUPP := valgrind.supp |
| 76 | +VALGRIND_OPTS := --tool=memcheck \ |
| 77 | + --leak-check=full \ |
| 78 | + --errors-for-leak-kinds=definite \ |
| 79 | + --error-exitcode=99 \ |
| 80 | + --suppressions=$(VALGRIND_SUPP) \ |
| 81 | + --quiet |
| 82 | + |
| 83 | +check: all |
| 84 | + @if ! command -v $(VALGRIND) >/dev/null 2>&1; then \ |
| 85 | + echo "ERROR: $(VALGRIND) not installed; please 'dnf install -y valgrind'"; \ |
| 86 | + exit 1; \ |
| 87 | + fi |
| 88 | + @pass=0; fail=0; failed=""; \ |
| 89 | + for t in $(ALL_TESTS); do \ |
| 90 | + printf "==> valgrind %-40s ... " "$$t"; \ |
| 91 | + if $(RUN_ENV) $(VALGRIND) $(VALGRIND_OPTS) ./$$t >/dev/null 2>/tmp/vg_$$t.$$$$.log; then \ |
| 92 | + echo "OK"; pass=$$((pass+1)); \ |
| 93 | + /data/workspace/rm_tmp_file.sh /tmp/vg_$$t.$$$$.log >/dev/null 2>&1; \ |
| 94 | + else \ |
| 95 | + echo "FAIL (see /tmp/vg_$$t.$$$$.log)"; \ |
| 96 | + fail=$$((fail+1)); failed="$$failed $$t"; \ |
| 97 | + fi; \ |
| 98 | + done; \ |
| 99 | + echo ""; \ |
| 100 | + echo "=== valgrind summary: $$pass pass / $$fail fail ($(words $(ALL_TESTS)) total) ==="; \ |
| 101 | + if [ $$fail -gt 0 ]; then \ |
| 102 | + echo "Failed binaries:$$failed"; \ |
| 103 | + exit 1; \ |
| 104 | + fi |
| 105 | + @echo "ALL VALGRIND CHECKS PASS" |
| 106 | + |
| 107 | +# ----- lib/*.c → lib_objs/*.o ----- |
| 108 | +$(LIB_OBJS_DIR): |
| 109 | + @mkdir -p $@ |
| 110 | + |
| 111 | +$(LIB_OBJS_DIR)/%.o: $(LIB_DIR)/%.c | $(LIB_OBJS_DIR) |
| 112 | + $(CC) $(CFLAGS) $(DPDK_CFLAGS) -c $< -o $@ |
| 113 | + |
| 114 | +# ----- common stubs ----- |
| 115 | +$(COMMON_DIR)/%.o: $(COMMON_DIR)/%.c |
| 116 | + $(CC) $(CFLAGS) -c $< -o $@ |
| 117 | + |
| 118 | +# ----- top-level test source ----- |
| 119 | +%.o: %.c |
| 120 | + $(CC) $(CFLAGS) -c $< -o $@ |
| 121 | + |
| 122 | +# ----- integration test binary ----- |
| 123 | +# Links full DPDK shared libs + ff_dpdk_if.o + ff_config.o + ff_ini_parser.o |
| 124 | +# + ff_log.o (real, since logging happens during ff_dpdk_init) + ff_host_interface.o |
| 125 | +# (for ff_clock_gettime in ff_get_tsc_ns). |
| 126 | +INT_LIB_OBJS := \ |
| 127 | + $(LIB_OBJS_DIR)/ff_dpdk_if.o \ |
| 128 | + $(LIB_OBJS_DIR)/ff_config.o \ |
| 129 | + $(LIB_OBJS_DIR)/ff_ini_parser.o \ |
| 130 | + $(LIB_OBJS_DIR)/ff_log.o \ |
| 131 | + $(LIB_OBJS_DIR)/ff_host_interface.o |
| 132 | + |
| 133 | +test_ff_dpdk_if_integration: test_ff_dpdk_if_integration.o $(INT_LIB_OBJS) $(COMMON_OBJS) |
| 134 | + $(CC) -o $@ $^ $(LDFLAGS_BASE) $(BASE_WRAPS) \ |
| 135 | + $(DPDK_LIBS) -lrte_net_bond -Wl,-rpath=$(DPDK_RUNPATH) \ |
| 136 | + -lpthread -lcrypto -ldl -lm |
| 137 | + |
| 138 | +# ----- clean (NFR-U-7: must use workspace wrapper, no direct rm) ----- |
| 139 | +clean: |
| 140 | + @find . -name '*.o' -type f -print 2>/dev/null | while read f; do \ |
| 141 | + /data/workspace/rm_tmp_file.sh "$$(realpath "$$f")" >/dev/null; \ |
| 142 | + done |
| 143 | + @for t in $(ALL_TESTS); do \ |
| 144 | + if [ -x "./$$t" ]; then \ |
| 145 | + /data/workspace/rm_tmp_file.sh "$$(realpath ./$$t)" >/dev/null; \ |
| 146 | + fi; \ |
| 147 | + done |
| 148 | + @if [ -d $(LIB_OBJS_DIR) ]; then \ |
| 149 | + /data/workspace/rm_tmp_file.sh "$$(realpath $(LIB_OBJS_DIR))" >/dev/null; \ |
| 150 | + fi |
| 151 | + @$(MAKE) -s coverage_clean |
| 152 | + @echo "clean done" |
| 153 | + |
| 154 | +# ----- coverage (FU-CB-DPDKIF-INTEGRATION: ff_dpdk_if.c init/port path) ----- |
| 155 | +COVERAGE_INFO := coverage.info |
| 156 | +COVERAGE_DIR := coverage_report |
| 157 | + |
| 158 | +coverage: CFLAGS += -fprofile-arcs -ftest-coverage |
| 159 | +coverage: LDFLAGS_BASE += -lgcov --coverage |
| 160 | +coverage: clean test |
| 161 | + @echo "==> Collecting coverage data via lcov..." |
| 162 | + @lcov --capture --directory $(LIB_OBJS_DIR) --directory . \ |
| 163 | + --output-file $(COVERAGE_INFO) \ |
| 164 | + --rc branch_coverage=1 \ |
| 165 | + --ignore-errors mismatch,negative,inconsistent,empty,unused,source 2>&1 \ |
| 166 | + | tail -8 |
| 167 | + @echo "==> Stripping system / cmocka / test-harness paths from report..." |
| 168 | + @lcov --remove $(COVERAGE_INFO) \ |
| 169 | + '/usr/*' '*/cmocka.h' \ |
| 170 | + '*/tests/integration/test_*.c' '*/tests/integration/common/*' \ |
| 171 | + --output-file $(COVERAGE_INFO).clean \ |
| 172 | + --rc branch_coverage=1 \ |
| 173 | + --ignore-errors unused,inconsistent,empty 2>&1 | tail -5 |
| 174 | + @mv $(COVERAGE_INFO).clean $(COVERAGE_INFO) |
| 175 | + @echo "==> Generating HTML report at $(COVERAGE_DIR)/..." |
| 176 | + @genhtml $(COVERAGE_INFO) --output-directory $(COVERAGE_DIR) \ |
| 177 | + --branch-coverage \ |
| 178 | + --title "F-Stack lib/ integration-test coverage" \ |
| 179 | + --ignore-errors source,inconsistent,corrupt 2>&1 | tail -6 |
| 180 | + |
| 181 | +coverage_clean: |
| 182 | + @find . \( -name '*.gcda' -o -name '*.gcno' \) -type f -print 2>/dev/null \ |
| 183 | + | while read f; do \ |
| 184 | + /data/workspace/rm_tmp_file.sh "$$(realpath "$$f")" >/dev/null; \ |
| 185 | + done |
| 186 | + @if [ -f $(COVERAGE_INFO) ]; then \ |
| 187 | + /data/workspace/rm_tmp_file.sh "$$(realpath $(COVERAGE_INFO))" >/dev/null; \ |
| 188 | + fi |
| 189 | + @if [ -d $(COVERAGE_DIR) ]; then \ |
| 190 | + /data/workspace/rm_tmp_file.sh "$$(realpath $(COVERAGE_DIR))" >/dev/null; \ |
| 191 | + fi |
0 commit comments