Files
hakorune/tools/smokes/v2/lib/test_runner.sh

517 lines
21 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# test_runner.sh - 中核実行器(強制使用)
# スモークテストv2の核心ライブラリ
# set -eは使わない個々のテストが失敗しても全体を続行するため
set -uo pipefail
# ルート/バイナリ検出CWDに依存しない実行を保証
if [ -z "${NYASH_ROOT:-}" ]; then
export NYASH_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
fi
# Prefer hakorune binary if exists; fallback to nyash for compatibility
if [ -z "${NYASH_BIN:-}" ]; then
if [ -x "$NYASH_ROOT/target/release/hakorune" ]; then
export NYASH_BIN="$NYASH_ROOT/target/release/hakorune"
else
export NYASH_BIN="$NYASH_ROOT/target/release/nyash"
fi
fi
# グローバル変数
export SMOKES_V2_LIB_LOADED=1
export SMOKES_START_TIME=$(date +%s.%N)
export SMOKES_TEST_COUNT=0
export SMOKES_PASS_COUNT=0
export SMOKES_FAIL_COUNT=0
export SMOKES_INCLUDE_SKIP_COUNT=0
declare -a SMOKES_INCLUDE_SKIP_LIST=()
# 色定義(重複回避)
if [ -z "${RED:-}" ]; then
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
fi
# ログ関数
log_info() {
echo -e "${BLUE}[INFO]${NC} $*" >&2
}
log_success() {
echo -e "${GREEN}[PASS]${NC} $*" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $*" >&2
}
log_error() {
echo -e "${RED}[FAIL]${NC} $*" >&2
}
# 共通イズフィルタVM実行時の出力整形
filter_noise() {
# プラグイン初期化やメタログ、動的ローダの案内等を除去
grep -v "^\[UnifiedBoxRegistry\]" \
| grep -v "^\[FileBox\]" \
| grep -v "^Net plugin:" \
| grep -v "^\[.*\] Plugin" \
| grep -v "Using builtin StringBox" \
| grep -v "Using builtin ArrayBox" \
| grep -v "Using builtin MapBox" \
| grep -v "^\[using\]" \
| grep -v "^\[using/resolve\]" \
| grep -v "^\[builder\]" \
| grep -v "^\\[vm-trace\\]" \
| grep -v "^\[DEBUG\]" \
| grep -v '^\{"ev":' \
| grep -v '^\[warn\] dev fallback: user instance BoxCall' \
| sed -E 's/^❌ VM fallback error: *//' \
| grep -v '^\[warn\] dev verify: NewBox ' \
| grep -v '^\[warn\] dev verify: NewBox→birth invariant warnings:' \
| grep -v '^\[ny-compiler\]' \
| grep -v '^\[using/cache\]' \
| grep -v "plugins/nyash-array-plugin" \
| grep -v "plugins/nyash-map-plugin" \
| grep -v "Phase 15.5: Everything is Plugin" \
| grep -v "cargo build -p nyash-string-plugin" \
| grep -v "^\[plugin-loader\] backend=" \
| grep -v "^\[using\] ctx:" \
| grep -v "^🔌 plugin host initialized" \
| grep -v "^✅ plugin host fully configured" \
| grep -v "Failed to load nyash.toml - plugins disabled" \
| grep -v "^🚀 Nyash VM Backend - Executing file:" \
| grep -v "^🚀 Hakorune VM Backend - Executing file:"
}
# 環境チェック(必須)
require_env() {
local required_tools=("cargo" "grep" "jq")
local missing_tools=()
for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &> /dev/null; then
missing_tools+=("$tool")
fi
done
if [ ${#missing_tools[@]} -ne 0 ]; then
log_error "Required tools missing: ${missing_tools[*]}"
log_error "Please install missing tools and try again"
return 1
fi
# Nyash実行ファイル確認
if [ ! -f "$NYASH_BIN" ]; then
log_error "Nyash executable not found at $NYASH_BIN"
log_error "Please run 'cargo build --release' first (in $NYASH_ROOT)"
return 1
fi
log_info "Environment check passed"
return 0
}
# プラグイン整合性チェック(必須)
preflight_plugins() {
# プラグインマネージャーが存在する場合は実行
if [ -f "$(dirname "${BASH_SOURCE[0]}")/plugin_manager.sh" ]; then
source "$(dirname "${BASH_SOURCE[0]}")/plugin_manager.sh"
check_plugin_integrity || return 1
else
log_warn "Plugin manager not found, skipping plugin checks"
fi
return 0
}
# テスト実行関数
run_test() {
local test_name="$1"
local test_func="$2"
((SMOKES_TEST_COUNT++))
local start_time=$(date +%s.%N)
log_info "Running test: $test_name"
if $test_func; then
local end_time=$(date +%s.%N)
local duration=$(echo "$end_time - $start_time" | bc -l)
log_success "$test_name (${duration}s)"
((SMOKES_PASS_COUNT++))
return 0
else
local end_time=$(date +%s.%N)
local duration=$(echo "$end_time - $start_time" | bc -l)
log_error "$test_name (${duration}s)"
((SMOKES_FAIL_COUNT++))
return 1
fi
}
# Nyash実行ヘルパーRust VM
run_nyash_vm() {
local program="$1"
shift
local USE_PYVM="${SMOKES_USE_PYVM:-0}"
local EXTRA_ARGS=()
if [ "${SMOKES_USE_DEV:-0}" = "1" ]; then
EXTRA_ARGS+=("--dev")
fi
# Optional env sanitization between rapid invocations (default OFF)
# Enable with: SMOKES_CLEAN_ENV=1
local ENV_PREFIX=( )
if [ "${SMOKES_CLEAN_ENV:-0}" = "1" ]; then
ENV_PREFIX=(env -u NYASH_DEBUG_ENABLE -u NYASH_DEBUG_KINDS -u NYASH_DEBUG_SINK \
-u NYASH_RESOLVE_FIX_BRACES -u NYASH_USING_AST \
-u NYASH_VM_TRACE -u NYASH_VM_VERIFY_MIR -u NYASH_VM_TOLERATE_VOID \
-u NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN)
fi
# -c オプションの場合は一時ファイル経由で実行
if [ "$program" = "-c" ]; then
local code="$1"
shift
local tmpfile="/tmp/nyash_test_$$.nyash"
echo "$code" > "$tmpfile"
# 軽量ASIFixテスト用: ブロック終端の余剰セミコロンを寛容に除去
if [ "${SMOKES_ASI_STRIP_SEMI:-1}" = "1" ]; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$tmpfile" || true
fi
# プラグイン初期化メッセージを除外
# Optional preinclude for include-based code
local runfile="$tmpfile"
if [ "${NYASH_PREINCLUDE:-0}" = "1" ] || [ "${HAKO_PREINCLUDE:-0}" = "1" ]; then
local prefile="/tmp/nyash_pre_$$.nyash"
"$NYASH_ROOT/tools/dev/hako_preinclude.sh" "$tmpfile" "$prefile" >/dev/null || true
runfile="$prefile"
fi
# Optional hint for include lines when preinclude is OFF
if grep -q '^include\s\"' "$tmpfile" 2>/dev/null && [ "${NYASH_PREINCLUDE:-0}" != "1" ] && [ "${HAKO_PREINCLUDE:-0}" != "1" ]; then
echo "[WARN] VM backend does not support include. Prefer using+alias, or set NYASH_PREINCLUDE=1 for dev." >&2
fi
NYASH_VM_USE_PY="$USE_PYVM" NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 \
NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
"${ENV_PREFIX[@]}" \
"$NYASH_BIN" --backend vm "$runfile" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
local exit_code=${PIPESTATUS[0]}
# prefile may be unset when preinclude is OFF; use default expansion to avoid set -u errors
rm -f "$tmpfile" "${prefile:-}" 2>/dev/null || true
return $exit_code
else
# 軽量ASIFixテスト用: ブロック終端の余剰セミコロンを寛容に除去
if [ "${SMOKES_ASI_STRIP_SEMI:-1}" = "1" ] && [ -f "$program" ]; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$program" || true
fi
# プラグイン初期化メッセージを除外
# Optional preinclude
local runfile2="$program"
if [ "${NYASH_PREINCLUDE:-0}" = "1" ] || [ "${HAKO_PREINCLUDE:-0}" = "1" ]; then
local prefile2="/tmp/nyash_pre_$$.nyash"
"$NYASH_ROOT/tools/dev/hako_preinclude.sh" "$program" "$prefile2" >/dev/null || true
runfile2="$prefile2"
fi
# Optional hint for include lines when preinclude is OFF
if [ -f "$program" ] && grep -q '^include\s\"' "$program" 2>/dev/null && [ "${NYASH_PREINCLUDE:-0}" != "1" ] && [ "${HAKO_PREINCLUDE:-0}" != "1" ]; then
# Policy: quick は SKIP 既定。それ以外は WARNSMOKES_INCLUDE_POLICY で上書き可能)。
local policy="${SMOKES_INCLUDE_POLICY:-}"
if [ -z "$policy" ]; then
case "$program" in
*/profiles/quick/*) policy="skip" ;;
*) policy="warn" ;;
esac
fi
if [ "$policy" = "skip" ]; then
SMOKES_INCLUDE_SKIP_COUNT=$((SMOKES_INCLUDE_SKIP_COUNT + 1))
local rel_path="$program"
if [[ "$program" == "$NYASH_ROOT/"* ]]; then
rel_path="${program#$NYASH_ROOT/}"
fi
SMOKES_INCLUDE_SKIP_LIST+=("$rel_path")
echo "[SKIP] include is deprecated in 20.36+ (quick). Prefer using+alias." >&2
return 0
elif [ "$policy" = "error" ]; then
echo "[ERROR] include is deprecated in 20.36+. Prefer using+alias." >&2
return 2
else
echo "[WARN] include is deprecated in 20.36+. Prefer using+alias. Preinclude is dev-only (NYASH_PREINCLUDE=1)." >&2
fi
fi
NYASH_VM_USE_PY="$USE_PYVM" NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 \
NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 \
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 \
"${ENV_PREFIX[@]}" \
"$NYASH_BIN" --backend vm "$runfile2" "${EXTRA_ARGS[@]}" "$@" 2>&1 | filter_noise
local exit_code=${PIPESTATUS[0]}
# prefile2 may be unset when preinclude is OFF
rm -f "${prefile2:-}" 2>/dev/null || true
return $exit_code
fi
}
# Verify MIR JSON rc using selected primary (Core or Hakorune VM)
verify_mir_rc() {
local json_path="$1"
# 20.36: hakovm を primary 既定へCore は診断 fallback
local primary="${HAKO_VERIFY_PRIMARY:-hakovm}"
if [ "$primary" = "hakovm" ]; then
# If the payload is MIR JSON v1 (schema_version present), Mini-VM cannot execute it yet.
# Route to Core fallback directly to keep canaries meaningful while Mini-VM gains v1 support.
if grep -q '"schema_version"' "$json_path" 2>/dev/null; then
# Optional: hakovm v1 verify (flagged). Default remains Core.
if [ "${HAKO_VERIFY_V1_HAKOVM:-0}" = "1" ]; then
local json_literal_v1
json_literal_v1="$(jq -Rs . < "$json_path")"
local code_v1=$(cat <<'HCODE'
using selfhost.vm.hv1.dispatch as NyVmDispatcherV1Box
static box Main { method main(args) {
local j = __MIR_JSON__
local rc = NyVmDispatcherV1Box.run(j)
print("" + rc)
return rc
} }
HCODE
)
code_v1="${code_v1/__MIR_JSON__/$json_literal_v1}"
local out_v1; out_v1=$(NYASH_USING_AST=1 run_nyash_vm -c "$code_v1" 2>/dev/null | tr -d '\r' | tail -n 1)
if [[ "$out_v1" =~ ^-?[0-9]+$ ]]; then
local n=$out_v1; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n
fi
# fallback to Core when hakovm v1 path not ready
fi
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1
return $?
fi
# Build a tiny driver to call MiniVmEntryBox.run_min with JSON literal embedded
if [ ! -f "$json_path" ]; then
echo "[FAIL] verify_mir_rc: json not found: $json_path" >&2
return 2
fi
# Escape JSON as a single string literal via jq -Rs (preserves newlines)
local json_literal
json_literal="$(jq -Rs . < "$json_path")"
build_and_run_driver_alias() {
local header="$1"
local code=$(cat <<HCODE
$header
static box Main { method main(args) {
local j = __MIR_JSON__;
local rc = MiniVmEntryBox.run_min(j)
print(MiniVmEntryBox.int_to_str(rc))
return rc
} }
HCODE
)
code="${code/__MIR_JSON__/$json_literal}"
NYASH_USING_AST=1 NYASH_RESOLVE_FIX_BRACES=1 run_nyash_vm -c "$code" 2>/dev/null | tr -d '\r' | tail -n 1
}
build_and_run_driver_include() {
local inc_path="$1"
local code=$(cat <<HCODE
include "$inc_path"
static box Main { method main(args) {
local j = __MIR_JSON__;
local rc = MiniVmEntryBox.run_min(j)
print(MiniVmEntryBox.int_to_str(rc))
return rc
} }
HCODE
)
code="${code/__MIR_JSON__/$json_literal}"
NYASH_PREINCLUDE=1 NYASH_RESOLVE_FIX_BRACES=1 run_nyash_vm -c "$code" 2>/dev/null | tr -d '\r' | tail -n 1
}
# Try alias header first; fallback to dev-file header; final fallback: include+preinclude
local out
out="$(build_and_run_driver_alias 'using selfhost.vm.entry as MiniVmEntryBox')"
if ! [[ "$out" =~ ^-?[0-9]+$ ]]; then
out="$(build_and_run_driver_alias 'using "lang/src/vm/boxes/mini_vm_entry.hako" as MiniVmEntryBox')"
fi
if ! [[ "$out" =~ ^-?[0-9]+$ ]]; then
out="$(build_and_run_driver_include 'lang/src/vm/boxes/mini_vm_entry.hako')"
fi
if [[ "$out" =~ ^-?[0-9]+$ ]]; then
local n=$out
# normalize into [0,255]
if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi
return $n
fi
# Fallback: core primary when MiniVM resolution is unavailable
if grep -q '"functions"' "$json_path" 2>/dev/null && grep -q '"blocks"' "$json_path" 2>/dev/null; then
local json_literal3; json_literal3="$(jq -Rs . < "$json_path")"
local code=$(cat <<HCODE
include "lang/src/vm/core/dispatcher.hako"
static box Main { method main(args) {
local j = __MIR_JSON__
local r = NyVmDispatcher.run(j)
print("" + r)
return r
} }
HCODE
)
code="${code/__MIR_JSON__/$json_literal3}"
local tmpwrap="/tmp/hako_core_wrap_$$.nyash"
echo "$code" > "$tmpwrap"
NYASH_PREINCLUDE=1 run_nyash_vm "$tmpwrap" >/dev/null 2>&1; local r=$?; rm -f "$tmpwrap"; return $r
fi
NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 "$NYASH_BIN" --json-file "$json_path" >/dev/null 2>&1; return $?
else
# Core primary: detect MIR(JSON) vs Program(JSON v0)
if grep -q '"functions"' "$json_path" 2>/dev/null && grep -q '"blocks"' "$json_path" 2>/dev/null; then
"$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1; return $?
fi
NYASH_GATE_C_CORE=1 HAKO_GATE_C_CORE=1 "$NYASH_BIN" --json-file "$json_path" >/dev/null 2>&1; return $?
fi
}
# Nyash実行ヘルパーLLVM
run_nyash_llvm() {
local program="$1"
shift
# Allow developer to force LLVM run (env guarantees availability)
if [ "${SMOKES_FORCE_LLVM:-0}" != "1" ]; then
# Skip gracefully when LLVM backend is not available in this build
# Primary check: version string advertises features
if ! "$NYASH_BIN" --version 2>/dev/null | grep -q "features.*llvm"; then
# Fallback check: binary contains LLVM harness symbols (ny-llvmc / NYASH_LLVM_USE_HARNESS)
if ! strings "$NYASH_BIN" 2>/dev/null | grep -E -q 'ny-llvmc|NYASH_LLVM_USE_HARNESS'; then
log_warn "LLVM backend not available in this build; skipping LLVM run"
log_info "Hint: build ny-llvmc + enable harness: cargo build --release -p nyash-llvm-compiler && cargo build --release --features llvm"
return 0
fi
fi
fi
# -c オプションの場合は一時ファイル経由で実行
if [ "$program" = "-c" ]; then
local code="$1"
shift
local tmpfile="/tmp/nyash_test_$$.nyash"
echo "$code" > "$tmpfile"
# 軽量ASIFixテスト用: ブロック終端の余剰セミコロンを寛容に除去
if [ "${SMOKES_ASI_STRIP_SEMI:-1}" = "1" ]; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$tmpfile" || true
fi
# 軽量ASIFixテスト用: ブロック終端の余剰セミコロンを寛容に除去
if [ "${SMOKES_ASI_STRIP_SEMI:-1}" = "1" ] && [ -f "$program" ]; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$program" || true
fi
# プラグイン初期化メッセージを除外
PYTHONPATH="${PYTHONPATH:-$NYASH_ROOT}" NYASH_NY_LLVM_COMPILER="$NYASH_ROOT/target/release/ny-llvmc" NYASH_LLVM_USE_HARNESS=1 NYASH_EMIT_EXE_NYRT="$NYASH_ROOT/target/release" NYASH_VM_USE_PY=0 NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 "$NYASH_BIN" --backend llvm "$tmpfile" "$@" 2>&1 | \
grep -v "^\[UnifiedBoxRegistry\]" | grep -v "^\[FileBox\]" | grep -v "^Net plugin:" | grep -v "^\[.*\] Plugin" | \
grep -v '^\[plugin-loader\] backend=' | \
grep -v '^🔌 plugin host initialized' | grep -v '^✅ plugin host fully configured' | \
grep -v '^⚡ Hakorune LLVM Backend' | \
grep -v '^✅ LLVM (harness) execution completed' | grep -v '^📊 MIR Module compiled successfully' | grep -v '^📊 Functions:' | grep -v 'JSON Parse Errors:' | grep -v 'Parsing errors' | grep -v 'No parsing errors' | grep -v 'Error at line ' | \
grep -v '^\[using\]' | grep -v '^\[using/resolve\]' | grep -v '^\[using/cache\]' | \
grep -v '^\[ny-llvmc\]' | grep -v '^\[harness\]' | grep -v '^Compiled to ' | grep -v '^/usr/bin/ld:'
local exit_code=${PIPESTATUS[0]}
rm -f "$tmpfile"
return $exit_code
else
# 軽量ASIFixテスト用: ブロック終端の余剰セミコロンを寛容に除去
if [ "${SMOKES_ASI_STRIP_SEMI:-1}" = "1" ] && [ -f "$program" ]; then
sed -i -E 's/;([[:space:]]*)(\}|$)/\1\2/g' "$program" || true
fi
# プラグイン初期化メッセージを除外
PYTHONPATH="${PYTHONPATH:-$NYASH_ROOT}" NYASH_NY_LLVM_COMPILER="$NYASH_ROOT/target/release/ny-llvmc" NYASH_LLVM_USE_HARNESS=1 NYASH_EMIT_EXE_NYRT="$NYASH_ROOT/target/release" NYASH_VM_USE_PY=0 NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_DISABLE_NY_COMPILER=1 HAKO_DISABLE_NY_COMPILER=1 "$NYASH_BIN" --backend llvm "$program" "$@" 2>&1 | \
grep -v "^\[UnifiedBoxRegistry\]" | grep -v "^\[FileBox\]" | grep -v "^Net plugin:" | grep -v "^\[.*\] Plugin" | \
grep -v '^\[plugin-loader\] backend=' | \
grep -v '^🔌 plugin host initialized' | grep -v '^✅ plugin host fully configured' | \
grep -v '^⚡ Hakorune LLVM Backend' | \
grep -v '^✅ LLVM (harness) execution completed' | grep -v '^📊 MIR Module compiled successfully' | grep -v '^📊 Functions:' | grep -v 'JSON Parse Errors:' | grep -v 'Parsing errors' | grep -v 'No parsing errors' | grep -v 'Error at line ' | \
grep -v '^\[using\]' | grep -v '^\[using/resolve\]' | grep -v '^\[using/cache\]' | \
grep -v '^\[ny-llvmc\]' | grep -v '^\[harness\]' | grep -v '^Compiled to ' | grep -v '^/usr/bin/ld:'
return ${PIPESTATUS[0]}
fi
}
# シンプルテスト補助(スクリプト互換)
test_pass() { log_success "$1"; return 0; }
test_fail() { log_error "$1 ${2:-}"; return 1; }
test_skip() { log_warn "SKIP $1 ${2:-}"; return 0; }
# 出力比較ヘルパー
compare_outputs() {
local expected="$1"
local actual="$2"
local test_name="$3"
if [ "$expected" = "$actual" ]; then
return 0
else
log_error "$test_name output mismatch:"
log_error " Expected: $expected"
log_error " Actual: $actual"
return 1
fi
}
# 結果サマリー出力
print_summary() {
local end_time=$(date +%s.%N)
local total_duration=$(echo "$end_time - $SMOKES_START_TIME" | bc -l)
echo ""
echo "==============================================="
echo "Smoke Test Summary"
echo "==============================================="
echo "Total tests: $SMOKES_TEST_COUNT"
echo "Passed: $SMOKES_PASS_COUNT"
echo "Failed: $SMOKES_FAIL_COUNT"
echo "Duration: ${total_duration}s"
echo ""
if [ "${SMOKES_INCLUDE_SKIP_COUNT:-0}" -gt 0 ]; then
echo "Include SKIPs: $SMOKES_INCLUDE_SKIP_COUNT"
for entry in "${SMOKES_INCLUDE_SKIP_LIST[@]}"; do
echo " - $entry"
done
echo ""
fi
if [ $SMOKES_FAIL_COUNT -eq 0 ]; then
log_success "All tests passed! ✨"
return 0
else
log_error "$SMOKES_FAIL_COUNT test(s) failed"
return 1
fi
}
# JSON出力関数
output_json() {
local profile="${1:-unknown}"
local end_time=$(date +%s.%N)
local total_duration=$(echo "$end_time - $SMOKES_START_TIME" | bc -l)
cat << EOF
{
"profile": "$profile",
"total": $SMOKES_TEST_COUNT,
"passed": $SMOKES_PASS_COUNT,
"failed": $SMOKES_FAIL_COUNT,
"duration": $total_duration,
"timestamp": "$(date -Iseconds)",
"success": $([ $SMOKES_FAIL_COUNT -eq 0 ] && echo "true" || echo "false")
}
EOF
}
# JUnit XML出力関数
output_junit() {
local profile="${1:-unknown}"
local end_time=$(date +%s.%N)
local total_duration=$(echo "$end_time - $SMOKES_START_TIME" | bc -l)
cat << EOF
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="smokes_$profile" tests="$SMOKES_TEST_COUNT" failures="$SMOKES_FAIL_COUNT" time="$total_duration">
<!-- Individual test cases would be added by specific test scripts -->
</testsuite>
EOF
}