test_host.sh (6672B)
1 #!/usr/bin/env bash 2 set -euo pipefail 3 4 script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" 5 platform_root="$(cd "${script_dir}/.." && pwd -P)" 6 repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel)" 7 date_utc="$(date -u +%F)" 8 9 require_command() { 10 if command -v "$1" >/dev/null 2>&1; then 11 return 12 fi 13 echo "missing required command: $1" >&2 14 exit 1 15 } 16 17 require_command /usr/libexec/PlistBuddy 18 require_command mktemp 19 require_command readlink 20 21 app_path="$("${script_dir}/build_host.sh")" 22 plist_path="${app_path}/Contents/Info.plist" 23 executable_name="$( 24 /usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "${plist_path}" 25 )" 26 bundle_name="$( 27 /usr/libexec/PlistBuddy -c 'Print :CFBundleName' "${plist_path}" 28 )" 29 bundle_id="$( 30 /usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "${plist_path}" 31 )" 32 icon_name="$( 33 /usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "${plist_path}" 34 )" 35 36 [[ "${bundle_name}" == "Radroots" ]] || { 37 echo "unexpected CFBundleName: ${bundle_name}" >&2 38 exit 1 39 } 40 41 [[ "${bundle_id}" == "org.radroots.app.macos" ]] || { 42 echo "unexpected CFBundleIdentifier: ${bundle_id}" >&2 43 exit 1 44 } 45 46 [[ -x "${app_path}/Contents/MacOS/${executable_name}" ]] || { 47 echo "missing bundle executable: ${app_path}/Contents/MacOS/${executable_name}" >&2 48 exit 1 49 } 50 51 [[ -f "${app_path}/Contents/Resources/${icon_name}.icns" ]] || { 52 echo "missing bundle icon: ${app_path}/Contents/Resources/${icon_name}.icns" >&2 53 exit 1 54 } 55 56 release_app_path="$( 57 CONFIGURATION=Release "${script_dir}/build_host.sh" 58 )" 59 [[ "${release_app_path}" == "${platform_root}/.derived-data/Build/Products/Release/Radroots.app" ]] || { 60 echo "unexpected release bundle path: ${release_app_path}" >&2 61 exit 1 62 } 63 [[ -x "${release_app_path}/Contents/MacOS/Radroots" ]] || { 64 echo "missing release bundle executable: ${release_app_path}/Contents/MacOS/Radroots" >&2 65 exit 1 66 } 67 68 tmp_root="$(mktemp -d)" 69 runner_pid="" 70 degraded_runner_pid="" 71 startup_wait_attempts="${RADROOTS_APP_HOST_STARTUP_ATTEMPTS:-300}" 72 73 wait_for_log_event() { 74 local structured_log_file="$1" 75 local expected_event="$2" 76 local runner_pid="$3" 77 local event_verified=false 78 79 for _ in $(seq 1 "${startup_wait_attempts}"); do 80 if [[ -f "${structured_log_file}" ]] && grep -q "\"event\":\"${expected_event}\"" "${structured_log_file}" 2>/dev/null; then 81 event_verified=true 82 break 83 fi 84 85 if ! kill -0 "${runner_pid}" 2>/dev/null; then 86 wait "${runner_pid}" 87 exit $? 88 fi 89 90 sleep 0.1 91 done 92 93 [[ "${event_verified}" == "true" ]] || { 94 echo "${expected_event} was not recorded by run_host.sh" >&2 95 exit 1 96 } 97 } 98 99 assert_latest_alias() { 100 local latest_log_path="$1" 101 102 [[ -e "${latest_log_path}" ]] || { 103 echo "missing latest structured log alias: ${latest_log_path}" >&2 104 exit 1 105 } 106 107 [[ "$(readlink "${latest_log_path}")" == "${date_utc}.jsonl" ]] || { 108 echo "latest structured log alias does not point at ${date_utc}.jsonl" >&2 109 exit 1 110 } 111 } 112 113 assert_raw_logs_exist() { 114 local stdout_file="$1" 115 local stderr_file="$2" 116 117 [[ -f "${stdout_file}" ]] || { 118 echo "missing raw stdout log: ${stdout_file}" >&2 119 exit 1 120 } 121 122 [[ -f "${stderr_file}" ]] || { 123 echo "missing raw stderr log: ${stderr_file}" >&2 124 exit 1 125 } 126 } 127 128 terminate_runner() { 129 local runner_pid="$1" 130 131 if [[ -n "${runner_pid}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then 132 kill "${runner_pid}" 2>/dev/null || true 133 fi 134 135 set +e 136 wait "${runner_pid}" 137 local exit_code="$?" 138 set -e 139 [[ "${exit_code}" == "0" ]] || [[ "${exit_code}" == "143" ]] || [[ "${exit_code}" == "130" ]] || { 140 echo "unexpected runner exit code after termination: ${exit_code}" >&2 141 exit 1 142 } 143 } 144 145 cleanup() { 146 if [[ -n "${runner_pid:-}" ]] && kill -0 "${runner_pid}" 2>/dev/null; then 147 kill "${runner_pid}" 2>/dev/null || true 148 wait "${runner_pid}" || true 149 fi 150 if [[ -n "${degraded_runner_pid:-}" ]] && kill -0 "${degraded_runner_pid}" 2>/dev/null; then 151 kill "${degraded_runner_pid}" 2>/dev/null || true 152 wait "${degraded_runner_pid}" || true 153 fi 154 rm -rf "${tmp_root}" 155 } 156 trap cleanup EXIT 157 158 runtime_mode="localhost-dev" 159 nostr_relay_urls="${RADROOTS_APP_NOSTR_RELAY_URLS:-ws://127.0.0.1:8080}" 160 local_log_root="${tmp_root}/logs" 161 structured_log_file="${local_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" 162 latest_log_path="${local_log_root}/apps/local/app/app-macos-native/latest.jsonl" 163 stdout_file="${local_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" 164 stderr_file="${local_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" 165 166 RADROOTS_APP_RUNTIME_MODE="${runtime_mode}" \ 167 RADROOTS_APP_NOSTR_RELAY_URLS="${nostr_relay_urls}" \ 168 RADROOTS_APP_LOCAL_LOG_ROOT="${local_log_root}" \ 169 RADROOTS_APP_HOST_BUNDLE_PATH="${app_path}" \ 170 RADROOTS_APP_HOST_STARTUP_ATTEMPTS="${startup_wait_attempts}" \ 171 "${script_dir}/run_host.sh" & 172 runner_pid="$!" 173 174 wait_for_log_event "${structured_log_file}" "runtime.launch" "${runner_pid}" 175 wait_for_log_event "${structured_log_file}" "logging.initialized" "${runner_pid}" 176 assert_latest_alias "${latest_log_path}" 177 assert_raw_logs_exist "${stdout_file}" "${stderr_file}" 178 terminate_runner "${runner_pid}" 179 runner_pid="" 180 181 degraded_log_root="${tmp_root}/degraded-logs" 182 degraded_structured_log_file="${degraded_log_root}/apps/local/app/app-macos-native/${date_utc}.jsonl" 183 degraded_latest_log_path="${degraded_log_root}/apps/local/app/app-macos-native/latest.jsonl" 184 degraded_stdout_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stdout.${date_utc}.log" 185 degraded_stderr_file="${degraded_log_root}/apps/local/app/app-macos-native/raw/stderr.${date_utc}.log" 186 187 env -u HOME \ 188 RADROOTS_APP_RUNTIME_MODE="${runtime_mode}" \ 189 RADROOTS_APP_NOSTR_RELAY_URLS="${nostr_relay_urls}" \ 190 RADROOTS_APP_LOCAL_LOG_ROOT="${degraded_log_root}" \ 191 RADROOTS_APP_HOST_BUNDLE_PATH="${app_path}" \ 192 RADROOTS_APP_HOST_STARTUP_ATTEMPTS="${startup_wait_attempts}" \ 193 "${script_dir}/run_host.sh" & 194 degraded_runner_pid="$!" 195 196 wait_for_log_event "${degraded_structured_log_file}" "runtime.launch" "${degraded_runner_pid}" 197 wait_for_log_event "${degraded_structured_log_file}" "runtime.degraded" "${degraded_runner_pid}" 198 wait_for_log_event "${degraded_structured_log_file}" "logging.initialized" "${degraded_runner_pid}" 199 assert_latest_alias "${degraded_latest_log_path}" 200 assert_raw_logs_exist "${degraded_stdout_file}" "${degraded_stderr_file}" 201 grep -q '"startup_issue":"desktop runtime roots require HOME for macos"' "${degraded_structured_log_file}" || { 202 echo "runtime.degraded did not record the expected startup issue" >&2 203 exit 1 204 } 205 terminate_runner "${degraded_runner_pid}" 206 degraded_runner_pid=""