已合并
rdv-support #931
sujunwei3创建于 6月1日
rdv-support #931
已合并
共 4 个文件变更+1509-0
| @@ -57,6 +57,7 @@ option(OP_GRAPH_UT "Enable graph ut" OFF) | |||
| 57 | option(OP_KERNEL_UT "Enable kernel ut" OFF) | 57 | option(OP_KERNEL_UT "Enable kernel ut" OFF) |
| 58 | option(OP_KERNEL_AICPU_UT "Enable aicpu kernel ut" OFF) | 58 | option(OP_KERNEL_AICPU_UT "Enable aicpu kernel ut" OFF) |
| 59 | option(UT_TEST_ALL "Enable all ut" OFF) | 59 | option(UT_TEST_ALL "Enable all ut" OFF) |
| 60 | +option(DOWNLOAD_OPS_TEST_KIT "Download ops-test-kit repository" OFF) | ||
| 60 | set(BISHENG_FLAGS "" CACHE STRING "bisheng compiler flags") | 61 | set(BISHENG_FLAGS "" CACHE STRING "bisheng compiler flags") |
| 61 | set(KERNEL_TEMPLATE_INPUT "" CACHE STRING " kernel template input") | 62 | set(KERNEL_TEMPLATE_INPUT "" CACHE STRING " kernel template input") |
| 62 | set(BUILD_MODE "" CACHE STRING "build mode -O0/O1/O2/O3") | 63 | set(BUILD_MODE "" CACHE STRING "build mode -O0/O1/O2/O3") |
| @@ -110,6 +111,9 @@ endif() | |||
| 110 | # Cmake Compile配置 | 111 | # Cmake Compile配置 |
| 111 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "c++17 is needed for this project") | 112 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "c++17 is needed for this project") |
| 112 | include(cmake/third_party/opbase.cmake) # 放在dependencies.cmake前面 | 113 | include(cmake/third_party/opbase.cmake) # 放在dependencies.cmake前面 |
| 114 | +if(DOWNLOAD_OPS_TEST_KIT) | ||
| 115 | + include(cmake/third_party/ops_test_kit.cmake) | ||
| 116 | +endif() | ||
| 113 | include(cmake/dependencies.cmake) | 117 | include(cmake/dependencies.cmake) |
| 114 | include(cmake/variables.cmake) | 118 | include(cmake/variables.cmake) |
| 115 | include(cmake/opbuild.cmake) | 119 | include(cmake/opbuild.cmake) |
| @@ -0,0 +1,57 @@ | |||
| 1 | +# ---------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------- | ||
| 10 | +set(OPS_TEST_KIT_TAG_ID master) | ||
| 11 | +set(OPS_TEST_KIT_PKG ops-test-kit.tar.gz) | ||
| 12 | + | ||
| 13 | +if(EXISTS "${PROJECT_SOURCE_DIR}/build/third_party/ops-test-kit") | ||
| 14 | + get_filename_component(OPS_TEST_KIT_SOURCE_PATH | ||
| 15 | + ${PROJECT_SOURCE_DIR}/build/third_party/ops-test-kit REALPATH) | ||
| 16 | + message(STATUS "Find ops-test-kit source dir: ${OPS_TEST_KIT_SOURCE_PATH}") | ||
| 17 | +elseif(EXISTS "${CANN_3RD_LIB_PATH}/ops-test-kit") | ||
| 18 | + get_filename_component(OPS_TEST_KIT_SOURCE_PATH | ||
| 19 | + ${CANN_3RD_LIB_PATH}/ops-test-kit REALPATH) | ||
| 20 | + message(STATUS "Find ops-test-kit source dir: ${OPS_TEST_KIT_SOURCE_PATH}") | ||
| 21 | +else() | ||
| 22 | + if(EXISTS "${CANN_3RD_LIB_PATH}/pkg/${OPS_TEST_KIT_PKG}") | ||
| 23 | + set(OPS_TEST_KIT_URL "file://${CANN_3RD_LIB_PATH}/pkg/${OPS_TEST_KIT_PKG}") | ||
| 24 | + message(STATUS "[ThirdPartyLib][ops-test-kit] found in ${OPS_TEST_KIT_URL}.") | ||
| 25 | + include(FetchContent) | ||
| 26 | + FetchContent_Declare( | ||
| 27 | + ops_test_kit | ||
| 28 | + URL ${OPS_TEST_KIT_URL} | ||
| 29 | + SOURCE_DIR ${PROJECT_SOURCE_DIR}/build/third_party/ops-test-kit | ||
| 30 | + ) | ||
| 31 | + else() | ||
| 32 | + execute_process( | ||
| 33 | + COMMAND git remote get-url origin | ||
| 34 | + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| 35 | + OUTPUT_VARIABLE GIT_REMOTE_URL | ||
| 36 | + OUTPUT_STRIP_TRAILING_WHITESPACE | ||
| 37 | + ERROR_QUIET | ||
| 38 | + ) | ||
| 39 | + if(GIT_REMOTE_URL MATCHES "^git@|^ssh://") | ||
| 40 | + set(OPS_TEST_KIT_GIT_URL "git@gitcode.com:cann/ops-test-kit.git") | ||
| 41 | + message(STATUS "[ThirdPartyLib][ops-test-kit] using SSH protocol: ${OPS_TEST_KIT_GIT_URL}") | ||
| 42 | + else() | ||
| 43 | + set(OPS_TEST_KIT_GIT_URL "https://gitcode.com/cann/ops-test-kit.git") | ||
| 44 | + message(STATUS "[ThirdPartyLib][ops-test-kit] using HTTPS protocol: ${OPS_TEST_KIT_GIT_URL}") | ||
| 45 | + endif() | ||
| 46 | + include(FetchContent) | ||
| 47 | + FetchContent_Declare( | ||
| 48 | + ops_test_kit | ||
| 49 | + GIT_REPOSITORY ${OPS_TEST_KIT_GIT_URL} | ||
| 50 | + GIT_TAG ${OPS_TEST_KIT_TAG_ID} | ||
| 51 | + GIT_PROGRESS TRUE | ||
| 52 | + SOURCE_DIR ${PROJECT_SOURCE_DIR}/build/third_party/ops-test-kit | ||
| 53 | + ) | ||
| 54 | + endif() | ||
| 55 | + FetchContent_Populate(ops_test_kit) | ||
| 56 | + set(OPS_TEST_KIT_SOURCE_PATH ${PROJECT_SOURCE_DIR}/build/third_party/ops-test-kit) | ||
| 57 | +endif() | ||
| @@ -0,0 +1,819 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 3 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 4 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 5 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 6 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 7 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 8 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 9 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 10 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 11 | + | ||
| 12 | +set -euo pipefail | ||
| 13 | + | ||
| 14 | +declare -A SOC_TO_ST_ARCH | ||
| 15 | +SOC_TO_ST_ARCH=(["ascend910b"]="arch22" ["ascend950"]="arch35") | ||
| 16 | + | ||
| 17 | +dotted_line="----------------------------------------------------------------" | ||
| 18 | +print_msg() { | ||
| 19 | + local msg="$1" | ||
| 20 | + local date_time | ||
| 21 | + date_time=$(date +%Y-%m-%d/%H.%M.%S) | ||
| 22 | + echo "[INFO]${date_time}: ${msg}" >&2 | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +print_error() { | ||
| 26 | + echo >&2 | ||
| 27 | + echo "${dotted_line}" >&2 | ||
| 28 | + local msg="$1" | ||
| 29 | + echo -e "\033[31m[ERROR] ${msg}\033[0m" >&2 | ||
| 30 | + echo "${dotted_line}" >&2 | ||
| 31 | + echo >&2 | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +print_success() { | ||
| 35 | + echo >&2 | ||
| 36 | + echo "${dotted_line}" >&2 | ||
| 37 | + local msg="$1" | ||
| 38 | + echo -e "\033[32m[SUCCESS] ${msg}\033[0m" >&2 | ||
| 39 | + echo "${dotted_line}" >&2 | ||
| 40 | + echo >&2 | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +print_warning() { | ||
| 44 | + echo >&2 | ||
| 45 | + echo "${dotted_line}" >&2 | ||
| 46 | + local msg="$1" | ||
| 47 | + echo -e "\033[33m[WARNING] ${msg}\033[0m" >&2 | ||
| 48 | + echo "${dotted_line}" >&2 | ||
| 49 | + echo >&2 | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +get_op_categories() { | ||
| 53 | + local cmake_file="${framework_path}/cmake/variables.cmake" | ||
| 54 | + local categories="" | ||
| 55 | + | ||
| 56 | + if [[ -f "${cmake_file}" ]]; then | ||
| 57 | + categories=$(grep "OP_CATEGORY_LIST" "${cmake_file}" | \ | ||
| 58 | + sed -n 's/set(OP_CATEGORY_LIST "\(.*\)")/\1/p' | \ | ||
| 59 | + tr -d '"') | ||
| 60 | + else | ||
| 61 | + categories="image objdetect" | ||
| 62 | + fi | ||
| 63 | + | ||
| 64 | + categories="${categories} common" | ||
| 65 | + echo "${categories}" | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +extract_op_from_path() { | ||
| 69 | + local file_path="$1" | ||
| 70 | + local op_categories="$2" | ||
| 71 | + local op_name="" | ||
| 72 | + | ||
| 73 | + local rel_path="${file_path#${framework_path}/}" | ||
| 74 | + local parts=(${rel_path//\// }) | ||
| 75 | + | ||
| 76 | + if [[ ${#parts[@]} -ge 2 ]]; then | ||
| 77 | + local first_dir="${parts[0]}" | ||
| 78 | + local second_dir="${parts[1]}" | ||
| 79 | + | ||
| 80 | + local is_category=0 | ||
| 81 | + for cat in ${op_categories}; do | ||
| 82 | + if [[ "${first_dir}" == "${cat}" ]]; then | ||
| 83 | + is_category=1 | ||
| 84 | + break | ||
| 85 | + fi | ||
| 86 | + done | ||
| 87 | + | ||
| 88 | + if [[ ${is_category} -eq 1 ]]; then | ||
| 89 | + if [[ "${second_dir}" == "common" ]]; then | ||
| 90 | + op_name="${first_dir}.common" | ||
| 91 | + else | ||
| 92 | + op_name="${second_dir}" | ||
| 93 | + fi | ||
| 94 | + elif [[ "${first_dir}" == "experimental" && ${#parts[@]} -ge 3 ]]; then | ||
| 95 | + local exp_type="${parts[1]}" | ||
| 96 | + local exp_name="${parts[2]}" | ||
| 97 | + for cat in ${op_categories}; do | ||
| 98 | + if [[ "${exp_type}" == "${cat}" ]]; then | ||
| 99 | + if [[ "${exp_name}" == "common" ]]; then | ||
| 100 | + op_name="${exp_type}.common" | ||
| 101 | + else | ||
| 102 | + op_name="${exp_name}" | ||
| 103 | + fi | ||
| 104 | + break | ||
| 105 | + fi | ||
| 106 | + done | ||
| 107 | + fi | ||
| 108 | + fi | ||
| 109 | + | ||
| 110 | + if [[ -n "${op_name}" ]]; then | ||
| 111 | + echo "${op_name}" | ||
| 112 | + fi | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +parse_ops_from_filelist() { | ||
| 116 | + local pr_filelist="$1" | ||
| 117 | + | ||
| 118 | + if [[ ! -f "${pr_filelist}" ]]; then | ||
| 119 | + print_error "pr_filelist not found: ${pr_filelist}" | ||
| 120 | + return 1 | ||
| 121 | + fi | ||
| 122 | + | ||
| 123 | + local op_categories=$(get_op_categories) | ||
| 124 | + local changed_files=$(cat "${pr_filelist}" | grep -v '^$' | grep -v '^#' || echo "") | ||
| 125 | + | ||
| 126 | + if [[ -z "${changed_files}" ]]; then | ||
| 127 | + print_msg "No changed files in pr_filelist" | ||
| 128 | + return 0 | ||
| 129 | + fi | ||
| 130 | + | ||
| 131 | + local ops_set="" | ||
| 132 | + while IFS= read -r file_line; do | ||
| 133 | + [[ -z "${file_line}" ]] && continue | ||
| 134 | + file_line=$(echo "${file_line}" | sed 's/^[MADRC]\t//') | ||
| 135 | + local op_name=$(extract_op_from_path "${file_line}" "${op_categories}") | ||
| 136 | + if [[ -n "${op_name}" ]]; then | ||
| 137 | + if [[ -z "${ops_set}" ]]; then | ||
| 138 | + ops_set="${op_name}" | ||
| 139 | + elif [[ ",${ops_set}," != *",${op_name},"* ]]; then | ||
| 140 | + ops_set="${ops_set},${op_name}" | ||
| 141 | + fi | ||
| 142 | + fi | ||
| 143 | + done <<< "${changed_files}" | ||
| 144 | + | ||
| 145 | + echo "${ops_set}" | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +merge_ops_lists() { | ||
| 149 | + local list1="$1" | ||
| 150 | + local list2="$2" | ||
| 151 | + local merged="" | ||
| 152 | + | ||
| 153 | + for op in ${list1//,/ }; do | ||
| 154 | + if [[ -z "${merged}" ]]; then | ||
| 155 | + merged="${op}" | ||
| 156 | + elif [[ ",${merged}," != *",${op},"* ]]; then | ||
| 157 | + merged="${merged},${op}" | ||
| 158 | + fi | ||
| 159 | + done | ||
| 160 | + | ||
| 161 | + for op in ${list2//,/ }; do | ||
| 162 | + if [[ -z "${merged}" ]]; then | ||
| 163 | + merged="${op}" | ||
| 164 | + elif [[ ",${merged}," != *",${op},"* ]]; then | ||
| 165 | + merged="${merged},${op}" | ||
| 166 | + fi | ||
| 167 | + done | ||
| 168 | + | ||
| 169 | + echo "${merged}" | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +usage() { | ||
| 173 | + echo "Usage: bash ops_st_test.sh [--soc_version=ascend950] [--ops=op1,op2,op3] [--test_type=kernel,aclnn,e2e] [--pr_filelist=pr_filelist.txt]" | ||
| 174 | + echo " bash ops_st_test.sh pr_filelist.txt" | ||
| 175 | + echo "Options:" | ||
| 176 | + echo " --soc_version (Optional) Specify soc version. Supported: ascend910b, ascend950. If not specified, auto-detect via 'asys info -r=status'." | ||
| 177 | + echo " --ops (Optional) Specify operators to test (comma-separated). If not specified, extract from git diff." | ||
| 178 | + echo " --test_type (Optional) Specify test types to run (comma-separated). Supported: kernel, aclnn, e2e. Default: all types." | ||
| 179 | + echo " --pr_filelist (Optional) Path to file containing list of changed files (one per line). If not specified, extract from git diff." | ||
| 180 | + echo " --case_path (Optional) Custom base path for test cases. If specified, st_path will be {case_path}/${op_type}/${op_name}" | ||
| 181 | + echo "Examples:" | ||
| 182 | + echo " bash ops_st_test.sh" | ||
| 183 | + echo " bash ops_st_test.sh pr_filelist.txt" | ||
| 184 | + echo " bash ops_st_test.sh --soc_version=ascend950" | ||
| 185 | + echo " bash ops_st_test.sh --pr_filelist=pr_filelist.txt" | ||
| 186 | + echo " bash ops_st_test.sh --soc_version=ascend910b --ops=resize_bilinear_v2,roi_align" | ||
| 187 | + echo " bash ops_st_test.sh --soc_version=ascend950 --test_type=kernel" | ||
| 188 | + echo " bash ops_st_test.sh --soc_version=ascend950 --test_type=kernel,e2e" | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +get_changed_ops() { | ||
| 192 | + local base_branch="master" | ||
| 193 | + local changed_files | ||
| 194 | + | ||
| 195 | + changed_files=$(git diff --name-only "${base_branch}...HEAD" 2>/dev/null || git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "") | ||
| 196 | + | ||
| 197 | + if [[ -z "${changed_files}" ]]; then | ||
| 198 | + print_msg "No changed files detected" | ||
| 199 | + return 0 | ||
| 200 | + fi | ||
| 201 | + | ||
| 202 | + local tmp_file="${build_path}/tmp/git_diff_filelist.txt" | ||
| 203 | + mkdir -p "${build_path}/tmp" | ||
| 204 | + echo "${changed_files}" > "${tmp_file}" | ||
| 205 | + local result=$(parse_ops_from_filelist "${tmp_file}") | ||
| 206 | + rm -f "${tmp_file}" | ||
| 207 | + | ||
| 208 | + if [[ -z "${result}" ]]; then | ||
| 209 | + print_msg "No ops detected from changed files" | ||
| 210 | + return 0 | ||
| 211 | + fi | ||
| 212 | + | ||
| 213 | + echo "${result}" | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +download_ops_test_kit() { | ||
| 217 | + print_msg "Preparing build environment..." | ||
| 218 | + | ||
| 219 | + mkdir -p "${build_path}" | ||
| 220 | + | ||
| 221 | + if [[ ! -d "${ttk_path}" ]]; then | ||
| 222 | + print_msg "Downloading ops-test-kit via cmake..." | ||
| 223 | + print_msg "ASCEND_HOME_PATH: ${ASCEND_HOME_PATH}" | ||
| 224 | + (cd "${build_path}" && cmake -DDOWNLOAD_OPS_TEST_KIT=ON -DASCEND_INSTALL_PATH="${ASCEND_HOME_PATH}" "${framework_path}") || { | ||
| 225 | + print_error "Failed to download ops-test-kit via cmake" | ||
| 226 | + exit 1 | ||
| 227 | + } | ||
| 228 | + print_msg "ops-test-kit downloaded successfully" | ||
| 229 | + else | ||
| 230 | + print_msg "ops-test-kit already exists, skipping download" | ||
| 231 | + fi | ||
| 232 | + | ||
| 233 | + if [[ ! -d "${ttk_path}" ]]; then | ||
| 234 | + print_error "ttk_path does not exist after download" | ||
| 235 | + exit 1 | ||
| 236 | + fi | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +find_op_code_path() { | ||
| 240 | + local op_name="$1" | ||
| 241 | + local code_path=$(find "${framework_path}" -type d -name "${op_name}" -not -path "*/build/*" -not -path "*/.git/*" -not -path "*/build_out/*" | head -1) | ||
| 242 | + | ||
| 243 | + if [[ -z "${code_path}" ]]; then | ||
| 244 | + return 1 | ||
| 245 | + fi | ||
| 246 | + | ||
| 247 | + echo "${code_path}" | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | +get_op_type() { | ||
| 251 | + local code_path="$1" | ||
| 252 | + local subdir_path=$(realpath "${code_path}") | ||
| 253 | + local op_type=$(basename "$(dirname "${subdir_path}")") | ||
| 254 | + echo "${op_type}" | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +find_test_cases() { | ||
| 258 | + local op_name="$1" | ||
| 259 | + local op_type="$2" | ||
| 260 | + local arch="$3" | ||
| 261 | + local test_case_files=() | ||
| 262 | + | ||
| 263 | + local st_path | ||
| 264 | + if [[ -n "${case_path}" ]]; then | ||
| 265 | + st_path="${case_path}/${op_type}/${op_name}/" | ||
| 266 | + else | ||
| 267 | + st_path="${framework_path}/${op_type}/${op_name}/tests/st" | ||
| 268 | + fi | ||
| 269 | + | ||
| 270 | + if [[ ! -d "${st_path}" ]]; then | ||
| 271 | + [[ "${DEBUG_DEPENDENCIES:-}" == "TRUE" ]] && print_msg "No st test directory found for ${op_name} at ${st_path}" | ||
| 272 | + return 0 | ||
| 273 | + fi | ||
| 274 | + | ||
| 275 | + local all_prefixes=("ttk_kernel" "ttk_aclnn" "ttk_e2e") | ||
| 276 | + local search_prefixes=() | ||
| 277 | + | ||
| 278 | + if [[ -n "${test_type_list}" ]]; then | ||
| 279 | + IFS=',' read -r -a input_types <<< "${test_type_list}" | ||
| 280 | + for input_type in "${input_types[@]}"; do | ||
| 281 | + # 兼容旧参数 pta,转换为 e2e | ||
| 282 | + if [[ "${input_type}" == "pta" ]]; then | ||
| 283 | + search_prefixes+=("ttk_e2e") | ||
| 284 | + else | ||
| 285 | + search_prefixes+=("ttk_${input_type}") | ||
| 286 | + fi | ||
| 287 | + done | ||
| 288 | + else | ||
| 289 | + search_prefixes=("${all_prefixes[@]}") | ||
| 290 | + fi | ||
| 291 | + | ||
| 292 | + # 第一步:查找通用用例(st/ttk_kernel_*.csv 等) | ||
| 293 | + for prefix in "${search_prefixes[@]}"; do | ||
| 294 | + local csv_files=$(find "${st_path}" -maxdepth 1 -name "${prefix}_*.csv" -type f 2>/dev/null) | ||
| 295 | + for csv_file in ${csv_files}; do | ||
| 296 | + local test_type="${prefix#ttk_}" | ||
| 297 | + test_case_files+=("${test_type}:${csv_file}") | ||
| 298 | + done | ||
| 299 | + done | ||
| 300 | + | ||
| 301 | + # 第二步:查找架构专用用例(st/arch35/ 或 st/arch22/) | ||
| 302 | + if [[ -n "${arch}" ]]; then | ||
| 303 | + local arch_path="${st_path}/${arch}" | ||
| 304 | + if [[ -d "${arch_path}" ]]; then | ||
| 305 | + for prefix in "${search_prefixes[@]}"; do | ||
| 306 | + local csv_files=$(find "${arch_path}" -maxdepth 1 -name "${prefix}_*.csv" -type f 2>/dev/null) | ||
| 307 | + for csv_file in ${csv_files}; do | ||
| 308 | + local test_type="${prefix#ttk_}" | ||
| 309 | + test_case_files+=("${test_type}:${csv_file}") | ||
| 310 | + done | ||
| 311 | + done | ||
| 312 | + fi | ||
| 313 | + fi | ||
| 314 | + | ||
| 315 | + echo "${test_case_files[*]}" | ||
| 316 | +} | ||
| 317 | + | ||
| 318 | +get_ops_test_path() { | ||
| 319 | + local op_name="$1" | ||
| 320 | + local op_type="$2" | ||
| 321 | + local ops_test_path="${framework_path}/${op_type}/${op_name}/tests" | ||
| 322 | + | ||
| 323 | + if [[ ! -d "${ops_test_path}" ]]; then | ||
| 324 | + print_msg "No tests directory found for ${op_name} at ${op_type}/${op_name}/tests" | ||
| 325 | + return 1 | ||
| 326 | + fi | ||
| 327 | + | ||
| 328 | + echo "${ops_test_path}" | ||
| 329 | +} | ||
| 330 | + | ||
| 331 | +check_precision_status() { | ||
| 332 | + local result_csv="$1" | ||
| 333 | + local op_name="$2" | ||
| 334 | + local testcase_name="$3" | ||
| 335 | + | ||
| 336 | + if [[ ! -f "${result_csv}" ]]; then | ||
| 337 | + print_warning "Result csv file not found: ${result_csv}" | ||
| 338 | + return 1 | ||
| 339 | + fi | ||
| 340 | + | ||
| 341 | + python3 "${framework_path}/scripts/ci/ops_test_util.py" \ | ||
| 342 | + --action=check_precision \ | ||
| 343 | + --result_csv="${result_csv}" \ | ||
| 344 | + --op_name="${op_name}" \ | ||
| 345 | + --testcase_name="${testcase_name}" | ||
| 346 | + | ||
| 347 | + return $? | ||
| 348 | +} | ||
| 349 | + | ||
| 350 | +check_plugin_assets() { | ||
| 351 | + local plugin_path="$1" | ||
| 352 | + local op_name="$2" | ||
| 353 | + | ||
| 354 | + local assets_path="${plugin_path}/assets" | ||
| 355 | + | ||
| 356 | + if [[ ! -d "${assets_path}" ]]; then | ||
| 357 | + print_warning "assets directory not found for ${op_name}: ${assets_path}" | ||
| 358 | + return 1 | ||
| 359 | + fi | ||
| 360 | + | ||
| 361 | + local py_files=$(find "${assets_path}" -maxdepth 1 -name "*.py" -type f 2>/dev/null | head -1) | ||
| 362 | + if [[ -z "${py_files}" ]]; then | ||
| 363 | + print_warning "No .py files found in assets directory for ${op_name}: ${assets_path}" | ||
| 364 | + return 1 | ||
| 365 | + fi | ||
| 366 | + | ||
| 367 | + return 0 | ||
| 368 | +} | ||
| 369 | + | ||
| 370 | +run_kernel_test() { | ||
| 371 | + local op_name="$1" | ||
| 372 | + local test_csv="$2" | ||
| 373 | + local ops_test_path="$3" | ||
| 374 | + | ||
| 375 | + if [[ ! -f "${test_csv}" ]]; then | ||
| 376 | + print_warning "Test csv file not found: ${test_csv}, skipping this test case" | ||
| 377 | + return 0 | ||
| 378 | + fi | ||
| 379 | + | ||
| 380 | + if [[ ! -d "${ops_test_path}" ]]; then | ||
| 381 | + print_warning "Plugin directory not found: ${ops_test_path}, skipping this test case" | ||
| 382 | + return 0 | ||
| 383 | + fi | ||
| 384 | + | ||
| 385 | + if ! check_plugin_assets "${ops_test_path}" "${op_name}"; then | ||
| 386 | + return 0 | ||
| 387 | + fi | ||
| 388 | + | ||
| 389 | + local testcase_name=$(basename "${test_csv}" .csv) | ||
| 390 | + if [[ "${test_csv}" == */arch3[0-9]/* || "${test_csv}" == */arch2[0-9]/* ]]; then | ||
| 391 | + testcase_name="$(basename "$(dirname "${test_csv}")")_${testcase_name}" | ||
| 392 | + fi | ||
| 393 | + local log_op_dir="${log_path}/${op_name}" | ||
| 394 | + mkdir -p "${log_op_dir}" | ||
| 395 | + | ||
| 396 | + print_msg "Running kernel test for ${op_name}, testcase: ${testcase_name}" | ||
| 397 | + | ||
| 398 | + cd "${ttk_path}" | ||
| 399 | + | ||
| 400 | + local cmd="python3 -m ttk kernel -i ${test_csv} -o ${log_op_dir}/${testcase_name}_result.csv --plugin ${ops_test_path} -c --pc=8 --warmup=false" | ||
| 401 | + print_msg "Executing: ${cmd}" | ||
| 402 | + | ||
| 403 | + local start_time=$(date +%s) | ||
| 404 | + set +e | ||
| 405 | + ${cmd} 2>&1 | tee "${log_op_dir}/${testcase_name}_run.log" > /dev/null | ||
| 406 | + local test_failed=${PIPESTATUS[0]} | ||
| 407 | + set -e | ||
| 408 | + local end_time=$(date +%s) | ||
| 409 | + local elapsed=$((end_time - start_time)) | ||
| 410 | + | ||
| 411 | + if [[ ${test_failed} -ne 0 ]]; then | ||
| 412 | + print_error "kernel test failed for ${op_name}, testcase: ${testcase_name}, elapsed: ${elapsed}s" | ||
| 413 | + else | ||
| 414 | + print_msg "kernel test completed for ${op_name}, testcase: ${testcase_name}, elapsed: ${elapsed}s" | ||
| 415 | + fi | ||
| 416 | + | ||
| 417 | + local result_csv="${log_op_dir}/${testcase_name}_result.csv" | ||
| 418 | + echo "${result_csv}" | ||
| 419 | + | ||
| 420 | + if [[ ${test_failed} -ne 0 ]]; then | ||
| 421 | + return 1 | ||
| 422 | + fi | ||
| 423 | +} | ||
| 424 | + | ||
| 425 | +run_aclnn_test() { | ||
| 426 | + local op_name="$1" | ||
| 427 | + local test_csv="$2" | ||
| 428 | + local ops_test_path="$3" | ||
| 429 | + | ||
| 430 | + local start_time=$(date +%s) | ||
| 431 | + print_warning "aclnn test not implemented yet for ${op_name}" | ||
| 432 | + # TODO: 待确定aclnn测试命令后实现 | ||
| 433 | + local end_time=$(date +%s) | ||
| 434 | + local elapsed=$((end_time - start_time)) | ||
| 435 | + print_msg "aclnn test elapsed: ${elapsed}s" | ||
| 436 | + return 0 | ||
| 437 | +} | ||
| 438 | + | ||
| 439 | +run_e2e_test() { | ||
| 440 | + local op_name="$1" | ||
| 441 | + local test_csv="$2" | ||
| 442 | + local ops_test_path="$3" | ||
| 443 | + | ||
| 444 | + local start_time=$(date +%s) | ||
| 445 | + print_warning "e2e test not implemented yet for ${op_name}" | ||
| 446 | + # TODO: 待确定e2e测试命令后实现 | ||
| 447 | + local end_time=$(date +%s) | ||
| 448 | + local elapsed=$((end_time - start_time)) | ||
| 449 | + print_msg "e2e test elapsed: ${elapsed}s" | ||
| 450 | + return 0 | ||
| 451 | +} | ||
| 452 | + | ||
| 453 | +summarize_op_results() { | ||
| 454 | + local op_name="$1" | ||
| 455 | + local test_type="$2" | ||
| 456 | + local result_csvs="$3" | ||
| 457 | + | ||
| 458 | + local summary_file="${log_path}/${test_type}_summary.csv" | ||
| 459 | + local summary_header="op_name,testcase_name,test_type,result_csv,status,precision" | ||
| 460 | + | ||
| 461 | + if [[ ! -f "${summary_file}" ]]; then | ||
| 462 | + echo "${summary_header}" > "${summary_file}" | ||
| 463 | + fi | ||
| 464 | + | ||
| 465 | + if [[ -z "${result_csvs}" ]]; then | ||
| 466 | + return 0 | ||
| 467 | + fi | ||
| 468 | + | ||
| 469 | + for result_csv in ${result_csvs}; do | ||
| 470 | + if [[ ! -f "${result_csv}" ]]; then | ||
| 471 | + continue | ||
| 472 | + fi | ||
| 473 | + | ||
| 474 | + python3 "${framework_path}/scripts/ci/ops_test_util.py" \ | ||
| 475 | + --action=summarize \ | ||
| 476 | + --result_csv="${result_csv}" \ | ||
| 477 | + --op_name="${op_name}" \ | ||
| 478 | + --test_type="${test_type}" \ | ||
| 479 | + --summary_file="${summary_file}" | ||
| 480 | + done | ||
| 481 | +} | ||
| 482 | + | ||
| 483 | +print_summary_table() { | ||
| 484 | + python3 "${framework_path}/scripts/ci/ops_test_util.py" \ | ||
| 485 | + --action=print_table \ | ||
| 486 | + --log_path="${log_path}" | ||
| 487 | +} | ||
| 488 | + | ||
| 489 | +run_single_op_test() { | ||
| 490 | + local op_name="$1" | ||
| 491 | + | ||
| 492 | + print_msg "=== Testing op: ${op_name} ===" | ||
| 493 | + | ||
| 494 | + local code_path | ||
| 495 | + code_path=$(find_op_code_path "${op_name}") || true | ||
| 496 | + if [[ -z "${code_path}" ]]; then | ||
| 497 | + print_warning "Cannot find op directory for ${op_name}, skipping" | ||
| 498 | + return 0 | ||
| 499 | + fi | ||
| 500 | + | ||
| 501 | + local op_type=$(get_op_type "${code_path}") | ||
| 502 | + print_msg "op_type: ${op_type}, op_name: ${op_name}" | ||
| 503 | + | ||
| 504 | + local arch="${SOC_TO_ST_ARCH[${soc_version}]:-}" | ||
| 505 | + if [[ -n "${arch}" ]]; then | ||
| 506 | + print_msg "soc_version: ${soc_version}, arch: ${arch}" | ||
| 507 | + fi | ||
| 508 | + | ||
| 509 | + local ops_test_path | ||
| 510 | + ops_test_path=$(get_ops_test_path "${op_name}" "${op_type}") || true | ||
| 511 | + if [[ -z "${ops_test_path}" ]]; then | ||
| 512 | + print_msg "No tests directory found, skipping ${op_name}" | ||
| 513 | + return 0 | ||
| 514 | + fi | ||
| 515 | + | ||
| 516 | + local test_cases=$(find_test_cases "${op_name}" "${op_type}" "${arch}") | ||
| 517 | + | ||
| 518 | + if [[ -z "${test_cases}" ]]; then | ||
| 519 | + print_msg "No test cases found for ${op_name}" | ||
| 520 | + return 0 | ||
| 521 | + fi | ||
| 522 | + | ||
| 523 | + local result_csvs=() | ||
| 524 | + local kernel_csvs=() | ||
| 525 | + local aclnn_csvs=() | ||
| 526 | + local e2e_csvs=() | ||
| 527 | + local test_case_array=(${test_cases}) | ||
| 528 | + local result_csv | ||
| 529 | + local testcase_name | ||
| 530 | + local op_error_flag=0 | ||
| 531 | + | ||
| 532 | + for test_item in "${test_case_array[@]}"; do | ||
| 533 | + local test_type=$(echo "${test_item}" | cut -d':' -f1) | ||
| 534 | + local test_csv=$(echo "${test_item}" | cut -d':' -f2-) | ||
| 535 | + local test_ret=0 | ||
| 536 | + | ||
| 537 | + case "${test_type}" in | ||
| 538 | + kernel) | ||
| 539 | + result_csv=$(run_kernel_test "${op_name}" "${test_csv}" "${ops_test_path}") || test_ret=$? | ||
| 540 | + if [[ ${test_ret} -ne 0 ]]; then | ||
| 541 | + op_error_flag=1 | ||
| 542 | + fi | ||
| 543 | + if [[ -n "${result_csv}" ]]; then | ||
| 544 | + kernel_csvs+=("${result_csv}") | ||
| 545 | + fi | ||
| 546 | + ;; | ||
| 547 | + aclnn) | ||
| 548 | + result_csv=$(run_aclnn_test "${op_name}" "${test_csv}" "${ops_test_path}") || test_ret=$? | ||
| 549 | + if [[ ${test_ret} -ne 0 ]]; then | ||
| 550 | + op_error_flag=1 | ||
| 551 | + fi | ||
| 552 | + if [[ -n "${result_csv}" ]]; then | ||
| 553 | + aclnn_csvs+=("${result_csv}") | ||
| 554 | + fi | ||
| 555 | + ;; | ||
| 556 | + e2e) | ||
| 557 | + result_csv=$(run_e2e_test "${op_name}" "${test_csv}" "${ops_test_path}") || test_ret=$? | ||
| 558 | + if [[ ${test_ret} -ne 0 ]]; then | ||
| 559 | + op_error_flag=1 | ||
| 560 | + fi | ||
| 561 | + if [[ -n "${result_csv}" ]]; then | ||
| 562 | + e2e_csvs+=("${result_csv}") | ||
| 563 | + fi | ||
| 564 | + ;; | ||
| 565 | + *) | ||
| 566 | + print_warning "Unknown test type: ${test_type}, skipping" | ||
| 567 | + continue | ||
| 568 | + ;; | ||
| 569 | + esac | ||
| 570 | + | ||
| 571 | + if [[ -n "${result_csv}" ]]; then | ||
| 572 | + result_csvs+=("${result_csv}") | ||
| 573 | + fi | ||
| 574 | + done | ||
| 575 | + | ||
| 576 | + summarize_op_results "${op_name}" "kernel" "${kernel_csvs[*]}" | ||
| 577 | + summarize_op_results "${op_name}" "aclnn" "${aclnn_csvs[*]}" | ||
| 578 | + summarize_op_results "${op_name}" "e2e" "${e2e_csvs[*]}" | ||
| 579 | + | ||
| 580 | + local arch="${SOC_TO_ST_ARCH[${soc_version}]:-}" | ||
| 581 | + if [[ -n "${arch}" ]]; then | ||
| 582 | + local merged_dir="${log_path}/${op_name}/${arch}" | ||
| 583 | + mkdir -p "${merged_dir}" | ||
| 584 | + | ||
| 585 | + local -A type_csvs_map=( | ||
| 586 | + ["kernel"]="${kernel_csvs[*]}" | ||
| 587 | + ["aclnn"]="${aclnn_csvs[*]}" | ||
| 588 | + ["e2e"]="${e2e_csvs[*]}" | ||
| 589 | + ) | ||
| 590 | + | ||
| 591 | + for test_type_name in kernel aclnn e2e; do | ||
| 592 | + local type_csvs_str="${type_csvs_map[${test_type_name}]}" | ||
| 593 | + if [[ -z "${type_csvs_str}" ]]; then | ||
| 594 | + continue | ||
| 595 | + fi | ||
| 596 | + local -a type_csvs=(${type_csvs_str}) | ||
| 597 | + if [[ ${#type_csvs[@]} -eq 0 ]]; then | ||
| 598 | + continue | ||
| 599 | + fi | ||
| 600 | + | ||
| 601 | + local merged_result="${merged_dir}/ttk_${test_type_name}_${op_name}_st_result.csv" | ||
| 602 | + local merged_log="${merged_dir}/ttk_${test_type_name}_${op_name}_st_run.log" | ||
| 603 | + | ||
| 604 | + local first_csv=1 | ||
| 605 | + for csv in "${type_csvs[@]}"; do | ||
| 606 | + if [[ -f "${csv}" ]]; then | ||
| 607 | + if [[ ${first_csv} -eq 1 ]]; then | ||
| 608 | + head -1 "${csv}" > "${merged_result}" | ||
| 609 | + first_csv=0 | ||
| 610 | + fi | ||
| 611 | + tail -n +2 "${csv}" >> "${merged_result}" | ||
| 612 | + fi | ||
| 613 | + done | ||
| 614 | + | ||
| 615 | + : > "${merged_log}" | ||
| 616 | + for csv in "${type_csvs[@]}"; do | ||
| 617 | + local run_log="${csv%_result.csv}_run.log" | ||
| 618 | + if [[ -f "${run_log}" ]]; then | ||
| 619 | + cat "${run_log}" >> "${merged_log}" | ||
| 620 | + rm -f "${run_log}" | ||
| 621 | + fi | ||
| 622 | + done | ||
| 623 | + | ||
| 624 | + for csv in "${type_csvs[@]}"; do | ||
| 625 | + rm -f "${csv}" | ||
| 626 | + done | ||
| 627 | + | ||
| 628 | + echo "${op_name}:ttk_${test_type_name}_${op_name}_st:${merged_result}" | ||
| 629 | + done | ||
| 630 | + else | ||
| 631 | + for csv in "${result_csvs[@]}"; do | ||
| 632 | + testcase_name=$(basename "${csv}" _result.csv) | ||
| 633 | + echo "${op_name}:${testcase_name}:${csv}" | ||
| 634 | + done | ||
| 635 | + fi | ||
| 636 | + | ||
| 637 | + if [[ ${op_error_flag} -ne 0 ]]; then | ||
| 638 | + return 1 | ||
| 639 | + fi | ||
| 640 | +} | ||
| 641 | + | ||
| 642 | +parse_args() { | ||
| 643 | + ops_list="" | ||
| 644 | + soc_version="" | ||
| 645 | + test_type_list="" | ||
| 646 | + pr_filelist="" | ||
| 647 | + case_path="" | ||
| 648 | + | ||
| 649 | + for arg in "$@"; do | ||
| 650 | + case "${arg}" in | ||
| 651 | + --ops=*) | ||
| 652 | + ops_list="${arg#*=}" | ||
| 653 | + ;; | ||
| 654 | + --soc_version=*) | ||
| 655 | + soc_version="${arg#*=}" | ||
| 656 | + ;; | ||
| 657 | + --test_type=*) | ||
| 658 | + test_type_list="${arg#*=}" | ||
| 659 | + ;; | ||
| 660 | + --pr_filelist=*) | ||
| 661 | + pr_filelist="${arg#*=}" | ||
| 662 | + ;; | ||
| 663 | + --case_path=*) | ||
| 664 | + case_path="${arg#*=}" | ||
| 665 | + ;; | ||
| 666 | + -h|--help) | ||
| 667 | + usage | ||
| 668 | + exit 0 | ||
| 669 | + ;; | ||
| 670 | + -*) | ||
| 671 | + print_error "Unknown argument: ${arg}" | ||
| 672 | + usage | ||
| 673 | + exit 1 | ||
| 674 | + ;; | ||
| 675 | + *) | ||
| 676 | + if [[ -z "${pr_filelist}" ]]; then | ||
| 677 | + pr_filelist="${arg}" | ||
| 678 | + else | ||
| 679 | + print_error "Multiple pr_filelist arguments: ${pr_filelist} and ${arg}" | ||
| 680 | + usage | ||
| 681 | + exit 1 | ||
| 682 | + fi | ||
| 683 | + ;; | ||
| 684 | + esac | ||
| 685 | + done | ||
| 686 | + | ||
| 687 | + if [[ -n "${pr_filelist}" && ! -f "${pr_filelist}" ]]; then | ||
| 688 | + print_error "pr_filelist not found: ${pr_filelist}" | ||
| 689 | + exit 1 | ||
| 690 | + fi | ||
| 691 | + | ||
| 692 | + if [[ -n "${test_type_list}" ]]; then | ||
| 693 | + IFS=',' read -r -a valid_types <<< "kernel,aclnn,e2e" | ||
| 694 | + IFS=',' read -r -a input_types <<< "${test_type_list}" | ||
| 695 | + for input_type in "${input_types[@]}"; do | ||
| 696 | + local found=0 | ||
| 697 | + for valid_type in "${valid_types[@]}"; do | ||
| 698 | + if [[ "${input_type}" == "${valid_type}" ]]; then | ||
| 699 | + found=1 | ||
| 700 | + break | ||
| 701 | + fi | ||
| 702 | + done | ||
| 703 | + if [[ ${found} -eq 0 ]]; then | ||
| 704 | + print_error "Unsupported test_type: ${input_type}. Supported: kernel, aclnn, e2e" | ||
| 705 | + exit 1 | ||
| 706 | + fi | ||
| 707 | + done | ||
| 708 | + fi | ||
| 709 | + | ||
| 710 | + local chip_info=$(asys info -r=status 2>/dev/null || echo "") | ||
| 711 | + local detected_soc="" | ||
| 712 | + if echo "${chip_info}" | grep -q "Ascend 950"; then | ||
| 713 | + detected_soc="ascend950" | ||
| 714 | + elif echo "${chip_info}" | grep -q "Ascend 910"; then | ||
| 715 | + detected_soc="ascend910b" | ||
| 716 | + fi | ||
| 717 | + | ||
| 718 | + if [[ -z "${soc_version}" ]]; then | ||
| 719 | + if [[ -z "${detected_soc}" ]]; then | ||
| 720 | + print_error "Failed to detect SOC version via 'asys info -r=status'. Current environment does not support auto-detection. Please specify --soc_version manually." | ||
| 721 | + exit 1 | ||
| 722 | + fi | ||
| 723 | + soc_version="${detected_soc}" | ||
| 724 | + print_msg "Auto-detected soc_version: ${soc_version}" | ||
| 725 | + else | ||
| 726 | + if [[ "${soc_version}" != "ascend910b" && "${soc_version}" != "ascend950" ]]; then | ||
| 727 | + print_error "Unsupported soc_version: ${soc_version}. Supported: ascend910b, ascend950" | ||
| 728 | + exit 1 | ||
| 729 | + fi | ||
| 730 | + | ||
| 731 | + if [[ -n "${detected_soc}" && "${detected_soc}" != "${soc_version}" ]]; then | ||
| 732 | + print_error "SOC version mismatch: specified '${soc_version}' but detected '${detected_soc}' from 'asys info -r=status'" | ||
| 733 | + exit 1 | ||
| 734 | + fi | ||
| 735 | + fi | ||
| 736 | + | ||
| 737 | + print_msg "soc_version: ${soc_version}" | ||
| 738 | + print_msg "ops_list: ${ops_list:-'auto detect from git diff or pr_filelist'}" | ||
| 739 | + print_msg "test_type_list: ${test_type_list:-'all types'}" | ||
| 740 | + if [[ -n "${pr_filelist}" ]]; then | ||
| 741 | + print_msg "pr_filelist: ${pr_filelist}" | ||
| 742 | + fi | ||
| 743 | + if [[ -n "${case_path}" ]]; then | ||
| 744 | + print_msg "case_path: ${case_path}" | ||
| 745 | + fi | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | +framework_path="$(cd "$(dirname "$0")/../.." && pwd)" | ||
| 749 | +build_path="${framework_path}/build" | ||
| 750 | +log_path="${framework_path}/st/log" | ||
| 751 | +ttk_path="${build_path}/third_party/ops-test-kit" | ||
| 752 | + | ||
| 753 | +parse_args "$@" | ||
| 754 | + | ||
| 755 | +rm -rf "${log_path:?}"/* | ||
| 756 | +mkdir -p "${log_path}" | ||
| 757 | + | ||
| 758 | +download_ops_test_kit | ||
| 759 | + | ||
| 760 | +if [[ -n "${ops_list}" && -z "${pr_filelist}" ]]; then | ||
| 761 | + print_msg "Using ops from --ops parameter: ${ops_list}" | ||
| 762 | +elif [[ -z "${ops_list}" && -n "${pr_filelist}" ]]; then | ||
| 763 | + print_msg "Extracting ops from pr_filelist..." | ||
| 764 | + print_msg "pr_filelist content:" | ||
| 765 | + cat "${pr_filelist}" | grep -v '^$' | grep -v '^#' | sed 's/^[MADRC]\t//' >&2 | ||
| 766 | + ops_list=$(parse_ops_from_filelist "${pr_filelist}") | ||
| 767 | +elif [[ -n "${ops_list}" && -n "${pr_filelist}" ]]; then | ||
| 768 | + print_msg "Merging ops from pr_filelist and --ops parameter..." | ||
| 769 | + print_msg "--ops input: ${ops_list}" | ||
| 770 | + print_msg "pr_filelist content:" | ||
| 771 | + cat "${pr_filelist}" | grep -v '^$' | grep -v '^#' | sed 's/^[MADRC]\t//' >&2 | ||
| 772 | + ops_from_filelist=$(parse_ops_from_filelist "${pr_filelist}") | ||
| 773 | + ops_list=$(merge_ops_lists "${ops_from_filelist}" "${ops_list}") | ||
| 774 | +else | ||
| 775 | + print_msg "Extracting ops from git diff..." | ||
| 776 | + ops_list=$(get_changed_ops) | ||
| 777 | +fi | ||
| 778 | + | ||
| 779 | +if [[ -z "${ops_list}" ]]; then | ||
| 780 | + print_msg "No ops to test" | ||
| 781 | + exit 0 | ||
| 782 | +fi | ||
| 783 | + | ||
| 784 | +print_msg "Ops to test: ${ops_list}" | ||
| 785 | + | ||
| 786 | +IFS=',' read -r -a op_name_array <<< "${ops_list}" | ||
| 787 | + | ||
| 788 | +all_result_csvs=() | ||
| 789 | +result_flag=0 | ||
| 790 | +for op_name in "${op_name_array[@]}"; do | ||
| 791 | + op_results=$(run_single_op_test "${op_name}") || result_flag=1 | ||
| 792 | + if [[ -n "${op_results}" ]]; then | ||
| 793 | + while IFS= read -r line; do | ||
| 794 | + all_result_csvs+=("${line}") | ||
| 795 | + done <<< "${op_results}" | ||
| 796 | + fi | ||
| 797 | +done | ||
| 798 | + | ||
| 799 | +print_msg "=== Starting precision check for all test cases ===" | ||
| 800 | +precision_flag=0 | ||
| 801 | +for result_info in "${all_result_csvs[@]}"; do | ||
| 802 | + op_name=$(echo "${result_info}" | cut -d':' -f1) | ||
| 803 | + testcase_name=$(echo "${result_info}" | cut -d':' -f2) | ||
| 804 | + result_csv=$(echo "${result_info}" | cut -d':' -f3) | ||
| 805 | + check_precision_status "${result_csv}" "${op_name}" "${testcase_name}" || precision_flag=1 | ||
| 806 | +done | ||
| 807 | + | ||
| 808 | +print_summary_table | ||
| 809 | + | ||
| 810 | +if [[ ${result_flag} -ne 0 ]]; then | ||
| 811 | + print_error "Some tests failed, please check the log for details." | ||
| 812 | + exit 1 | ||
| 813 | +elif [[ ${precision_flag} -ne 0 ]]; then | ||
| 814 | + print_error "Some precision checks failed, please check the details above." | ||
| 815 | + exit 1 | ||
| 816 | +else | ||
| 817 | + print_success "All tests and precision checks passed." | ||
| 818 | + exit 0 | ||
| 819 | +fi | ||
| @@ -0,0 +1,629 @@ | |||
| 1 | +#!/usr/bin/env python3 | ||
| 2 | +# -*- coding: utf-8 -*- | ||
| 3 | +# ---------------------------------------------------------------------------- | ||
| 4 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 5 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 6 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 7 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 8 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 9 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 10 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 11 | +# ---------------------------------------------------------------------------- | ||
| 12 | +import argparse | ||
| 13 | +import csv | ||
| 14 | +import logging | ||
| 15 | +import os | ||
| 16 | +import re | ||
| 17 | +import sys | ||
| 18 | +from dataclasses import dataclass | ||
| 19 | + | ||
| 20 | +# 日志配置:错误/警告输出到stderr | ||
| 21 | +logging.basicConfig( | ||
| 22 | + level=logging.INFO, | ||
| 23 | + format='[%(levelname)s] %(message)s', | ||
| 24 | + stream=sys.stderr | ||
| 25 | +) | ||
| 26 | +logger = logging.getLogger(__name__) | ||
| 27 | + | ||
| 28 | +# 表格专用logger:输出到stdout,保持表格格式 | ||
| 29 | +table_logger = logging.getLogger('table_output') | ||
| 30 | +table_handler = logging.StreamHandler(sys.stdout) | ||
| 31 | +table_handler.setFormatter(logging.Formatter('%(message)s')) | ||
| 32 | +table_logger.addHandler(table_handler) | ||
| 33 | +table_logger.setLevel(logging.INFO) | ||
| 34 | +table_logger.propagate = False | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +class SummaryRowData: | ||
| 39 | + """汇总行数据封装 | ||
| 40 | + | ||
| 41 | + 用于封装写入汇总时所需的参数,避免函数参数过多 | ||
| 42 | + """ | ||
| 43 | + rows: list | ||
| 44 | + op_name: str | ||
| 45 | + test_type: str | ||
| 46 | + result_csv: str | ||
| 47 | + summary_file: str | ||
| 48 | + precision_idx: int | ||
| 49 | + dyn_idx: int | ||
| 50 | + cst_idx: int | ||
| 51 | + bin_idx: int | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +class SingleRowData: | ||
| 56 | + """单行数据封装 | ||
| 57 | + | ||
| 58 | + 用于封装写入单行时所需的参数 | ||
| 59 | + """ | ||
| 60 | + out_f: object | ||
| 61 | + row: list | ||
| 62 | + op_name: str | ||
| 63 | + test_type: str | ||
| 64 | + result_csv: str | ||
| 65 | + precision_idx: int | ||
| 66 | + dyn_idx: int | ||
| 67 | + cst_idx: int | ||
| 68 | + bin_idx: int | ||
| 69 | + | ||
| 70 | + | ||
| 71 | + | ||
| 72 | +class TableRowData: | ||
| 73 | + """表格行数据封装 | ||
| 74 | + | ||
| 75 | + 用于封装打印表格行时所需的参数 | ||
| 76 | + """ | ||
| 77 | + op: str | ||
| 78 | + testcase: str | ||
| 79 | + test_type: str | ||
| 80 | + status: str | ||
| 81 | + dyn_prec: str | ||
| 82 | + cst_prec: str | ||
| 83 | + bin_prec: str | ||
| 84 | + | ||
| 85 | + | ||
| 86 | +class OpTestUtil: | ||
| 87 | + """OPS测试工具主类 | ||
| 88 | + | ||
| 89 | + 整合了精度检查、结果汇总和表格打印功能 | ||
| 90 | + | ||
| 91 | + 日志设计: | ||
| 92 | + - logger: 用于错误/警告信息,输出到stderr | ||
| 93 | + - table_logger: 用于表格可视化输出,输出到stdout | ||
| 94 | + """ | ||
| 95 | + | ||
| 96 | + col_widths = { | ||
| 97 | + 'op': 20, | ||
| 98 | + 'testcase': 70, | ||
| 99 | + 'type': 8, | ||
| 100 | + 'status': 8, | ||
| 101 | + 'dyn_prec': 9, | ||
| 102 | + 'cst_prec': 9, | ||
| 103 | + 'bin_prec': 9 | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + def __init__(self): | ||
| 107 | + pass | ||
| 108 | + | ||
| 109 | + | ||
| 110 | + def check(result_csv, op_name, testcase_name): | ||
| 111 | + """检查精度状态 | ||
| 112 | + | ||
| 113 | + Args: | ||
| 114 | + result_csv: 结果CSV文件路径 | ||
| 115 | + op_name: 算子名称 | ||
| 116 | + testcase_name: 测试用例名称 | ||
| 117 | + | ||
| 118 | + Returns: | ||
| 119 | + int: 0表示全部通过,1表示有失败用例 | ||
| 120 | + """ | ||
| 121 | + if not OpTestUtil._validate_file(result_csv): | ||
| 122 | + return 1 | ||
| 123 | + | ||
| 124 | + try: | ||
| 125 | + with open(result_csv, 'r') as f: | ||
| 126 | + reader = csv.reader(f) | ||
| 127 | + headers = next(reader) | ||
| 128 | + precision_idx = OpTestUtil._find_precision_column(headers) | ||
| 129 | + | ||
| 130 | + if precision_idx == -1: | ||
| 131 | + logger.warning("precision_status column not found in result csv") | ||
| 132 | + return 1 | ||
| 133 | + | ||
| 134 | + total_cases, passed_cases = OpTestUtil._count_results(reader, precision_idx) | ||
| 135 | + | ||
| 136 | + if total_cases - passed_cases > 0: | ||
| 137 | + return 1 | ||
| 138 | + return 0 | ||
| 139 | + except Exception as e: | ||
| 140 | + logger.error(f"Failed to parse result csv: {e}") | ||
| 141 | + return 1 | ||
| 142 | + | ||
| 143 | + | ||
| 144 | + def summarize(result_csv, op_name, test_type, summary_file): | ||
| 145 | + """汇总测试结果 | ||
| 146 | + | ||
| 147 | + Args: | ||
| 148 | + result_csv: 结果CSV文件路径 | ||
| 149 | + op_name: 算子名称 | ||
| 150 | + test_type: 测试类型 (kernel/aclnn/e2e) | ||
| 151 | + summary_file: 汇总CSV文件路径 | ||
| 152 | + """ | ||
| 153 | + if not os.path.exists(result_csv): | ||
| 154 | + return | ||
| 155 | + | ||
| 156 | + OpTestUtil._ensure_summary_file(summary_file) | ||
| 157 | + OpTestUtil._process_csv(result_csv, op_name, test_type, summary_file) | ||
| 158 | + | ||
| 159 | + | ||
| 160 | + def print_table(log_path): | ||
| 161 | + """打印可视化表格 | ||
| 162 | + | ||
| 163 | + Args: | ||
| 164 | + log_path: 日志目录路径 | ||
| 165 | + """ | ||
| 166 | + summary_files = ["kernel_summary.csv", "aclnn_summary.csv", "e2e_summary.csv"] | ||
| 167 | + | ||
| 168 | + all_rows = OpTestUtil._load_summary_data(log_path, summary_files) | ||
| 169 | + | ||
| 170 | + if not all_rows: | ||
| 171 | + logger.warning("No summary data found") | ||
| 172 | + return | ||
| 173 | + | ||
| 174 | + total = len(all_rows) | ||
| 175 | + passed = sum(1 for r in all_rows if r.get('status', '').upper() == 'PASS') | ||
| 176 | + failed = total - passed | ||
| 177 | + | ||
| 178 | + OpTestUtil._print_title_section() | ||
| 179 | + | ||
| 180 | + if failed > 0: | ||
| 181 | + OpTestUtil._print_failed_rows(all_rows) | ||
| 182 | + | ||
| 183 | + OpTestUtil._print_summary(total, passed, failed) | ||
| 184 | + | ||
| 185 | + | ||
| 186 | + def check_precision(result_csv, op_name, testcase_name): | ||
| 187 | + """检查精度状态 | ||
| 188 | + | ||
| 189 | + Args: | ||
| 190 | + result_csv: 结果CSV文件路径 | ||
| 191 | + op_name: 算子名称 | ||
| 192 | + testcase_name: 测试用例名称 | ||
| 193 | + | ||
| 194 | + Returns: | ||
| 195 | + int: 0表示全部通过,1表示有失败用例 | ||
| 196 | + """ | ||
| 197 | + return OpTestUtil.check(result_csv, op_name, testcase_name) | ||
| 198 | + | ||
| 199 | + | ||
| 200 | + def summarize_results(result_csv, op_name, test_type, summary_file): | ||
| 201 | + """汇总测试结果 | ||
| 202 | + | ||
| 203 | + Args: | ||
| 204 | + result_csv: 结果CSV文件路径 | ||
| 205 | + op_name: 算子名称 | ||
| 206 | + test_type: 测试类型 (kernel/aclnn/e2e) | ||
| 207 | + summary_file: 汇总CSV文件路径 | ||
| 208 | + """ | ||
| 209 | + OpTestUtil.summarize(result_csv, op_name, test_type, summary_file) | ||
| 210 | + | ||
| 211 | + | ||
| 212 | + def print_summary_table(log_path): | ||
| 213 | + """打印可视化表格 | ||
| 214 | + | ||
| 215 | + Args: | ||
| 216 | + log_path: 日志目录路径 | ||
| 217 | + """ | ||
| 218 | + OpTestUtil.print_table(log_path) | ||
| 219 | + | ||
| 220 | + | ||
| 221 | + def _validate_file(result_csv): | ||
| 222 | + """验证文件是否存在 | ||
| 223 | + | ||
| 224 | + Args: | ||
| 225 | + result_csv: 结果CSV文件路径 | ||
| 226 | + | ||
| 227 | + Returns: | ||
| 228 | + bool: 文件存在返回True | ||
| 229 | + """ | ||
| 230 | + if not os.path.exists(result_csv): | ||
| 231 | + logger.warning(f"Result csv file not found: {result_csv}") | ||
| 232 | + return False | ||
| 233 | + return True | ||
| 234 | + | ||
| 235 | + | ||
| 236 | + def _find_precision_column(headers): | ||
| 237 | + """查找precision_status列索引 | ||
| 238 | + | ||
| 239 | + Args: | ||
| 240 | + headers: CSV表头列表 | ||
| 241 | + | ||
| 242 | + Returns: | ||
| 243 | + int: 列索引,未找到返回-1 | ||
| 244 | + """ | ||
| 245 | + try: | ||
| 246 | + return headers.index('precision_status') | ||
| 247 | + except ValueError: | ||
| 248 | + return -1 | ||
| 249 | + | ||
| 250 | + | ||
| 251 | + def _count_results(reader, precision_idx): | ||
| 252 | + """统计测试结果 | ||
| 253 | + | ||
| 254 | + Args: | ||
| 255 | + reader: CSV reader对象 | ||
| 256 | + precision_idx: precision_status列索引 | ||
| 257 | + | ||
| 258 | + Returns: | ||
| 259 | + tuple: (总数, 通过数) | ||
| 260 | + """ | ||
| 261 | + total_cases = 0 | ||
| 262 | + passed_cases = 0 | ||
| 263 | + | ||
| 264 | + for row in reader: | ||
| 265 | + if len(row) <= precision_idx: | ||
| 266 | + continue | ||
| 267 | + total_cases += 1 | ||
| 268 | + if row[precision_idx] == "PASS": | ||
| 269 | + passed_cases += 1 | ||
| 270 | + | ||
| 271 | + return total_cases, passed_cases | ||
| 272 | + | ||
| 273 | + | ||
| 274 | + def _ensure_summary_file(summary_file): | ||
| 275 | + """确保汇总文件存在并写入表头 | ||
| 276 | + | ||
| 277 | + Args: | ||
| 278 | + summary_file: 汇总文件路径 | ||
| 279 | + """ | ||
| 280 | + if not os.path.exists(summary_file): | ||
| 281 | + summary_header = "op_name,testcase_name,test_type,result_csv,status,dyn_prec,cst_prec,bin_prec" | ||
| 282 | + with open(summary_file, 'w') as f: | ||
| 283 | + f.write(summary_header + '\n') | ||
| 284 | + | ||
| 285 | + | ||
| 286 | + def _read_csv_rows(result_csv): | ||
| 287 | + """读取CSV文件 | ||
| 288 | + | ||
| 289 | + Args: | ||
| 290 | + result_csv: CSV文件路径 | ||
| 291 | + | ||
| 292 | + Returns: | ||
| 293 | + tuple: (headers, rows) 或 (None, None) | ||
| 294 | + """ | ||
| 295 | + try: | ||
| 296 | + with open(result_csv, 'r') as f: | ||
| 297 | + reader = csv.reader(f) | ||
| 298 | + headers = next(reader) | ||
| 299 | + rows = list(reader) | ||
| 300 | + return headers, rows | ||
| 301 | + except Exception as e: | ||
| 302 | + logger.error(f"Failed to read {result_csv}: {e}") | ||
| 303 | + return None, None | ||
| 304 | + | ||
| 305 | + | ||
| 306 | + def _find_column_indices(headers): | ||
| 307 | + """查找关键列索引 | ||
| 308 | + | ||
| 309 | + Args: | ||
| 310 | + headers: CSV表头列表 | ||
| 311 | + | ||
| 312 | + Returns: | ||
| 313 | + tuple: (precision_status索引, dyn_precision索引, cst_precision索引, bin_precision索引) | ||
| 314 | + """ | ||
| 315 | + precision_idx = -1 | ||
| 316 | + dyn_idx = -1 | ||
| 317 | + cst_idx = -1 | ||
| 318 | + bin_idx = -1 | ||
| 319 | + | ||
| 320 | + for i, h in enumerate(headers): | ||
| 321 | + if h == 'precision_status': | ||
| 322 | + precision_idx = i | ||
| 323 | + elif h == 'dyn_precision': | ||
| 324 | + dyn_idx = i | ||
| 325 | + elif h == 'cst_precision': | ||
| 326 | + cst_idx = i | ||
| 327 | + elif h == 'bin_precision': | ||
| 328 | + bin_idx = i | ||
| 329 | + | ||
| 330 | + return precision_idx, dyn_idx, cst_idx, bin_idx | ||
| 331 | + | ||
| 332 | + | ||
| 333 | + def _get_status(row, precision_idx): | ||
| 334 | + """获取状态值 | ||
| 335 | + | ||
| 336 | + Args: | ||
| 337 | + row: 数据行 | ||
| 338 | + precision_idx: precision_status列索引 | ||
| 339 | + | ||
| 340 | + Returns: | ||
| 341 | + str: 状态值 | ||
| 342 | + """ | ||
| 343 | + if precision_idx == -1: | ||
| 344 | + return "PASS" | ||
| 345 | + if precision_idx >= 0 and len(row) > precision_idx: | ||
| 346 | + return row[precision_idx] | ||
| 347 | + return "FAIL" | ||
| 348 | + | ||
| 349 | + | ||
| 350 | + def _get_precision(row, idx): | ||
| 351 | + """获取精度值 | ||
| 352 | + | ||
| 353 | + Args: | ||
| 354 | + row: 数据行 | ||
| 355 | + idx: 精度列索引 | ||
| 356 | + | ||
| 357 | + Returns: | ||
| 358 | + str: 精度值 | ||
| 359 | + """ | ||
| 360 | + if idx >= 0 and len(row) > idx: | ||
| 361 | + return OpTestUtil._parse_precision(row[idx]) | ||
| 362 | + return "N/A" | ||
| 363 | + | ||
| 364 | + | ||
| 365 | + def _get_all_precisions(row, dyn_idx, cst_idx, bin_idx): | ||
| 366 | + """获取三个精度值 | ||
| 367 | + | ||
| 368 | + Args: | ||
| 369 | + row: 数据行 | ||
| 370 | + dyn_idx: dyn_precision列索引 | ||
| 371 | + cst_idx: cst_precision列索引 | ||
| 372 | + bin_idx: bin_precision列索引 | ||
| 373 | + | ||
| 374 | + Returns: | ||
| 375 | + tuple: (dyn_prec, cst_prec, bin_prec) | ||
| 376 | + """ | ||
| 377 | + dyn_prec = OpTestUtil._get_precision(row, dyn_idx) | ||
| 378 | + cst_prec = OpTestUtil._get_precision(row, cst_idx) | ||
| 379 | + bin_prec = OpTestUtil._get_precision(row, bin_idx) | ||
| 380 | + return dyn_prec, cst_prec, bin_prec | ||
| 381 | + | ||
| 382 | + | ||
| 383 | + def _parse_precision(value): | ||
| 384 | + """解析精度值 | ||
| 385 | + | ||
| 386 | + Args: | ||
| 387 | + value: 精度值字符串 | ||
| 388 | + | ||
| 389 | + Returns: | ||
| 390 | + str: 格式化后的精度值 | ||
| 391 | + """ | ||
| 392 | + if not value: | ||
| 393 | + return 'N/A' | ||
| 394 | + match = re.search(r'([\d.]+)%', str(value)) | ||
| 395 | + if match: | ||
| 396 | + return f"{float(match.group(1)):.2f}%" | ||
| 397 | + return str(value)[:30] | ||
| 398 | + | ||
| 399 | + | ||
| 400 | + def _write_single_row(single_data): | ||
| 401 | + """写入单行数据 | ||
| 402 | + | ||
| 403 | + Args: | ||
| 404 | + single_data: SingleRowData数据封装对象 | ||
| 405 | + """ | ||
| 406 | + tc_name = single_data.row[0] | ||
| 407 | + status = OpTestUtil._get_status(single_data.row, single_data.precision_idx) | ||
| 408 | + dyn_prec, cst_prec, bin_prec = OpTestUtil._get_all_precisions( | ||
| 409 | + single_data.row, single_data.dyn_idx, single_data.cst_idx, single_data.bin_idx) | ||
| 410 | + | ||
| 411 | + single_data.out_f.write(f"{single_data.op_name},{tc_name},{single_data.test_type}," | ||
| 412 | + f"{single_data.result_csv},{status},{dyn_prec},{cst_prec},{bin_prec}\n") | ||
| 413 | + | ||
| 414 | + | ||
| 415 | + def _process_csv(result_csv, op_name, test_type, summary_file): | ||
| 416 | + """处理CSV文件并写入汇总 | ||
| 417 | + | ||
| 418 | + Args: | ||
| 419 | + result_csv: 结果CSV文件路径 | ||
| 420 | + op_name: 算子名称 | ||
| 421 | + test_type: 测试类型 | ||
| 422 | + summary_file: 汇总文件路径 | ||
| 423 | + """ | ||
| 424 | + try: | ||
| 425 | + headers, rows = OpTestUtil._read_csv_rows(result_csv) | ||
| 426 | + if headers is None: | ||
| 427 | + return | ||
| 428 | + | ||
| 429 | + precision_idx, dyn_idx, cst_idx, bin_idx = OpTestUtil._find_column_indices(headers) | ||
| 430 | + | ||
| 431 | + row_data = SummaryRowData( | ||
| 432 | + rows=rows, | ||
| 433 | + op_name=op_name, | ||
| 434 | + test_type=test_type, | ||
| 435 | + result_csv=result_csv, | ||
| 436 | + summary_file=summary_file, | ||
| 437 | + precision_idx=precision_idx, | ||
| 438 | + dyn_idx=dyn_idx, | ||
| 439 | + cst_idx=cst_idx, | ||
| 440 | + bin_idx=bin_idx | ||
| 441 | + ) | ||
| 442 | + OpTestUtil._write_summary_rows(row_data) | ||
| 443 | + except Exception as e: | ||
| 444 | + logger.error(f"Failed to process {result_csv}: {e}") | ||
| 445 | + | ||
| 446 | + | ||
| 447 | + def _write_summary_rows(row_data): | ||
| 448 | + """写入汇总行数据 | ||
| 449 | + | ||
| 450 | + Args: | ||
| 451 | + row_data: SummaryRowData数据封装对象 | ||
| 452 | + """ | ||
| 453 | + with open(row_data.summary_file, 'a') as out_f: | ||
| 454 | + for row in row_data.rows: | ||
| 455 | + if len(row) == 0: | ||
| 456 | + continue | ||
| 457 | + | ||
| 458 | + single_data = SingleRowData( | ||
| 459 | + out_f=out_f, | ||
| 460 | + row=row, | ||
| 461 | + op_name=row_data.op_name, | ||
| 462 | + test_type=row_data.test_type, | ||
| 463 | + result_csv=row_data.result_csv, | ||
| 464 | + precision_idx=row_data.precision_idx, | ||
| 465 | + dyn_idx=row_data.dyn_idx, | ||
| 466 | + cst_idx=row_data.cst_idx, | ||
| 467 | + bin_idx=row_data.bin_idx | ||
| 468 | + ) | ||
| 469 | + OpTestUtil._write_single_row(single_data) | ||
| 470 | + | ||
| 471 | + | ||
| 472 | + def _read_summary_file(filepath): | ||
| 473 | + """读取单个汇总文件 | ||
| 474 | + | ||
| 475 | + Args: | ||
| 476 | + filepath: 文件路径 | ||
| 477 | + | ||
| 478 | + Returns: | ||
| 479 | + list: 数据行列表 | ||
| 480 | + """ | ||
| 481 | + try: | ||
| 482 | + with open(filepath, 'r') as f: | ||
| 483 | + reader = csv.DictReader(f) | ||
| 484 | + return list(reader) | ||
| 485 | + except Exception as e: | ||
| 486 | + logger.error(f"Failed to read {filepath}: {e}") | ||
| 487 | + return [] | ||
| 488 | + | ||
| 489 | + | ||
| 490 | + def _print_title_section(): | ||
| 491 | + """打印标题区域 | ||
| 492 | + | ||
| 493 | + 使用table_logger输出到stdout,保持表格格式 | ||
| 494 | + """ | ||
| 495 | + table_logger.info('') | ||
| 496 | + table_logger.info('=' * 131) | ||
| 497 | + table_logger.info('{:^129}'.format('PRECISION TEST RESULTS SUMMARY')) | ||
| 498 | + table_logger.info('=' * 131) | ||
| 499 | + | ||
| 500 | + | ||
| 501 | + def _load_summary_data(log_path, summary_files): | ||
| 502 | + """加载汇总数据 | ||
| 503 | + | ||
| 504 | + Args: | ||
| 505 | + log_path: 日志目录路径 | ||
| 506 | + summary_files: 汇总文件列表 | ||
| 507 | + | ||
| 508 | + Returns: | ||
| 509 | + list: 所有数据行 | ||
| 510 | + """ | ||
| 511 | + all_rows = [] | ||
| 512 | + for sf in summary_files: | ||
| 513 | + filepath = os.path.join(log_path, sf) | ||
| 514 | + if os.path.exists(filepath): | ||
| 515 | + rows = OpTestUtil._read_summary_file(filepath) | ||
| 516 | + all_rows.extend(rows) | ||
| 517 | + return all_rows | ||
| 518 | + | ||
| 519 | + | ||
| 520 | + def _print_separator(): | ||
| 521 | + """打印分隔线 | ||
| 522 | + | ||
| 523 | + 使用table_logger输出到stdout | ||
| 524 | + """ | ||
| 525 | + line = '+' + '-' * OpTestUtil.col_widths['op'] + '+' + '-' * OpTestUtil.col_widths['testcase'] + \ | ||
| 526 | + '+' + '-' * OpTestUtil.col_widths['type'] + '+' + '-' * OpTestUtil.col_widths['status'] + \ | ||
| 527 | + '+' + '-' * OpTestUtil.col_widths['dyn_prec'] + '+' + '-' * OpTestUtil.col_widths['cst_prec'] + \ | ||
| 528 | + '+' + '-' * OpTestUtil.col_widths['bin_prec'] + '+' | ||
| 529 | + table_logger.info(line) | ||
| 530 | + | ||
| 531 | + | ||
| 532 | + def _print_header(): | ||
| 533 | + """打印表头 | ||
| 534 | + | ||
| 535 | + 使用table_logger输出到stdout | ||
| 536 | + """ | ||
| 537 | + OpTestUtil._print_separator() | ||
| 538 | + header = '| {:^18} | {:^68} | {:^6} | {:^6} | {:^7} | {:^7} | {:^7} |'.format( | ||
| 539 | + 'Op Name', 'Testcase Name', 'Type', 'Status', 'DynPrec', 'CstPrec', 'BinPrec') | ||
| 540 | + table_logger.info(header) | ||
| 541 | + OpTestUtil._print_separator() | ||
| 542 | + | ||
| 543 | + | ||
| 544 | + def _print_row(row_data): | ||
| 545 | + """打印单行数据 | ||
| 546 | + | ||
| 547 | + Args: | ||
| 548 | + row_data: TableRowData数据封装对象 | ||
| 549 | + | ||
| 550 | + 使用table_logger输出到stdout | ||
| 551 | + """ | ||
| 552 | + status_display = '\033[31mFAIL\033[0m' | ||
| 553 | + | ||
| 554 | + tc_display = row_data.testcase if len(row_data.testcase) <= OpTestUtil.col_widths['testcase'] \ | ||
| 555 | + else row_data.testcase[:35] + '...' + row_data.testcase[-32:] | ||
| 556 | + | ||
| 557 | + row = '| {:<18} | {:<68} | {:^6} | {:^6} | {:^7} | {:^7} | {:^7} |'.format( | ||
| 558 | + row_data.op, tc_display, row_data.test_type, status_display, | ||
| 559 | + row_data.dyn_prec or 'N/A', row_data.cst_prec or 'N/A', row_data.bin_prec or 'N/A') | ||
| 560 | + table_logger.info(row) | ||
| 561 | + | ||
| 562 | + | ||
| 563 | + def _print_failed_rows(all_rows): | ||
| 564 | + """打印失败的行数据 | ||
| 565 | + | ||
| 566 | + Args: | ||
| 567 | + all_rows: 所有数据行列表 | ||
| 568 | + | ||
| 569 | + 使用table_logger输出到stdout | ||
| 570 | + """ | ||
| 571 | + OpTestUtil._print_header() | ||
| 572 | + | ||
| 573 | + failed_rows = [r for r in all_rows if r.get('status', '').upper() != 'PASS'] | ||
| 574 | + for row in failed_rows: | ||
| 575 | + row_data = TableRowData( | ||
| 576 | + op=row.get('op_name', ''), | ||
| 577 | + testcase=row.get('testcase_name', ''), | ||
| 578 | + test_type=row.get('test_type', ''), | ||
| 579 | + status=row.get('status', ''), | ||
| 580 | + dyn_prec=row.get('dyn_prec', ''), | ||
| 581 | + cst_prec=row.get('cst_prec', ''), | ||
| 582 | + bin_prec=row.get('bin_prec', '') | ||
| 583 | + ) | ||
| 584 | + OpTestUtil._print_row(row_data) | ||
| 585 | + | ||
| 586 | + | ||
| 587 | + def _print_summary(total, passed, failed): | ||
| 588 | + """打印汇总统计 | ||
| 589 | + | ||
| 590 | + Args: | ||
| 591 | + total: 总数 | ||
| 592 | + passed: 通过数 | ||
| 593 | + failed: 失败数 | ||
| 594 | + | ||
| 595 | + 使用table_logger输出到stdout | ||
| 596 | + """ | ||
| 597 | + OpTestUtil._print_separator() | ||
| 598 | + pass_rate = (passed / total * 100) if total > 0 else 0.0 | ||
| 599 | + summary_line = '| TOTAL: {:^5} | PASSED: {:^4} | FAILED: {:^4} | PASS RATE: {:.2f}%{} |'.format( | ||
| 600 | + total, passed, failed, pass_rate, ' ' * 57) | ||
| 601 | + table_logger.info(summary_line) | ||
| 602 | + OpTestUtil._print_separator() | ||
| 603 | + | ||
| 604 | + | ||
| 605 | +def main(): | ||
| 606 | + parser = argparse.ArgumentParser(description='OPS Test Utilities') | ||
| 607 | + parser.add_argument('--action', required=True, | ||
| 608 | + choices=['check_precision', 'summarize', 'print_table'], | ||
| 609 | + help='Action to perform') | ||
| 610 | + parser.add_argument('--result_csv', help='Result CSV file path') | ||
| 611 | + parser.add_argument('--op_name', help='Operator name') | ||
| 612 | + parser.add_argument('--testcase_name', help='Testcase name') | ||
| 613 | + parser.add_argument('--test_type', help='Test type (kernel/aclnn/e2e)') | ||
| 614 | + parser.add_argument('--summary_file', help='Summary CSV file path') | ||
| 615 | + parser.add_argument('--log_path', help='Log directory path') | ||
| 616 | + | ||
| 617 | + args = parser.parse_args() | ||
| 618 | + | ||
| 619 | + if args.action == 'check_precision': | ||
| 620 | + ret = OpTestUtil.check_precision(args.result_csv, args.op_name, args.testcase_name) | ||
| 621 | + sys.exit(ret) | ||
| 622 | + elif args.action == 'summarize': | ||
| 623 | + OpTestUtil.summarize_results(args.result_csv, args.op_name, args.test_type, args.summary_file) | ||
| 624 | + elif args.action == 'print_table': | ||
| 625 | + OpTestUtil.print_summary_table(args.log_path) | ||
| 626 | + | ||
| 627 | + | ||
| 628 | +if __name__ == '__main__': | ||
| 629 | + main() | ||