find_program(GCOV_PATH gcov)
find_program(LCOV_PATH lcov)
find_program(GENHTML_PATH genhtml)
find_program(GCOVR_PATH gcovr)
find_program(POWERSHELL_PATH powershell.exe)
find_program(Python3 python3)

if (CMAKE_CROSSCOMPILING)
    set(GCOV_PATH "$ENV{GCOV_PATH}")
endif()

function(setup_coverage)
    if (NOT GCOV_PATH)
        message(FATAL_ERROR "gcov not found")
    else()
        message(STATUS "gcov found at ${GCOV_PATH}")
    endif()

    set(COVERAGE_COMPILER_FLAGS "--coverage")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
    link_libraries(gcov)
    set(COVERAGE_DIR ${CMAKE_BINARY_DIR}/coverage)

    execute_process(COMMAND mkdir -p ${CMAKE_BINARY_DIR}/coverage
        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    )

    if(LCOV_PATH)
        message(STATUS "lcov found at ${LCOV_PATH}")
        set(HTML_PATH "${COVERAGE_DIR}/index.html")
        add_custom_target(coverage
            COMMAND ${LCOV_PATH} --rc lcov_branch_coverage=1 --capture --directory . --output-file ${COVERAGE_DIR}/${TEST_MODULE}_coverage.info >> ${COVERAGE_DIR}/${TEST_MODULE}_coverage.log
            COMMAND ${LCOV_PATH} --rc lcov_branch_coverage=1 --remove ${COVERAGE_DIR}/${TEST_MODULE}_coverage.info '*/third_party/*' '*/tests/*' '*/proto/*' -o ${COVERAGE_DIR}/${TEST_MODULE}_coverage.info >> ${COVERAGE_DIR}/${TEST_MODULE}_coverage.log
            COMMAND ${GENHTML_PATH} ${COVERAGE_DIR}/${TEST_MODULE}_coverage.info -o ${COVERAGE_DIR} --rc lcov_branch_coverage=1 >> ${COVERAGE_DIR}/${TEST_MODULE}_coverage.log
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        )
    elseif(GCOVR_PATH)
        message(STATUS "gcovr found at ${GCOVR_PATH}")
        set(HTML_PATH "${COVERAGE_DIR}/index.html")
        add_custom_target(coverage
            COMMAND ${GCOVR_PATH} -f src --gcov-exclude '.+log.+' --html-details ${HTML_PATH}
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        )
    endif()

    if (POWSHELL_PATH)
        message(STATUS "powershell found at ${POWERSHELL_PATH}")
        add_custom_command(TARGET coverage POST_BUILD
            COMMAND ${POWERSHELL_PATH} /c start build/coverage/index.html;
            COMMENT "Open ${HTML_PATH} in your browser to view the coverage report."
            WORKING_DIRECTORY ${{PROJECT_SOURCE_DIR}}
        )
    else()
        add_custom_command(TARGET coverage POST_BUILD
            COMMAND ;
            COMMENT "Open ${HTML_PATH} in your browser to view the coverage report."
        )
    endif()
endfunction()

# 添加明确的分隔符(如空格)
function(build_test module type list_libraries list_includes)
    set(TEST_BINARY ${CMAKE_PROJECT_NAME}_${module}_${type})
    file(GLOB_RECURSE TEST_SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/*.cc
        ${CMAKE_CURRENT_SOURCE_DIR}/*.h
    )
    add_executable(${TEST_BINARY} ${TEST_SOURCES})
    # 控制权限放开,private函数可测试
    target_compile_options(${TEST_BINARY} PRIVATE -fno-access-control)
    set_target_properties(${TEST_BINARY} PROPERTIES LINK_FLAGS "-Wl,--as-needed")
    target_link_options(${TEST_BINARY} PRIVATE -rdynamic)

    set(INCLUDES "")
    foreach(include_folder ${list_includes})
        list(APPEND INCLUDES ${include_folder})
    endforeach()
    message("INCLUDES: ${INCLUDES}")

    target_include_directories(${TEST_BINARY} PRIVATE
        ${THIRD_PARTY_OUTPUT_DIR}/googletest/include
        ${THIRD_PARTY_OUTPUT_DIR}/mockcpp/include
        ${INCLUDES}
    )

    set(LIBRARIES "")
    foreach(lib ${list_libraries})
        list(APPEND LIBRARIES ${lib})
    endforeach()
    message("LIBRARIES: ${LIBRARIES}")

    target_link_directories(${TEST_BINARY} PRIVATE
        ${THIRD_PARTY_OUTPUT_DIR}/gtest/lib
        ${THIRD_PARTY_OUTPUT_DIR}/mockcpp/lib
    )
    file(GLOB ABSL_LIBS
        ${THIRD_PARTY_OUTPUT_DIR}/abseil-cpp/lib/libabsl_*.so
    )
    file(GLOB CARES_LIBS
        ${THIRD_PARTY_OUTPUT_DIR}/cares/lib/libcares.so*
        ${THIRD_PARTY_OUTPUT_DIR}/cares/lib/libcares.a
    )
    target_link_libraries(${TEST_BINARY} PRIVATE ${ABSL_LIBS} ${CARES_LIBS})
    target_link_libraries(${TEST_BINARY} PUBLIC
        gtest
        gtest_main
        pthread
        mockcpp
        ${LIBRARIES}
    )

    add_test(NAME ${module}_${type} COMMAND ${TEST_BINARY} --gtest_color=yes --gtest_brief=0 --gtest_output=xml:${CMAKE_BINARY_DIR}/dlt_info/${module}_${type}_detail.xml --gtest_break_on_failure)

    string(JOIN ":" LD_LIBRARY_PATH_STR
        ${THIRD_PARTY_OUTPUT_DIR}/prometheus-cpp/lib
        ${THIRD_PARTY_OUTPUT_DIR}/libboundscheck/lib
        ${THIRD_PARTY_OUTPUT_DIR}/grpc/lib
        ${THIRD_PARTY_OUTPUT_DIR}/re2/lib
        ${THIRD_PARTY_OUTPUT_DIR}/zlib/lib
        ${THIRD_PARTY_OUTPUT_DIR}/protobuf/lib
        ${THIRD_PARTY_OUTPUT_DIR}/abseil-cpp/lib
        ${THIRD_PARTY_OUTPUT_DIR}/cares/lib
        ${THIRD_PARTY_OUTPUT_DIR}/openssl/lib
        ${THIRD_PARTY_OUTPUT_DIR}/boost/lib
        ${CMAKE_SOURCE_DIR}/src/utils/lib
        ${CMAKE_SOURCE_DIR}/src/utils/log/lib
        ${CMAKE_BINARY_DIR}/src/utils/system_log/
        ${CMAKE_BINARY_DIR}/src/server/tokenizer
        ${CMAKE_BINARY_DIR}/src/server/endpoint
        ${CMAKE_BINARY_DIR}/src/server/infer_instances
        ${CMAKE_BINARY_DIR}/src/config_manager
        ${CMAKE_BINARY_DIR}/src/engine
        ${CMAKE_BINARY_DIR}/src/executor
        ${CMAKE_BINARY_DIR}/src/block_manager
        ${CMAKE_BINARY_DIR}/src/scheduler
        ${CMAKE_BINARY_DIR}/src/sequence
        ${CMAKE_BINARY_DIR}/src/load_balance
        ${CMAKE_BINARY_DIR}/src/llm_manager_v2
        ${CMAKE_BINARY_DIR}/src/llm_manager
        ${CMAKE_BINARY_DIR}/src/llm_manager/python_api
        ${PYTORCH_INSTALL_PATH}/lib
        ${PYTORCH_EXTRA_LIB_SO}
    )
    set_tests_properties(${module}_${type} PROPERTIES
        ENVIRONMENT "LD_LIBRARY_PATH=${LD_LIBRARY_PATH_STR}"
    )
endfunction()