feat(e2e): dispatch direct/recorder scenario kinds in run_e2e.sh
Extends the scenario-list builder and main loop to actually execute s52_direct_unicast/s53_direct_multicast (self-contained single-MARTeApp FileReader->UDPStreamer->UDPStreamerClient->FileWriter round trip) and s54_recorder (StreamHub BinaryRecorder round trip, ported from run_recorder_e2e.sh) alongside the existing chain scenarios, and tags each results.json record with its scenario kind. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+111
-5
@@ -45,6 +45,7 @@ source "${ENV_SCRIPT}"
|
|||||||
COMP="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
|
COMP="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
|
||||||
export LD_LIBRARY_PATH="\
|
export LD_LIBRARY_PATH="\
|
||||||
${BUILD_DIR}/Components/DataSources/UDPStreamer:\
|
${BUILD_DIR}/Components/DataSources/UDPStreamer:\
|
||||||
|
${BUILD_DIR}/Components/DataSources/UDPStreamerClient:\
|
||||||
${BUILD_DIR}/Components/Interfaces/UDPStream:\
|
${BUILD_DIR}/Components/Interfaces/UDPStream:\
|
||||||
${MARTe2_DIR}/Build/${TARGET}/Core:\
|
${MARTe2_DIR}/Build/${TARGET}/Core:\
|
||||||
${COMP}/DataSources/LinuxTimer:\
|
${COMP}/DataSources/LinuxTimer:\
|
||||||
@@ -76,6 +77,7 @@ if [ "${SKIP_BUILD}" -eq 0 ]; then
|
|||||||
echo "── Building components ──"
|
echo "── Building components ──"
|
||||||
make -C "${REPO_ROOT}/Source/Components/Interfaces/UDPStream" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
make -C "${REPO_ROOT}/Source/Components/Interfaces/UDPStream" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
||||||
make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamer" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamer" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
||||||
|
make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamerClient" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
||||||
make -C "${REPO_ROOT}/Source/Applications/StreamHub" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
make -C "${REPO_ROOT}/Source/Applications/StreamHub" -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -1
|
||||||
fi
|
fi
|
||||||
if [ ! -x "${CLIENT}" ]; then
|
if [ ! -x "${CLIENT}" ]; then
|
||||||
@@ -85,7 +87,10 @@ fi
|
|||||||
[ -x "${MARTE_APP}" ] || { echo "ERROR: MARTeApp.ex not found at ${MARTE_APP}" >&2; exit 1; }
|
[ -x "${MARTE_APP}" ] || { echo "ERROR: MARTeApp.ex not found at ${MARTE_APP}" >&2; exit 1; }
|
||||||
[ -x "${STREAMHUB_EX}" ] || { echo "ERROR: StreamHub.ex not found" >&2; exit 1; }
|
[ -x "${STREAMHUB_EX}" ] || { echo "ERROR: StreamHub.ex not found" >&2; exit 1; }
|
||||||
|
|
||||||
# ── Scenario list (id|ws_port|udp_port0|network|oracle|trig|checks) ──────────
|
# ── Scenario list (kind|id|f2|f3|f4|f5|f6|f7) ────────────────────────────────
|
||||||
|
# chain: kind|id|ws_port|udp_port0|network|oracle|trig|checks
|
||||||
|
# direct: kind|id|cfg|-|-|-|-|-
|
||||||
|
# recorder: kind|id|marte_cfg|hub_cfg|-|-|-|-
|
||||||
LIST="$(${PY} - "${ONLY}" <<'PY'
|
LIST="$(${PY} - "${ONLY}" <<'PY'
|
||||||
import sys, os
|
import sys, os
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath("Test/E2E/suite/scenarios.py")))
|
sys.path.insert(0, os.path.dirname(os.path.abspath("Test/E2E/suite/scenarios.py")))
|
||||||
@@ -95,12 +100,18 @@ only = sys.argv[1] if len(sys.argv) > 1 else ""
|
|||||||
for s in S.SCENARIOS:
|
for s in S.SCENARIOS:
|
||||||
if only and s["id"] != only:
|
if only and s["id"] != only:
|
||||||
continue
|
continue
|
||||||
|
kind = s["kind"]
|
||||||
|
if kind == "chain":
|
||||||
trig = s.get("trig_signal") or ""
|
trig = s.get("trig_signal") or ""
|
||||||
checks = ",".join(s.get("client_checks", []))
|
checks = ",".join(s.get("client_checks", []))
|
||||||
if not trig:
|
if not trig:
|
||||||
checks = ",".join(c for c in s.get("client_checks", []) if c != "trigger")
|
checks = ",".join(c for c in s.get("client_checks", []) if c != "trigger")
|
||||||
print("|".join([s["id"], str(s["ws_port"]), str(s["sources"][0]["udp_port"]),
|
print("|".join([kind, s["id"], str(s["ws_port"]), str(s["sources"][0]["udp_port"]),
|
||||||
s["network"], s["oracle"], trig, checks]))
|
s["network"], s["oracle"], trig, checks]))
|
||||||
|
elif kind == "direct":
|
||||||
|
print("|".join([kind, s["id"], s["cfg"], "", "", "", "", ""]))
|
||||||
|
elif kind == "recorder":
|
||||||
|
print("|".join([kind, s["id"], s["marte_cfg"], s["hub_cfg"], "", "", "", ""]))
|
||||||
PY
|
PY
|
||||||
)"
|
)"
|
||||||
|
|
||||||
@@ -116,12 +127,16 @@ cleanup() {
|
|||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
|
|
||||||
while IFS='|' read -r ID WSPORT UDPPORT NET ORACLE TRIG CHECKS; do
|
while IFS='|' read -r KIND ID F2 F3 F4 F5 F6 F7; do
|
||||||
[ -z "${ID}" ] && continue
|
[ -z "${ID}" ] && continue
|
||||||
SCEN_IDS="${SCEN_IDS} ${ID}"
|
SCEN_IDS="${SCEN_IDS} ${ID}"
|
||||||
|
: > "${WORK}/status_${ID}.txt"
|
||||||
|
|
||||||
|
case "${KIND}" in
|
||||||
|
chain)
|
||||||
|
WSPORT="${F2}"; UDPPORT="${F3}"; NET="${F4}"; ORACLE="${F5}"; TRIG="${F6}"; CHECKS="${F7}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "══ scenario ${ID} (net=${NET} oracle=${ORACLE} ws=${WSPORT}) ══"
|
echo "══ scenario ${ID} (net=${NET} oracle=${ORACLE} ws=${WSPORT}) ══"
|
||||||
: > "${WORK}/status_${ID}.txt"
|
|
||||||
|
|
||||||
# multicast route probe
|
# multicast route probe
|
||||||
if [ "${NET}" = "multicast" ]; then
|
if [ "${NET}" = "multicast" ]; then
|
||||||
@@ -187,6 +202,95 @@ while IFS='|' read -r ID WSPORT UDPPORT NET ORACLE TRIG CHECKS; do
|
|||||||
echo "FAIL" > "${WORK}/status_${ID}.txt"
|
echo "FAIL" > "${WORK}/status_${ID}.txt"
|
||||||
fi
|
fi
|
||||||
${PY} "${SCRIPT_DIR}/plots.py" --scenario "${ID}" --dir "${WORK}" >/dev/null 2>&1 || true
|
${PY} "${SCRIPT_DIR}/plots.py" --scenario "${ID}" --dir "${WORK}" >/dev/null 2>&1 || true
|
||||||
|
;;
|
||||||
|
|
||||||
|
direct)
|
||||||
|
CFG="${F2}"
|
||||||
|
echo ""
|
||||||
|
echo "══ scenario ${ID} (kind=direct cfg=${CFG}) ══"
|
||||||
|
|
||||||
|
DS_DIR="${SCRIPT_DIR}/../datasources"
|
||||||
|
INPUT_BIN="/tmp/udpstreamer_test_input.bin"
|
||||||
|
# The FileWriterDS output path is baked into the cfg (not per-scenario);
|
||||||
|
# the last `Filename = "..."` in the file is always the writer (the
|
||||||
|
# FileReaderDS input path is always listed first in these cfgs).
|
||||||
|
OUTPUT_BIN="$(grep -oP 'Filename\s*=\s*"\K[^"]+' "${REPO_ROOT}/${CFG}" | tail -1)"
|
||||||
|
rm -f "${INPUT_BIN}" "${OUTPUT_BIN}"
|
||||||
|
(cd "${DS_DIR}" && ${PY} -c "import gen_test_data; gen_test_data.generate()") > "${OUT_DIR}/gen_${ID}.log" 2>&1
|
||||||
|
|
||||||
|
APP_LOG="${OUT_DIR}/marte_${ID}.log"
|
||||||
|
timeout 8 "${MARTE_APP}" -l RealTimeLoader -f "${REPO_ROOT}/${CFG}" -s Running > "${APP_LOG}" 2>&1 &
|
||||||
|
APP_PID=$!
|
||||||
|
sleep 5
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
MSG="$(${PY} -c "
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '${DS_DIR}')
|
||||||
|
import validate_binary as V
|
||||||
|
ok, msg, details = V.validate('${INPUT_BIN}', '${OUTPUT_BIN}', label='${ID}')
|
||||||
|
print(msg)
|
||||||
|
sys.exit(0 if ok else 1)
|
||||||
|
" 2>&1)" && STATUS=PASS || STATUS=FAIL
|
||||||
|
echo " ${MSG}"
|
||||||
|
echo "${STATUS}" > "${WORK}/status_${ID}.txt"
|
||||||
|
echo "${ID}: ${STATUS}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
recorder)
|
||||||
|
RCFG_MARTE="${F2}"; RCFG_HUB="${F3}"
|
||||||
|
echo ""
|
||||||
|
echo "══ scenario ${ID} (kind=recorder marte_cfg=${RCFG_MARTE} hub_cfg=${RCFG_HUB}) ══"
|
||||||
|
|
||||||
|
DS_DIR="${SCRIPT_DIR}/../datasources"
|
||||||
|
INPUT_BIN="/tmp/udpstreamer_test_input.bin"
|
||||||
|
REC_DIR="/tmp/streamhub_rec_e2e"
|
||||||
|
rm -rf "${REC_DIR}"; mkdir -p "${REC_DIR}"
|
||||||
|
(cd "${DS_DIR}" && ${PY} -c "import gen_test_data; gen_test_data.generate()") > "${OUT_DIR}/gen_${ID}.log" 2>&1
|
||||||
|
|
||||||
|
HUB_LOG="${OUT_DIR}/hub_${ID}.log"
|
||||||
|
APP_LOG="${OUT_DIR}/marte_${ID}.log"
|
||||||
|
|
||||||
|
# Start StreamHub first so it is ready to receive the CONFIG packet.
|
||||||
|
"${STREAMHUB_EX}" -cfg "${REPO_ROOT}/${RCFG_HUB}" > "${HUB_LOG}" 2>&1 &
|
||||||
|
HUB_PID=$!
|
||||||
|
sleep 1
|
||||||
|
timeout 8 "${MARTE_APP}" -l RealTimeLoader -f "${REPO_ROOT}/${RCFG_MARTE}" -s Running > "${APP_LOG}" 2>&1 &
|
||||||
|
APP_PID=$!
|
||||||
|
|
||||||
|
# Let data flow, then stop the streamer and give the push/flush thread
|
||||||
|
# time to write the recorder file before killing the hub.
|
||||||
|
sleep 7
|
||||||
|
kill "${APP_PID}" 2>/dev/null; wait "${APP_PID}" 2>/dev/null; APP_PID=""
|
||||||
|
sleep 2
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
# The recorder names files <sourceId>_<UTCstamp>_<seq>.bin; pick the newest.
|
||||||
|
REC_FILE="$(ls -t "${REC_DIR}"/*.bin 2>/dev/null | head -1)"
|
||||||
|
if [ -z "${REC_FILE}" ]; then
|
||||||
|
echo " FAIL: no recorder output file found in ${REC_DIR}"
|
||||||
|
echo "FAIL" > "${WORK}/status_${ID}.txt"
|
||||||
|
echo "${ID}: FAIL"
|
||||||
|
else
|
||||||
|
MSG="$(${PY} -c "
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, '${DS_DIR}')
|
||||||
|
import validate_binary as V
|
||||||
|
ok, msg, details = V.validate('${INPUT_BIN}', '${REC_FILE}', label='${ID}')
|
||||||
|
print(msg)
|
||||||
|
sys.exit(0 if ok else 1)
|
||||||
|
" 2>&1)" && STATUS=PASS || STATUS=FAIL
|
||||||
|
echo " ${MSG}"
|
||||||
|
echo "${STATUS}" > "${WORK}/status_${ID}.txt"
|
||||||
|
echo "${ID}: ${STATUS}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo " SKIP: unknown scenario kind '${KIND}' for ${ID}"
|
||||||
|
echo "SKIP" > "${WORK}/status_${ID}.txt"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done <<< "${LIST}"
|
done <<< "${LIST}"
|
||||||
|
|
||||||
trap - EXIT
|
trap - EXIT
|
||||||
@@ -207,11 +311,13 @@ sys.path.insert(0, os.environ["SCRIPT_DIR"])
|
|||||||
try:
|
try:
|
||||||
from scenarios import SCENARIOS
|
from scenarios import SCENARIOS
|
||||||
known = {s["id"]: s.get("known_issue") for s in SCENARIOS}
|
known = {s["id"]: s.get("known_issue") for s in SCENARIOS}
|
||||||
|
kind_map = {s["id"]: s["kind"] for s in SCENARIOS}
|
||||||
except Exception:
|
except Exception:
|
||||||
known = {}
|
known = {}
|
||||||
|
kind_map = {}
|
||||||
results = []
|
results = []
|
||||||
for sid in ids:
|
for sid in ids:
|
||||||
rec = {"id": sid}
|
rec = {"id": sid, "kind": kind_map.get(sid, "chain")}
|
||||||
st = os.path.join(work, f"status_{sid}.txt")
|
st = os.path.join(work, f"status_{sid}.txt")
|
||||||
raw = open(st).read().strip() if os.path.exists(st) else "UNKNOWN"
|
raw = open(st).read().strip() if os.path.exists(st) else "UNKNOWN"
|
||||||
ki = known.get(sid)
|
ki = known.get(sid)
|
||||||
|
|||||||
Reference in New Issue
Block a user