test: les requêtes réelles du moteur jugées contre le contrat
optimizerclient : chaque Handshake envoyé ; socketscheduler : tout ce que le moteur envoie au faux optimiseur, plus testTheEngineSpeaksTheContractItServes (GetOptimizerProtocol == embarqué == docs, requêtes jugées avec le contrat servi) ; refoptimizer : session capturée par la référence, requêtes toujours, session entière et règles sémantiques en normal et en 1.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d95a65cb71
commit
07cb44d35f
@ -130,6 +130,13 @@ public:
|
|||||||
|
|
||||||
int connections = 0;
|
int connections = 0;
|
||||||
int disconnections = 0;
|
int disconnections = 0;
|
||||||
|
//! Tout ce que le MOTEUR a envoyé à un faux optimiseur dans ce processus, dans l'ordre — jugé
|
||||||
|
//! contre le contrat OpenRPC par la suite (lot « contrat OpenRPC »), puis vidé.
|
||||||
|
static QList<QJsonObject> &capture()
|
||||||
|
{
|
||||||
|
static QList<QJsonObject> messages;
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
//! Répondeur automatique (brief 4 T3) : appelé sur chaque message reçu ; s'il rend vrai, le
|
//! Répondeur automatique (brief 4 T3) : appelé sur chaque message reçu ; s'il rend vrai, le
|
||||||
//! message est consommé et n'entre pas dans la file de waitMessage(). Vide = tout est filé.
|
//! message est consommé et n'entre pas dans la file de waitMessage(). Vide = tout est filé.
|
||||||
std::function<bool(const QJsonObject &)> responder;
|
std::function<bool(const QJsonObject &)> responder;
|
||||||
@ -146,6 +153,7 @@ private:
|
|||||||
while ((nl = m_buffer.indexOf('\n')) >= 0) {
|
while ((nl = m_buffer.indexOf('\n')) >= 0) {
|
||||||
const QJsonObject message = QJsonDocument::fromJson(m_buffer.left(nl)).object();
|
const QJsonObject message = QJsonDocument::fromJson(m_buffer.left(nl)).object();
|
||||||
m_buffer.remove(0, nl + 1);
|
m_buffer.remove(0, nl + 1);
|
||||||
|
capture().append(message);
|
||||||
if (!(responder && responder(message)))
|
if (!(responder && responder(message)))
|
||||||
m_messages.append(message);
|
m_messages.append(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,10 @@ PKGCONFIG += nymea
|
|||||||
|
|
||||||
TARGET = etm-powersync-optimizerclient
|
TARGET = etm-powersync-optimizerclient
|
||||||
|
|
||||||
|
# Lot contrat OpenRPC : les Handshake envoyés sont jugés par tools/etm-powersync-schema-check contre
|
||||||
|
# docs/etm-powersync-optimizer-protocol.openrpc.json. Surchargée par $ETM_POWERSYNC_SOURCE_DIR.
|
||||||
|
DEFINES += ETM_POWERSYNC_SOURCE_DIR=\\\"$$clean_path($$PWD/../../..)\\\"
|
||||||
|
|
||||||
# Cible VOLONTAIREMENT légère, comme loadmodel : aucun serveur nymea, aucun EnergyTestBase, et
|
# Cible VOLONTAIREMENT légère, comme loadmodel : aucun serveur nymea, aucun EnergyTestBase, et
|
||||||
# du moteur le seul client de transport (brief 4 T2). Les faux optimiseurs sont des
|
# du moteur le seul client de transport (brief 4 T2). Les faux optimiseurs sont des
|
||||||
# QLocalServer/QTcpServer du processus de test, et le temps est SIMULÉ : aucun des délais
|
# QLocalServer/QTcpServer du processus de test, et le temps est SIMULÉ : aucun des délais
|
||||||
@ -15,7 +19,8 @@ INCLUDEPATH += $$PWD/../../../energyplugin
|
|||||||
|
|
||||||
HEADERS += $$PWD/../../../energyplugin/etm/scheduler/optimizerclient.h \
|
HEADERS += $$PWD/../../../energyplugin/etm/scheduler/optimizerclient.h \
|
||||||
$$PWD/../../../energyplugin/etm/scheduler/optimizersecret.h \
|
$$PWD/../../../energyplugin/etm/scheduler/optimizersecret.h \
|
||||||
$$PWD/../common/etm-powersync-fakeoptimizer.h
|
$$PWD/../common/etm-powersync-fakeoptimizer.h \
|
||||||
|
$$PWD/../common/etm-powersync-contrat.h
|
||||||
SOURCES += $$PWD/../../../energyplugin/etm/scheduler/optimizerclient.cpp \
|
SOURCES += $$PWD/../../../energyplugin/etm/scheduler/optimizerclient.cpp \
|
||||||
$$PWD/../../../energyplugin/etm/scheduler/optimizersecret.cpp \
|
$$PWD/../../../energyplugin/etm/scheduler/optimizersecret.cpp \
|
||||||
testoptimizerclient.cpp
|
testoptimizerclient.cpp
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "etm/scheduler/optimizerclient.h"
|
#include "etm/scheduler/optimizerclient.h"
|
||||||
#include "../common/etm-powersync-fakeoptimizer.h"
|
#include "../common/etm-powersync-fakeoptimizer.h"
|
||||||
|
#include "../common/etm-powersync-contrat.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -88,6 +89,7 @@ class TestOptimizerClient : public QObject
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void testEndpointParsing_data();
|
void testEndpointParsing_data();
|
||||||
@ -118,6 +120,21 @@ private:
|
|||||||
QTemporaryDir m_dir;
|
QTemporaryDir m_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void TestOptimizerClient::cleanupTestCase()
|
||||||
|
{
|
||||||
|
// Lot contrat OpenRPC : chaque Handshake que le client a ENVOYÉ dans ce processus est jugé contre
|
||||||
|
// le contrat (docs/ — cette suite n'embarque pas le sérialiseur). Les GetPlan de cette suite ne
|
||||||
|
// le sont pas : leurs params sont FABRIQUÉS par le test, le client n'en connaît pas le contenu ;
|
||||||
|
// ceux du sérialiseur réel sont jugés par `socketscheduler`, `refoptimizer` et `protocolschema`.
|
||||||
|
const QList<QJsonObject> handshakes = Contrat::deMethode(FakeOptimizer::capture(), QStringLiteral("Handshake"));
|
||||||
|
FakeOptimizer::capture().clear();
|
||||||
|
if (handshakes.isEmpty())
|
||||||
|
return;
|
||||||
|
const Contrat::Verdict v = Contrat::juger(handshakes, Contrat::schemaDuDepot(), {QStringLiteral("--requests-only")});
|
||||||
|
QVERIFY2(v.code == 0 && v.compte(QStringLiteral("Handshake.params")) == handshakes.size(),
|
||||||
|
qUtf8Printable(QStringLiteral("Handshake du client NON CONFORME au contrat :\n") + v.sortie.right(3000)));
|
||||||
|
}
|
||||||
|
|
||||||
void TestOptimizerClient::initTestCase()
|
void TestOptimizerClient::initTestCase()
|
||||||
{
|
{
|
||||||
QLoggingCategory::setFilterRules(QStringLiteral("NymeaEnergy.debug=true"));
|
QLoggingCategory::setFilterRules(QStringLiteral("NymeaEnergy.debug=true"));
|
||||||
|
|||||||
@ -29,7 +29,8 @@ HEADERS += \
|
|||||||
$$SIM/experience/energylogger.h \
|
$$SIM/experience/energylogger.h \
|
||||||
$$SIM/experience/energymanagermock.h \
|
$$SIM/experience/energymanagermock.h \
|
||||||
$$SIM/simulationbase.h \
|
$$SIM/simulationbase.h \
|
||||||
$$PWD/../common/etm-powersync-fakeoptimizer.h
|
$$PWD/../common/etm-powersync-fakeoptimizer.h \
|
||||||
|
$$PWD/../common/etm-powersync-contrat.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$SIM/experience/energyexperienceenergymock.cpp \
|
$$SIM/experience/energyexperienceenergymock.cpp \
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
#include "simulationbase.h"
|
#include "simulationbase.h"
|
||||||
#include "experience/energymanagermock.h"
|
#include "experience/energymanagermock.h"
|
||||||
#include "../common/etm-powersync-fakeoptimizer.h"
|
#include "../common/etm-powersync-fakeoptimizer.h"
|
||||||
|
#include "../common/etm-powersync-contrat.h"
|
||||||
|
|
||||||
#include "energysettings.h"
|
#include "energysettings.h"
|
||||||
#include "etm/energyarbitrator.h"
|
#include "etm/energyarbitrator.h"
|
||||||
@ -223,8 +224,13 @@ private slots:
|
|||||||
void testTracesReplayWithZeroDivergence();
|
void testTracesReplayWithZeroDivergence();
|
||||||
void testSafetyWinsOverTheOptimizer();
|
void testSafetyWinsOverTheOptimizer();
|
||||||
void testAnEchoedTokenLeaksNowhereEndToEnd();
|
void testAnEchoedTokenLeaksNowhereEndToEnd();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
//! Lot contrat OpenRPC : la session capturée par l'optimiseur de référence (\c --capture),
|
||||||
|
//! jugée contre le contrat EMBARQUÉ ; \p complete = réponses comprises, règles sémantiques
|
||||||
|
//! appliquées (modes « bons » seulement). La capture est consommée.
|
||||||
|
Contrat::Verdict jugerLaSession(bool complete);
|
||||||
void monter(Banc banc);
|
void monter(Banc banc);
|
||||||
//! Démarre \p ref et y pointe le moteur par la RPC de production ; attend la connexion si
|
//! Démarre \p ref et y pointe le moteur par la RPC de production ; attend la connexion si
|
||||||
//! \p attendrePret.
|
//! \p attendrePret.
|
||||||
@ -345,7 +351,9 @@ void TestRefOptimizer::brancher(RefOptimizer &ref, Transport transport, const QS
|
|||||||
? QStringLiteral("unix://%1/ref-%2.sock").arg(m_dir.path()).arg(++n)
|
? QStringLiteral("unix://%1/ref-%2.sock").arg(m_dir.path()).arg(++n)
|
||||||
: QStringLiteral("tcp://127.0.0.1:0");
|
: QStringLiteral("tcp://127.0.0.1:0");
|
||||||
QString erreur;
|
QString erreur;
|
||||||
QVERIFY2(ref.demarrer(ecoute, args, &erreur), qUtf8Printable(erreur));
|
// Toute session du MOTEUR avec la référence est capturée, puis jugée contre le contrat.
|
||||||
|
QVERIFY2(ref.demarrer(ecoute, args + QStringList{QStringLiteral("--capture"), m_dir.path() + QStringLiteral("/capture.ndjson")},
|
||||||
|
&erreur), qUtf8Printable(erreur));
|
||||||
QCOMPARE(rpc("SetOptimizerEndpoint", {{"endpoint", ref.endpoint()}}).value("energyError").toString(),
|
QCOMPARE(rpc("SetOptimizerEndpoint", {{"endpoint", ref.endpoint()}}).value("energyError").toString(),
|
||||||
QString("EnergyErrorNoError"));
|
QString("EnergyErrorNoError"));
|
||||||
SocketScheduler *s = m_arb->socketScheduler();
|
SocketScheduler *s = m_arb->socketScheduler();
|
||||||
@ -597,6 +605,13 @@ void TestRefOptimizer::testNormalReproducesTheRules()
|
|||||||
.arg(i).arg(surplus.at(i - 1)).arg(cleEtat(b.at(i))).arg(i - 1).arg(cleEtat(a.at(i - 1)));
|
.arg(i).arg(surplus.at(i - 1)).arg(cleEtat(b.at(i))).arg(i - 1).arg(cleEtat(a.at(i - 1)));
|
||||||
}
|
}
|
||||||
QVERIFY2(ecarts.isEmpty(), qUtf8Printable(ecarts.join('\n')));
|
QVERIFY2(ecarts.isEmpty(), qUtf8Printable(ecarts.join('\n')));
|
||||||
|
// Lot contrat OpenRPC : la session ENTIÈRE est conforme — requêtes du moteur, réponses de la
|
||||||
|
// référence, et chaque plan applicable selon les règles sémantiques du contrat.
|
||||||
|
ref.arreter();
|
||||||
|
const Contrat::Verdict v = jugerLaSession(true);
|
||||||
|
QVERIFY2(v.code == 0, qUtf8Printable(v.sortie.right(4000)));
|
||||||
|
QVERIFY2(v.compte("GetPlan.result") >= surplus.count() - 1 && v.compte("Handshake.result") >= 1,
|
||||||
|
qUtf8Printable(v.sortie.right(1000)));
|
||||||
// L'exemple GPL s'annonce `powersync-optimizer` ; `etm-optimizer` est l'optimiseur propriétaire.
|
// L'exemple GPL s'annonce `powersync-optimizer` ; `etm-optimizer` est l'optimiseur propriétaire.
|
||||||
QCOMPARE(rpc("GetOptimizerStatus").value("name").toString(), QString("powersync-optimizer"));
|
QCOMPARE(rpc("GetOptimizerStatus").value("name").toString(), QString("powersync-optimizer"));
|
||||||
// Régime stable : à la fin de chaque palier, les deux runs coïncident aussi SANS décalage.
|
// Régime stable : à la fin de chaque palier, les deux runs coïncident aussi SANS décalage.
|
||||||
@ -772,6 +787,10 @@ void TestRefOptimizer::testAProtocol10OptimizerIsAccepted()
|
|||||||
QCOMPARE(v.toMap().value("decision").toMap().value("code").toString(), QString(DecisionCode::OptimizerDecision));
|
QCOMPARE(v.toMap().value("decision").toMap().value("code").toString(), QString(DecisionCode::OptimizerDecision));
|
||||||
const QVariantMap plan = rpc("GetPlan");
|
const QVariantMap plan = rpc("GetPlan");
|
||||||
QCOMPARE(plan.value("strategy").toString(), QString("optimizer"));
|
QCOMPARE(plan.value("strategy").toString(), QString("optimizer"));
|
||||||
|
// Lot contrat OpenRPC : un optimiseur 1.0 est conforme au contrat 1.1 (champs 1.1 optionnels).
|
||||||
|
ref.arreter();
|
||||||
|
const Contrat::Verdict v = jugerLaSession(true);
|
||||||
|
QVERIFY2(v.code == 0 && v.compte("GetPlan.result") >= 2, qUtf8Printable(v.sortie.right(4000)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestRefOptimizer::testTracesReplayWithZeroDivergence()
|
void TestRefOptimizer::testTracesReplayWithZeroDivergence()
|
||||||
@ -970,4 +989,33 @@ void TestRefOptimizer::testAnEchoedTokenLeaksNowhereEndToEnd()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(TestRefOptimizer)
|
QTEST_MAIN(TestRefOptimizer)
|
||||||
|
// --- Lot contrat OpenRPC ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void TestRefOptimizer::cleanupTestCase()
|
||||||
|
{
|
||||||
|
SimulationBase::cleanupTestCase();
|
||||||
|
// Tout ce que le MOTEUR a envoyé à la référence, quels que soient les modes : ses requêtes sont
|
||||||
|
// toujours conformes, même quand la référence est réglée pour mal répondre.
|
||||||
|
const Contrat::Verdict v = jugerLaSession(false);
|
||||||
|
QVERIFY2(v.code == 0 || (v.code == -1 && v.sortie.isEmpty()), qUtf8Printable(QStringLiteral("requêtes du moteur NON CONFORMES au contrat :\n") + v.sortie.right(4000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Contrat::Verdict TestRefOptimizer::jugerLaSession(bool complete)
|
||||||
|
{
|
||||||
|
QFile f(m_dir.path() + QStringLiteral("/capture.ndjson"));
|
||||||
|
QList<QJsonObject> messages;
|
||||||
|
if (f.open(QIODevice::ReadOnly)) {
|
||||||
|
for (const QByteArray &ligne : f.readAll().split('\n'))
|
||||||
|
if (!ligne.trimmed().isEmpty())
|
||||||
|
messages << QJsonDocument::fromJson(ligne).object();
|
||||||
|
f.close();
|
||||||
|
f.remove();
|
||||||
|
}
|
||||||
|
if (messages.isEmpty())
|
||||||
|
return Contrat::Verdict(); // code -1 : rien à juger
|
||||||
|
QTemporaryDir dir;
|
||||||
|
return Contrat::juger(messages, Contrat::ecrireSchema(dir, OptimizerProtocol::schemaDocumentBytes()),
|
||||||
|
complete ? QStringList{QStringLiteral("--semantic")} : QStringList{QStringLiteral("--requests-only")});
|
||||||
|
}
|
||||||
|
|
||||||
#include "testrefoptimizer.moc"
|
#include "testrefoptimizer.moc"
|
||||||
|
|||||||
@ -15,6 +15,8 @@ CONFIG += testcase
|
|||||||
TARGET = etm-powersync-socketscheduler
|
TARGET = etm-powersync-socketscheduler
|
||||||
|
|
||||||
DEFINES += ENERGY_SIMULATION
|
DEFINES += ENERGY_SIMULATION
|
||||||
|
# Lot contrat OpenRPC : le validateur et le contrat de docs/. Surchargée par $ETM_POWERSYNC_SOURCE_DIR.
|
||||||
|
DEFINES += ETM_POWERSYNC_SOURCE_DIR=\\\"$$clean_path($$PWD/../../..)\\\"
|
||||||
|
|
||||||
SIM = $$PWD/../simulation
|
SIM = $$PWD/../simulation
|
||||||
INCLUDEPATH += $$SIM
|
INCLUDEPATH += $$SIM
|
||||||
@ -28,7 +30,8 @@ HEADERS += \
|
|||||||
$$SIM/experience/energylogger.h \
|
$$SIM/experience/energylogger.h \
|
||||||
$$SIM/experience/energymanagermock.h \
|
$$SIM/experience/energymanagermock.h \
|
||||||
$$SIM/simulationbase.h \
|
$$SIM/simulationbase.h \
|
||||||
$$PWD/../common/etm-powersync-fakeoptimizer.h
|
$$PWD/../common/etm-powersync-fakeoptimizer.h \
|
||||||
|
$$PWD/../common/etm-powersync-contrat.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$SIM/experience/energyexperienceenergymock.cpp \
|
$$SIM/experience/energyexperienceenergymock.cpp \
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "simulationbase.h"
|
#include "simulationbase.h"
|
||||||
#include "experience/energymanagermock.h"
|
#include "experience/energymanagermock.h"
|
||||||
#include "../common/etm-powersync-fakeoptimizer.h"
|
#include "../common/etm-powersync-fakeoptimizer.h"
|
||||||
|
#include "../common/etm-powersync-contrat.h"
|
||||||
|
|
||||||
#include "energysettings.h"
|
#include "energysettings.h"
|
||||||
#include "etm/energyarbitrator.h"
|
#include "etm/energyarbitrator.h"
|
||||||
@ -16,6 +17,7 @@
|
|||||||
#include "etm/scheduler/optimizerstatus.h"
|
#include "etm/scheduler/optimizerstatus.h"
|
||||||
#include "etm/scheduler/publishedplan.h"
|
#include "etm/scheduler/publishedplan.h"
|
||||||
#include "etm/scheduler/optimizertrace.h"
|
#include "etm/scheduler/optimizertrace.h"
|
||||||
|
#include "etm/scheduler/optimizerprotocol.h"
|
||||||
#include "etm/types/decisionreason.h"
|
#include "etm/types/decisionreason.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@ -137,8 +139,13 @@ private slots:
|
|||||||
void testTracerPairsTheResponseByRequestId();
|
void testTracerPairsTheResponseByRequestId();
|
||||||
// Brief 4 T7 — correctif A : un token recopié par l'optimiseur ne sort nulle part
|
// Brief 4 T7 — correctif A : un token recopié par l'optimiseur ne sort nulle part
|
||||||
void testAnEchoedTokenLeaksNowhere();
|
void testAnEchoedTokenLeaksNowhere();
|
||||||
|
// Lot contrat OpenRPC — le moteur parle le contrat qu'il sert
|
||||||
|
void testTheEngineSpeaksTheContractItServes();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
//! Juge contre le contrat EMBARQUÉ tout ce que le moteur a envoyé au faux optimiseur, puis vide.
|
||||||
|
void jugerLesRequetesDuMoteur();
|
||||||
//! Banc commun ; \p avecOptimiseur faux = rien n'est configuré.
|
//! Banc commun ; \p avecOptimiseur faux = rien n'est configuré.
|
||||||
void monter(bool avecOptimiseur = true, FakeOptimizer::Transport transport = FakeOptimizer::Unix);
|
void monter(bool avecOptimiseur = true, FakeOptimizer::Transport transport = FakeOptimizer::Unix);
|
||||||
//! Un cycle à \p t, compteur à \p compteurW (négatif = export) ; attend le jugement de la
|
//! Un cycle à \p t, compteur à \p compteurW (négatif = export) ; attend le jugement de la
|
||||||
@ -2120,4 +2127,65 @@ void TestSocketScheduler::testAnEchoedTokenLeaksNowhere()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(TestSocketScheduler)
|
QTEST_MAIN(TestSocketScheduler)
|
||||||
|
// --- Lot contrat OpenRPC ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void TestSocketScheduler::cleanupTestCase()
|
||||||
|
{
|
||||||
|
SimulationBase::cleanupTestCase();
|
||||||
|
jugerLesRequetesDuMoteur();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSocketScheduler::jugerLesRequetesDuMoteur()
|
||||||
|
{
|
||||||
|
// TOUT ce que le moteur a émis dans ce processus — Handshake du client, GetPlan du sérialiseur
|
||||||
|
// réel sur le contexte réel de l'arbitre —, jugé par le validateur publié contre le contrat
|
||||||
|
// EMBARQUÉ. Appelé aussi par monter() (qui relance le serveur) : chaque banc est jugé.
|
||||||
|
const QList<QJsonObject> requetes = FakeOptimizer::capture();
|
||||||
|
FakeOptimizer::capture().clear();
|
||||||
|
if (requetes.isEmpty())
|
||||||
|
return;
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
const Contrat::Verdict v = Contrat::juger(requetes, Contrat::ecrireSchema(dir, OptimizerProtocol::schemaDocumentBytes()),
|
||||||
|
{QStringLiteral("--requests-only")});
|
||||||
|
QVERIFY2(v.code == 0, qUtf8Printable(QStringLiteral("requêtes du moteur NON CONFORMES au contrat embarqué :\n")
|
||||||
|
+ v.sortie.right(4000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSocketScheduler::testTheEngineSpeaksTheContractItServes()
|
||||||
|
{
|
||||||
|
// Le contrat SERVI par NymeaEnergy.GetOptimizerProtocol est celui qu'embarque le binaire, et
|
||||||
|
// celui de docs/ ; et ce que le moteur ENVOIE à un optimiseur y est conforme. Garde de
|
||||||
|
// pertinence : des Handshake et des GetPlan ont réellement été jugés — un jugement sur une
|
||||||
|
// capture vide ne dirait rien.
|
||||||
|
FakeOptimizer::capture().clear();
|
||||||
|
monter();
|
||||||
|
m_repondre = planValide();
|
||||||
|
const QDateTime t0 = utcDateTime(QDate(2026, 6, 8), QTime(13, 0, 0));
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
cycle(t0.addSecs(60 * i), -3000);
|
||||||
|
|
||||||
|
const QVariantMap r = rpc("GetOptimizerProtocol");
|
||||||
|
QCOMPARE(r.value("protocolVersion").toString(), QString("1.1"));
|
||||||
|
const QJsonObject servi = QJsonObject::fromVariantMap(r.value("schema").toMap());
|
||||||
|
const QJsonObject embarque = QJsonDocument::fromJson(OptimizerProtocol::schemaDocumentBytes()).object();
|
||||||
|
QVERIFY(!embarque.isEmpty());
|
||||||
|
QCOMPARE(servi.value("openrpc").toString(), QString("1.3.2"));
|
||||||
|
QVERIFY2(servi == embarque, "GetOptimizerProtocol ne sert pas le contrat embarqué");
|
||||||
|
QFile f(Contrat::schemaDuDepot());
|
||||||
|
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||||
|
QVERIFY2(QJsonDocument::fromJson(f.readAll()).object() == servi, "le contrat servi n'est pas celui de docs/");
|
||||||
|
|
||||||
|
// Jugé avec le contrat SERVI, pas une copie : c'est celui qu'un auteur d'optimiseur lirait.
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
const QList<QJsonObject> requetes = FakeOptimizer::capture();
|
||||||
|
FakeOptimizer::capture().clear();
|
||||||
|
const Contrat::Verdict v = Contrat::juger(requetes, Contrat::ecrireSchema(dir, QJsonDocument(servi).toJson()),
|
||||||
|
{QStringLiteral("--requests-only")});
|
||||||
|
QVERIFY2(v.code == 0, qUtf8Printable(v.sortie.right(4000)));
|
||||||
|
QVERIFY2(v.compte("Handshake.params") >= 1, qUtf8Printable(v.sortie.right(1000)));
|
||||||
|
QVERIFY2(v.compte("GetPlan.params") >= 3, qUtf8Printable(v.sortie.right(1000)));
|
||||||
|
}
|
||||||
|
|
||||||
#include "testsocketscheduler.moc"
|
#include "testsocketscheduler.moc"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user