proto-socket/agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh
toki 5df325bf96 feat: Dart Web E2E 테스트 및 크로스 테스트 추가
- Dart Web 브라우저 E2E 테스트 프레임워크 추가
- browser_ws_dart_io_test, browser_ws_kotlin_test 등 브라우저 통합 테스트 추가
- Dart Web 크로스테스트 케이스 추가 (go, kotlin, python, typescript)
- TypeScript 브라우저 웹소켓 클라이언트 개선
- Node.js 웹소켓 서버 클라이언트 업데이트
- 테스트 실행 매트릭스 스킬 및 스크립트 업데이트
- README.md 문서 업데이트
2026-05-25 00:30:37 +09:00

365 lines
11 KiB
Bash
Executable file

#!/usr/bin/env bash
set -u
usage() {
cat <<'USAGE'
Usage: run_matrix.sh [--all|--unit|--cross|--proto]
Runs Proto Socket proto schema sync, 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" ;;
--proto) mode="proto" ;;
-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 pub get && dart test && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js"
["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
proto_cmd="tools/check_proto_sync.sh"
proto_dir="$repo_root"
proto_result="-"
proto_status=""
proto_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
web_cases=(
"Dart.io|$repo_root/dart|dart run crosstest/dart_web.dart"
"Go|$repo_root/go|go run ./crosstest/go_dart_web.go"
"Kotlin|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartWebKt"
"Python|$repo_root/python|python3 crosstest/python_dart_web.py"
"TypeScript|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_dart_web.ts"
)
declare -A web_result
declare -A web_status
declare -A web_pass_lines
declare -A web_fail_lines
declare -A web_cmds
expected_web_pass_lines=2
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 -c "$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_proto_sync() {
local log_file status
log_file="$log_dir/proto_sync.log"
run_logged "proto sync: $proto_cmd" "$proto_dir" "$proto_cmd" "$log_file"
status=$?
proto_status="$status"
if [ "$status" -eq 0 ]; then
proto_result="PASS"
else
proto_result="FAIL"
fi
proto_summary="$(tail -n 1 "$log_file" | tr '\n' ' ')"
}
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
}
run_web_tests() {
local item server dir cmd key log_file status pass_count fail_count
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
log_file="$log_dir/web_${server}.log"
web_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)"
run_logged "web $server->Dart.web: $cmd" "$dir" "$cmd" "$log_file"
status=$?
pass_count="$(count_lines '^PASS scenario=' "$log_file")"
fail_count="$(count_lines '^FAIL ' "$log_file")"
web_status[$key]="$status"
web_pass_lines[$key]="$pass_count"
web_fail_lines[$key]="$fail_count"
if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_web_pass_lines" ]; then
web_result[$key]="PASS"
else
web_result[$key]="FAIL"
fi
done
}
print_proto_table() {
printf '\n**Proto 동기화**\n'
printf '| 검사 | 명령 | 결과 |\n'
printf '|---|---|---|\n'
printf '| schema sync | `%s` | %s |\n' "$proto_cmd" "$proto_result"
}
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_dart_web_table() {
local item server dir cmd key
printf '\n**Dart.web 클라이언트 통신**\n'
printf '| 서버 | Dart.web (WS) | PASS scenarios | Expected | FAIL lines |\n'
printf '|---|---:|---:|---:|---:|\n'
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
printf '| %s | %s | %s | %s | %s |\n' \
"$server" "${web_result[$key]:--}" "${web_pass_lines[$key]:--}" "$expected_web_pass_lines" "${web_fail_lines[$key]:--}"
done
}
print_failures() {
local failed=0
local lang item server client dir cmd key log_file
if [ "${proto_result:-}" = "FAIL" ]; then
failed=1
log_file="$log_dir/proto_sync.log"
printf '\n**실패: Proto 동기화 검사**\n'
printf '재현 명령: `(cd . && %s)`\n\n' "$proto_cmd"
tail -n 80 "$log_file"
printf '\n'
fi
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
for item in "${web_cases[@]}"; do
IFS='|' read -r server dir cmd <<<"$item"
key="$server"
if [ "${web_result[$key]:-}" = "FAIL" ]; then
failed=1
log_file="$log_dir/web_${server}.log"
printf '\n**실패: %s -> Dart.web 크로스테스트**\n' "$server"
printf '재현 명령: `%s`\n\n' "${web_cmds[$key]}"
tail -n 80 "$log_file"
printf '\n'
fi
done
return "$failed"
}
main() {
case "$mode" in
all)
run_proto_sync
run_unit_tests
run_cross_tests
run_web_tests
;;
unit)
run_unit_tests
;;
cross)
run_cross_tests
run_web_tests
;;
proto)
run_proto_sync
;;
esac
if [ "$mode" = "all" ] || [ "$mode" = "proto" ]; then
print_proto_table
fi
if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then
print_unit_table
fi
if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then
print_cross_table
print_cross_detail_table
print_dart_web_table
fi
printf '\n로그 디렉터리: `%s`\n' "$log_dir"
if print_failures; then
exit 0
fi
exit 1
}
main "$@"