test(décisions): horloge murale figée au démarrage de chaque processus (brief 4 T0.5)

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>
This commit is contained in:
Patrick Schurig 2026-10-16 14:18:09 +02:00
parent ac6dedbd53
commit e5c649e69a
2 changed files with 37 additions and 1 deletions

View File

@ -33,6 +33,9 @@
* Deux exécutions du même code doivent donner le même fichier à l'octet près : c'est ainsi que
* cette liste a été établie (2026-10-16), et c'est ainsi qu'on vérifiera un ajout.
*
* L'horloge murale du processus est figée à son démarrage (ETM_DECISIONS_EPOCH, posé par le
* lanceur) : voir `clock_gettime` plus bas.
*
* Le contrôle ne doit pas pouvoir disparaître en silence (AGENTS.md) : un cycle sur un
* gestionnaire qui n'est pas un EnergyArbitrator s'écrit `"arbitre": false`, et le lanceur
* refuse un fichier où aucun cycle n'a été enregistré.
@ -53,6 +56,34 @@
#include "../../../energyplugin/etm/scheduler/rulebasedscheduler.h"
#include <dlfcn.h>
#include <time.h>
/* L'HORLOGE MURALE FIGÉE AU DÉMARRAGE. Des scénarios bâtissent leurs dates sur
* QDateTime::currentDateTimeUtc() (testTheLearningLogIsFedByTheCycle : l'heure courante, plus
* trois jours) : sans ceci, la référence ne vaudrait que dans l'heure UTC où elle a été générée
* — constaté le 2026-10-16, une heure plus tard, sur un code inchangé.
*
* Avec ETM_DECISIONS_EPOCH=<secondes Unix>, CLOCK_REALTIME part de cet instant au premier appel
* du processus, puis avance au rythme réel. Les horloges monotones (QTimer, QElapsedTimer) ne
* sont pas touchées. L'exécutable définit `clock_gettime` : Qt et la libstdc++, qui l'appellent
* par la PLT, résolvent vers celle-ci avant celle de la libc. */
extern "C" int clock_gettime(clockid_t horloge, struct timespec *ts) noexcept
{
using Reelle = int (*)(clockid_t, struct timespec *);
static const Reelle reelle = reinterpret_cast<Reelle>(dlsym(RTLD_NEXT, "clock_gettime"));
const int r = reelle(horloge, ts);
if (r != 0 || (horloge != CLOCK_REALTIME && horloge != CLOCK_REALTIME_COARSE))
return r;
static const char *const epoque = getenv("ETM_DECISIONS_EPOCH");
if (!epoque)
return r;
// Initialisation d'une statique locale : une seule fois, au premier appel, et sûre entre fils.
static const long long decalage = atoll(epoque) - static_cast<long long>(ts->tv_sec);
ts->tv_sec += decalage;
return r;
}
#define REEL_CYCLE __real__ZN20SmartChargingManager20simulationCallUpdateERK9QDateTime
#define ENVEL_CYCLE __wrap__ZN20SmartChargingManager20simulationCallUpdateERK9QDateTime
#define REEL_PLAN __real__ZN18RuleBasedScheduler7getPlanERK14SurplusContext

View File

@ -32,6 +32,11 @@ 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
@ -41,7 +46,7 @@ export NYMEA_PLUGINS_PATH="$BUILD/tests/mocks/plugins/energymocks:/usr/lib/x86_6
# 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" \
( 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" \