sunspec: add a failing test that sets up a Fronius connection
The existing suite only exercises free functions and the plugin JSON, so it cannot see whether a thing class is dispatched at all. A thing class missing from the dispatch in setupThing() falls into the final Q_ASSERT_X: in a debug build the process dies, in a release build the assert is compiled out and setupThing() returns without ever calling info->finish(), leaving the pairing stuck with no message. Split the tests into two suites. The logic one keeps its former scope and its single dependency on libnymea-sunspec. The new one instantiates the plugin behind the real ThingManager of libnymea-core and a faked HardwareManager, so the dispatch of setupThing(), postSetupThing() and thingRemoved() is covered. No network access takes place: the faked monitor is never reachable, so the plugin never attempts a Modbus connection. The four new cases fail: froniusConnection is not dispatched yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
372be1644c
commit
c2eccd96e5
29
sunspec/tests/logic/logic.pro
Normal file
29
sunspec/tests/logic/logic.pro
Normal file
@ -0,0 +1,29 @@
|
||||
include(../../../sunspec.pri)
|
||||
|
||||
QT += testlib
|
||||
QT -= gui
|
||||
CONFIG += console testcase
|
||||
CONFIG -= app_bundle
|
||||
TEMPLATE = app
|
||||
TARGET = tst_sunspec
|
||||
|
||||
# Suite de logique pure : ce que le plugin *decide*. Elle ne charge ni nymea ni
|
||||
# le plugin lui-meme, et ne demande donc que libnymea-sunspec. La suite qui
|
||||
# instancie reellement le plugin vit dans ../plugin/ et porte ses propres
|
||||
# dependances. Ce qui est emis sur le bus releve du banc (voir "Non verifiable
|
||||
# par les tests unitaires" dans specs/fronius-sunspec.md).
|
||||
|
||||
INCLUDEPATH += ../..
|
||||
|
||||
# Chemin du JSON de definition du plugin, pour les criteres qui portent sur les
|
||||
# thingClasses et leurs plages de valeurs (AC-1, AC-7, AC-8, AC-10).
|
||||
DEFINES += PLUGIN_JSON_PATH=\\\"$$PWD/../../integrationpluginsunspec.json\\\"
|
||||
|
||||
SOURCES += \
|
||||
tst_sunspec.cpp \
|
||||
../../sunspecstoragelogic.cpp \
|
||||
../../froniusrules.cpp
|
||||
|
||||
HEADERS += \
|
||||
../../sunspecstoragelogic.h \
|
||||
../../froniusrules.h
|
||||
62
sunspec/tests/plugin/plugin.pro
Normal file
62
sunspec/tests/plugin/plugin.pro
Normal file
@ -0,0 +1,62 @@
|
||||
include(../../../sunspec.pri)
|
||||
|
||||
QT += testlib network serialbus
|
||||
QT -= gui
|
||||
CONFIG += console testcase c++17
|
||||
CONFIG -= app_bundle
|
||||
TEMPLATE = app
|
||||
TARGET = tst_sunspecplugin
|
||||
|
||||
# Suite qui instancie reellement IntegrationPluginSunSpec, derriere un
|
||||
# ThingManager reel (celui de libnymea-core) et un HardwareManager double. Elle
|
||||
# couvre ce que la suite de logique pure ne peut pas voir : l'aiguillage de
|
||||
# setupThing() / postSetupThing() / thingRemoved() sur la thingClass.
|
||||
#
|
||||
# Dependances supplementaires par rapport a ../logic : libnymea1-dev et
|
||||
# libnymea-core (pkg-config nymea et nymea-core).
|
||||
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += nymea nymea-core
|
||||
|
||||
PLUGIN_DIR = $$PWD/../..
|
||||
INCLUDEPATH += $$PLUGIN_DIR
|
||||
|
||||
# Le plugin s'appuie sur les identifiants generes depuis son JSON. On les
|
||||
# fabrique ici exactement comme le fait plugin.pri pour le plugin lui-meme :
|
||||
# plugininfo.h (definitions, inclus par integrationpluginsunspec.cpp) et
|
||||
# extern-plugininfo.h (declarations, inclus par les en-tetes et par le test).
|
||||
JSONFILE = $$PLUGIN_DIR/integrationpluginsunspec.json
|
||||
OTHER_FILES += $$JSONFILE
|
||||
|
||||
plugininfo.input = JSONFILE
|
||||
plugininfo.output = plugininfo.h
|
||||
plugininfo.commands = nymea-plugininfocompiler ${QMAKE_FILE_NAME} --output ${QMAKE_FILE_OUT}
|
||||
plugininfo.dependency_type = TYPE_C
|
||||
plugininfo.CONFIG += no_link combine
|
||||
QMAKE_EXTRA_COMPILERS += plugininfo
|
||||
|
||||
extern_plugininfo.input = JSONFILE
|
||||
extern_plugininfo.output = extern-plugininfo.h
|
||||
extern_plugininfo.commands = nymea-plugininfocompiler ${QMAKE_FILE_NAME} --extern ${QMAKE_FILE_OUT}
|
||||
extern_plugininfo.dependency_type = TYPE_C
|
||||
extern_plugininfo.CONFIG += no_link combine
|
||||
QMAKE_EXTRA_COMPILERS += extern_plugininfo
|
||||
|
||||
DEFINES += PLUGIN_JSON_PATH=\\\"$$JSONFILE\\\"
|
||||
|
||||
SOURCES += \
|
||||
tst_sunspecplugin.cpp \
|
||||
$$PLUGIN_DIR/integrationpluginsunspec.cpp \
|
||||
$$PLUGIN_DIR/froniusrules.cpp \
|
||||
$$PLUGIN_DIR/solaredgebattery.cpp \
|
||||
$$PLUGIN_DIR/sunspecdiscovery.cpp \
|
||||
$$PLUGIN_DIR/sunspecstoragelogic.cpp \
|
||||
$$PLUGIN_DIR/sunspecthing.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$PLUGIN_DIR/integrationpluginsunspec.h \
|
||||
$$PLUGIN_DIR/froniusrules.h \
|
||||
$$PLUGIN_DIR/solaredgebattery.h \
|
||||
$$PLUGIN_DIR/sunspecdiscovery.h \
|
||||
$$PLUGIN_DIR/sunspecstoragelogic.h \
|
||||
$$PLUGIN_DIR/sunspecthing.h
|
||||
388
sunspec/tests/plugin/tst_sunspecplugin.cpp
Normal file
388
sunspec/tests/plugin/tst_sunspecplugin.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright (C) 2026, ETM-PowerSync
|
||||
*
|
||||
* This file is part of nymea.
|
||||
*
|
||||
* nymea is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* nymea is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* nymea. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
/* Suite qui instancie reellement le plugin.
|
||||
*
|
||||
* La suite de logique pure (../logic) ne peut voir que ce que le plugin
|
||||
* *decide* : elle appelle des fonctions libres et lit le JSON. Elle ne peut pas
|
||||
* voir l'aiguillage, c'est a dire le fait qu'une thingClass soit reconnue par
|
||||
* setupThing(), postSetupThing() et thingRemoved(). Une thingClass oubliee dans
|
||||
* l'un de ces trois aiguillages tombe dans un Q_ASSERT_X final : en debug le
|
||||
* processus meurt, en release l'assert est compile hors du binaire et
|
||||
* setupThing() rend la main sans jamais appeler info->finish(). L'appairage
|
||||
* reste alors bloque indefiniment, sans le moindre message.
|
||||
*
|
||||
* C'est exactement ce qui est arrive a froniusConnection, et aucun test ne
|
||||
* pouvait le voir.
|
||||
*
|
||||
* Le plugin est monte derriere le vrai ThingManager de libnymea-core - c'est
|
||||
* lui qui construit les Thing et les ThingSetupInfo, et lui qui appelle
|
||||
* postSetupThing() - et derriere un HardwareManager double. Aucun acces reseau
|
||||
* n'a lieu : le moniteur double n'est jamais joignable, donc le plugin ne tente
|
||||
* jamais de connexion Modbus.
|
||||
*/
|
||||
|
||||
#include <QtTest>
|
||||
#include <QCoreApplication>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include <nymeasettings.h>
|
||||
#include <hardwaremanager.h>
|
||||
#include <plugintimer.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
#include <network/networkdevicemonitor.h>
|
||||
#include <integrations/thingmanagerimplementation.h>
|
||||
#include <integrations/thingsetupinfo.h>
|
||||
|
||||
#include "integrationpluginsunspec.h"
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* Doublures du materiel
|
||||
*
|
||||
* Le moniteur ne connait aucune adresse et n'est jamais joignable : c'est l'etat
|
||||
* dans lequel se trouve une connexion dont l'appareil n'a pas encore ete vu sur
|
||||
* le reseau. Le plugin doit y repondre, pas s'y bloquer.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
class FakeNetworkDeviceMonitor : public NetworkDeviceMonitor
|
||||
{
|
||||
public:
|
||||
explicit FakeNetworkDeviceMonitor(QObject *parent = nullptr) : NetworkDeviceMonitor(parent) {}
|
||||
|
||||
MacAddress macAddress() const override { return MacAddress(); }
|
||||
QString hostName() const override { return QString(); }
|
||||
QHostAddress address() const override { return QHostAddress(); }
|
||||
NetworkDeviceInfo::MonitorMode monitorMode() const override { return NetworkDeviceInfo::MonitorModeIp; }
|
||||
NetworkDeviceInfo networkDeviceInfo() const override { return NetworkDeviceInfo(); }
|
||||
bool reachable() const override { return false; }
|
||||
QDateTime lastSeen() const override { return QDateTime(); }
|
||||
uint pingRetries() const override { return 0; }
|
||||
void setPingRetries(uint) override {}
|
||||
};
|
||||
|
||||
class FakeNetworkDeviceDiscovery : public NetworkDeviceDiscovery
|
||||
{
|
||||
public:
|
||||
explicit FakeNetworkDeviceDiscovery(QObject *parent = nullptr) : NetworkDeviceDiscovery(parent) {}
|
||||
|
||||
/* registerMonitor() n'est appele que par la branche "connexion" de
|
||||
* setupThing(). Compter les appels dit donc si le plugin a bien pris cette
|
||||
* branche, et pas seulement s'il a fini par repondre quelque chose. */
|
||||
int registerMonitorCount = 0;
|
||||
int unregisterMonitorCount = 0;
|
||||
|
||||
NetworkDeviceDiscoveryReply *discover() override { return nullptr; }
|
||||
bool running() const override { return false; }
|
||||
|
||||
NetworkDeviceMonitor *registerMonitor(Thing *) override
|
||||
{
|
||||
registerMonitorCount++;
|
||||
return new FakeNetworkDeviceMonitor(this);
|
||||
}
|
||||
|
||||
void unregisterMonitor(NetworkDeviceMonitor *monitor) override
|
||||
{
|
||||
unregisterMonitorCount++;
|
||||
delete monitor;
|
||||
}
|
||||
|
||||
PingReply *ping(const QHostAddress &, uint) override { return nullptr; }
|
||||
PingReply *ping(const QString &, uint) override { return nullptr; }
|
||||
MacAddressDatabaseReply *lookupMacAddress(const QString &) override { return nullptr; }
|
||||
MacAddressDatabaseReply *lookupMacAddress(const MacAddress &) override { return nullptr; }
|
||||
bool sendArpRequest(const QHostAddress &) override { return false; }
|
||||
NetworkDeviceInfos cache() const override { return NetworkDeviceInfos(); }
|
||||
|
||||
bool available() const override { return true; }
|
||||
bool enabled() const override { return true; }
|
||||
|
||||
protected:
|
||||
void setEnabled(bool) override {}
|
||||
};
|
||||
|
||||
class FakePluginTimer : public PluginTimer
|
||||
{
|
||||
public:
|
||||
explicit FakePluginTimer(int seconds, QObject *parent = nullptr)
|
||||
: PluginTimer(parent), m_interval(seconds) {}
|
||||
|
||||
int interval() const override { return m_interval; }
|
||||
int currentTick() const override { return 0; }
|
||||
bool running() const override { return m_running; }
|
||||
|
||||
void reset() override {}
|
||||
void start() override { m_running = true; }
|
||||
void stop() override { m_running = false; }
|
||||
void pause() override {}
|
||||
void resume() override {}
|
||||
|
||||
private:
|
||||
int m_interval = 0;
|
||||
bool m_running = false;
|
||||
};
|
||||
|
||||
class FakePluginTimerManager : public PluginTimerManager
|
||||
{
|
||||
public:
|
||||
explicit FakePluginTimerManager(QObject *parent = nullptr) : PluginTimerManager(parent) {}
|
||||
|
||||
/* Le minuteur d'interrogation est arme par postSetupThing(). Le compteur dit
|
||||
* donc si postSetupThing() a bien traite la connexion. */
|
||||
int registerTimerCount = 0;
|
||||
|
||||
PluginTimer *registerTimer(int seconds) override
|
||||
{
|
||||
registerTimerCount++;
|
||||
return new FakePluginTimer(seconds, this);
|
||||
}
|
||||
|
||||
void unregisterTimer(PluginTimer *timer) override { delete timer; }
|
||||
|
||||
bool available() const override { return true; }
|
||||
bool enabled() const override { return true; }
|
||||
|
||||
protected:
|
||||
void setEnabled(bool) override {}
|
||||
};
|
||||
|
||||
class FakeHardwareManager : public HardwareManager
|
||||
{
|
||||
public:
|
||||
explicit FakeHardwareManager(QObject *parent = nullptr)
|
||||
: HardwareManager(parent)
|
||||
, m_pluginTimerManager(new FakePluginTimerManager(this))
|
||||
, m_networkDeviceDiscovery(new FakeNetworkDeviceDiscovery(this))
|
||||
{}
|
||||
|
||||
Radio433 *radio433() override { return nullptr; }
|
||||
PluginTimerManager *pluginTimerManager() override { return m_pluginTimerManager; }
|
||||
NetworkAccessManager *networkManager() override { return nullptr; }
|
||||
UpnpDiscovery *upnpDiscovery() override { return nullptr; }
|
||||
PlatformZeroConfController *zeroConfController() override { return nullptr; }
|
||||
BluetoothLowEnergyManager *bluetoothLowEnergyManager() override { return nullptr; }
|
||||
MqttProvider *mqttProvider() override { return nullptr; }
|
||||
I2CManager *i2cManager() override { return nullptr; }
|
||||
ZigbeeHardwareResource *zigbeeResource() override { return nullptr; }
|
||||
ZWaveHardwareResource *zwaveResource() override { return nullptr; }
|
||||
ModbusRtuHardwareResource *modbusRtuResource() override { return nullptr; }
|
||||
NetworkDeviceDiscovery *networkDeviceDiscovery() override { return m_networkDeviceDiscovery; }
|
||||
|
||||
FakePluginTimerManager *timers() const { return m_pluginTimerManager; }
|
||||
FakeNetworkDeviceDiscovery *monitors() const { return m_networkDeviceDiscovery; }
|
||||
|
||||
private:
|
||||
FakePluginTimerManager *m_pluginTimerManager = nullptr;
|
||||
FakeNetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
};
|
||||
|
||||
/* setMetaData() est protege : le ThingManager le renseigne normalement depuis le
|
||||
* QPluginLoader. Ici le plugin est instancie en dur, donc le test lui passe la
|
||||
* meme metadonnee, lue dans le meme JSON. */
|
||||
class TestableSunSpecPlugin : public IntegrationPluginSunSpec
|
||||
{
|
||||
public:
|
||||
using IntegrationPlugin::setMetaData;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
class SunSpecPluginTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void test_F1_stored_fronius_connection_completes_its_setup();
|
||||
void test_F1_stored_fronius_connection_is_removed_cleanly();
|
||||
void test_F1_added_fronius_connection_is_answered();
|
||||
void test_F1_added_fronius_connection_answers_like_the_generic_vendor();
|
||||
|
||||
private:
|
||||
QTemporaryDir m_pluginSearchDir;
|
||||
FakeHardwareManager *m_hardwareManager = nullptr;
|
||||
ThingManagerImplementation *m_thingManager = nullptr;
|
||||
|
||||
ThingSetupInfo *addConnection(const ThingClassId &thingClassId, const QString &name);
|
||||
};
|
||||
|
||||
/* Thing deja configure, ecrit dans les reglages avant le demarrage du
|
||||
* ThingManager. C'est le chemin emprunte a chaque demarrage de nymead pour une
|
||||
* connexion deja appairee, et le seul qui aille jusqu'au bout du setup sans
|
||||
* appareil joignable. */
|
||||
static const ThingId StoredFroniusThingId = ThingId("4e9b1c2d-6f31-4a58-91a7-2b8c0d5e7f14");
|
||||
|
||||
void SunSpecPluginTest::initTestCase()
|
||||
{
|
||||
/* "nymea-test" deporte les reglages dans /tmp, hors de la configuration
|
||||
* reelle de l'utilisateur. */
|
||||
QCoreApplication::setOrganizationName("nymea-test");
|
||||
|
||||
NymeaSettings thingSettings(NymeaSettings::SettingsRoleThings);
|
||||
thingSettings.clear();
|
||||
NymeaSettings pluginSettings(NymeaSettings::SettingsRolePlugins);
|
||||
pluginSettings.clear();
|
||||
|
||||
/* Repertoire de recherche vide : le seul plugin en jeu est celui que le test
|
||||
* enregistre lui-meme, pas ceux installes sur la machine. */
|
||||
QVERIFY(m_pluginSearchDir.isValid());
|
||||
qputenv("NYMEA_PLUGINS_PATH", m_pluginSearchDir.path().toUtf8());
|
||||
|
||||
QFile jsonFile(PLUGIN_JSON_PATH);
|
||||
QVERIFY2(jsonFile.open(QIODevice::ReadOnly), qPrintable(QString("Cannot open %1").arg(PLUGIN_JSON_PATH)));
|
||||
QJsonParseError error;
|
||||
const QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &error);
|
||||
QVERIFY2(error.error == QJsonParseError::NoError, qPrintable(error.errorString()));
|
||||
|
||||
const PluginMetadata metadata(document.object());
|
||||
QVERIFY2(metadata.isValid(), "metadonnee du plugin invalide");
|
||||
|
||||
NymeaSettings settings(NymeaSettings::SettingsRoleThings);
|
||||
settings.beginGroup("ThingConfig");
|
||||
settings.beginGroup(StoredFroniusThingId.toString());
|
||||
settings.setValue("thingName", "Fronius");
|
||||
settings.setValue("pluginid", metadata.pluginId().toString());
|
||||
settings.setValue("thingClassId", froniusConnectionThingClassId.toString());
|
||||
/* Le ThingManager est monte sans LogEngine - son en-tete ne fait pas partie
|
||||
* de l'API publique de nymea-core - donc le thing ne doit journaliser aucun
|
||||
* state, sans quoi le ThingManager enregistrerait un logger sur un moteur
|
||||
* absent. Ces trois cles ecrasent les valeurs suggerees par la thingClass. */
|
||||
settings.setValue("loggedStateTypeIds", QStringList());
|
||||
settings.setValue("loggedEventTypeIds", QStringList());
|
||||
settings.setValue("loggedActionTypeIds", QStringList());
|
||||
settings.beginGroup("Params");
|
||||
settings.setValue(froniusConnectionThingAddressParamTypeId.toString(), "192.0.2.1");
|
||||
settings.setValue(froniusConnectionThingPortParamTypeId.toString(), 502);
|
||||
settings.setValue(froniusConnectionThingSlaveIdParamTypeId.toString(), 1);
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
|
||||
m_hardwareManager = new FakeHardwareManager(this);
|
||||
m_thingManager = new ThingManagerImplementation(m_hardwareManager, nullptr, QLocale(), this);
|
||||
|
||||
TestableSunSpecPlugin *plugin = new TestableSunSpecPlugin();
|
||||
plugin->setMetaData(metadata);
|
||||
m_thingManager->registerStaticPlugin(plugin);
|
||||
|
||||
QVERIFY2(!m_thingManager->findThingClass(froniusConnectionThingClassId).id().isNull(),
|
||||
"la thingClass froniusConnection n'est pas connue du ThingManager");
|
||||
}
|
||||
|
||||
void SunSpecPluginTest::cleanupTestCase()
|
||||
{
|
||||
delete m_thingManager;
|
||||
m_thingManager = nullptr;
|
||||
}
|
||||
|
||||
ThingSetupInfo *SunSpecPluginTest::addConnection(const ThingClassId &thingClassId, const QString &name)
|
||||
{
|
||||
const ThingClass thingClass = m_thingManager->findThingClass(thingClassId);
|
||||
|
||||
ParamList params;
|
||||
params.append(Param(thingClass.paramTypes().findByName("address").id(), QString("192.0.2.1")));
|
||||
|
||||
return m_thingManager->addConfiguredThing(thingClassId, params, name);
|
||||
}
|
||||
|
||||
/* Le chemin du demarrage de nymead : la connexion est deja dans les reglages,
|
||||
* le setup n'est pas un setup initial et doit aboutir sans appareil joignable.
|
||||
* C'est le seul cas ou l'on peut verifier que le succes est bien annonce.
|
||||
*
|
||||
* Avant correction, setupThing() ne reconnait pas froniusConnection, ne finit
|
||||
* jamais, et le thing reste en ThingSetupStatusInProgress pour toujours. */
|
||||
void SunSpecPluginTest::test_F1_stored_fronius_connection_completes_its_setup()
|
||||
{
|
||||
Thing *thing = nullptr;
|
||||
QTRY_VERIFY_WITH_TIMEOUT((thing = m_thingManager->findConfiguredThing(StoredFroniusThingId)) != nullptr, 2000);
|
||||
QCOMPARE(thing->thingClassId(), froniusConnectionThingClassId);
|
||||
|
||||
QTRY_COMPARE_WITH_TIMEOUT(thing->setupStatus(), Thing::ThingSetupStatusComplete, 2000);
|
||||
QCOMPARE(thing->setupError(), Thing::ThingErrorNoError);
|
||||
|
||||
/* La branche connexion de setupThing() a bien ete prise... */
|
||||
QCOMPARE(m_hardwareManager->monitors()->registerMonitorCount, 1);
|
||||
/* ...et postSetupThing() a arme le minuteur d'interrogation. */
|
||||
QCOMPARE(m_hardwareManager->timers()->registerTimerCount, 1);
|
||||
}
|
||||
|
||||
/* thingRemoved() est indexe sur m_sunSpecConnections, pas sur la thingClass : il
|
||||
* n'a donc rien de specifique a faire pour Fronius. Verifie plutot que suppose. */
|
||||
void SunSpecPluginTest::test_F1_stored_fronius_connection_is_removed_cleanly()
|
||||
{
|
||||
QVERIFY(m_thingManager->findConfiguredThing(StoredFroniusThingId));
|
||||
|
||||
const int unregisteredBefore = m_hardwareManager->monitors()->unregisterMonitorCount;
|
||||
|
||||
QCOMPARE(m_thingManager->removeConfiguredThing(StoredFroniusThingId), Thing::ThingErrorNoError);
|
||||
|
||||
QVERIFY(!m_thingManager->findConfiguredThing(StoredFroniusThingId));
|
||||
QCOMPARE(m_hardwareManager->monitors()->unregisterMonitorCount, unregisteredBefore + 1);
|
||||
}
|
||||
|
||||
/* Le chemin de l'appairage. Sans adresse connue, un setup initial se termine en
|
||||
* ThingErrorHardwareFailure : c'est un refus explicite, pas un blocage. Avant
|
||||
* correction, aucune reponse n'arrive jamais. */
|
||||
void SunSpecPluginTest::test_F1_added_fronius_connection_is_answered()
|
||||
{
|
||||
const int monitorsBefore = m_hardwareManager->monitors()->registerMonitorCount;
|
||||
|
||||
ThingSetupInfo *info = addConnection(froniusConnectionThingClassId, "Fronius appaire");
|
||||
QVERIFY(info);
|
||||
|
||||
if (!info->isFinished()) {
|
||||
QSignalSpy finished(info, &ThingSetupInfo::finished);
|
||||
QVERIFY2(finished.wait(2000), "setupThing() n'a jamais appele info->finish()");
|
||||
}
|
||||
|
||||
QCOMPARE(m_hardwareManager->monitors()->registerMonitorCount, monitorsBefore + 1);
|
||||
QCOMPARE(info->status(), Thing::ThingErrorHardwareFailure);
|
||||
QVERIFY(!info->displayMessage().isEmpty());
|
||||
}
|
||||
|
||||
/* Le vendor generique passe par exactement le meme chemin. Si les deux reponses
|
||||
* divergent, c'est que froniusConnection a ete branche autrement, pas comme les
|
||||
* vendors deja fonctionnels. */
|
||||
void SunSpecPluginTest::test_F1_added_fronius_connection_answers_like_the_generic_vendor()
|
||||
{
|
||||
ThingSetupInfo *generic = addConnection(sunspecConnectionThingClassId, "SunSpec appaire");
|
||||
QVERIFY(generic);
|
||||
if (!generic->isFinished()) {
|
||||
QSignalSpy finished(generic, &ThingSetupInfo::finished);
|
||||
QVERIFY2(finished.wait(2000), "setupThing() n'a jamais appele info->finish() pour sunspecConnection");
|
||||
}
|
||||
|
||||
ThingSetupInfo *fronius = addConnection(froniusConnectionThingClassId, "Fronius appaire 2");
|
||||
QVERIFY(fronius);
|
||||
if (!fronius->isFinished()) {
|
||||
QSignalSpy finished(fronius, &ThingSetupInfo::finished);
|
||||
QVERIFY2(finished.wait(2000), "setupThing() n'a jamais appele info->finish() pour froniusConnection");
|
||||
}
|
||||
|
||||
QCOMPARE(fronius->status(), generic->status());
|
||||
QCOMPARE(fronius->displayMessage(), generic->displayMessage());
|
||||
}
|
||||
|
||||
QTEST_MAIN(SunSpecPluginTest)
|
||||
#include "tst_sunspecplugin.moc"
|
||||
@ -1,30 +1,10 @@
|
||||
include(../../sunspec.pri)
|
||||
TEMPLATE = subdirs
|
||||
|
||||
QT += testlib
|
||||
QT -= gui
|
||||
CONFIG += console testcase
|
||||
CONFIG -= app_bundle
|
||||
TEMPLATE = app
|
||||
TARGET = tst_sunspec
|
||||
# logic : logique pure du plugin, sans nymea. Ne demande que libnymea-sunspec.
|
||||
# plugin : instancie reellement IntegrationPluginSunSpec derriere un
|
||||
# ThingManager et un HardwareManager doubles. Demande en plus
|
||||
# libnymea1-dev et libnymea-core (pkg-config nymea, nymea-core).
|
||||
|
||||
# Les tests pilotent la logique pure du plugin. Ils ne chargent pas le plugin
|
||||
# nymea lui-meme : cela demanderait un ThingManager, un PluginTimer et un
|
||||
# NetworkDeviceDiscovery, c'est-a-dire un nymead complet. Ce qui est teste ici,
|
||||
# c'est ce que le plugin *decide* ; ce qu'il emet reellement sur le bus releve
|
||||
# du banc (voir "Non verifiable par les tests unitaires" dans
|
||||
# specs/fronius-sunspec.md).
|
||||
|
||||
INCLUDEPATH += ..
|
||||
|
||||
# Chemin du JSON de definition du plugin, pour les criteres qui portent sur les
|
||||
# thingClasses et leurs plages de valeurs (AC-1, AC-7, AC-8, AC-10).
|
||||
DEFINES += PLUGIN_JSON_PATH=\\\"$$PWD/../integrationpluginsunspec.json\\\"
|
||||
|
||||
SOURCES += \
|
||||
tst_sunspec.cpp \
|
||||
../sunspecstoragelogic.cpp \
|
||||
../froniusrules.cpp
|
||||
|
||||
HEADERS += \
|
||||
../sunspecstoragelogic.h \
|
||||
../froniusrules.h
|
||||
SUBDIRS += \
|
||||
logic \
|
||||
plugin
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user