Constaté le 2026-10-16 : sur un code inchangé, la vérification échouait une heure après la génération de la référence. testTheLearningLogIsFedByTheCycle date ses entrées sur QDateTime::currentDateTimeUtc() (l'heure courante plus trois jours) : la référence ne valait que dans l'heure UTC où elle avait été produite. L'enregistreur définit clock_gettime : avec ETM_DECISIONS_EPOCH, CLOCK_REALTIME part de cet instant au premier appel puis avance au rythme réel ; les horloges monotones (QTimer, QElapsedTimer) ne sont pas touchées. Le lanceur pose 2026-10-16T05:00:00Z, l'heure où la référence a été générée : la référence commitée n'est pas régénérée, elle est reproduite à l'octet (md5 ea2c1db7…, 23 739 lignes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
4.4 KiB
Bash
Executable File
96 lines
4.4 KiB
Bash
Executable File
#!/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 <build> record <sortie.ndjson> enregistre seulement
|
|
# run-decisions.sh <build> check enregistre puis compare à la référence
|
|
# (code 1 au premier écart, et il est montré)
|
|
#
|
|
# <build> : 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
|
|
|
|
# L'HORLOGE MURALE de chaque processus part de cet instant (voir decisionrecorder.cpp) : des
|
|
# scénarios datent leurs entrées sur l'heure courante, et la référence doit valoir à toute heure.
|
|
# 2026-10-16T05:00:00Z, début d'heure : un test qui arrondit à l'heure a 60 min de marge.
|
|
EPOQUE=1792126800
|
|
|
|
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" ETM_DECISIONS_EPOCH="$EPOQUE" \
|
|
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
|