From 7d4e2351f40883658d15f7c42e80fb6c4888078f Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Thu, 27 Aug 2026 10:05:51 +0200 Subject: [PATCH] fix(tests): la suite de simulation ne compilait plus depuis 639689b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le lot E §12 a ajouté le paramètre minPowerW au constructeur d'EtmVariableLoadAdapter sans le répercuter sur les cinq sites d'appel de tests/auto/simulation. La suite ne compile donc plus depuis ce commit — autrement dit, personne ne l'a exécutée depuis. Les cinq sites passent désormais minPowerW = 0 explicitement. Deux attentes avaient rouillé pendant ce temps, et les deux portent sur du TEXTE publié — ce qui est précisément ce que ces tests doivent surveiller : - testEcsColdStartLockExpires attendait « insuffisant » là où f134048 a délibérément fait sortir BELOW_MIN_POWER. Le cas est « il y a un budget, il n'est pas au bon FORMAT » : le motif doit nommer le palier. Ce que le test éprouve reste le même — ce n'est pas le verrou. - testDecisionReasonRendering attendait « Surplus insuffisant » pour SG_NORMAL, reformulé en « Aucun surplus à allouer » par le même commit, parce que ce motif ne sort plus que pour un budget nul ou négatif. Le catalogue de motifs avait dérivé au passage : BATTERY_RESERVE et BELOW_MIN_POWER étaient entrés en 3g-1 sans ligne de rendu attendue ni entrée dans la liste de clôture. Leur texte — celui sur lequel le banc s'appuie pour distinguer « il n'y a rien » de « il y en a, mais pas au bon format » ou « une règle le retient » — n'était donc éprouvé par rien. Refermé, avec les deux formes de BELOW_MIN_POWER (avec et sans état). Suite verte : simulation 24/24 fonctions, dont les 16 scénarios de `run`. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj --- tests/auto/simulation/simulation.cpp | 47 +++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/tests/auto/simulation/simulation.cpp b/tests/auto/simulation/simulation.cpp index 27c1ffe..1eccd17 100644 --- a/tests/auto/simulation/simulation.cpp +++ b/tests/auto/simulation/simulation.cpp @@ -201,7 +201,7 @@ void Simulation::testEcsSurplusPV() EtmVariableLoadAdapter *ecs = new EtmVariableLoadAdapter( thingManager, ecsId.toString(), "ECS variable", - QList({0, 1200, 2400}), 2400, 1, LoadNeeds(), arbitrator); + QList({0, 1200, 2400}), 2400, /* minPowerW */ 0, 1, LoadNeeds(), arbitrator); arbitrator->registerEtmVariableLoadAdapter(ecs); auto setMeterW = [&](double signedW){ meterThing->setStateValue("currentPower", signedW); }; // <0 = export @@ -249,7 +249,7 @@ void Simulation::testEcsSurplusPV() rThing->setStateValue("currentPowerW", 0); EtmVariableLoadAdapter *router = new EtmVariableLoadAdapter( - tm2, rId.toString(), "Routeur PV", QList(), 3000, 1, LoadNeeds(), arb2); // powerLevels vide → dynamic + tm2, rId.toString(), "Routeur PV", QList(), 3000, /* minPowerW */ 0, 1, LoadNeeds(), arb2); // powerLevels vide → dynamic arb2->registerEtmVariableLoadAdapter(router); m2->setStateValue("currentPower", -2000); arb2->simulationCallUpdate(t0); QCoreApplication::processEvents(); @@ -290,7 +290,7 @@ void Simulation::testMeterSilentFallback() EtmVariableLoadAdapter *ecs = new EtmVariableLoadAdapter( thingManager, ecsId.toString(), "ECS repli", - QList({0, 2400}), 2400, 1, LoadNeeds(), arbitrator); + QList({0, 2400}), 2400, /* minPowerW */ 0, 1, LoadNeeds(), arbitrator); arbitrator->registerEtmVariableLoadAdapter(ecs); const QDateTime t0 = utcDateTime(QDate(2026, 6, 8), QTime(13, 0, 0)); @@ -1415,7 +1415,11 @@ void Simulation::testEcsColdStartLockExpires() RelayRouter *r3 = new RelayRouter(tm, "froid3", "Charge à froid 3", QList({ {rA.toString(), 1000} }), 0, 0, 1, LoadNeeds(), arb); const QString mBudget = motifPour(r3, 200, t0); // 200 W < palier 1000 W - QVERIFY2(mBudget.contains("insuffisant", Qt::CaseInsensitive), qUtf8Printable("motif: " + mBudget)); + // §12 / f134048 — ce cas n'est plus « surplus insuffisant » mais BELOW_MIN_POWER : il y a + // un budget, il n'est simplement pas au bon FORMAT. Le motif doit donc nommer le palier — + // la distinction envoie l'installateur attendre le soleil ou relire son câblage, pas la + // même chose. Ce qui reste inchangé, et qui est l'objet du test : ce n'est pas le verrou. + QVERIFY2(mBudget.contains("palier", Qt::CaseInsensitive), qUtf8Printable("motif: " + mBudget)); QVERIFY2(!mBudget.contains("Verrou"), qUtf8Printable("motif: " + mBudget)); // 3. Verrou minOn : palier maintenu AU-DESSUS du budget. Troisième cas, troisième texte. @@ -4022,8 +4026,31 @@ void Simulation::testDecisionReasonRendering() { { DecisionCode::SgRecommended, {{"budgetW", 2000}, {"estimatedW", 1500}} }, QStringLiteral("PAC test"), QStringLiteral("Surplus PV 2000 W — PAC test recommandée (état 3, ~1500 W)") }, + // §12 / f134048 — SG_NORMAL ne sort plus QUE pour un budget nul ou négatif : le cas + // « budget présent mais sous l'état 3 » est passé à BELOW_MIN_POWER. La phrase dit + // donc « aucun surplus », là où « insuffisant » couvrait aussi, et à tort, un budget + // de 933 W devant un état à 1 500 W. { { DecisionCode::SgNormal, {{"budgetW", 1000}} }, QStringLiteral("PAC test"), - QStringLiteral("Surplus insuffisant (1000 W) — PAC test en normal (état 2, non pilotée)") }, + QStringLiteral("Aucun surplus à allouer (1000 W) — PAC test en normal (état 2, non pilotée)") }, + + // 3g-1 — les deux motifs sur lesquels le banc s'appuie pour distinguer « il n'y a + // rien » de « il y en a, mais pas au bon format » ou « une règle le retient ». Leur + // TEXTE n'était pas éprouvé : ils ont rejoint le catalogue sans ligne ici. + { { DecisionCode::BatteryReserve, + {{"withheldW", 3200}, {"socPercent", 35}, {"reservePercent", 80}} }, + QStringLiteral("Chauffe-eau"), + QStringLiteral("3200 W de surplus retenus — batterie à 35 %, sous la réserve de 80 % ; " + "aucune charge n'est servie") }, + { { DecisionCode::BelowMinPower, {{"budgetW", 1096}, {"minPowerW", 2070}} }, + QStringLiteral("Borne du garage"), + QStringLiteral("Budget 1096 W sous le premier palier de Borne du garage (2070 W) — 0 W, " + "le budget passe à la charge suivante") }, + // La variante à ÉTATS nomme l'état hors d'atteinte : sans lui, une phrase vague en + // remplacerait une autre (note de DecisionCode::BelowMinPower). + { { DecisionCode::BelowMinPower, {{"budgetW", 933}, {"minPowerW", 1500}, {"state", 3}} }, + QStringLiteral("PAC test"), + QStringLiteral("Budget 933 W sous l'état 3 de PAC test (1500 W estimés) — état 2, " + "le budget passe à la charge suivante") }, // --- Sécurité et cycle de vie --- { { DecisionCode::DegradedL2 }, QString(), @@ -4054,7 +4081,10 @@ void Simulation::testDecisionReasonRendering() DecisionCode::SurplusSetpoint, DecisionCode::SurplusInsufficient, DecisionCode::LockMinStateHold, DecisionCode::SgForced, DecisionCode::SgRecommended, DecisionCode::SgNormal, DecisionCode::DegradedL2, DecisionCode::SafeStateRelay, - DecisionCode::SafeStateSetpoint, DecisionCode::SafeStateSgReady }; + DecisionCode::SafeStateSetpoint, DecisionCode::SafeStateSgReady, + // 3g-1 : ajoutés au catalogue sans être ajoutés ICI — la clôture avait dérivé, donc + // la garantie « tout code a une ligne de rendu éprouvée » ne tenait plus. + DecisionCode::BatteryReserve, DecisionCode::BelowMinPower }; for (const QString &code : catalogue) QVERIFY2(codesCouverts.contains(code), qUtf8Printable(QStringLiteral("code %1 sans ligne de rendu attendue").arg(code))); @@ -4603,7 +4633,7 @@ void Simulation::testRuntimeViewIsSubstantial() QVERIFY(varThing); varThing->setStateValue("currentPowerW", 0); EtmVariableLoadAdapter *continu = new EtmVariableLoadAdapter( - tm, varId.toString(), "Routeur PV", QList(), 4000, 2, LoadNeeds(), arb); + tm, varId.toString(), "Routeur PV", QList(), 4000, /* minPowerW */ 0, 2, LoadNeeds(), arb); LoadAction consigne; consigne.kind = LoadAction::Setpoint; consigne.funding = LoadAction::Surplus; @@ -4626,7 +4656,7 @@ void Simulation::testRuntimeViewIsSubstantial() // Le code de défaut n'est pas du code mort : un thing absent de la configuration le lève. EtmVariableLoadAdapter *fantome = new EtmVariableLoadAdapter( tm, "{deadbeef-0000-0000-0000-000000000042}", "Charge fantôme", - QList(), 1000, 3, LoadNeeds(), arb); + QList(), 1000, /* minPowerW */ 0, 3, LoadNeeds(), arb); QCOMPARE(fantome->runtimeView(t0).faultCode, QString("THING_MISSING")); // ---- 3. SgReadyAdapter : état normé, estimation, verrou minStateHold ------------------ @@ -4933,3 +4963,4 @@ QStringList Simulation::plotSpotMarketData(int powerBalanceCount) QTEST_MAIN(Simulation) +