fix: ECS-410-b — l'issue de ClearLoadFault, et l'asymétrie refus/succès (règles 7-c, 7-d)
ClearLoadFault est le SEUL levier de reprise à distance : ECS-410 pose un verrou collant que seuls ce RPC ou une reconstruction lèvent. L'arbitre journalisait la demande de l'opérateur, jamais son issue — si bien qu'appelé depuis l'étranger, on ne distinguait pas « le défaut a été levé » de « il n'y en avait pas » de « l'appel n'a pas atteint l'adaptateur ». L'opérateur relance trois fois et conclut que le système est cassé. L'issue est écrite dans l'ARBITRE, pas dans chaque adaptateur : une ligne là couvre les quatre d'un coup et ne peut pas être oubliée par un adaptateur futur ; répétée dans chacun, elle manquerait le cinquième. Les trois cas sont distincts à la lecture. ILoadAdapter::clearFault() rend un bool au lieu de void. Le type de retour force chaque implémentation, présente et future, à répondre à la question « un défaut a-t-il réellement été levé ». EvAdapter, qui n'a pas de mécanisme de défaut, le dit désormais explicitement plutôt que par un corps vide. Règle 7-d — un refus doit être au moins aussi visible que l'application correspondante. Le refus par verrou minStateHold de SgReadyAdapter était en qCDebug quand l'application, dix lignes plus bas, était en qCInfo. L'asymétrie est le défaut, pas le niveau : un resserrement de la journalisation — et on vient d'en faire un pour tenir deux semaines — aurait fait disparaître le refus AVANT le succès, ne laissant au journal que les décisions abouties. Or un refus est plus informatif qu'une application réussie : il dit qu'une décision a été prise et n'a pas été exécutée. Vérification faite sur les autres adaptateurs : c'était le seul cas. Les qCDebug restants sont symétriques, et le marqueur L2 par cycle reste en debug à dessein, son entrée étant en qCWarning et sa sortie en qCInfo. Le retour idempotent du routeur reste DIFFÉRÉ, et ira en qCDebug : c'est le cas le plus fréquent — plusieurs milliers de lignes par jour sur un budget ramené à 6,5 Mo — et son ambiguïté est partiellement couverte, le scheduler journalisant sa décision et l'armement à froid s'exécutant avant ce retour. Faible gain, coût réel : l'inverse des deux cas corrigés. Le test porte sur le TEXTE des trois issues et vérifie qu'elles sont deux à deux distinctes. Suite complète : 112 tests, 0 échec. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b52e900e17
commit
cd1a0c83a5
20
AGENTS.md
20
AGENTS.md
@ -347,7 +347,25 @@ Règles absolues :
|
||||
> encodages solo, que la déduplication peut évincer — d'où un palier 0 annoncé pendant
|
||||
> que 1500 W circulaient. **Rendre la lecture visible est ce qui rend le défaut
|
||||
> trouvable** ; c'est le sens de la règle, pas un effet secondaire.
|
||||
- Les tests de ces trois points portent sur le **TEXTE** publié, jamais sur la
|
||||
- **7-d — un refus DOIT être au moins aussi visible que l'application correspondante.**
|
||||
Jamais un refus en `qCDebug` quand le succès qu'il remplace est en `qCInfo`.
|
||||
L'asymétrie est le défaut, pas le niveau : un resserrement de la journalisation fait
|
||||
alors disparaître le refus **avant** le succès, et il ne reste au journal que les
|
||||
décisions qui ont abouti. Or **un refus est plus informatif qu'une application
|
||||
réussie** — il dit qu'une décision a été prise et n'a PAS été exécutée.
|
||||
> `sgreadyadapter.cpp:150` journalisait le refus par verrou `minStateHold` en
|
||||
> `qCDebug` quand l'application, dix lignes plus bas, était en `qCInfo`. Corrigé le
|
||||
> 2026-08-13 ; c'était le seul cas, les autres `qCDebug` du moteur étant symétriques
|
||||
> (traces d'enregistrement et de construction, où les deux branches sont au même
|
||||
> niveau). Le marqueur « mode dégradé L2 actif » reste en `qCDebug` à dessein :
|
||||
> l'entrée est en `qCWarning` et la sortie en `qCInfo`, donc l'épisode reste
|
||||
> délimité sans une ligne par cycle.
|
||||
- **Une issue rendue à un opérateur distant se journalise là où elle est décidée**, pas
|
||||
dans chaque implémentation. `ClearLoadFault` est le seul levier de reprise à distance :
|
||||
son issue est écrite dans l'arbitre, qui couvre tous les adaptateurs d'un coup et ne
|
||||
peut pas être oublié par un adaptateur futur. Quand le type de retour permet de forcer
|
||||
la réponse — `clearFault()` rend un `bool` — le préférer à la discipline.
|
||||
- Les tests de ces quatre points portent sur le **TEXTE** publié, jamais sur la
|
||||
non-vacuité d'une chaîne : un test « motif non vide » n'aurait rien vu, dans aucun
|
||||
de ces cas.
|
||||
8. **Pas de boucle de feedback** : surplus = PV mesurée + compteur, jamais le net
|
||||
|
||||
@ -20,7 +20,26 @@ powersync-energy-plugin-nymea (1.15.2+etm12) trixie; urgency=medium
|
||||
* Protocole de mesure (RELEVE_ECS306 §3.0) : un état nymea n'émet changed() que si la
|
||||
valeur bouge, donc une grandeur forcée constante déclenche le watchdog L2 en 90 s et
|
||||
fait mesurer le watchdog au lieu de l'arbitrage. Constaté au banc.
|
||||
* Suite complète : 112 tests, 0 échec. Les deux correctifs sont vérifiés échouant sans eux.
|
||||
* ECS-410-b — l'issue de ClearLoadFault est journalisée, et par l'ARBITRE. Elle distingue
|
||||
trois cas : défaut levé, aucun défaut à lever, adaptateur introuvable. C'est le seul
|
||||
levier de reprise à distance : ECS-410 pose un verrou collant que seuls ce RPC ou une
|
||||
reconstruction lèvent, et sans issue lisible l'opérateur ne sait pas si son geste a agi
|
||||
— il relance, puis conclut que le système est cassé. Écrite au niveau de l'arbitre, la
|
||||
ligne couvre les quatre adaptateurs d'un coup et ne peut pas être oubliée par un
|
||||
adaptateur futur. ILoadAdapter::clearFault() rend désormais un bool plutôt que void : le
|
||||
type de retour force chaque implémentation à répondre à la question, là où une consigne
|
||||
de journalisation se serait oubliée à la cinquième.
|
||||
* Règle 7-d d'AGENTS.md : un refus DOIT être au moins aussi visible que l'application
|
||||
correspondante. Le refus par verrou minStateHold de SgReadyAdapter était en qCDebug quand
|
||||
l'application, dix lignes plus bas, était en qCInfo — un resserrement de la journalisation
|
||||
aurait fait disparaître le refus AVANT le succès, ne laissant au journal que les décisions
|
||||
abouties. Or un refus est plus informatif : il dit qu'une décision a été prise et n'a pas
|
||||
été exécutée. Les autres qCDebug du moteur ont été vérifiés symétriques.
|
||||
* Retour idempotent du routeur (relayrouter.cpp:228) : DIFFÉRÉ, et en qCDebug le jour venu.
|
||||
C'est le cas le plus fréquent — plusieurs milliers de lignes par jour — et l'ambiguïté y
|
||||
est partiellement couverte, le scheduler journalisant sa décision et l'armement à froid
|
||||
s'exécutant avant ce retour.
|
||||
* Suite complète : 112 tests, 0 échec. Tous les correctifs sont vérifiés échouant sans eux.
|
||||
|
||||
-- Patrick Schurig <etm.schurig@gmail.com> Thu, 13 Aug 2026 21:00:00 +0200
|
||||
|
||||
|
||||
@ -223,10 +223,10 @@ void EtmVariableLoadAdapter::settleTransition()
|
||||
}
|
||||
}
|
||||
|
||||
void EtmVariableLoadAdapter::clearFault()
|
||||
bool EtmVariableLoadAdapter::clearFault()
|
||||
{
|
||||
if (!m_faulted)
|
||||
return;
|
||||
return false; // rien à lever — l'arbitre le dira (règle 7-c)
|
||||
qCInfo(dcNymeaEnergy()) << "[EtmVariableLoadAdapter]" << m_label
|
||||
<< "— défaut levé par l'opérateur (ClearLoadFault).";
|
||||
m_faulted = false;
|
||||
@ -234,4 +234,5 @@ void EtmVariableLoadAdapter::clearFault()
|
||||
m_writeFailed = false;
|
||||
m_currentSetpointW = readCurrentPowerW(); // relu, pas supposé
|
||||
m_setpointPrev = m_setpointTarget = m_currentSetpointW;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ public:
|
||||
//! La généralisation est portée par **ECS-414** (lot de mise en configuration
|
||||
//! du SgReadyAdapter). Déclaré explicitement plutôt qu'hérité d'un défaut vide.
|
||||
//! \brief Lève le verrou de défaut (ECS-414). La consigne réelle est RELUE, pas supposée.
|
||||
void clearFault() override;
|
||||
bool clearFault() override;
|
||||
|
||||
//! \return Vrai si la charge est en défaut (plus aucune consigne écrite).
|
||||
bool faulted() const { return m_faulted; }
|
||||
|
||||
@ -79,7 +79,9 @@ public:
|
||||
//! \brief Sans effet : l'EV n'implémente pas encore l'échelle de défaut ECS-410.
|
||||
//! La généralisation est portée par **ECS-414** (lot de mise en configuration
|
||||
//! du SgReadyAdapter). Déclaré explicitement plutôt qu'hérité d'un défaut vide.
|
||||
void clearFault() override {}
|
||||
//! Aucun mécanisme de défaut côté EV : il n'y a jamais rien à lever. Le dire
|
||||
//! explicitement plutôt que de laisser un corps vide (règle 7-c).
|
||||
bool clearFault() override { return false; }
|
||||
|
||||
//! \brief Sans effet : l'EV n'est pas construit depuis \c LoadConfig et ne peut donc pas
|
||||
//! être « désactivé » par ce chemin. \param now Ignoré.
|
||||
|
||||
@ -104,8 +104,19 @@ public:
|
||||
* \note **Pure virtuelle à dessein.** Tous les adaptateurs ne savent pas encore tomber en
|
||||
* défaut — seul \c RelayRouter implémente l'échelle ECS-410 — mais chacun doit le
|
||||
* DÉCLARER. La généralisation est portée par **ECS-414**.
|
||||
*
|
||||
* \return \c true si un défaut a RÉELLEMENT été levé, \c false s'il n'y en avait pas.
|
||||
*
|
||||
* \note **Pourquoi un booléen et non \c void** (règle 7-c d'AGENTS.md). C'est le seul
|
||||
* levier de reprise à distance : ECS-410 pose un verrou collant que seuls ce RPC ou une
|
||||
* reconstruction lèvent. Sans valeur de retour, l'arbitre ne peut pas distinguer
|
||||
* « défaut levé » de « aucun défaut à lever », et un opérateur à distance ne peut pas
|
||||
* savoir si son geste a agi — il relancera, puis conclura que le système est cassé.
|
||||
* Le type de retour force chaque implémentation, présente et future, à répondre à la
|
||||
* question ; une ligne de journal dans chaque adaptateur se serait oubliée à la
|
||||
* cinquième.
|
||||
*/
|
||||
virtual void clearFault() = 0;
|
||||
virtual bool clearFault() = 0;
|
||||
|
||||
/*!
|
||||
* \brief Amène le matériel dans l'ÉTAT SÛR de cet adaptateur (ECS-413).
|
||||
|
||||
@ -489,10 +489,10 @@ void RelayRouter::applySafeState(const QDateTime &now)
|
||||
applyAction(sur, now);
|
||||
}
|
||||
|
||||
void RelayRouter::clearFault()
|
||||
bool RelayRouter::clearFault()
|
||||
{
|
||||
if (!m_faulted)
|
||||
return;
|
||||
return false; // rien à lever — l'arbitre le dira (règle 7-c)
|
||||
qCInfo(dcNymeaEnergy()) << "[RelayRouter]" << m_label
|
||||
<< "— défaut levé par l'opérateur (ClearLoadFault).";
|
||||
m_faulted = false;
|
||||
@ -502,6 +502,7 @@ void RelayRouter::clearFault()
|
||||
// (même principe qu'ECS-411, et jamais moins que ce qui peut être appliqué).
|
||||
m_currentStage = deduceStageFromThings();
|
||||
m_stagePrev = m_stageTarget = m_currentStage;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RelayRouter::emitRelayWrites(int stage)
|
||||
|
||||
@ -102,7 +102,7 @@ public:
|
||||
* une expiration. L'état matériel réel étant inconnu après un défaut, il est RELU
|
||||
* (même principe qu'ECS-411) et non supposé.
|
||||
*/
|
||||
void clearFault() override;
|
||||
bool clearFault() override;
|
||||
|
||||
//! \brief État sûr du routeur : TOUS relais ouverts (ECS-413).
|
||||
//! \param now Temps de cycle — l'action passe en \c force = true, verrous bypassés.
|
||||
|
||||
@ -148,8 +148,14 @@ LoadAction SgReadyAdapter::applyAction(const LoadAction &action, const QDateTime
|
||||
// Verrou minStateHold évalué au temps de cycle (même fenêtre que le scheduler) —
|
||||
// bypassé si force == true (L2 watchdog → état 2).
|
||||
if (!action.force && lockActive(newState, now)) {
|
||||
qCDebug(dcNymeaEnergy()) << "[SgReadyAdapter]" << m_label
|
||||
<< "— verrou minStateHold actif, état" << newState << "ignoré.";
|
||||
// Règle 7-c — un REFUS doit être au moins aussi visible que l'application
|
||||
// correspondante. Ce refus était en qCDebug quand l'application juste en dessous est
|
||||
// en qCInfo : un resserrement de la journalisation faisait disparaître le refus AVANT
|
||||
// le succès. Or un refus est plus informatif qu'une application réussie — il dit
|
||||
// qu'une décision a été prise et n'a PAS été exécutée.
|
||||
qCInfo(dcNymeaEnergy()) << "[SgReadyAdapter]" << m_label
|
||||
<< "— verrou minStateHold actif, état" << newState
|
||||
<< "REFUSÉ (état" << m_currentState << "maintenu).";
|
||||
return action;
|
||||
}
|
||||
|
||||
@ -349,10 +355,10 @@ void SgReadyAdapter::settleTransition()
|
||||
}
|
||||
}
|
||||
|
||||
void SgReadyAdapter::clearFault()
|
||||
bool SgReadyAdapter::clearFault()
|
||||
{
|
||||
if (!m_faulted)
|
||||
return;
|
||||
return false; // rien à lever — l'arbitre le dira (règle 7-c)
|
||||
qCInfo(dcNymeaEnergy()) << "[SgReadyAdapter]" << m_label
|
||||
<< "— défaut levé par l'opérateur (ClearLoadFault).";
|
||||
m_faulted = false;
|
||||
@ -363,6 +369,7 @@ void SgReadyAdapter::clearFault()
|
||||
if (lu > 0)
|
||||
m_currentState = lu;
|
||||
m_statePrev = m_stateTarget = m_currentState;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SgReadyAdapter::applyStateRelays(const QSet<QString> ¤tOn, int toState)
|
||||
|
||||
@ -106,7 +106,7 @@ public:
|
||||
* \note L'état réel est RELU depuis les contacts, jamais supposé : après un défaut, le
|
||||
* motif 2 bits peut être valide mais non voulu.
|
||||
*/
|
||||
void clearFault() override;
|
||||
bool clearFault() override;
|
||||
|
||||
//! \return Vrai si la PAC est en défaut (plus aucune commande émise).
|
||||
bool faulted() const { return m_faulted; }
|
||||
|
||||
@ -171,13 +171,32 @@ bool EnergyArbitrator::sameHardware(const LoadConfig &a, const LoadConfig &b)
|
||||
|
||||
bool EnergyArbitrator::clearLoadFault(const QString &loadId)
|
||||
{
|
||||
// Règle 7-c — l'ISSUE est journalisée ici, au niveau de l'arbitre, et pas dans chaque
|
||||
// adaptateur. Une ligne écrite là couvre les quatre adaptateurs d'un coup et ne peut pas
|
||||
// être oubliée par un adaptateur futur ; répétée dans chacun, elle manquerait le cinquième.
|
||||
//
|
||||
// Pourquoi cet appel mérite une issue plutôt qu'une simple trace de demande : c'est le
|
||||
// SEUL levier de reprise à distance. ECS-410 pose un verrou collant que seuls ce RPC ou
|
||||
// une reconstruction lèvent. Sans issue lisible, un opérateur à distance ne distingue pas
|
||||
// « défaut levé » de « aucun défaut à lever » de « l'appel n'a pas atteint l'adaptateur » :
|
||||
// il relance, puis conclut que le système est cassé.
|
||||
ILoadAdapter *adapter = m_loadAdapters.value(loadId, nullptr);
|
||||
if (!adapter) {
|
||||
qCWarning(dcNymeaEnergy()) << "[Arbitre] ClearLoadFault : charge inconnue" << loadId;
|
||||
qCWarning(dcNymeaEnergy()) << "[Arbitre] ClearLoadFault" << loadId
|
||||
<< "→ SANS EFFET : adaptateur introuvable "
|
||||
"(identifiant inconnu ou charge désactivée).";
|
||||
return false;
|
||||
}
|
||||
|
||||
qCInfo(dcNymeaEnergy()) << "[Arbitre] ClearLoadFault demandé par l'opérateur pour" << loadId;
|
||||
adapter->clearFault();
|
||||
const bool leve = adapter->clearFault();
|
||||
if (leve)
|
||||
qCInfo(dcNymeaEnergy()) << "[Arbitre] ClearLoadFault" << loadId
|
||||
<< "→ défaut LEVÉ ; la charge revient à l'arbitrage.";
|
||||
else
|
||||
qCInfo(dcNymeaEnergy()) << "[Arbitre] ClearLoadFault" << loadId
|
||||
<< "→ sans objet : AUCUN défaut à lever, la charge n'était "
|
||||
"pas verrouillée.";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -517,14 +517,35 @@ DOIT être publiée en **avertissement**, l'état déduit étant incertain.
|
||||
> (`m_relayNominalW`). Rendre la lecture visible est ce qui a rendu le défaut
|
||||
> trouvable.
|
||||
|
||||
> **Autres silences ambigus — SIGNALÉS, non corrigés.** Relevés en passant, à
|
||||
> traiter dans leur propre lot :
|
||||
> **Autres silences ambigus.** Relevés en passant le 2026-08-13 ; deux corrigés le
|
||||
> jour même, un différé.
|
||||
>
|
||||
> | Endroit | Silence |
|
||||
> |---|---|
|
||||
> | `relayrouter.cpp:228` | `if (newStage == m_currentStage) return applied;` — le cas idempotent, le plus fréquent, ne produit aucune ligne côté adaptateur. L'issue reste visible par le `[Arbitre]` du cycle, mais la décision de l'adaptateur, non. |
|
||||
> | `etmvariableloadadapter.cpp:228` | `clearFault()` sort en silence si la charge n'est pas en défaut. L'arbitre journalise la *demande* de l'opérateur (`energyarbitrator.cpp:179`) mais jamais son issue : « reçu, rien à lever » ne se distingue pas de « jamais arrivé ». C'est un geste opérateur, donc le cas le plus visible de la règle après ECS-411. |
|
||||
> | `sgreadyadapter.cpp:150` | Le refus par verrou `minStateHold` est en `qCDebug`, l'application en `qCInfo`. Le refus disparaît donc avant le succès si la journalisation est resserrée. Atténué aujourd'hui : le scheduler annonce le verrou en `qCInfo` de son côté. |
|
||||
> | Endroit | Silence | État |
|
||||
> |---|---|---|
|
||||
> | `clearFault()`, tous adaptateurs | « reçu, rien à lever » ne se distinguait pas de « jamais arrivé ». **Seul levier de reprise à distance** : ECS-410 pose un verrou collant que seuls ce RPC ou une reconstruction lèvent. | **FAIT** — voir ECS-410-b ci-dessous |
|
||||
> | `sgreadyadapter.cpp:150` | Refus par verrou `minStateHold` en `qCDebug`, application en `qCInfo` : un resserrement de la journalisation faisait disparaître le refus AVANT le succès. | **FAIT** — passé en `qCInfo`, règle 7-d |
|
||||
> | `relayrouter.cpp:228` | `if (newStage == m_currentStage) return applied;` — le cas idempotent ne produit aucune ligne côté adaptateur. | **DIFFÉRÉ** (après vacances) |
|
||||
>
|
||||
> **Pourquoi le retour idempotent est différé, et en `qCDebug` le jour venu.** C'est
|
||||
> le cas le **plus fréquent** : une ligne par cycle et par charge, soit plusieurs
|
||||
> milliers par jour, sur un budget de journal qu'on vient de ramener à ~6,5 Mo/jour
|
||||
> pour tenir la campagne. Et l'ambiguïté y est **partiellement couverte** : le
|
||||
> scheduler journalise sa décision à chaque cycle, et l'armement à froid d'ECS-412
|
||||
> s'exécute **avant** le retour idempotent — il n'est donc jamais manqué. Le gain
|
||||
> est faible, le coût réel : l'inverse des deux cas corrigés.
|
||||
|
||||
**ECS-410-b — L'issue de `ClearLoadFault` est journalisée, et par l'arbitre.**
|
||||
L'issue DOIT distinguer trois cas : **défaut levé**, **aucun défaut à lever**,
|
||||
**adaptateur introuvable**. Elle DOIT être écrite au niveau de l'**arbitre**, pas
|
||||
dans chaque adaptateur : une ligne écrite là couvre tous les adaptateurs d'un coup
|
||||
et ne peut pas être oubliée par un adaptateur futur. `ILoadAdapter::clearFault()`
|
||||
rend un `bool` — un défaut a-t-il réellement été levé — plutôt que `void` : le type
|
||||
de retour force chaque implémentation, présente et future, à répondre à la question,
|
||||
là où une consigne de journalisation se serait oubliée à la cinquième.
|
||||
|
||||
> C'est le seul levier de reprise à distance. Appelé depuis l'étranger sans retour
|
||||
> lisible, l'opérateur ne distingue pas les trois cas, relance, et conclut que le
|
||||
> système est cassé.
|
||||
|
||||
**ECS-414 — Généralisation de l'échelle d'échec. FAIT (2026-08-09).** ECS-410 était
|
||||
implémenté par le seul `RelayRouter`. `SgReadyAdapter` (`sgreadyadapter.cpp:212`) et
|
||||
|
||||
@ -1379,9 +1379,52 @@ void Simulation::testEcsPartialFailure()
|
||||
QVERIFY(r->faulted());
|
||||
|
||||
// Levée DÉLIBÉRÉE, par l'opérateur — le seul chemin hors reconstruction.
|
||||
r->clearFault();
|
||||
// Enregistré auprès de l'arbitre : c'est LUI qui porte l'issue (règle 7-c), donc la
|
||||
// levée passe par le chemin RPC réel et non par un appel direct à l'adaptateur.
|
||||
arb->registerRelayRouter(r);
|
||||
|
||||
// [Règle 7-c] L'issue DOIT être journalisée par l'ARBITRE, et distinguer trois cas :
|
||||
// défaut levé, aucun défaut à lever, adaptateur introuvable. C'est le seul levier de
|
||||
// reprise à distance : sans issue lisible, un opérateur ne sait pas si son geste a agi,
|
||||
// relance, puis conclut que le système est cassé. Le test porte sur le TEXTE publié.
|
||||
QVERIFY(r->faulted());
|
||||
s_journal.clear();
|
||||
s_handlerPrecedent = qInstallMessageHandler(collecterJournal);
|
||||
const bool leve = arb->clearLoadFault(QStringLiteral("ko"));
|
||||
qInstallMessageHandler(s_handlerPrecedent);
|
||||
QVERIFY(leve);
|
||||
QVERIFY(!r->faulted());
|
||||
QCOMPARE(r->telemetry().available, true);
|
||||
|
||||
const QString issue1 = s_journal.filter(QStringLiteral("ClearLoadFault")).join(QStringLiteral(" | "));
|
||||
QVERIFY2(issue1.contains(QStringLiteral("défaut LEVÉ")), qUtf8Printable(issue1));
|
||||
|
||||
// Second appel, charge SAINE : « aucun défaut à lever » doit se lire, et ne pas se
|
||||
// confondre avec le premier cas. Le RPC réussit — il n'y a pas d'erreur — mais l'issue
|
||||
// est différente, et c'est précisément la distinction que l'opérateur ne pouvait pas
|
||||
// faire avant.
|
||||
s_journal.clear();
|
||||
s_handlerPrecedent = qInstallMessageHandler(collecterJournal);
|
||||
const bool leve2 = arb->clearLoadFault(QStringLiteral("ko"));
|
||||
qInstallMessageHandler(s_handlerPrecedent);
|
||||
QVERIFY2(leve2, "la charge existe : l'appel aboutit, même sans défaut à lever");
|
||||
|
||||
const QString issue2 = s_journal.filter(QStringLiteral("ClearLoadFault")).join(QStringLiteral(" | "));
|
||||
QVERIFY2(issue2.contains(QStringLiteral("AUCUN défaut")), qUtf8Printable(issue2));
|
||||
QVERIFY2(!issue2.contains(QStringLiteral("défaut LEVÉ")), qUtf8Printable(issue2));
|
||||
|
||||
// Troisième cas : adaptateur introuvable. Ni l'un ni l'autre des deux précédents.
|
||||
s_journal.clear();
|
||||
s_handlerPrecedent = qInstallMessageHandler(collecterJournal);
|
||||
const bool leve3 = arb->clearLoadFault(QStringLiteral("charge-qui-n-existe-pas"));
|
||||
qInstallMessageHandler(s_handlerPrecedent);
|
||||
QVERIFY2(!leve3, "un identifiant inconnu ne doit pas réussir");
|
||||
|
||||
const QString issue3 = s_journal.filter(QStringLiteral("ClearLoadFault")).join(QStringLiteral(" | "));
|
||||
QVERIFY2(issue3.contains(QStringLiteral("introuvable")), qUtf8Printable(issue3));
|
||||
|
||||
// Les trois issues sont distinctes : c'est ce que la règle 7-c exige.
|
||||
QVERIFY(issue1 != issue2 && issue2 != issue3 && issue1 != issue3);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user