# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------

set(PTO_Fwk_UTestCaseLibraries         "" CACHE INTERNAL "" FORCE)     # UTest 各模块 用例实现二进制
set(PTO_Fwk_UTestCaseLkLibraries       "" CACHE INTERNAL "" FORCE)     # UTest 各模块 额外 Link 二进制
set(PTO_Fwk_UTestCaseLdLibrariesExt    "" CACHE INTERNAL "" FORCE)     # UTest 各模块 额外 Load 二进制

# UTest 添加测试用例二进制库
#[[
Parameters:
  one_value_keywords:
      TARGET                      : [Required] 具体测试用例二进制库名称
  multi_value_keywords:
      SOURCES                     : [Required] 编译源码
      PRIVATE_INCLUDE_DIRECTORIES : [Optional] 头文件搜索路径(PRIVATE)
      LINK_LIBRARIES              : [Optional] 链接库(PUBLIC), 仅会在最终编译出可执行文件时链接;
      LD_LIBRARIES_EXT            : [Optional] 需要在执行时将所在路径配置到环境变量 LD_LIBRARY_PATH 中的 Libraries
Attention:
    1. 一般 LD_LIBRARIES_EXT 内配置的二进制, 在正常 source CANN 包环境变量后, LD_LIBRARY_PATH 内也应包含其所在路径;
]]
function(PTO_Fwk_UTest_AddCaseLib)
    cmake_parse_arguments(
            ARG
            ""
            "TARGET"
            "SOURCES;PRIVATE_INCLUDE_DIRECTORIES;LINK_LIBRARIES;LD_LIBRARIES_EXT"
            ""
            ${ARGN}
    )
    add_library(${ARG_TARGET} STATIC)
    target_sources(${ARG_TARGET} PRIVATE ${ARG_SOURCES})
    target_include_directories(${ARG_TARGET} PRIVATE ${ARG_PRIVATE_INCLUDE_DIRECTORIES})
    target_link_libraries(${ARG_TARGET}
            PRIVATE
                ${PTO_Fwk_UTestNamePrefix}_utils
                GTest::gtest
                json
                c_sec
    )
    set(PTO_Fwk_UTestCaseLibraries       ${PTO_Fwk_UTestCaseLibraries}       ${ARG_TARGET}            CACHE INTERNAL "" FORCE)
    set(PTO_Fwk_UTestCaseLkLibrariesExt  ${PTO_Fwk_UTestCaseLkLibrariesExt}  ${ARG_LINK_LIBRARIES}    CACHE INTERNAL "" FORCE)
    set(PTO_Fwk_UTestCaseLdLibrariesExt  ${PTO_Fwk_UTestCaseLdLibrariesExt}  ${ARG_LD_LIBRARIES_EXT}  CACHE INTERNAL "" FORCE)
endfunction()

# UTest 执行可执行程序
#[[
Parameters:
  one_value_keywords:
      TARGET             : [Required] 用于指定具体 GTest 可执行目标, 用例会在该目标编译完成后(POST_BUILD)启动执行
  multi_value_keywords:
      LD_LIBRARIES_EXT   : [Optional] 需要在执行时将所在路径配置到环境变量 LD_LIBRARY_PATH 中的 Libraries
      ENV_LINES_EXT      : [Optional] 需要额外配置的环境变量, 按照 "K=V" 格式组织
      GTEST_FILTER_LIST  : [Optional] GTestFilter 配置, Filter 间以 ';' 分割
Attention:
    1. 可以多次调用本函数以添加多个'执行任务'; 单次调用本函数时, 可以通过在 GTEST_FILTER_LIST 中配置多个过滤条件('gtest_filter') 以实现执行多用例;
]]
function(PTO_Fwk_UTest_RunExe)
    cmake_parse_arguments(
            ARG
            ""
            "TARGET"
            "LD_LIBRARIES_EXT;ENV_LINES_EXT;GTEST_FILTER_LIST"
            ""
            ${ARGN}
    )
    if (ENABLE_TESTS_EXECUTE)
        # 命令行参数处理
        PTO_Fwk_GTest_RunExe_GetPreExecSetup(PyCmdSetup PyEnvLines BashCmdSetup
                TARGET              ${ARG_TARGET}
                ENV_LINES_EXT       ${ARG_ENV_LINES_EXT}
                LD_LIBRARIES_EXT    ${ARG_LD_LIBRARIES_EXT}
        )
        # 执行流程
        list(LENGTH ARG_GTEST_FILTER_LIST GtestFilterListLen)
        string(REPLACE ";" ":" GtestFilterStr "${ARG_GTEST_FILTER_LIST}")
        message(STATUS "Run GTest(${ARG_TARGET}), XSAN(ASAN:${ENABLE_ASAN} UBSAN:${ENABLE_UBSAN}), GTestFilter(${GtestFilterListLen})=${GtestFilterStr}")
        set(Comment "Run GTest(${ARG_TARGET}), XSAN(ASAN:${ENABLE_ASAN} UBSAN:${ENABLE_UBSAN})")

        if (ARG_GTEST_FILTER_LIST)
            # 使能并行执行
            set(_File $<TARGET_FILE:${ARG_TARGET}>)
            set(_Args "-t=${_File}" "--cases=${GtestFilterStr}" "--halt_on_error")
            if (PyEnvLines)
                list(APPEND _Args "--env" "${PyEnvLines}")
            endif ()
            get_filename_component(ParallelPy    "${PTO_FWK_SRC_ROOT}/cmake/scripts/utest_accelerate.py" REALPATH)
            get_filename_component(ParallelPyCwd "${PTO_FWK_SRC_ROOT}/cmake/scripts" REALPATH)
            add_custom_command(
                    TARGET ${ARG_TARGET} POST_BUILD
                    COMMAND ${PyCmdSetup} ${Python3_EXECUTABLE} ${ParallelPy} ARGS ${_Args}
                    COMMENT "${Comment} With Parallel Execute Accelerate"
                    WORKING_DIRECTORY ${ParallelPyCwd}
            )
        else ()
            add_custom_command(
                    TARGET ${ARG_TARGET} POST_BUILD
                    COMMAND ${BashCmdSetup} ./${ARG_TARGET}
                    COMMENT "${Comment}"
                    WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
            )
        endif ()
    endif ()
endfunction()

# UTest 添加并执行可执行程序
#[[
Parameters:
  one_value_keywords:
      TARGET                        : [Required] 用于指定具体 GTest 可执行目标, 用例会在该目标编译完成后(POST_BUILD)启动执行
Attention:
    1. 执行本函数后, 会产生名称为 TARGET 内容指定的构建目标, 外部可根据该目标名设置其他 custom_command 或处理依赖关系;
    2. 串行执行场景下 TARGET 内容指定最终 executable 名称;
    3. 并行执行场景下 TARGET 内容指定中间 custom_target 名称, 并行执行的各 executable 会依赖该 custom_target;
]]
function(PTO_Fwk_UTest_AddExe_RunExe)
    cmake_parse_arguments(
            ARG
            ""
            "TARGET"
            ""
            ""
            ${ARGN}
    )
    set(_Sources ${CMAKE_CURRENT_BINARY_DIR}/${PTO_Fwk_UTestNamePrefix}_main_stub.cpp)
    execute_process(COMMAND touch ${_Sources})

    # 默认全部执行
    set(GTestFilterList "*")
    # 支持由 ENABLE_UTEST 传入指定的 Filter
    if (NOT "${ENABLE_UTEST}" STREQUAL "ON")
        set(GTestFilterList ${ENABLE_UTEST})
        string(REPLACE ":" ";" GTestFilterList "${GTestFilterList}")
    endif ()

    list(REMOVE_DUPLICATES PTO_Fwk_UTestCaseLibraries)
    list(REMOVE_DUPLICATES PTO_Fwk_UTestCaseLkLibrariesExt)
    list(REMOVE_DUPLICATES PTO_Fwk_UTestCaseLdLibrariesExt)
    list(REMOVE_DUPLICATES GTestFilterList)

    if (NOT "$ENV{GTEST_START}" STREQUAL "")
        list(FIND GTestFilterList $ENV{GTEST_START} idx)
        if (NOT ${idx} EQUAL -1)
            list(SUBLIST GTestFilterList ${idx} -1 GTestFilterList)
        endif ()
    endif ()

    set(_PrivateLinkLibraries
            ${PTO_Fwk_UTestNamePrefix}_utils
            # 基本依赖
            # Interface 内 HostMachine 存在 dlopen 逻辑, 此处增加对应库连接, 触发相关 so 被添加到可执行程序依赖中
            tile_fwk_utils
            tile_fwk_adapter
            tile_fwk_cann_host_runtime
            tile_fwk_platform
            tile_fwk_interface
            tile_fwk_codegen
            tile_fwk_compiler
            # 用例特殊依赖
            ${PTO_Fwk_UTestCaseLkLibrariesExt}
            ${PTO_Fwk_UTestCaseLibraries}
    )
    list(REMOVE_DUPLICATES _PrivateLinkLibraries)
    PTO_Fwk_GTest_AddExe(
            TARGET                      ${ARG_TARGET}
            SOURCES                     ${_Sources}
            PRIVATE_INCLUDE_DIRECTORIES ${ARG_PRIVATE_INCLUDE_DIRECTORIES}
            PRIVATE_LINK_LIBRARIES      ${_PrivateLinkLibraries}
    )
    PTO_Fwk_UTest_RunExe(
            TARGET              ${ARG_TARGET}
            LD_LIBRARIES_EXT    ${PTO_Fwk_UTestCaseLdLibrariesExt}
            GTEST_FILTER_LIST   ${GTestFilterList}
    )

    # 生成覆盖率
    PTO_Fwk_GTest_GenerateCoverage(TARGET ${ARG_TARGET})
endfunction()

function(PTO_Fwk_UTest_AddModuleDir)
    cmake_parse_arguments(
            ARG
            ""
            "DIR"
            ""
            ${ARGN}
    )
    PTO_Fwk_GTest_AddModuleDir(
            MARK "UTest"
            DIR ${ARG_DIR}
            MODULE_LIST ${ENABLE_UTEST_MODULE}
    )
endfunction()