#!/bin/bash # Décisions de référence (brief 4 T0.5) — rejoue les scénarios de `simulation` et compare, cycle # par cycle, ce que l'arbitre a décidé au fichier de référence commité. # # run-decisions.sh record enregistre seulement # run-decisions.sh check enregistre puis compare à la référence # (code 1 au premier écart, et il est montré) # # : arbre construit depuis le .pro racine avec CONFIG+=build_tests (le binaire # `nymea-energy-decisions` y est sous tests/auto/decisions/). # # AGENTS.md : UN PROCESSUS PAR TEST, chacun sous MemoryMax=2G. Les tests ne tournent jamais en # parallèle (le harnais nymea ouvre des ports fixes). Un test qui sort en erreur est rejoué UNE # fois — instabilité connue du port 4847 — et le rejeu est annoncé, jamais masqué. set -u ICI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REFERENCE="$ICI/reference/decisions.ndjson.xz" BUILD="$(cd "${1:?build}" && pwd)" MODE="${2:?record|check}" BIN_DIR="$BUILD/tests/auto/decisions" BIN="$BIN_DIR/nymea-energy-decisions" [ -x "$BIN" ] || { echo "binaire absent : $BIN" >&2; exit 2; } case "$MODE" in record) SORTIE="${3:?sortie}" ;; check) SORTIE="$(mktemp --suffix=.ndjson)" ;; *) echo "mode inconnu : $MODE" >&2; exit 2 ;; esac TRAVAIL="$(mktemp -d)" trap 'rm -rf "$TRAVAIL"' EXIT export NYMEA_PLUGINS_PATH="$BUILD/tests/mocks/plugins/energymocks:/usr/lib/x86_64-linux-gnu/nymea/plugins" # UN /tmp PRIVÉ ET VIDE PAR PROCESSUS. libnymea range l'état du serveur de test sous # `/tmp/nymea-test`, en dur : configuration des charges, energy.conf, magasin de prévision. Ce # répertoire est partagé par toutes les exécutions de la machine et il s'accumule — mesuré le # 2026-10-16, un energy-load-configuration.json de 348 bornes orphelines laissé là le 14. Sans # isolation, le résultat dépendrait de l'historique de la machine, pas du code. lancer() { # $1 = fonction, $2 = fichier NDJSON du test rm -f "$2" ( cd "$BIN_DIR" && ETM_DECISIONS_OUT="$2" \ systemd-run --user --scope -q -p MemoryMax=2G -p MemorySwapMax=0 \ bwrap --dev-bind / / --tmpfs /tmp --bind "$BUILD" "$BUILD" --bind "$TRAVAIL" "$TRAVAIL" \ --chdir "$BIN_DIR" \ ./target_wrapper.sh ./nymea-energy-decisions "$1" > "$TRAVAIL/$1.log" 2>&1 ) } mapfile -t FONCTIONS < <(cd "$BIN_DIR" && ./nymea-energy-decisions -functions 2>/dev/null \ | sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)()$/\1/p') [ "${#FONCTIONS[@]}" -gt 0 ] || { echo "aucune fonction listée" >&2; exit 2; } : > "$SORTIE" total=0 for f in "${FONCTIONS[@]}"; do out="$TRAVAIL/$f.ndjson" lancer "$f" "$out"; code=$? if [ "$code" -ne 0 ]; then echo "REJEU $f (code $code) — $(grep -m1 -E 'FAIL!' "$TRAVAIL/$f.log" | cut -c1-160)" >&2 lancer "$f" "$out"; code=$? fi n=0; [ -f "$out" ] && { n=$(wc -l < "$out"); cat "$out" >> "$SORTIE"; } total=$((total + n)) resume=$(grep -m1 '^Totals:' "$TRAVAIL/$f.log") printf '{"fn":"%s","fin":true,"exit":%d,"cycles":%d}\n' "$f" "$code" "$n" >> "$SORTIE" printf '%-48s exit=%d cycles=%-6d %s\n' "$f" "$code" "$n" "$resume" >&2 done # Le contrôle ne doit pas pouvoir disparaître : un enregistreur débranché (wrap perdu, binaire # reconstruit sans lui) produirait un fichier sans cycle, que la comparaison verrait comme un # écart massif — mais on le dit ici en clair. [ "$total" -gt 0 ] || { echo "AUCUN cycle enregistré — l'enregistreur n'est pas branché" >&2; exit 2; } echo "fonctions=${#FONCTIONS[@]} cycles=$total" >&2 [ "$MODE" = record ] && exit 0 ATTENDU="$TRAVAIL/attendu.ndjson" xz -dc "$REFERENCE" > "$ATTENDU" || exit 2 if cmp -s "$ATTENDU" "$SORTIE"; then echo "IDENTIQUE à la référence ($(wc -l < "$ATTENDU") lignes)" >&2 rm -f "$SORTIE" exit 0 fi echo "ÉCART avec la référence — premières lignes divergentes (- attendu, + obtenu) :" >&2 diff "$ATTENDU" "$SORTIE" | head -c 6000 >&2 echo >&2 echo "fichier obtenu conservé : $SORTIE" >&2 exit 1