powersync-energy-plugin-etm/tests/auto/common/energytestbase.cpp

487 lines
19 KiB
C++

// SPDX-License-Identifier: GPL-3.0-or-later
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-energy-plugin-nymea.
*
* nymea-energy-plugin-nymea.s 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-energy-plugin-nymea.s 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-energy-plugin-nymea. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "energytestbase.h"
#include <QTimeZone>
EnergyTestBase::EnergyTestBase(QObject *parent) :
NymeaTestBase(parent),
m_networkAccessManager(new QNetworkAccessManager(this))
{
qCDebug(dcTests()) << "Running in:" << QDir::currentPath();
QString mockPluginEnvPath = QDir(QDir::currentPath() + "/../../mocks/plugins/energymocks/").absolutePath();
qCDebug(dcTests()) << "Mock plugin path:" << mockPluginEnvPath;
QString energyPluginEnvPath = QDir(QDir::currentPath() + "/../../../energyplugin/").absolutePath();
qCDebug(dcTests()) << "Energy plugin path:" << energyPluginEnvPath;
// Add our mock plugin to the loading path
if (QString(qgetenv("NYMEA_PLUGINS_EXTRA_PATH")).isEmpty()) {
qputenv("NYMEA_PLUGINS_EXTRA_PATH", mockPluginEnvPath.toUtf8());
} else {
qputenv("NYMEA_PLUGINS_EXTRA_PATH", QString(QString(qgetenv("NYMEA_PLUGINS_EXTRA_PATH")) + ":" + mockPluginEnvPath).toUtf8());
}
QFileInfo configurationFileInfo(":/energy-manager-configuration.json");
if (configurationFileInfo.exists()) {
qputenv("NYMEA_ENERGY_MANAGER_CONFIG", ":/energy-manager-configuration.json");
}
// Add this enery plugin to the paths
qputenv("NYMEA_ENERGY_PLUGINS_EXTRA_PATH", energyPluginEnvPath.toUtf8());
qCDebug(dcTests()).nospace() << "NYMEA_PLUGINS_EXTRA_PATH=" << qgetenv("NYMEA_PLUGINS_EXTRA_PATH");
qCDebug(dcTests()).nospace() << "NYMEA_ENERGY_PLUGINS_EXTRA_PATH=" << qgetenv("NYMEA_ENERGY_PLUGINS_EXTRA_PATH");
qCDebug(dcTests()).nospace() << "NYMEA_EXPERIENCE_PLUGINS_PATH=" << qgetenv("NYMEA_EXPERIENCE_PLUGINS_PATH");
}
QDateTime EnergyTestBase::utcDateTime(const QDate &date, const QTime &time)
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
return QDateTime(date, time, QTimeZone::UTC);
#else
return QDateTime(date, time, Qt::UTC);
#endif
}
void EnergyTestBase::initTestCase(const QString &loggingRules, bool checkExperienceLoaded, bool disableLogEngine)
{
QDir dir(NymeaSettings::translationsPath());
dir.mkpath(NymeaSettings::translationsPath());
QStringList languages = {"de", "en_US"};
foreach (const QString &language, languages) {
QFile f(NymeaSettings::translationsPath().append("/nymead-" + language + ".qm"));
QVERIFY2(f.open(QFile::WriteOnly), "Could not create translation file.");
f.write(" ");
f.close();
}
// If testcase asserts cleanup won't do. Lets clear any previous test run settings leftovers
QSettings energySettings(NymeaSettings::settingsPath() + "/energy.conf", QSettings::IniFormat);
energySettings.clear();
if (loggingRules.isEmpty()) {
NymeaTestBase::initTestCase("*.info=true\n*.debug=false\nApplication.debug=true\nTests.debug=true\nExperiences.debug=true\nNymeaEnergy.debug=true\nEnergyMocks.debug=true\nDBus.warning=false", disableLogEngine);
} else {
NymeaTestBase::initTestCase(loggingRules, disableLogEngine);
}
QVariant reply = injectAndWait("JSONRPC.Hello");
qCDebug(dcTests()) << qUtf8Printable(QJsonDocument::fromVariant(reply).toJson());
if (checkExperienceLoaded) {
QString experienceVersion = "0.8"; // TODO: Perhaps this should be defined in the project file somewhere
QVERIFY2(reply.toMap().value("params").toMap().contains("experiences"),
QString("No experience plugins loaded!\n%1")
.arg(QString(QJsonDocument::fromVariant(reply).toJson())).toUtf8());
QVariantMap experienceDefinition;
experienceDefinition.insert("name", "NymeaEnergy");
experienceDefinition.insert("version", experienceVersion);
QVERIFY2(reply.toMap().value("params").toMap().value("experiences").toList().contains(experienceDefinition),
QString("NymeaEnergy %1 experience plugins not loaded!\n%2")
.arg(experienceVersion)
.arg(QString(QJsonDocument::fromVariant(reply).toJson())).toUtf8());
enableNotifications({"Integrations", "Energy", "NymeaEnergy"});
}
removeDevices();
}
void EnergyTestBase::cleanupTestCase()
{
removeDevices();
NymeaTestBase::cleanupTestCase();
}
void EnergyTestBase::init()
{
removeDevices();
}
void EnergyTestBase::cleanup()
{
removeDevices();
}
QNetworkReply *EnergyTestBase::setMeterStates(const QVariantMap &phasesPower, bool connected, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("currentPowerPhaseA", QString::number(phasesPower.value("A").toInt()));
query.addQueryItem("currentPowerPhaseB", QString::number(phasesPower.value("B").toInt()));
query.addQueryItem("currentPowerPhaseC", QString::number(phasesPower.value("C").toInt()));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setCarStates(uint batteryLevel, uint capacity, uint minChargingCurrent, uint phaseCount, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("batteryLevel", QString::number(batteryLevel));
query.addQueryItem("capacity", QString::number(capacity));
query.addQueryItem("minChargingCurrent", QString::number(minChargingCurrent));
query.addQueryItem("phaseCount", QString::number(phaseCount));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setChargerStates(bool connected, bool power, bool pluggedIn, const QString &phases, int maxChargingCurrent, int maxChargingCurrentMaxValue, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("usedPhases", phases);
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
query.addQueryItem("maxChargingCurrentMaxValue", QString::number(maxChargingCurrentMaxValue));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setChargerWithPhaseCountSwitchingStates(bool connected, bool power, bool pluggedIn, const QString &phases, int maxChargingCurrent, int maxChargingCurrentMaxValue, uint desiredPhaseCount, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("usedPhases", phases);
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
query.addQueryItem("maxChargingCurrentMaxValue", QString::number(maxChargingCurrentMaxValue));
query.addQueryItem("desiredPhaseCount", QString::number(desiredPhaseCount));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setSimpleChargerStates(bool connected, bool power, bool pluggedIn, int phaseCount, int maxChargingCurrent, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("connected", connected ? "true" : "false");
query.addQueryItem("power", power ? "true" : "false");
query.addQueryItem("pluggedIn", pluggedIn ? "true" : "false");
query.addQueryItem("phaseCount", QString::number(phaseCount));
query.addQueryItem("maxChargingCurrent", QString::number(maxChargingCurrent));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::setEnergyStorageStates(uint batteryLevel, int currentPower, quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/setstates");
QUrlQuery query;
query.addQueryItem("batteryLevel", QString::number(batteryLevel));
query.addQueryItem("currentPower", QString::number(currentPower));
requestUrl.setQuery(query);
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->post(request, QByteArray());
}
QNetworkReply *EnergyTestBase::getActionHistory(quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/actionhistory");
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->get(request);
}
QNetworkReply *EnergyTestBase::clearActionHistroy(quint16 port)
{
QUrl requestUrl;
requestUrl.setScheme("http");
requestUrl.setHost("127.0.0.1");
requestUrl.setPort(port);
requestUrl.setPath("/clearactionhistory");
QNetworkRequest request(requestUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
return m_networkAccessManager->get(request);
}
QUuid EnergyTestBase::addMeter(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{7abcc8a1-08b1-45bc-9116-10f9848359f9}");
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", mockMeterThingClassId.toString());
params.insert("name", "Meter");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addCar(quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{29533bc8-d71e-4ce5-8ce3-bf87f0370391}");
portParam.insert("value", port);
thingParams.append(portParam);
QVariantMap params;
params.insert("thingClassId", mockCarThingClassId.toString());
params.insert("name", "Tschitti Tschitti Bäng Bäng");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addCharger(const QString &phases, double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{652624a2-8f9a-4bc3-b34f-5e3492af4d30}");
portParam.insert("value", port);
QVariantMap phasesParam;
phasesParam.insert("paramTypeId", "{facd5c76-d15e-4e29-9929-5e1764ae05dc}");
phasesParam.insert("value", phases);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{234c6676-1ec0-4eff-bed0-ecee7ce82074}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(phasesParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockChargerThingClassId.toString());
params.insert("name", "Charger");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addChargerWithPhaseCountSwitching(const QString &phases, double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{d4492038-51bf-4f3c-8b93-89af4d8edd6b}");
portParam.insert("value", port);
QVariantMap phasesParam;
phasesParam.insert("paramTypeId", "{fb64b557-d4ee-4a89-9141-0c585ee476c9}");
phasesParam.insert("value", phases);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{7c1a941d-3eb2-4c6b-90b5-b0faf82dcb73}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(phasesParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockChargerWithPhaseSwitchingThingClassId.toString());
params.insert("name", "Charger with phase count switching");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addSimpleCharger(double maxChargingCurrentUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{e94f863b-47a2-44e4-8103-bb4b8a817f47}");
portParam.insert("value", port);
QVariantMap maxCurrentParam;
maxCurrentParam.insert("paramTypeId", "{61943026-985b-4728-bfaa-299d0fbcdcae}");
maxCurrentParam.insert("value", maxChargingCurrentUpperLimit);
thingParams.append(portParam);
thingParams.append(maxCurrentParam);
QVariantMap params;
params.insert("thingClassId", mockSimpleChargerThingClassId.toString());
params.insert("name", "Simple charger");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
QUuid EnergyTestBase::addEnergyStorage(uint capacity, double maxChargingPowerUpperLimit, double maxDischargingPowerUpperLimit, quint16 port)
{
QVariantList thingParams;
QVariantMap portParam;
portParam.insert("paramTypeId", "{b074a76f-6382-48b9-b101-1d13904f30c8}");
portParam.insert("value", port);
QVariantMap capacityParam;
capacityParam.insert("paramTypeId", "{fd56c49e-98e2-4373-bf23-fd8b5a58e339}");
capacityParam.insert("value", capacity);
QVariantMap maxChargingPowerParam;
maxChargingPowerParam.insert("paramTypeId", "{769391a8-b801-42fc-af63-90367bf46bdf}");
maxChargingPowerParam.insert("value", maxChargingPowerUpperLimit);
QVariantMap maxDischargingPowerParam;
maxDischargingPowerParam.insert("paramTypeId", "{559d9434-9b31-4e0d-a654-2ea1e5f65a82}");
maxDischargingPowerParam.insert("value", maxDischargingPowerUpperLimit);
thingParams.append(portParam);
thingParams.append(capacityParam);
thingParams.append(maxChargingPowerParam);
thingParams.append(maxDischargingPowerParam);
QVariantMap params;
params.insert("thingClassId", mockEnergyStorageThingClassId.toString());
params.insert("name", "Energy storage");
params.insert("thingParams", thingParams);
QVariant response = injectAndWait("Integrations.AddThing", params);
verifyThingError(response);
return response.toMap().value("params").toMap().value("thingId").toUuid();
}
void EnergyTestBase::removeDevices()
{
QVariant configuredDevices = injectAndWait("Integrations.GetThings");
foreach (const QVariant &dev, configuredDevices.toMap().value("params").toMap().value("things").toList()) {
qCDebug(dcTests()) << "Removing thing" << dev.toMap().value("name").toString() << dev.toMap().value("id").toUuid();
QVariant response = removeDevice(dev.toMap().value("id").toUuid());
verifyThingError(response);
}
}
QVariant EnergyTestBase::removeDevice(const QUuid &thingId)
{
qCDebug(dcTests()) << "Remove device" << thingId.toString();
QVariantMap params;
params.insert("thingId", thingId.toString());
return injectAndWait("Integrations.RemoveThing", params);
}
bool EnergyTestBase::verifyActionExecuted(const QVariantList &actionHistory, const QString &actionName)
{
foreach (const QVariant &actionHistoryVariant, actionHistory) {
QVariantMap actionHistoryMap = actionHistoryVariant.toMap();
if (actionHistoryMap.value("name").toString() == actionName) {
return true;
}
}
return false;
}
QVariant EnergyTestBase::getLastValueFromExecutedAction(const QVariantList &actionHistory, const QString &actionName, const QString &paramName)
{
QVariant paramValue;
foreach (const QVariant &actionHistoryVariant, actionHistory) {
QVariantMap actionHistoryMap = actionHistoryVariant.toMap();
if (actionHistoryMap.value("name").toString() == actionName) {
foreach (const QVariant &paramVariant, actionHistoryMap.value("params").toList()) {
if (paramVariant.toMap().value("name").toString() == paramName) {
paramValue = paramVariant.toMap().value("value");
}
}
}
}
return paramValue;
}