From e5c649e69a08e9c09adece0152a37a800d85e249 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Fri, 16 Oct 2026 14:18:09 +0200 Subject: [PATCH] =?UTF-8?q?test(d=C3=A9cisions):=20horloge=20murale=20fig?= =?UTF-8?q?=C3=A9e=20au=20d=C3=A9marrage=20de=20chaque=20processus=20(brie?= =?UTF-8?q?f=204=20T0.5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- tests/auto/decisions/decisionrecorder.cpp | 31 +++++++++++++++++++++++ tests/auto/decisions/run-decisions.sh | 7 ++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/auto/decisions/decisionrecorder.cpp b/tests/auto/decisions/decisionrecorder.cpp index 17f93cd..3cf80a4 100644 --- a/tests/auto/decisions/decisionrecorder.cpp +++ b/tests/auto/decisions/decisionrecorder.cpp @@ -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 +#include + +/* 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=, 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(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(ts->tv_sec); + ts->tv_sec += decalage; + return r; +} + #define REEL_CYCLE __real__ZN20SmartChargingManager20simulationCallUpdateERK9QDateTime #define ENVEL_CYCLE __wrap__ZN20SmartChargingManager20simulationCallUpdateERK9QDateTime #define REEL_PLAN __real__ZN18RuleBasedScheduler7getPlanERK14SurplusContext diff --git a/tests/auto/decisions/run-decisions.sh b/tests/auto/decisions/run-decisions.sh index 23fe63d..da9bc95 100755 --- a/tests/auto/decisions/run-decisions.sh +++ b/tests/auto/decisions/run-decisions.sh @@ -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" \