254 lines
7.5 KiB
Bash
Executable file
254 lines
7.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -u
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: run_matrix.sh [--all|--unit|--cross]
|
|
|
|
Runs Proto Socket unit/same-language tests and/or cross-language communication
|
|
tests, then prints Markdown PASS/FAIL tables. Defaults to --all.
|
|
USAGE
|
|
}
|
|
|
|
mode="all"
|
|
case "${1:-}" in
|
|
""|--all) mode="all" ;;
|
|
--unit) mode="unit" ;;
|
|
--cross) mode="cross" ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) usage >&2; exit 2 ;;
|
|
esac
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "$script_dir/../../../../.." && pwd)"
|
|
log_dir="$(mktemp -d "${TMPDIR:-/tmp}/proto-socket-matrix.XXXXXX")"
|
|
|
|
languages=("Dart" "Go" "Kotlin" "Python" "TypeScript")
|
|
|
|
declare -A unit_cmds=(
|
|
["Dart"]="dart test"
|
|
["Go"]="go test ./..."
|
|
["Kotlin"]="./gradlew test"
|
|
["Python"]="python3 -m pytest -q"
|
|
["TypeScript"]="npm run check && npm test"
|
|
)
|
|
declare -A unit_dirs=(
|
|
["Dart"]="$repo_root/dart"
|
|
["Go"]="$repo_root/go"
|
|
["Kotlin"]="$repo_root/kotlin"
|
|
["Python"]="$repo_root/python"
|
|
["TypeScript"]="$repo_root/typescript"
|
|
)
|
|
declare -A unit_result
|
|
declare -A unit_status
|
|
declare -A unit_summary
|
|
|
|
cross_cases=(
|
|
"Go|Dart|$repo_root/go|go run ./crosstest/go_dart.go"
|
|
"Go|Kotlin|$repo_root/go|go run ./crosstest/go_kotlin.go"
|
|
"Go|Python|$repo_root/go|go run ./crosstest/go_python.go"
|
|
"Go|TypeScript|$repo_root/go|go run ./crosstest/go_typescript.go"
|
|
"Dart|Go|$repo_root/dart|dart run crosstest/dart_go.dart"
|
|
"Dart|Kotlin|$repo_root/dart|dart run crosstest/dart_kotlin.dart"
|
|
"Dart|Python|$repo_root/dart|dart run crosstest/dart_python.dart"
|
|
"Dart|TypeScript|$repo_root/dart|dart run crosstest/dart_typescript.dart"
|
|
"Kotlin|Dart|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartKt"
|
|
"Kotlin|Go|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinGoKt"
|
|
"Kotlin|Python|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinPythonKt"
|
|
"Kotlin|TypeScript|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinTypescriptKt"
|
|
"Python|Dart|$repo_root/python|python3 crosstest/python_dart.py"
|
|
"Python|Go|$repo_root/python|python3 crosstest/python_go.py"
|
|
"Python|Kotlin|$repo_root/python|python3 crosstest/python_kotlin.py"
|
|
"Python|TypeScript|$repo_root/python|python3 crosstest/python_typescript.py"
|
|
"TypeScript|Dart|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_dart.ts"
|
|
"TypeScript|Go|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_go.ts"
|
|
"TypeScript|Kotlin|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_kotlin.ts"
|
|
"TypeScript|Python|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_python.ts"
|
|
)
|
|
declare -A cross_result
|
|
declare -A cross_status
|
|
declare -A cross_pass_lines
|
|
declare -A cross_fail_lines
|
|
declare -A cross_cmds
|
|
|
|
expected_cross_pass_lines=16
|
|
|
|
count_lines() {
|
|
local pattern="$1"
|
|
local file="$2"
|
|
local count
|
|
if command -v rg >/dev/null 2>&1; then
|
|
count="$(rg -c "$pattern" "$file" 2>/dev/null || true)"
|
|
else
|
|
count="$(grep -Ec "$pattern" "$file" 2>/dev/null || true)"
|
|
fi
|
|
echo "${count:-0}"
|
|
}
|
|
|
|
run_logged() {
|
|
local name="$1"
|
|
local dir="$2"
|
|
local cmd="$3"
|
|
local log_file="$4"
|
|
|
|
printf 'RUN %s\n' "$name" >&2
|
|
(
|
|
cd "$dir" &&
|
|
bash -lc "$cmd"
|
|
) >"$log_file" 2>&1
|
|
}
|
|
|
|
run_unit_tests() {
|
|
local lang cmd dir log_file status
|
|
for lang in "${languages[@]}"; do
|
|
cmd="${unit_cmds[$lang]}"
|
|
dir="${unit_dirs[$lang]}"
|
|
log_file="$log_dir/unit_${lang}.log"
|
|
run_logged "unit $lang: $cmd" "$dir" "$cmd" "$log_file"
|
|
status=$?
|
|
unit_status[$lang]="$status"
|
|
if [ "$status" -eq 0 ]; then
|
|
unit_result[$lang]="PASS"
|
|
else
|
|
unit_result[$lang]="FAIL"
|
|
fi
|
|
unit_summary[$lang]="$(tail -n 1 "$log_file" | tr '\n' ' ')"
|
|
done
|
|
}
|
|
|
|
run_cross_tests() {
|
|
local item server client dir cmd key log_file status pass_count fail_count
|
|
for item in "${cross_cases[@]}"; do
|
|
IFS='|' read -r server client dir cmd <<<"$item"
|
|
key="$server|$client"
|
|
log_file="$log_dir/cross_${server}_${client}.log"
|
|
cross_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)"
|
|
run_logged "cross $server->$client: $cmd" "$dir" "$cmd" "$log_file"
|
|
status=$?
|
|
pass_count="$(count_lines '^PASS scenario=' "$log_file")"
|
|
fail_count="$(count_lines '^FAIL ' "$log_file")"
|
|
cross_status[$key]="$status"
|
|
cross_pass_lines[$key]="$pass_count"
|
|
cross_fail_lines[$key]="$fail_count"
|
|
if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_cross_pass_lines" ]; then
|
|
cross_result[$key]="PASS"
|
|
else
|
|
cross_result[$key]="FAIL"
|
|
fi
|
|
done
|
|
}
|
|
|
|
print_unit_table() {
|
|
local lang
|
|
printf '\n**동일언어**\n'
|
|
printf '| 언어 | 명령 | 결과 |\n'
|
|
printf '|---|---|---|\n'
|
|
for lang in "${languages[@]}"; do
|
|
printf '| %s | `%s` | %s |\n' "$lang" "${unit_cmds[$lang]}" "${unit_result[$lang]:--}"
|
|
done
|
|
}
|
|
|
|
cell_value() {
|
|
local server="$1"
|
|
local client="$2"
|
|
local key="$server|$client"
|
|
if [ "$server" = "$client" ]; then
|
|
if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
|
|
echo "${unit_result[$server]:--}"
|
|
else
|
|
echo "-"
|
|
fi
|
|
return
|
|
fi
|
|
echo "${cross_result[$key]:--}"
|
|
}
|
|
|
|
print_cross_table() {
|
|
local server client
|
|
printf '\n**언어간 통신**\n'
|
|
printf '| 서버 \\ 클라이언트 | Dart | Go | Kotlin | Python | TypeScript |\n'
|
|
printf '|---|---|---|---|---|---|\n'
|
|
for server in "${languages[@]}"; do
|
|
printf '| %s' "$server"
|
|
for client in "${languages[@]}"; do
|
|
printf ' | %s' "$(cell_value "$server" "$client")"
|
|
done
|
|
printf ' |\n'
|
|
done
|
|
}
|
|
|
|
print_cross_detail_table() {
|
|
local item server client dir cmd key
|
|
printf '\n**크로스테스트 상세**\n'
|
|
printf '| 방향 | 결과 | PASS scenarios | Expected | FAIL lines |\n'
|
|
printf '|---|---:|---:|---:|---:|\n'
|
|
for item in "${cross_cases[@]}"; do
|
|
IFS='|' read -r server client dir cmd <<<"$item"
|
|
key="$server|$client"
|
|
printf '| %s -> %s | %s | %s | %s | %s |\n' \
|
|
"$server" "$client" "${cross_result[$key]:--}" "${cross_pass_lines[$key]:--}" "$expected_cross_pass_lines" "${cross_fail_lines[$key]:--}"
|
|
done
|
|
}
|
|
|
|
print_failures() {
|
|
local failed=0
|
|
local lang item server client dir cmd key log_file
|
|
|
|
for lang in "${languages[@]}"; do
|
|
if [ "${unit_result[$lang]:-}" = "FAIL" ]; then
|
|
failed=1
|
|
log_file="$log_dir/unit_${lang}.log"
|
|
printf '\n**실패: %s 유닛/동일언어 테스트**\n' "$lang"
|
|
printf '재현 명령: `(cd %s && %s)`\n\n' "${unit_dirs[$lang]#$repo_root/}" "${unit_cmds[$lang]}"
|
|
tail -n 80 "$log_file"
|
|
printf '\n'
|
|
fi
|
|
done
|
|
|
|
for item in "${cross_cases[@]}"; do
|
|
IFS='|' read -r server client dir cmd <<<"$item"
|
|
key="$server|$client"
|
|
if [ "${cross_result[$key]:-}" = "FAIL" ]; then
|
|
failed=1
|
|
log_file="$log_dir/cross_${server}_${client}.log"
|
|
printf '\n**실패: %s -> %s 크로스테스트**\n' "$server" "$client"
|
|
printf '재현 명령: `%s`\n\n' "${cross_cmds[$key]}"
|
|
tail -n 80 "$log_file"
|
|
printf '\n'
|
|
fi
|
|
done
|
|
|
|
return "$failed"
|
|
}
|
|
|
|
main() {
|
|
case "$mode" in
|
|
all)
|
|
run_unit_tests
|
|
run_cross_tests
|
|
;;
|
|
unit)
|
|
run_unit_tests
|
|
;;
|
|
cross)
|
|
run_cross_tests
|
|
;;
|
|
esac
|
|
|
|
if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
|
|
print_unit_table
|
|
fi
|
|
if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then
|
|
print_cross_table
|
|
print_cross_detail_table
|
|
fi
|
|
|
|
printf '\n로그 디렉터리: `%s`\n' "$log_dir"
|
|
|
|
if print_failures; then
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
main "$@"
|