| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- #!/bin/bash
- set -euo pipefail
- # 单元测试配置矩阵入口(Linux)。
- # 脚本路径:scripts/ci/runBaseCoverage.sh
- # 执行模式:UNIT_MODE=quick|nightly|full
- # quick: 2 组(PR 快检)
- # nightly: 4 组(strict × addAtHead,scientific 固定 true)
- # full: 8 组(三个布尔宏全组合)
- # 常用参数:
- # UNIT_SKIP_COV=0|1:是否跳过覆盖率
- # UNIT_STOP_ON_FAIL=0|1:失败是否立即终止
- # XMAKE_FORCE_CLEAN=0|1:每组前是否先清理配置
- unitMode="${UNIT_MODE:-full}"
- unitSkipCov="${UNIT_SKIP_COV:-0}"
- unitStopOnFail="${UNIT_STOP_ON_FAIL:-1}"
- xmakeForceClean="${XMAKE_FORCE_CLEAN:-0}"
- # 统一切到仓库根目录,避免从任意 cwd 启动时相对路径失效
- scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- repoRoot="$(cd "${scriptDir}/../.." && pwd)"
- cd "${repoRoot}"
- # 覆盖率目录固定为 coverage/unitMatrix,每次执行前清理,保证只保留最新结果
- coverageRoot="coverage/unitMatrix"
- rm -rf "${coverageRoot}"
- profileRoot="${coverageRoot}/profiles"
- mkdir -p "${profileRoot}"
- declare -a caseList=()
- addCase() {
- local strictKey="$1"
- local addAtHead="$2"
- local scientific="$3"
- caseList+=("${strictKey} ${addAtHead} ${scientific}")
- }
- # 根据模式生成组合列表
- case "${unitMode}" in
- quick)
- # PR 快检:默认组合 + 对立组合
- addCase false true true
- addCase true false true
- ;;
- nightly)
- # 夜间:覆盖 strict × addAtHead 四种核心语义
- for strictKey in false true; do
- for addAtHead in false true; do
- addCase "${strictKey}" "${addAtHead}" true
- done
- done
- ;;
- full)
- # 全量:三个布尔宏全组合
- for strictKey in false true; do
- for addAtHead in false true; do
- for scientific in false true; do
- addCase "${strictKey}" "${addAtHead}" "${scientific}"
- done
- done
- done
- ;;
- *)
- echo "[错误] UNIT_MODE 仅支持 quick/nightly/full,当前值:${unitMode}"
- exit 1
- ;;
- esac
- totalCases="${#caseList[@]}"
- caseIndex=0
- failedCases=0
- runCase() {
- local index="$1"
- local total="$2"
- local strictKey="$3"
- local addAtHead="$4"
- local scientific="$5"
- local caseName="strict_${strictKey}__head_${addAtHead}__sci_${scientific}"
- local profraw="${profileRoot}/${caseName}.profraw"
- echo "===================================================="
- echo "【用例 ${index}/${total}】${caseName}"
- echo " - RyanJsonStrictObjectKeyCheck=${strictKey}"
- echo " - RyanJsonDefaultAddAtHead=${addAtHead}"
- echo " - RyanJsonSnprintfSupportScientific=${scientific}"
- echo "===================================================="
- export RYANJSON_STRICT_OBJECT_KEY_CHECK="${strictKey}"
- export RYANJSON_DEFAULT_ADD_AT_HEAD="${addAtHead}"
- export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${scientific}"
- # 重新配置,确保宏变化进入编译命令
- # 默认走增量配置,配合第三方静态库可减少重复编译
- if [[ "${xmakeForceClean}" == "1" ]]; then
- echo "[阶段] 正在执行 xmake 配置(clean 模式)..."
- if ! xmake f -c; then
- echo "[错误] xmake 配置失败:${caseName}"
- return 1
- fi
- else
- echo "[阶段] 正在执行 xmake 配置(增量模式)..."
- if ! xmake f; then
- echo "[错误] xmake 配置失败:${caseName}"
- return 1
- fi
- fi
- echo "[阶段] 正在执行 xmake 构建(target=RyanJson)..."
- if ! xmake -b RyanJson; then
- echo "[错误] xmake 构建失败:${caseName}"
- return 1
- fi
- # 单测执行,profile 分文件隔离,避免组合间互相覆盖
- echo "[阶段] 正在运行单元测试二进制..."
- if ! LLVM_PROFILE_FILE="${profraw}" ./build/linux/x86/release/RyanJson; then
- echo "[错误] 单元测试执行失败:${caseName}"
- return 1
- fi
- # 快检模式可跳过覆盖率阶段以缩短总时长
- if [[ "${unitSkipCov}" == "1" ]]; then
- echo "[信息] UNIT_SKIP_COV=1,已跳过覆盖率生成。"
- return 0
- fi
- }
- # 按组合清单执行
- for entry in "${caseList[@]}"; do
- caseIndex=$((caseIndex + 1))
- read -r strictKey addAtHead scientific <<< "${entry}"
- if runCase "${caseIndex}" "${totalCases}" "${strictKey}" "${addAtHead}" "${scientific}"; then
- :
- else
- failedCases=$((failedCases + 1))
- if [[ "${unitStopOnFail}" == "1" ]]; then
- echo
- echo "[错误] 用例 ${caseIndex}/${totalCases} 失败,已按 UNIT_STOP_ON_FAIL=1 提前终止。"
- exit 1
- fi
- fi
- done
- # 若启用覆盖率,则把矩阵中所有组合的 profraw 合并后只生成一份报告
- if [[ "${unitSkipCov}" != "1" ]]; then
- if ! command -v llvm-profdata >/dev/null 2>&1; then
- echo "[错误] 未找到 llvm-profdata,无法生成覆盖率。"
- exit 1
- fi
- if ! command -v llvm-cov >/dev/null 2>&1; then
- echo "[错误] 未找到 llvm-cov,无法生成覆盖率。"
- exit 1
- fi
- shopt -s nullglob
- profrawFiles=("${profileRoot}"/*.profraw)
- shopt -u nullglob
- if [[ "${#profrawFiles[@]}" -eq 0 ]]; then
- echo "[错误] 未找到可合并的 profraw 文件。"
- exit 1
- fi
- mergedProfdata="${coverageRoot}/coverage.profdata"
- reportTxt="${coverageRoot}/report.txt"
- reportHtml="${coverageRoot}/html"
- if ! llvm-profdata merge -sparse "${profrawFiles[@]}" -o "${mergedProfdata}"; then
- echo "[错误] profraw 合并失败。"
- exit 1
- fi
- if ! llvm-cov report ./build/linux/x86/release/RyanJson \
- -instr-profile="${mergedProfdata}" \
- -show-mcdc-summary \
- -sources ./RyanJson > "${reportTxt}"; then
- echo "[错误] 文本覆盖率生成失败。"
- exit 1
- fi
- if ! llvm-cov show ./build/linux/x86/release/RyanJson \
- -instr-profile="${mergedProfdata}" \
- -format=html \
- -output-dir="${reportHtml}" \
- -show-mcdc-summary \
- -show-branches=count \
- -show-expansions \
- -show-regions \
- -show-line-counts-or-regions \
- -sources ./RyanJson; then
- echo "[错误] HTML 覆盖率生成失败。"
- exit 1
- fi
- fi
- echo
- echo "单元测试矩阵执行完成。"
- echo "执行模式:${unitMode}"
- echo "总用例数:${totalCases}"
- echo "失败用例数:${failedCases}"
- echo "覆盖率输出目录:${coverageRoot}"
- if [[ "${unitSkipCov}" != "1" ]]; then
- echo "覆盖率文本报告:${coverageRoot}/report.txt"
- echo "覆盖率HTML目录:${coverageRoot}/html"
- fi
- if [[ "${failedCases}" -gt 0 ]]; then
- exit 1
- fi
|