| #!/bin/sh
#
# Copy C lws' state machine oracles from a C checkout:
#
# scripts/sync-c-states.sh /path/to/libwebsockets [build dir]
#
# Into crates/npro-test/states/, replacing what is there:
#
# wsi-event-edges.txt the rows of C's event table, each line of the table
# prefixed by its line in wsi-state.c, which is how
# C's trace names a row
# edges.txt every distinct state edge C's ctest suite takes,
# sorted, without each connection's tag
# rows-fired.txt every row of the table the suite fires, from C's
# LRSROW lines
# README.lws.md C's README.wsi-state-machines.md, which lists the
# rows no test fires and why
# C-COMMIT the C commit
#
# The suite runs in the three builds C's README measures coverage over: the
# default, one adding the options some rows need, and one with tls accepts
# on a worker. Each has LWS_WITH_STATE_TRACE and LWS_WITH_STATE_CHECK, so
# the suite also aborts on any edge the table does not allow. h3 is left
# out, since it needs gnutls.
#
# The builds go to <build dir>-<name>, by default <checkout>/build-npro-
# states-<name>; they are reconfigured each time. C's ctest-background.sh
# needs netstat or ss to see its test servers come up. Expect most of an
# hour. The checkout should be clean, as for sync-c-oracle.sh.
#
# Any failing test stops the sync: the edges are only an oracle from a
# suite that passes. CTEST_ARGS is passed to every ctest, eg,
# CTEST_ARGS="-E api-test-foo" to leave out a test that fails for reasons
# outside the state machines; the commit names it, and why.
set -eu
if [ $# -lt 1 ] || [ $# -gt 2 ] || [ ! -f "$1/lib/sansio/wsi-state.c" ]; then
echo "usage: $0 <libwebsockets checkout> [build dir]" >&2
exit 1
fi
c="$1"
b="${2:-$c/build-npro-states}"
here="$(cd "$(dirname "$0")/.." && pwd)"
dst="$here/crates/npro-test/states"
jobs="${SAI_PARALLEL:-4}"
if [ -n "$(git -C "$c" status --porcelain -- lib include CMakeLists.txt)" ]; then
echo "$c has uncommitted changes" >&2
exit 1
fi
if ! command -v netstat >/dev/null 2>&1 && ! command -v ss >/dev/null 2>&1; then
echo "C's test fixtures need netstat or ss" >&2
exit 1
fi
raw="$(mktemp)"
trap 'rm -f "$raw"' EXIT
# run C's suite in one build: $1 its name, the rest its cmake options
suite() {
name="$1"
shift
echo "== C build: $name"
cmake -S "$c" -B "$b-$name" -DCMAKE_BUILD_TYPE=DEBUG \
-DLWS_WITH_STATE_TRACE=ON -DLWS_WITH_STATE_CHECK=ON \
-DLWS_WITH_HTTP3=OFF -DLWS_WITH_MINIMAL_EXAMPLES=ON \
-DLWS_WITHOUT_EXTENSIONS=OFF -DLWS_WITH_ZLIB=ON "$@"
cmake --build "$b-$name" -j "$jobs"
# shellcheck disable=SC2086
(cd "$b-$name" && LWS_STATE_TRACE_FILE="$raw" \
ctest -j "$jobs" --timeout 180 ${CTEST_ARGS:-})
}
suite default
suite options -DLWS_WITH_ASYNC_QUEUE=ON -DLWS_WITH_SOCKS5=ON \
-DLWS_ROLE_MQTT=ON -DLWS_WITH_HTTP_PROXY=ON -DLWS_ROLE_RAW_PROXY=ON \
-DLWS_WITH_SYS_FAULT_INJECTION=ON -DLWS_WITH_EMAIL=ON
suite worker -DLWS_WITH_ASYNC_QUEUE=ON -DLWS_MAX_SMP=2
# the table, each line with its line in wsi-state.c
awk '/^static const struct lws_wsi_event_edge lws_wsi_event_edges\[\] = \{/ { f = 1; next }
f && /^\};/ { exit }
f { printf "%d\t%s\n", NR, $0 }' "$c/lib/sansio/wsi-state.c" > "$dst/wsi-event-edges.txt"
# each edge once, without the connection's tag: "LRS from -> to how [ev=X]"
grep '^LRS ' "$raw" |
sed -E -e 's/^(LRS [^ ]+ -> [^ ]+ [^ ]+) .*( ev=[A-Z0-9_]+)$/\1\2/' -e t \
-e 's/^(LRS [^ ]+ -> [^ ]+ [^ ]+) .*$/\1/' |
LC_ALL=C sort -u > "$dst/edges.txt"
# each row fired, once: "LRSROW <line> role side from event"
grep '^LRSROW ' "$raw" | sort -u -k2,2n > "$dst/rows-fired.txt"
cp "$c/READMEs/README.wsi-state-machines.md" "$dst/README.lws.md"
git -C "$c" log -1 --format='%H %s' > "$dst/C-COMMIT"
echo "$(grep -c ' [[:space:]]*R(' "$dst/wsi-event-edges.txt") rows," \
"$(wc -l < "$dst/rows-fired.txt") fired, and" \
"$(wc -l < "$dst/edges.txt") edges from $(cut -c1-12 "$dst/C-COMMIT")"
|