#!/usr/bin/env bash # The boot order is a performance guarantee, so it is asserted rather than left # to a comment. Two JVMs sit on the path between RunTask and a playable match, # and they only overlap if the host is launched before anything waits on # Suramadu's port. A refactor that puts the wait back in between costs every # match the host's whole startup, and nothing else in the suite would notice. # # Structural assertions against the script text, in the style of # test-profiling.sh: the entrypoint cannot be run here - it launches JVMs and # reads /proc/net/tcp - but its ordering is a property of the file. set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ENTRYPOINT="$ROOT/container/entrypoint.sh" fail=0 check() { # if "${@:2}"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi } # line - the line number of the first match, or empty line() { grep -n -- "$1" "$ENTRYPOINT" | head -1 | cut -d: -f1; } WS_LAUNCH="$(line '^WS_PID=\$!')" HOST_LAUNCH="$(line '^HOST_PID=\$!')" WS_WAIT="$(line '^await_port Suramadu')" HOST_WAIT="$(line '^await_port "match host"')" READY="$(line '^emit ready')" # The launch, not the mention of it in the header comment. OBSERVER="$(line '^ *arena\.MatchWatcher --host')" # The host is told which fight it is playing, which is what puts the scenario in # the result. init/30-assets.sh writes both into run/scenario.env. SCENARIO_SOURCE="$(line '--scenario-source')" SCENARIO_LIBRARY="$(line '--scenario-library')" for var in WS_LAUNCH HOST_LAUNCH WS_WAIT HOST_WAIT READY OBSERVER \ SCENARIO_SOURCE SCENARIO_LIBRARY; do check "$var was found in the entrypoint" test -n "${!var}" done [ "$fail" = 0 ] || { echo "cannot check ordering; the anchors moved"; exit 1; } # The whole point: both JVMs are running before either is waited on. check "the host launches before the wait for Suramadu" \ test "$HOST_LAUNCH" -lt "$WS_WAIT" check "Suramadu launches before the host" \ test "$WS_LAUNCH" -lt "$HOST_LAUNCH" # On the host's own command line, which is the whole path by which the scenario # reaches the result document: run/scenario.env -> the entrypoint -> MatchHost. between() { [ "$1" -gt "$2" ] && [ "$1" -lt "$3" ]; } check "the host is told which form the scenario came in" \ between "$SCENARIO_SOURCE" "$WS_LAUNCH" "$HOST_LAUNCH" check "and which library path it was picked from" \ between "$SCENARIO_LIBRARY" "$WS_LAUNCH" "$HOST_LAUNCH" # Readiness means both ports, not just the web server's - headquarters sends a # browser the moment it hears `ready`. check "the ready callback follows the wait for Suramadu" \ test "$WS_WAIT" -lt "$READY" check "the ready callback follows the wait for the host" \ test "$HOST_WAIT" -lt "$READY" # Third JVM. It must start beside the host, not after readiness: its cold start # is a full MegaMek client and after `ready` it lands on top of the player's own # client JVM spawning and the first board paint. check "the observer starts before readiness is announced" \ test "$OBSERVER" -lt "$READY" # A startup failure has to take the other JVM down and tell headquarters, or the # waiting screen sits through the provisioning timeout for a task that is gone. check "startup_failed reports the failure to the control plane" \ grep -q 'emit error' "$ENTRYPOINT" check "startup_failed kills both JVMs" \ grep -qF 'for pid in "$HOST_PID" "$WS_PID"' "$ENTRYPOINT" # The phase marks are what the whole change is for; losing them loses the # measurement. for p in init-done suramadu-launched host-launched suramadu-ready host-ready; do check "phase $p is stamped" grep -qF "phase $p" "$ENTRYPOINT" done exit "$fail"