// 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 . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "energytestbase.h" #include 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 ¶mName) { QVariant paramValue; foreach (const QVariant &actionHistoryVariant, actionHistory) { QVariantMap actionHistoryMap = actionHistoryVariant.toMap(); if (actionHistoryMap.value("name").toString() == actionName) { foreach (const QVariant ¶mVariant, actionHistoryMap.value("params").toList()) { if (paramVariant.toMap().value("name").toString() == paramName) { paramValue = paramVariant.toMap().value("value"); } } } } return paramValue; }