|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Real Linux ABI end-to-end test for ADR 0011: |
| 3 | +# |
| 4 | +# sensor model -> delayed DRDY -> signal router -> GPIO runtime -> gpio-sim |
| 5 | +# -> /dev/gpiochipN -> gpiomon -> SPI read (spidev_test) -> read-clear -> DRDY falls |
| 6 | +# |
| 7 | +# Needs root, the gpio-sim and cuse kernel modules, configfs, libgpiod tools and an |
| 8 | +# upstream spidev_test. Run it directly on such a host or use run-qemu.sh. |
| 9 | +set -u |
| 10 | + |
| 11 | +REPO="${REPO:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)}" |
| 12 | +BIN_SERVER="${BIN_SERVER:-vds-server}" |
| 13 | +BIN_GPIO_SIM="${BIN_GPIO_SIM:-vds4e-gpio-sim}" |
| 14 | +BIN_SPI_CUSE="${BIN_SPI_CUSE:-vds4e-spi-cuse}" |
| 15 | +SPIDEV_TEST="${SPIDEV_TEST:-spidev_test}" |
| 16 | +WORK="$(mktemp -d)" |
| 17 | +export HOME="$WORK/home" |
| 18 | +mkdir -p "$HOME" |
| 19 | +SOCKET="$WORK/vds4e.sock" |
| 20 | +PIDS=() |
| 21 | +FAIL=0 |
| 22 | + |
| 23 | +cleanup() { |
| 24 | + for pid in "${PIDS[@]}"; do kill "$pid" 2>/dev/null; done |
| 25 | + wait 2>/dev/null |
| 26 | + rm -rf "$WORK" |
| 27 | +} |
| 28 | +trap cleanup EXIT |
| 29 | + |
| 30 | +now() { date +%s.%N; } |
| 31 | +ms() { awk -v a="$1" -v b="$2" 'BEGIN { printf "%.1f", (b - a) * 1000 }'; } |
| 32 | +check() { |
| 33 | + if [ "$2" = "$3" ]; then echo "PASS $1 (got '$2')"; else echo "FAIL $1 (expected '$3', got '$2')"; FAIL=1; fi |
| 34 | +} |
| 35 | +wait_for() { # <seconds*10> <test command...> |
| 36 | + local tries=$1; shift |
| 37 | + for _ in $(seq "$tries"); do "$@" && return 0; sleep 0.1; done |
| 38 | + return 1 |
| 39 | +} |
| 40 | +await_exit() { # wait up to 5 s for a background pid to exit |
| 41 | + local pid=$1 |
| 42 | + for _ in $(seq 50); do kill -0 "$pid" 2>/dev/null || return 0; sleep 0.1; done |
| 43 | + kill "$pid" 2>/dev/null |
| 44 | + return 1 |
| 45 | +} |
| 46 | + |
| 47 | +cat > "$WORK/server.yaml" <<YAML |
| 48 | +schema_version: 1 |
| 49 | +server: { control_address: '127.0.0.1:0' } |
| 50 | +data_plane: { unix_socket: $SOCKET } |
| 51 | +observability: { log_level: debug } |
| 52 | +event_store: { enabled: false } |
| 53 | +topology: $WORK/topology.yaml |
| 54 | +device_packages: |
| 55 | + - $REPO/device-models/examples/spi-sensor-drdy |
| 56 | + - $REPO/device-models/examples/generic-gpio-bank |
| 57 | +YAML |
| 58 | +cat > "$WORK/topology.yaml" <<YAML |
| 59 | +schema_version: 1 |
| 60 | +connections: |
| 61 | + - from: spi-sensor-drdy.drdy |
| 62 | + to: generic-gpio-bank-32.GPIO16 |
| 63 | +YAML |
| 64 | + |
| 65 | +"$BIN_SERVER" --config "$WORK/server.yaml" > "$WORK/server.log" 2>&1 & |
| 66 | +PIDS+=($!) |
| 67 | +wait_for 100 test -S "$SOCKET" || { echo "FAIL server socket"; cat "$WORK/server.log"; exit 1; } |
| 68 | + |
| 69 | +"$BIN_GPIO_SIM" --name vds4e-e2e-gpio --label "VDS4E E2E GPIO" --lines 32 \ |
| 70 | + --device-id generic-gpio-bank-32 --socket "$SOCKET" > "$WORK/gpio.out" 2> "$WORK/gpio.err" & |
| 71 | +PIDS+=($!) |
| 72 | +wait_for 100 test -s "$WORK/gpio.out" |
| 73 | +CHIP=$(head -1 "$WORK/gpio.out") |
| 74 | +[ -c "$CHIP" ] || { echo "FAIL no gpiochip ($CHIP)"; cat "$WORK/gpio.err"; exit 1; } |
| 75 | +echo "### gpio-sim controller: $CHIP" |
| 76 | + |
| 77 | +"$BIN_SPI_CUSE" --name spidev0.0 --device-id spi-sensor-drdy --socket "$SOCKET" \ |
| 78 | + > "$WORK/spi.out" 2> "$WORK/spi.err" & |
| 79 | +PIDS+=($!) |
| 80 | +wait_for 100 test -c /dev/spidev0.0 || { echo "FAIL no /dev/spidev0.0"; cat "$WORK/spi.err"; exit 1; } |
| 81 | +gpiodetect |
| 82 | +sleep 0.3 |
| 83 | + |
| 84 | +echo; echo "=== cycle 1: conversion -> DRDY rising edge -> read-clear -> falling edge ===" |
| 85 | +check "DRDY low at idle" "$(gpioget "$CHIP" 16)" 0 |
| 86 | + |
| 87 | +gpiomon --num-events=1 --rising-edge --format='%e line=%o' "$CHIP" 16 > "$WORK/rise1.out" 2>&1 & |
| 88 | +RISE=$!; sleep 0.5 |
| 89 | +T0=$(now) |
| 90 | +"$SPIDEV_TEST" -D /dev/spidev0.0 -p '\x10' > "$WORK/start1.out" 2>&1 || { echo "FAIL spidev_test START"; cat "$WORK/start1.out"; FAIL=1; } |
| 91 | +if await_exit "$RISE"; then |
| 92 | + echo "gpiomon: $(cat "$WORK/rise1.out") (~$(ms "$T0" "$(now)") ms incl. spidev_test start-up)" |
| 93 | +else echo "FAIL no rising edge within 5 s"; FAIL=1; fi |
| 94 | +check "DRDY high after the edge" "$(gpioget "$CHIP" 16)" 1 |
| 95 | +sleep 0.4 |
| 96 | +check "DRDY stays high until the sample is read" "$(gpioget "$CHIP" 16)" 1 |
| 97 | + |
| 98 | +gpiomon --num-events=1 --falling-edge --format='%e line=%o' "$CHIP" 16 > "$WORK/fall1.out" 2>&1 & |
| 99 | +FALL=$!; sleep 0.5 |
| 100 | +"$SPIDEV_TEST" -D /dev/spidev0.0 -v -p '\x20\x00\x00\x00\x00' > "$WORK/read1.out" 2>&1 || { echo "FAIL spidev_test READ"; FAIL=1; } |
| 101 | +sed 's/^/ /' "$WORK/read1.out" | grep -E "TX|RX" |
| 102 | +if await_exit "$FALL"; then echo "gpiomon: $(cat "$WORK/fall1.out")"; else echo "FAIL no falling edge within 5 s"; FAIL=1; fi |
| 103 | +check "DRDY low after read-clear" "$(gpioget "$CHIP" 16)" 0 |
| 104 | +if grep -q "RX | 00 80 00 12 34" "$WORK/read1.out"; then echo "PASS sample bytes 80 00 12 34"; else echo "FAIL sample bytes"; FAIL=1; fi |
| 105 | + |
| 106 | +echo; echo "=== cycle 2: the signal re-arms ===" |
| 107 | +gpiomon --num-events=1 --rising-edge --format='%e line=%o' "$CHIP" 16 > "$WORK/rise2.out" 2>&1 & |
| 108 | +RISE=$!; sleep 0.3 |
| 109 | +"$SPIDEV_TEST" -D /dev/spidev0.0 -p '\x10' > /dev/null 2>&1 |
| 110 | +if await_exit "$RISE"; then echo "PASS second rising edge: $(cat "$WORK/rise2.out")"; else echo "FAIL no second edge"; FAIL=1; fi |
| 111 | +check "DRDY high in cycle 2" "$(gpioget "$CHIP" 16)" 1 |
| 112 | + |
| 113 | +echo; echo "=== negative: a read without a new sample must not raise DRDY ===" |
| 114 | +"$SPIDEV_TEST" -D /dev/spidev0.0 -p '\x20\x00\x00\x00\x00' > /dev/null 2>&1; sleep 0.3 |
| 115 | +check "DRDY low after the read" "$(gpioget "$CHIP" 16)" 0 |
| 116 | +"$SPIDEV_TEST" -D /dev/spidev0.0 -p '\x20\x00\x00\x00\x00' > /dev/null 2>&1; sleep 0.3 |
| 117 | +check "DRDY still low after a second read" "$(gpioget "$CHIP" 16)" 0 |
| 118 | + |
| 119 | +echo; echo "### server-side timeline" |
| 120 | +grep "transaction completed" "$WORK/server.log" | grep spi-sensor \ |
| 121 | + | grep -o '"timestamp":"[^"]*"\|"request":"[^"]*"' | paste - - | sed 's/^/ spi: /' |
| 122 | +grep "GPIO edge detected" "$WORK/server.log" | grep -o '"timestamp":"[^"]*"\|"edges":"[^"]*"' | paste - - | sed 's/^/ gpio: /' |
| 123 | +grep -Ei '"level":"(WARN|ERROR)"|panic' "$WORK/server.log" | head -5 |
| 124 | +echo "### RESULT: $([ "$FAIL" = 0 ] && echo ALL_PASS || echo FAILURES)" |
| 125 | +exit "$FAIL" |
0 commit comments