575 lines
19 KiB
Bash
Executable File
575 lines
19 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Read-only RK3588 media hardware inventory with optional short workloads.
|
|
|
|
set -uo pipefail
|
|
export LC_ALL=C
|
|
|
|
JSON=0
|
|
QUICK=0
|
|
STRICT=0
|
|
MEDIA_DIR=""
|
|
|
|
SYS_ROOT=${RK1_SYSFS_ROOT:-/sys}
|
|
PROC_ROOT=${RK1_PROCFS_ROOT:-/proc}
|
|
DEV_ROOT=${RK1_DEV_ROOT:-/dev}
|
|
RKNN_HOME=${RKNN_HOME:-/opt/rknn/current}
|
|
|
|
declare -a CHECK_GROUP=()
|
|
declare -a CHECK_NAME=()
|
|
declare -a CHECK_STATUS=()
|
|
declare -a CHECK_MESSAGE=()
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: rk1-media-selftest [OPTIONS]
|
|
|
|
Options:
|
|
--json Emit one JSON document instead of readable text.
|
|
--quick Inventory only; skip GPU, codec, and NPU workloads.
|
|
--strict Return nonzero for warnings as well as failures.
|
|
--media-dir PATH Exercise RKMPP decoding with named sample files.
|
|
Recognized names contain h264, hevc, vp9, or av1.
|
|
-h, --help Show this help.
|
|
|
|
The default run is non-destructive. It executes only short inference, Vulkan,
|
|
and encoder probes when the relevant runtime and devices are present. It never
|
|
changes clocks, governors, display modes, storage, or network configuration.
|
|
EOF
|
|
}
|
|
|
|
invocation_error() {
|
|
printf 'rk1-media-selftest: %s\n' "$*" >&2
|
|
exit 2
|
|
}
|
|
|
|
while (($#)); do
|
|
case "$1" in
|
|
--json)
|
|
JSON=1
|
|
shift
|
|
;;
|
|
--quick)
|
|
QUICK=1
|
|
shift
|
|
;;
|
|
--strict)
|
|
STRICT=1
|
|
shift
|
|
;;
|
|
--media-dir)
|
|
(($# >= 2)) || invocation_error "--media-dir requires a path"
|
|
MEDIA_DIR=$2
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
invocation_error "unknown option: $1"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -n "$MEDIA_DIR" && ! -d "$MEDIA_DIR" ]]; then
|
|
invocation_error "media directory is not a directory: $MEDIA_DIR"
|
|
fi
|
|
|
|
add_check() {
|
|
CHECK_GROUP+=("$1")
|
|
CHECK_NAME+=("$2")
|
|
CHECK_STATUS+=("$3")
|
|
CHECK_MESSAGE+=("$4")
|
|
}
|
|
|
|
read_text() {
|
|
local path=$1
|
|
if [[ -r "$path" ]]; then
|
|
tr -d '\000' <"$path" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
short_message() {
|
|
local value=$1
|
|
value=${value//$'\r'/}
|
|
value=${value//$'\n'/'; '}
|
|
value=${value//$'\t'/ }
|
|
printf '%s' "${value:0:320}"
|
|
}
|
|
|
|
driver_binding_count() {
|
|
local pattern=$1
|
|
local directory entry base
|
|
local count=0
|
|
shopt -s nullglob
|
|
for directory in "$SYS_ROOT"/bus/platform/drivers/$pattern; do
|
|
[[ -d "$directory" ]] || continue
|
|
for entry in "$directory"/*; do
|
|
base=${entry##*/}
|
|
case "$base" in
|
|
bind|unbind|uevent|module|new_id|remove_id) continue ;;
|
|
esac
|
|
if [[ -L "$entry" || -d "$entry" ]]; then
|
|
((count++))
|
|
fi
|
|
done
|
|
done
|
|
shopt -u nullglob
|
|
printf '%d' "$count"
|
|
}
|
|
|
|
find_sample() {
|
|
local token=$1
|
|
[[ -n "$MEDIA_DIR" ]] || return 1
|
|
find "$MEDIA_DIR" -maxdepth 1 -type f \
|
|
\( -iname "*${token}*.mkv" -o -iname "*${token}*.mp4" \
|
|
-o -iname "*${token}*.webm" -o -iname "*${token}*.ts" \) \
|
|
-print -quit 2>/dev/null
|
|
}
|
|
|
|
# System identity
|
|
machine=$(uname -m 2>/dev/null || printf unknown)
|
|
if [[ "$machine" == "aarch64" ]]; then
|
|
add_check system architecture pass "aarch64 userspace"
|
|
else
|
|
add_check system architecture fail "expected aarch64, found $machine"
|
|
fi
|
|
|
|
model=$(read_text "$PROC_ROOT/device-tree/model")
|
|
if [[ "$model" == *"Turing"*"RK1"* ]]; then
|
|
add_check system board pass "$model"
|
|
elif [[ -n "$model" ]]; then
|
|
add_check system board warn "device-tree model is $model"
|
|
else
|
|
add_check system board warn "device-tree model is unavailable"
|
|
fi
|
|
|
|
# HDMI / DRM connector state
|
|
shopt -s nullglob
|
|
hdmi_connectors=("$SYS_ROOT"/class/drm/card*-HDMI-A-*)
|
|
shopt -u nullglob
|
|
if ((${#hdmi_connectors[@]} == 0)); then
|
|
add_check hdmi connector warn "no DRM HDMI connector is registered"
|
|
else
|
|
connected_count=0
|
|
for connector in "${hdmi_connectors[@]}"; do
|
|
connector_name=${connector##*/}
|
|
state=$(read_text "$connector/status")
|
|
case "$state" in
|
|
connected)
|
|
((connected_count++))
|
|
add_check hdmi "$connector_name" pass "connected"
|
|
if [[ -s "$connector/edid" ]]; then
|
|
edid_bytes=$(wc -c <"$connector/edid" 2>/dev/null || printf 0)
|
|
add_check hdmi "$connector_name-edid" pass \
|
|
"$edid_bytes bytes of EDID data"
|
|
else
|
|
add_check hdmi "$connector_name-edid" warn \
|
|
"connected but EDID is empty or unreadable"
|
|
fi
|
|
if [[ -r "$connector/modes" ]] &&
|
|
grep -qx '3840x2160' "$connector/modes" 2>/dev/null; then
|
|
add_check hdmi "$connector_name-4k" pass \
|
|
"EDID advertises a 3840x2160 mode"
|
|
else
|
|
add_check hdmi "$connector_name-4k" warn \
|
|
"3840x2160 is not present in the connector mode list"
|
|
fi
|
|
;;
|
|
disconnected)
|
|
add_check hdmi "$connector_name" warn \
|
|
"disconnected (expected when no display is attached)"
|
|
;;
|
|
*)
|
|
add_check hdmi "$connector_name" warn \
|
|
"connector state is ${state:-unknown}"
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
|
|
hdmi_driver_count=$(driver_binding_count 'dwhdmi*')
|
|
hdmi_phy_count=$(driver_binding_count '*hdptx*hdmi*')
|
|
if ((hdmi_driver_count > 0 && hdmi_phy_count > 0)); then
|
|
add_check hdmi drivers pass \
|
|
"$hdmi_driver_count controller and $hdmi_phy_count HDMI PHY binding(s)"
|
|
else
|
|
add_check hdmi drivers warn \
|
|
"HDMI controller bindings=$hdmi_driver_count, PHY bindings=$hdmi_phy_count"
|
|
fi
|
|
|
|
# GPU / Vulkan
|
|
gpu_driver=""
|
|
for candidate in panthor panfrost mali; do
|
|
if (($(driver_binding_count "$candidate") > 0)); then
|
|
gpu_driver=$candidate
|
|
break
|
|
fi
|
|
done
|
|
case "$gpu_driver" in
|
|
panthor)
|
|
add_check gpu kernel-driver pass "Panthor is bound to the GPU"
|
|
;;
|
|
panfrost)
|
|
add_check gpu kernel-driver warn "Panfrost is bound; the image expects Panthor"
|
|
;;
|
|
mali)
|
|
add_check gpu kernel-driver warn "proprietary Mali kernel driver is bound"
|
|
;;
|
|
*)
|
|
add_check gpu kernel-driver warn "no bound Mali GPU driver was found"
|
|
;;
|
|
esac
|
|
|
|
shopt -s nullglob
|
|
gpu_devfreq=("$SYS_ROOT"/class/devfreq/*.gpu)
|
|
render_nodes=("$DEV_ROOT"/dri/renderD*)
|
|
shopt -u nullglob
|
|
if ((${#gpu_devfreq[@]} > 0)); then
|
|
add_check gpu devfreq pass "${#gpu_devfreq[@]} GPU devfreq device(s)"
|
|
else
|
|
add_check gpu devfreq warn "GPU devfreq node is missing"
|
|
fi
|
|
if ((${#render_nodes[@]} > 0)); then
|
|
add_check gpu render-node pass "${#render_nodes[@]} DRM render node(s)"
|
|
else
|
|
add_check gpu render-node warn "no accessible DRM render node"
|
|
fi
|
|
|
|
if ((QUICK)); then
|
|
add_check gpu vulkan skip "active Vulkan probe disabled by --quick"
|
|
elif command -v vulkaninfo >/dev/null 2>&1; then
|
|
vulkan_output=$(timeout 20s vulkaninfo --summary 2>&1)
|
|
vulkan_rc=$?
|
|
if ((vulkan_rc == 0)); then
|
|
gpu_name=$(printf '%s\n' "$vulkan_output" |
|
|
sed -n 's/^[[:space:]]*deviceName[[:space:]]*=[[:space:]]*//p' |
|
|
head -1)
|
|
add_check gpu vulkan pass "${gpu_name:-vulkaninfo completed}"
|
|
else
|
|
add_check gpu vulkan fail \
|
|
"vulkaninfo failed: $(short_message "$vulkan_output")"
|
|
fi
|
|
else
|
|
add_check gpu vulkan skip "vulkaninfo is not installed"
|
|
fi
|
|
|
|
# VPU bindings and FFmpeg integration
|
|
decoder_bindings=$(driver_binding_count 'mpp_rkvdec*')
|
|
encoder_bindings=$(driver_binding_count 'mpp_rkvenc*')
|
|
av1_bindings=$(driver_binding_count '*av1*')
|
|
if ((decoder_bindings > 0)); then
|
|
add_check vpu decoder-driver pass "$decoder_bindings RKMPP decoder binding(s)"
|
|
else
|
|
add_check vpu decoder-driver warn "RKMPP decoder bindings are missing"
|
|
fi
|
|
if ((encoder_bindings > 0)); then
|
|
add_check vpu encoder-driver pass "$encoder_bindings RKMPP encoder binding(s)"
|
|
else
|
|
add_check vpu encoder-driver warn "RKMPP encoder bindings are missing"
|
|
fi
|
|
if ((av1_bindings > 0)); then
|
|
add_check vpu av1-driver pass "$av1_bindings AV1 decoder binding(s)"
|
|
else
|
|
add_check vpu av1-driver warn "a separate AV1 driver binding was not found"
|
|
fi
|
|
|
|
FFMPEG=""
|
|
for candidate in /opt/rkmedia/bin/ffmpeg-rk /usr/local/bin/ffmpeg-rk; do
|
|
if [[ -x "$candidate" ]]; then
|
|
FFMPEG=$candidate
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$FFMPEG" ]] && command -v ffmpeg-rk >/dev/null 2>&1; then
|
|
FFMPEG=$(command -v ffmpeg-rk)
|
|
fi
|
|
|
|
if [[ -z "$FFMPEG" ]]; then
|
|
add_check vpu ffmpeg-rkmpp skip "ffmpeg-rk is not installed"
|
|
add_check rga ffmpeg-filter skip "ffmpeg-rk is not installed"
|
|
else
|
|
hwaccels=$($FFMPEG -hide_banner -hwaccels 2>&1)
|
|
if grep -qE '(^|[[:space:]])rkmpp($|[[:space:]])' <<<"$hwaccels"; then
|
|
add_check vpu ffmpeg-rkmpp pass "$FFMPEG advertises the rkmpp hwaccel"
|
|
else
|
|
add_check vpu ffmpeg-rkmpp fail "$FFMPEG does not advertise rkmpp"
|
|
fi
|
|
|
|
encoders=$($FFMPEG -hide_banner -encoders 2>&1)
|
|
for codec in h264 hevc mjpeg; do
|
|
if grep -q "${codec}_rkmpp" <<<"$encoders"; then
|
|
add_check vpu "encode-$codec-capability" pass \
|
|
"${codec}_rkmpp is registered"
|
|
else
|
|
add_check vpu "encode-$codec-capability" fail \
|
|
"${codec}_rkmpp is not registered"
|
|
fi
|
|
done
|
|
|
|
filters=$($FFMPEG -hide_banner -filters 2>&1)
|
|
if grep -q 'scale_rkrga' <<<"$filters"; then
|
|
add_check rga ffmpeg-filter pass "scale_rkrga is registered"
|
|
else
|
|
add_check rga ffmpeg-filter fail "scale_rkrga is not registered"
|
|
fi
|
|
|
|
if ((QUICK)); then
|
|
add_check vpu encode-workload skip "active encoder probe disabled by --quick"
|
|
else
|
|
for codec in h264 hevc mjpeg; do
|
|
encode_output=$(timeout 30s "$FFMPEG" -nostdin -v error \
|
|
-f lavfi -i 'color=size=320x240:rate=30:duration=0.2' \
|
|
-frames:v 3 -c:v "${codec}_rkmpp" -f null - 2>&1)
|
|
encode_rc=$?
|
|
if ((encode_rc == 0)); then
|
|
add_check vpu "encode-$codec-workload" pass \
|
|
"three frames submitted successfully"
|
|
else
|
|
add_check vpu "encode-$codec-workload" fail \
|
|
"hardware encode failed: $(short_message "$encode_output")"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ -n "$MEDIA_DIR" && $QUICK -eq 0 ]]; then
|
|
first_decode_sample=""
|
|
for codec in h264 hevc vp9 av1; do
|
|
sample=$(find_sample "$codec" || true)
|
|
if [[ -z "$sample" ]]; then
|
|
add_check vpu "decode-$codec-workload" skip \
|
|
"no sample containing '$codec' in $MEDIA_DIR"
|
|
continue
|
|
fi
|
|
[[ -n "$first_decode_sample" ]] || first_decode_sample=$sample
|
|
decode_output=$(timeout 180s "$FFMPEG" -nostdin -v error \
|
|
-hwaccel rkmpp -i "$sample" -map 0:v:0 -f null - 2>&1)
|
|
decode_rc=$?
|
|
if ((decode_rc == 0)); then
|
|
add_check vpu "decode-$codec-workload" pass \
|
|
"decoded ${sample##*/} through RKMPP"
|
|
else
|
|
add_check vpu "decode-$codec-workload" fail \
|
|
"decode failed: $(short_message "$decode_output")"
|
|
fi
|
|
done
|
|
|
|
if [[ -n "$first_decode_sample" ]] && grep -q 'scale_rkrga' <<<"$filters"; then
|
|
rga_output=$(timeout 60s "$FFMPEG" -nostdin -v error \
|
|
-hwaccel rkmpp -hwaccel_output_format drm_prime \
|
|
-i "$first_decode_sample" -map 0:v:0 -frames:v 30 \
|
|
-vf 'scale_rkrga=w=1280:h=720:format=nv12' -f null - 2>&1)
|
|
rga_rc=$?
|
|
if ((rga_rc == 0)); then
|
|
add_check rga workload pass "RKMPP-to-RGA scaling completed"
|
|
else
|
|
add_check rga workload fail \
|
|
"RGA workload failed: $(short_message "$rga_output")"
|
|
fi
|
|
else
|
|
add_check rga workload skip "no decode sample is available for RGA"
|
|
fi
|
|
elif [[ -z "$MEDIA_DIR" ]]; then
|
|
add_check vpu decode-workload skip "use --media-dir to test hardware decoding"
|
|
add_check rga workload skip "use --media-dir to test zero-copy RGA scaling"
|
|
fi
|
|
fi
|
|
|
|
rga2_bindings=$(driver_binding_count 'rga2')
|
|
rga3_bindings=$(driver_binding_count 'rga3')
|
|
if ((rga2_bindings + rga3_bindings > 0)); then
|
|
add_check rga kernel-driver pass \
|
|
"RGA2 bindings=$rga2_bindings, RGA3 bindings=$rga3_bindings"
|
|
else
|
|
add_check rga kernel-driver warn "no RGA2/RGA3 binding was found"
|
|
fi
|
|
|
|
# NPU runtime and per-core inference
|
|
npu_bindings=$(driver_binding_count 'rknpu')
|
|
((npu_bindings += $(driver_binding_count 'RKNPU')))
|
|
shopt -s nullglob
|
|
npu_devfreq=("$SYS_ROOT"/class/devfreq/*.npu)
|
|
shopt -u nullglob
|
|
if ((npu_bindings > 0 || ${#npu_devfreq[@]} > 0)); then
|
|
add_check npu kernel-driver pass \
|
|
"RKNPU bindings=$npu_bindings, devfreq nodes=${#npu_devfreq[@]}"
|
|
npu_present=1
|
|
else
|
|
add_check npu kernel-driver warn "RKNPU driver/device was not found"
|
|
npu_present=0
|
|
fi
|
|
|
|
runtime_so="$RKNN_HOME/lib/librknnrt.so"
|
|
model_path="$RKNN_HOME/share/models/rk3588/mobilenet_v1.rknn"
|
|
npu_test="$RKNN_HOME/bin/rknn-inference-test"
|
|
if [[ -r "$runtime_so" ]]; then
|
|
runtime_hash=$(sha256sum "$runtime_so" 2>/dev/null)
|
|
runtime_hash=${runtime_hash%% *}
|
|
if [[ "$runtime_hash" == \
|
|
"d31fc19c85b85f6091b2bd0f6af9d962d5264a4e410bfb536402ec92bac738e8" ]]; then
|
|
add_check npu runtime pass "RKNN Runtime 2.3.2 checksum matches"
|
|
else
|
|
add_check npu runtime fail "unexpected librknnrt.so checksum $runtime_hash"
|
|
fi
|
|
else
|
|
add_check npu runtime warn "RKNN runtime is not installed at $runtime_so"
|
|
fi
|
|
|
|
if ((QUICK)); then
|
|
add_check npu inference skip "active inference disabled by --quick"
|
|
elif ((npu_present == 0)); then
|
|
add_check npu inference skip "NPU driver is absent; inference was not attempted"
|
|
elif [[ ! -x "$npu_test" || ! -r "$model_path" ]]; then
|
|
add_check npu inference skip "test binary or pinned RK3588 model is unavailable"
|
|
else
|
|
for core in 0 1 2 all; do
|
|
npu_output=$(timeout 45s "$npu_test" --model "$model_path" \
|
|
--core "$core" --iterations 1 2>&1)
|
|
npu_rc=$?
|
|
if ((npu_rc == 0)) && grep -q 'RKNN_RESULT status=pass' <<<"$npu_output"; then
|
|
add_check npu "core-$core" pass "$(short_message "$npu_output")"
|
|
else
|
|
add_check npu "core-$core" fail \
|
|
"inference failed: $(short_message "$npu_output")"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# HDMI ALSA devices
|
|
asound_cards=$(read_text "$PROC_ROOT/asound/cards")
|
|
if [[ -n "$asound_cards" ]] && grep -qiE 'hdmi|rockchiphdmi' <<<"$asound_cards"; then
|
|
add_check audio hdmi-card pass "an HDMI ALSA card is registered"
|
|
elif [[ -n "$asound_cards" ]]; then
|
|
add_check audio hdmi-card warn "ALSA cards exist, but none is labeled HDMI"
|
|
else
|
|
add_check audio hdmi-card warn "ALSA card inventory is unavailable"
|
|
fi
|
|
if command -v aplay >/dev/null 2>&1; then
|
|
aplay_output=$(aplay -l 2>&1)
|
|
if grep -qi 'hdmi' <<<"$aplay_output"; then
|
|
add_check audio pcm-device pass "aplay lists an HDMI PCM device"
|
|
else
|
|
add_check audio pcm-device warn \
|
|
"aplay did not list an HDMI PCM device: $(short_message "$aplay_output")"
|
|
fi
|
|
else
|
|
add_check audio pcm-device skip "aplay is not installed"
|
|
fi
|
|
|
|
# Link state only: no pings, DNS requests, or network mutations.
|
|
shopt -s nullglob
|
|
interfaces=("$SYS_ROOT"/class/net/*)
|
|
shopt -u nullglob
|
|
interface_names=()
|
|
up_names=()
|
|
for interface in "${interfaces[@]}"; do
|
|
name=${interface##*/}
|
|
[[ "$name" == lo ]] && continue
|
|
interface_names+=("$name")
|
|
state=$(read_text "$interface/operstate")
|
|
carrier=$(read_text "$interface/carrier")
|
|
if [[ "$state" == up || "$carrier" == 1 ]]; then
|
|
up_names+=("$name")
|
|
fi
|
|
done
|
|
if ((${#interface_names[@]} == 0)); then
|
|
add_check network interfaces warn "no non-loopback interface was found"
|
|
elif ((${#up_names[@]} > 0)); then
|
|
add_check network link pass "up: ${up_names[*]}"
|
|
else
|
|
add_check network link warn \
|
|
"interfaces present but down: ${interface_names[*]}"
|
|
fi
|
|
|
|
# Root filesystem, eMMC, and NVMe presence. No serial numbers are read.
|
|
if command -v findmnt >/dev/null 2>&1; then
|
|
root_source=$(findmnt -n -o SOURCE / 2>/dev/null || true)
|
|
root_fstype=$(findmnt -n -o FSTYPE / 2>/dev/null || true)
|
|
if [[ -n "$root_source" ]]; then
|
|
add_check storage rootfs pass "$root_source ($root_fstype)"
|
|
else
|
|
add_check storage rootfs warn "root filesystem source is unavailable"
|
|
fi
|
|
else
|
|
add_check storage rootfs skip "findmnt is not installed"
|
|
fi
|
|
|
|
shopt -s nullglob
|
|
emmc_devices=("$SYS_ROOT"/block/mmcblk*)
|
|
nvme_devices=("$SYS_ROOT"/block/nvme*n1)
|
|
shopt -u nullglob
|
|
if ((${#emmc_devices[@]} > 0)); then
|
|
add_check storage emmc pass "${#emmc_devices[@]} MMC block device(s)"
|
|
else
|
|
add_check storage emmc warn "no MMC/eMMC block device was found"
|
|
fi
|
|
if ((${#nvme_devices[@]} > 0)); then
|
|
add_check storage nvme pass "${#nvme_devices[@]} NVMe namespace(s)"
|
|
else
|
|
add_check storage nvme warn "no NVMe namespace was found"
|
|
fi
|
|
|
|
pass_count=0
|
|
warn_count=0
|
|
fail_count=0
|
|
skip_count=0
|
|
for status in "${CHECK_STATUS[@]}"; do
|
|
case "$status" in
|
|
pass) ((pass_count++)) ;;
|
|
warn) ((warn_count++)) ;;
|
|
fail) ((fail_count++)) ;;
|
|
skip) ((skip_count++)) ;;
|
|
esac
|
|
done
|
|
|
|
if ((fail_count > 0)); then
|
|
overall=fail
|
|
elif ((warn_count > 0)); then
|
|
overall=warn
|
|
else
|
|
overall=pass
|
|
fi
|
|
|
|
json_escape() {
|
|
local value=$1
|
|
value=${value//\\/\\\\}
|
|
value=${value//\"/\\\"}
|
|
value=${value//$'\n'/\\n}
|
|
value=${value//$'\r'/\\r}
|
|
value=${value//$'\t'/\\t}
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
if ((JSON)); then
|
|
printf '{"schema_version":1,"overall":"%s","quick":%s,' \
|
|
"$overall" "$([[ $QUICK -eq 1 ]] && printf true || printf false)"
|
|
printf '"summary":{"pass":%d,"warn":%d,"fail":%d,"skip":%d},' \
|
|
"$pass_count" "$warn_count" "$fail_count" "$skip_count"
|
|
printf '"checks":['
|
|
for ((i = 0; i < ${#CHECK_NAME[@]}; ++i)); do
|
|
((i == 0)) || printf ','
|
|
printf '{"group":"%s","name":"%s","status":"%s","message":"%s"}' \
|
|
"$(json_escape "${CHECK_GROUP[i]}")" \
|
|
"$(json_escape "${CHECK_NAME[i]}")" \
|
|
"${CHECK_STATUS[i]}" \
|
|
"$(json_escape "${CHECK_MESSAGE[i]}")"
|
|
done
|
|
printf ']}\n'
|
|
else
|
|
printf '%-9s %-24s %-6s %s\n' GROUP CHECK STATUS DETAIL
|
|
printf '%-9s %-24s %-6s %s\n' '---------' '------------------------' \
|
|
'------' '------'
|
|
for ((i = 0; i < ${#CHECK_NAME[@]}; ++i)); do
|
|
printf '%-9s %-24s %-6s %s\n' \
|
|
"${CHECK_GROUP[i]}" "${CHECK_NAME[i]}" \
|
|
"${CHECK_STATUS[i]^^}" "${CHECK_MESSAGE[i]}"
|
|
done
|
|
printf '\nOverall: %s (pass=%d warn=%d fail=%d skip=%d)\n' \
|
|
"${overall^^}" "$pass_count" "$warn_count" "$fail_count" "$skip_count"
|
|
fi
|
|
|
|
if ((fail_count > 0 || (STRICT && warn_count > 0))); then
|
|
exit 1
|
|
fi
|
|
exit 0
|