#!/usr/bin/env bash # run_e2e_report.sh — End-to-end test + report generation # # Usage: ./run_e2e_report.sh [--skip-tests] [--pdf-only] # # Steps: # 1. Generate multi-signal test data # 2. Build UDPStreamer + UDPStreamerClient # 3. Run unicast and multicast E2E tests # 4. Compare output against input # 5. Generate plots (input/output/diff, latency budget, latency histogram) # 6. Compile Typst report → PDF set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" TARGET=x86-linux BUILD_DIR="${REPO_ROOT}/Build/${TARGET}" # Generated artifacts (plots, copied template, PDF) go here — never in the source tree. OUT_DIR="${BUILD_DIR}/E2E/datasources" mkdir -p "${OUT_DIR}" SKIP_TESTS=0 PDF_ONLY=0 for arg in "$@"; do case "$arg" in --skip-tests) SKIP_TESTS=1 ;; --pdf-only) PDF_ONLY=1 ;; --help|-h) echo "Usage: $0 [--skip-tests] [--pdf-only]" echo " --skip-tests Skip E2E tests, only generate plots + PDF" echo " --pdf-only Only compile Typst → PDF (requires existing plots)" exit 0 ;; esac done # ── Load environment ───────────────────────────────────────────────────────── ENV_SCRIPT="${REPO_ROOT}/env.sh" if [ ! -f "${ENV_SCRIPT}" ]; then echo "ERROR: ${ENV_SCRIPT} not found." >&2 exit 1 fi source "${ENV_SCRIPT}" COMP="${MARTe2_Components_DIR}/Build/${TARGET}/Components" export LD_LIBRARY_PATH="\ ${BUILD_DIR}/Components/DataSources/UDPStreamerClient:\ ${BUILD_DIR}/Components/DataSources/UDPStreamer:\ ${BUILD_DIR}/Components/Interfaces/UDPStream:\ ${MARTe2_DIR}/Build/${TARGET}/Core:\ ${COMP}/DataSources/LinuxTimer:\ ${COMP}/DataSources/LoggerDataSource:\ ${COMP}/DataSources/FileDataSource:\ ${COMP}/GAMs/IOGAM:\ ${LD_LIBRARY_PATH}" MARTE_APP="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex" INPUT="/tmp/udpstreamer_test_input.bin" OUTPUT_U="/tmp/udpstreamer_test_output.bin" OUTPUT_M="/tmp/udpstreamer_test_output_multicast.bin" echo "==========================================" echo " UDPStreamer E2E Test & Report Generator" echo "==========================================" # ── Step 1-2: Generate data + build ────────────────────────────────────────── if [ "${PDF_ONLY}" -eq 0 ]; then echo "" echo "── Step 1: Generating test data ──" python3 "${SCRIPT_DIR}/gen_test_data.py" echo "" echo "── Step 2: Building components ──" make -C "${REPO_ROOT}/Source/Components/Interfaces/UDPStream" \ -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -2 make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamerClient" \ -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -2 make -C "${REPO_ROOT}/Source/Components/DataSources/UDPStreamer" \ -f Makefile.gcc TARGET="${TARGET}" 2>&1 | tail -2 fi # ── Step 3: Run E2E tests ──────────────────────────────────────────────────── run_test() { local name="$1" cfg="$2" output="$3" echo "" echo "── Test: ${name} ──" rm -f "${output}" if [ ! -x "${MARTE_APP}" ]; then echo " SKIP: MARTeApp.ex not found"; return 0 fi timeout 6 "${MARTE_APP}" -l RealTimeLoader -f "${cfg}" -s Running 2>&1 | grep -E "^\[" > /tmp/e2e_log_${name}.txt & local pid=$! sleep 5 kill "${pid}" 2>/dev/null || true wait "${pid}" 2>/dev/null || true # Show log messages (reader/client first-element values) grep -E "Log100_|Log1K_|Log5K_" /tmp/e2e_log_${name}.txt 2>/dev/null | head -20 || true echo " Done." } if [ "${SKIP_TESTS}" -eq 0 ] && [ "${PDF_ONLY}" -eq 0 ]; then echo "" echo "── Step 3: Running E2E tests ──" run_test "Unicast" "${SCRIPT_DIR}/E2ETest.cfg" "${OUTPUT_U}" run_test "Multicast" "${SCRIPT_DIR}/E2EMulticastTest.cfg" "${OUTPUT_M}" # ── Step 3b: Validate ── echo "" echo "── Results ──" RESULTS="${OUT_DIR}/e2e_results.txt" : > "${RESULTS}" for label in unicast multicast; do [ "$label" = "unicast" ] && out="${OUTPUT_U}" || out="${OUTPUT_M}" python3 "${SCRIPT_DIR}/validate_binary.py" "${INPUT}" "${out}" --label "${label}" \ --json "${OUT_DIR}/e2e_${label}.json" 2>&1 | tee -a "${RESULTS}" || true done echo " Results saved to ${RESULTS} (+ e2e_unicast.json, e2e_multicast.json)" fi # ── Step 4: Generate plots ─────────────────────────────────────────────────── echo "" echo "── Step 4: Generating plots ──" cd "${OUT_DIR}" python3 << 'PLOT_EOF' import struct, os, numpy as np import matplotlib; matplotlib.use('Agg'); import matplotlib.pyplot as plt from matplotlib.gridspec import GridSpec INPUT="/tmp/udpstreamer_test_input.bin" OUTPUT_U="/tmp/udpstreamer_test_output.bin" def read_binary(fn): if not os.path.exists(fn): return None,None with open(fn,'rb') as f: ns=struct.unpack('=1 else f'{l*1000:.0f} µs',va='center',fontsize=9,fontweight='bold') ax.text(0.2,b.get_y()+b.get_height()/2,comps[len(comps)-int(b.get_y()+b.get_height())],va='center',fontsize=8,color='white',fontweight='bold') ax.set_xlabel('Latency (ms)',fontsize=11) ax.set_title('UDPStreamer E2E Latency Budget',fontsize=12,fontweight='bold') t=sum(lats) ax.text(0.15,-0.4,f'Total: {t:.1f} ms | Max throughput: {1000/t:.0f} Hz',fontsize=11,fontweight='bold',transform=ax.get_xaxis_transform()) plt.tight_layout(); plt.savefig('latency_budget.png',dpi=150); plt.close() print(' ✓ latency_budget.png') print(' All plots generated.') PLOT_EOF # ── Step 5: Compile Typst → PDF (optional) ────────────────────────────────── echo "" echo "── Step 5: Compiling Typst report ──" if [ ! -f "${SCRIPT_DIR}/E2E_Report.typ" ]; then echo " SKIP: E2E_Report.typ template not present." elif ! command -v typst >/dev/null 2>&1; then echo " SKIP: typst not installed." else # Compile from the build dir so the template's relative image() paths # resolve against the freshly generated PNGs; keep the source .typ pristine. cp "${SCRIPT_DIR}/E2E_Report.typ" "${OUT_DIR}/E2E_Report.typ" typst compile "${OUT_DIR}/E2E_Report.typ" "${OUT_DIR}/E2E_Report.pdf" 2>&1 if [ -f "${OUT_DIR}/E2E_Report.pdf" ]; then SIZE=$(ls -lh "${OUT_DIR}/E2E_Report.pdf" | awk '{print $5}') echo " ✓ Report generated: ${OUT_DIR}/E2E_Report.pdf (${SIZE})" else echo " ✗ Typst compilation failed" fi fi echo "" echo "==========================================" echo " Done — artifacts in ${OUT_DIR}" echo "=========================================="