CC = gcc CFLAGS = -Wall -Wextra -O2 -g -D_GNU_SOURCE CFLAGS_COV = -Wall -Wextra -g -D_GNU_SOURCE --coverage -fprofile-arcs -ftest-coverage LDFLAGS = -lssl -lcrypto -lsqlite3 -lm -lpthread LDFLAGS_COV = -lssl -lcrypto -lsqlite3 -lm -lpthread --coverage SRC_DIR = src BUILD_DIR = build TESTS_DIR = tests SOURCES = $(SRC_DIR)/main.c \ $(SRC_DIR)/buffer.c \ $(SRC_DIR)/logging.c \ $(SRC_DIR)/config.c \ $(SRC_DIR)/monitor.c \ $(SRC_DIR)/http.c \ $(SRC_DIR)/ssl_handler.c \ $(SRC_DIR)/connection.c \ $(SRC_DIR)/dashboard.c \ $(SRC_DIR)/rate_limit.c \ $(SRC_DIR)/auth.c \ $(SRC_DIR)/health_check.c \ $(SRC_DIR)/patch.c \ cJSON.c OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(SOURCES))) TARGET = rproxy TEST_SOURCES = $(TESTS_DIR)/test_main.c \ $(TESTS_DIR)/test_http.c \ $(TESTS_DIR)/test_buffer.c \ $(TESTS_DIR)/test_config.c \ $(TESTS_DIR)/test_routing.c \ $(TESTS_DIR)/test_host_rewrite.c \ $(TESTS_DIR)/test_http_helpers.c \ $(TESTS_DIR)/test_patch.c \ $(TESTS_DIR)/test_auth.c \ $(TESTS_DIR)/test_rate_limit.c TEST_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_SOURCES))) TEST_LIB_SOURCES = $(SRC_DIR)/buffer.c \ $(SRC_DIR)/logging.c \ $(SRC_DIR)/config.c \ $(SRC_DIR)/monitor.c \ $(SRC_DIR)/http.c \ $(SRC_DIR)/ssl_handler.c \ $(SRC_DIR)/connection.c \ $(SRC_DIR)/dashboard.c \ $(SRC_DIR)/rate_limit.c \ $(SRC_DIR)/auth.c \ $(SRC_DIR)/health_check.c \ $(SRC_DIR)/patch.c \ cJSON.c TEST_LIB_OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(TEST_LIB_SOURCES))) TEST_TARGET = rproxy_test MIN_COVERAGE = 70 COVERAGE_MODULES = auth.c buffer.c config.c http.c logging.c patch.c rate_limit.c .PHONY: all clean test legacy run coverage coverage-html all: $(BUILD_DIR) $(TARGET) $(BUILD_DIR): mkdir -p $(BUILD_DIR) $(TARGET): $(OBJECTS) $(CC) $(OBJECTS) -o $@ $(LDFLAGS) $(BUILD_DIR)/main.o: $(SRC_DIR)/main.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/buffer.o: $(SRC_DIR)/buffer.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/logging.o: $(SRC_DIR)/logging.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/config.o: $(SRC_DIR)/config.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/monitor.o: $(SRC_DIR)/monitor.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/http.o: $(SRC_DIR)/http.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/ssl_handler.o: $(SRC_DIR)/ssl_handler.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/connection.o: $(SRC_DIR)/connection.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/dashboard.o: $(SRC_DIR)/dashboard.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/rate_limit.o: $(SRC_DIR)/rate_limit.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/auth.o: $(SRC_DIR)/auth.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/health_check.o: $(SRC_DIR)/health_check.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/patch.o: $(SRC_DIR)/patch.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/cJSON.o: cJSON.c $(CC) $(CFLAGS) -c $< -o $@ $(BUILD_DIR)/test_main.o: $(TESTS_DIR)/test_main.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_http.o: $(TESTS_DIR)/test_http.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_buffer.o: $(TESTS_DIR)/test_buffer.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_config.o: $(TESTS_DIR)/test_config.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_routing.o: $(TESTS_DIR)/test_routing.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_host_rewrite.o: $(TESTS_DIR)/test_host_rewrite.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_http_helpers.o: $(TESTS_DIR)/test_http_helpers.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_patch.o: $(TESTS_DIR)/test_patch.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_auth.o: $(TESTS_DIR)/test_auth.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(BUILD_DIR)/test_rate_limit.o: $(TESTS_DIR)/test_rate_limit.c $(CC) $(CFLAGS) -I$(SRC_DIR) -c $< -o $@ $(TEST_TARGET): $(BUILD_DIR) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) $(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $@ $(LDFLAGS) test: $(TEST_TARGET) ./$(TEST_TARGET) legacy: rproxy.c cJSON.c cJSON.h $(CC) $(CFLAGS) rproxy.c cJSON.c -o rproxy_legacy $(LDFLAGS) run: $(TARGET) ./$(TARGET) coverage: clean mkdir -p $(BUILD_DIR) $(CC) $(CFLAGS_COV) -Isrc -c tests/test_main.c -o build/test_main.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_http.c -o build/test_http.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_buffer.c -o build/test_buffer.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_config.c -o build/test_config.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_routing.c -o build/test_routing.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_host_rewrite.c -o build/test_host_rewrite.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_http_helpers.c -o build/test_http_helpers.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_patch.c -o build/test_patch.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_auth.c -o build/test_auth.o $(CC) $(CFLAGS_COV) -Isrc -c tests/test_rate_limit.c -o build/test_rate_limit.o $(CC) $(CFLAGS_COV) -c src/buffer.c -o build/buffer.o $(CC) $(CFLAGS_COV) -c src/logging.c -o build/logging.o $(CC) $(CFLAGS_COV) -c src/config.c -o build/config.o $(CC) $(CFLAGS_COV) -c src/monitor.c -o build/monitor.o $(CC) $(CFLAGS_COV) -c src/http.c -o build/http.o $(CC) $(CFLAGS_COV) -c src/ssl_handler.c -o build/ssl_handler.o $(CC) $(CFLAGS_COV) -c src/connection.c -o build/connection.o $(CC) $(CFLAGS_COV) -c src/dashboard.c -o build/dashboard.o $(CC) $(CFLAGS_COV) -c src/rate_limit.c -o build/rate_limit.o $(CC) $(CFLAGS_COV) -c src/auth.c -o build/auth.o $(CC) $(CFLAGS_COV) -c src/health_check.c -o build/health_check.o $(CC) $(CFLAGS_COV) -c src/patch.c -o build/patch.o $(CC) $(CFLAGS_COV) -c cJSON.c -o build/cJSON.o $(CC) $(TEST_OBJECTS) $(TEST_LIB_OBJECTS) -o $(TEST_TARGET) $(LDFLAGS_COV) ./$(TEST_TARGET) @echo "" @echo "=== Coverage Report ===" @gcov -n build/*.gcno 2>/dev/null | grep -A 1 "^File.*src/" | grep -v "^--$$" || true @echo "" @echo "=== Coverage Check (minimum $(MIN_COVERAGE)%) ===" @echo "Checking modules: $(COVERAGE_MODULES)" @total_lines=0; covered_lines=0; \ for gcno in build/*.gcno; do \ output=$$(gcov -n "$$gcno" 2>/dev/null); \ file=$$(echo "$$output" | grep "^File.*src/" | head -1 | sed "s/File '\\(.*\\)'/\\1/"); \ if [ -n "$$file" ]; then \ basename=$$(basename "$$file"); \ is_tracked=0; \ for mod in $(COVERAGE_MODULES); do \ if [ "$$basename" = "$$mod" ]; then is_tracked=1; break; fi; \ done; \ if [ $$is_tracked -eq 1 ]; then \ lines=$$(echo "$$output" | grep "Lines executed:" | head -1 | sed 's/.*of \([0-9]*\)/\1/'); \ pct=$$(echo "$$output" | grep "Lines executed:" | head -1 | sed 's/Lines executed:\([0-9.]*\)%.*/\1/'); \ if [ -n "$$lines" ] && [ -n "$$pct" ]; then \ covered=$$(echo "$$pct * $$lines / 100" | bc -l | cut -d. -f1); \ covered=$${covered:-0}; \ total_lines=$$((total_lines + lines)); \ covered_lines=$$((covered_lines + covered)); \ echo " $$basename: $$pct% ($$covered/$$lines lines)"; \ fi; \ fi; \ fi; \ done; \ if [ $$total_lines -gt 0 ]; then \ avg=$$(echo "scale=2; $$covered_lines * 100 / $$total_lines" | bc -l); \ echo ""; \ echo "Total: $$covered_lines/$$total_lines lines covered ($$avg%)"; \ avg_int=$$(echo "$$avg" | cut -d. -f1); \ if [ $$avg_int -lt $(MIN_COVERAGE) ]; then \ echo "FAILED: Coverage $$avg% is below minimum $(MIN_COVERAGE)%"; \ exit 1; \ else \ echo "PASSED: Coverage $$avg% meets minimum $(MIN_COVERAGE)%"; \ fi; \ else \ echo "FAILED: No coverage data found"; \ exit 1; \ fi coverage-html: coverage mkdir -p coverage_report lcov --capture --directory build --output-file coverage_report/coverage.info --ignore-errors source 2>/dev/null || true genhtml coverage_report/coverage.info --output-directory coverage_report 2>/dev/null || echo "Install lcov for HTML reports: sudo apt install lcov" @echo "Coverage report generated in coverage_report/index.html" clean: rm -rf $(BUILD_DIR) $(TARGET) $(TEST_TARGET) rproxy_legacy *.gcov coverage_report