test coverage
This commit is contained in:
parent
6bbd39178a
commit
60dda17042
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@ build-*
|
|||||||
.cache
|
.cache
|
||||||
webserv.log
|
webserv.log
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
iwyu_results/
|
iwyu_results/
|
||||||
|
build_coverage/
|
||||||
154
CMakeLists.txt
154
CMakeLists.txt
@ -30,6 +30,8 @@ include_directories(
|
|||||||
|
|
||||||
|
|
||||||
# Build type options
|
# Build type options
|
||||||
|
option(ENABLE_COVERAGE "Enable code coverage" OFF)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
@ -45,6 +47,58 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
|||||||
|
|
||||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
# Coverage configuration
|
||||||
|
if(ENABLE_COVERAGE)
|
||||||
|
message(STATUS "Code coverage enabled")
|
||||||
|
|
||||||
|
# Find required tools
|
||||||
|
find_program(GCOV_PATH gcov)
|
||||||
|
find_program(LCOV_PATH lcov)
|
||||||
|
find_program(GENHTML_PATH genhtml)
|
||||||
|
find_program(GCOVR_PATH gcovr)
|
||||||
|
|
||||||
|
if(NOT GCOV_PATH)
|
||||||
|
message(FATAL_ERROR "gcov not found! Please install gcov. On Ubuntu: sudo apt-get install gcc")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check for coverage tools (prefer lcov, fallback to gcovr)
|
||||||
|
if(LCOV_PATH AND GENHTML_PATH)
|
||||||
|
message(STATUS "Found lcov and genhtml - using lcov for coverage reports")
|
||||||
|
set(COVERAGE_TOOL "lcov")
|
||||||
|
elseif(GCOVR_PATH)
|
||||||
|
message(STATUS "Found gcovr - using gcovr for coverage reports")
|
||||||
|
set(COVERAGE_TOOL "gcovr")
|
||||||
|
else()
|
||||||
|
message(STATUS "Coverage tools not found. Install with:")
|
||||||
|
message(STATUS " Ubuntu: sudo apt-get install lcov")
|
||||||
|
message(STATUS " Or: pip install gcovr")
|
||||||
|
message(STATUS "Coverage data will still be generated, but no HTML report")
|
||||||
|
set(COVERAGE_TOOL "none")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set coverage flags based on compiler
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
message(STATUS "Using GCC coverage flags")
|
||||||
|
set(COVERAGE_FLAGS "--coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline")
|
||||||
|
set(COVERAGE_LINK_FLAGS "--coverage")
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
message(STATUS "Using Clang coverage flags")
|
||||||
|
# Use --coverage for Clang to generate gcov-compatible data
|
||||||
|
set(COVERAGE_FLAGS "--coverage -fprofile-arcs -ftest-coverage")
|
||||||
|
set(COVERAGE_LINK_FLAGS "--coverage")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Code coverage only supported with GCC or Clang")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Apply coverage flags
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${COVERAGE_LINK_FLAGS}")
|
||||||
|
|
||||||
|
# Ensure debug information is available
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Set build flags for different build types
|
# Set build flags for different build types
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
message(STATUS "Debug build: adding debug flags")
|
message(STATUS "Debug build: adding debug flags")
|
||||||
@ -155,3 +209,103 @@ gtest_discover_tests(webserv_tests)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Coverage targets
|
||||||
|
if(ENABLE_COVERAGE AND BUILD_TESTS)
|
||||||
|
# Coverage cleanup target
|
||||||
|
add_custom_target(coverage_clean
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/coverage_report
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/coverage.info
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/coverage.xml
|
||||||
|
COMMENT "Cleaning coverage data"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(COVERAGE_TOOL STREQUAL "lcov")
|
||||||
|
# Main coverage target using lcov
|
||||||
|
add_custom_target(coverage
|
||||||
|
DEPENDS webserv_tests
|
||||||
|
|
||||||
|
# Cleanup previous coverage data
|
||||||
|
COMMAND ${LCOV_PATH} --directory . --zerocounters
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
||||||
|
|
||||||
|
# Capture coverage data
|
||||||
|
COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
|
||||||
|
|
||||||
|
# Remove system and test files from coverage
|
||||||
|
COMMAND ${LCOV_PATH} --remove coverage.info '/usr/*' '/opt/*' '*/tests/*' '*/test/*' '*/_deps/*' '*/build/*' --output-file coverage.info
|
||||||
|
|
||||||
|
# Generate HTML report
|
||||||
|
COMMAND ${GENHTML_PATH} coverage.info --output-directory coverage_report --title "Webserv Coverage Report" --show-details --legend
|
||||||
|
|
||||||
|
# Print summary
|
||||||
|
COMMAND ${LCOV_PATH} --summary coverage.info
|
||||||
|
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Generating code coverage report with lcov"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Coverage summary target
|
||||||
|
add_custom_target(coverage_summary
|
||||||
|
DEPENDS webserv_tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
||||||
|
COMMAND ${LCOV_PATH} --directory . --capture --output-file coverage.info
|
||||||
|
COMMAND ${LCOV_PATH} --remove coverage.info '/usr/*' '/opt/*' '*/tests/*' '*/test/*' '*/_deps/*' '*/build/*' --output-file coverage.info
|
||||||
|
COMMAND ${LCOV_PATH} --summary coverage.info
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Generating coverage summary"
|
||||||
|
)
|
||||||
|
|
||||||
|
elseif(COVERAGE_TOOL STREQUAL "gcovr")
|
||||||
|
# Coverage target using gcovr
|
||||||
|
add_custom_target(coverage
|
||||||
|
DEPENDS webserv_tests
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
||||||
|
|
||||||
|
# Generate HTML report
|
||||||
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*' --html --html-details -o coverage_report.html
|
||||||
|
|
||||||
|
# Generate XML report (for CI)
|
||||||
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*' --xml -o coverage.xml
|
||||||
|
|
||||||
|
# Print summary to console
|
||||||
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*'
|
||||||
|
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Generating code coverage report with gcovr"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Coverage summary target
|
||||||
|
add_custom_target(coverage_summary
|
||||||
|
DEPENDS webserv_tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
||||||
|
COMMAND ${GCOVR_PATH} --root ${CMAKE_SOURCE_DIR} --exclude-unreachable-branches --exclude '.*test.*' --exclude '.*_deps.*'
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Generating coverage summary"
|
||||||
|
)
|
||||||
|
|
||||||
|
else()
|
||||||
|
# Basic coverage target without report generation
|
||||||
|
add_custom_target(coverage
|
||||||
|
DEPENDS webserv_tests
|
||||||
|
COMMAND ${CMAKE_CTEST_COMMAND} --verbose
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "Coverage data generated. Install lcov or gcovr to generate HTML reports."
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Running tests with coverage enabled"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add coverage dependencies
|
||||||
|
if(TARGET coverage)
|
||||||
|
add_dependencies(coverage webserv_tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Coverage targets available:")
|
||||||
|
message(STATUS " make coverage - Run tests and generate coverage report")
|
||||||
|
message(STATUS " make coverage_summary - Run tests and show coverage summary")
|
||||||
|
message(STATUS " make coverage_clean - Clean coverage data")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|||||||
19
Makefile
19
Makefile
@ -86,5 +86,22 @@ test_build: release
|
|||||||
@echo "Building tests only..."
|
@echo "Building tests only..."
|
||||||
$(CMAKE_BUILD) $(BUILD_DIR) --target webserv_tests
|
$(CMAKE_BUILD) $(BUILD_DIR) --target webserv_tests
|
||||||
|
|
||||||
|
# Coverage targets
|
||||||
|
coverage:
|
||||||
|
@echo "Running coverage analysis..."
|
||||||
|
./coverage.sh
|
||||||
|
|
||||||
|
coverage_clean:
|
||||||
|
@echo "Cleaning coverage data..."
|
||||||
|
rm -rf build_coverage
|
||||||
|
|
||||||
|
# Manual coverage build (advanced users)
|
||||||
|
coverage_manual:
|
||||||
|
@echo "Building with coverage manually..."
|
||||||
|
@mkdir -p build_coverage
|
||||||
|
cd build_coverage && $(CMAKE) .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DENABLE_COVERAGE=ON $(CMAKE_FLAGS)
|
||||||
|
$(CMAKE_BUILD) build_coverage
|
||||||
|
cd build_coverage && $(CMAKE_BUILD) . --target coverage
|
||||||
|
|
||||||
# Mark targets as phony
|
# Mark targets as phony
|
||||||
.PHONY: all release debug asan run run_release run_debug run_asan clean fclean re test test_verbose test_build
|
.PHONY: all release debug asan run run_release run_debug run_asan clean fclean re test test_verbose test_build coverage coverage_clean coverage_manual
|
||||||
|
|||||||
106
coverage.sh
Executable file
106
coverage.sh
Executable file
@ -0,0 +1,106 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Webserv Code Coverage Script
|
||||||
|
# This script builds the project with coverage enabled and generates a coverage report
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$SCRIPT_DIR"
|
||||||
|
BUILD_DIR="$PROJECT_ROOT/build_coverage"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${BLUE}Webserv Code Coverage Generator${NC}"
|
||||||
|
echo "=================================="
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
echo -e "${YELLOW}Checking for required tools...${NC}"
|
||||||
|
|
||||||
|
# Check for CMake
|
||||||
|
if ! command -v cmake &> /dev/null; then
|
||||||
|
echo -e "${RED}Error: CMake not found. Please install CMake.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for compiler
|
||||||
|
if command -v g++ &> /dev/null; then
|
||||||
|
COMPILER="g++"
|
||||||
|
echo -e "${GREEN}Found compiler: $COMPILER${NC}"
|
||||||
|
elif command -v clang++ &> /dev/null; then
|
||||||
|
COMPILER="clang++"
|
||||||
|
echo -e "${GREEN}Found compiler: $COMPILER${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: No suitable C++ compiler found (g++ or clang++)${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for coverage tools
|
||||||
|
COVERAGE_TOOL=""
|
||||||
|
if command -v lcov &> /dev/null && command -v genhtml &> /dev/null; then
|
||||||
|
COVERAGE_TOOL="lcov"
|
||||||
|
echo -e "${GREEN}Found coverage tool: lcov${NC}"
|
||||||
|
elif command -v gcovr &> /dev/null; then
|
||||||
|
COVERAGE_TOOL="gcovr"
|
||||||
|
echo -e "${GREEN}Found coverage tool: gcovr${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: No coverage report tools found.${NC}"
|
||||||
|
echo -e "${YELLOW}Install with: sudo apt-get install lcov${NC}"
|
||||||
|
echo -e "${YELLOW}Or: pip install gcovr${NC}"
|
||||||
|
echo -e "${YELLOW}Coverage data will still be generated.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean previous build
|
||||||
|
echo -e "${YELLOW}Cleaning previous coverage build...${NC}"
|
||||||
|
rm -rf "$BUILD_DIR"
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
|
||||||
|
# Configure with coverage
|
||||||
|
echo -e "${YELLOW}Configuring build with coverage enabled...${NC}"
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_BUILD_TYPE=Debug \
|
||||||
|
-DBUILD_TESTS=ON \
|
||||||
|
-DENABLE_COVERAGE=ON \
|
||||||
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
|
||||||
|
# Build
|
||||||
|
echo -e "${YELLOW}Building project...${NC}"
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
echo -e "${YELLOW}Running tests with coverage...${NC}"
|
||||||
|
if make coverage; then
|
||||||
|
echo -e "${GREEN}Coverage report generated successfully!${NC}"
|
||||||
|
|
||||||
|
# Show where to find the report
|
||||||
|
if [ "$COVERAGE_TOOL" = "lcov" ]; then
|
||||||
|
REPORT_PATH="$BUILD_DIR/coverage_report/index.html"
|
||||||
|
if [ -f "$REPORT_PATH" ]; then
|
||||||
|
echo -e "${GREEN}Coverage report available at: $REPORT_PATH${NC}"
|
||||||
|
echo -e "${BLUE}Open with: firefox $REPORT_PATH${NC}"
|
||||||
|
fi
|
||||||
|
elif [ "$COVERAGE_TOOL" = "gcovr" ]; then
|
||||||
|
REPORT_PATH="$BUILD_DIR/coverage_report.html"
|
||||||
|
if [ -f "$REPORT_PATH" ]; then
|
||||||
|
echo -e "${GREEN}Coverage report available at: $REPORT_PATH${NC}"
|
||||||
|
echo -e "${BLUE}Open with: firefox $REPORT_PATH${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show summary
|
||||||
|
echo -e "${YELLOW}Coverage Summary:${NC}"
|
||||||
|
make coverage_summary 2>/dev/null || echo "Summary not available"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo -e "${RED}Coverage generation failed!${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}Coverage analysis complete!${NC}"
|
||||||
Loading…
Reference in New Issue
Block a user