Test: Add mqttitem unit tests

This commit is contained in:
Carl Philipp Klemm 2026-04-17 18:32:41 +02:00
parent cb05c2237e
commit 869067c2f9
2 changed files with 309 additions and 1 deletions

View file

@ -12,10 +12,11 @@ add_executable(test_sensor unit/sensors/test_sensor.cpp)
add_executable(test_actor unit/actors/test_actor.cpp)
add_executable(test_itemstore unit/items/test_itemstore.cpp)
add_executable(test_itemloadersource unit/items/test_itemloadersource.cpp)
add_executable(test_mqttitem unit/items/test_mqttitem.cpp)
add_executable(test_tcp unit/service/test_tcp.cpp)
# Link all tests to static library
foreach(test test_item test_sensor test_actor test_itemstore test_itemloadersource test_tcp)
foreach(test test_item test_sensor test_actor test_itemstore test_itemloadersource test_mqttitem test_tcp)
target_link_libraries(${test}
smartvos_core
Qt6::Core
@ -41,4 +42,5 @@ add_test(NAME test_sensor COMMAND test_sensor)
add_test(NAME test_actor COMMAND test_actor)
add_test(NAME test_itemstore COMMAND test_itemstore)
add_test(NAME test_itemloadersource COMMAND test_itemloadersource)
add_test(NAME test_mqttitem COMMAND test_mqttitem)
add_test(NAME test_tcp COMMAND test_tcp)

View file

@ -0,0 +1,306 @@
#include <QtTest/QtTest>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QSignalSpy>
#include <QStandardPaths>
#include <QFile>
#include <QDebug>
#include <QTextStream>
#include "items/mqttitem.h"
#include "mqttclient.h"
#include "programmode.h"
class TestMqttItem : public QObject
{
Q_OBJECT
private slots:
void initTestCase()
{
// Setup for all tests
// Try to load config and connect to MQTT broker if configured
QString settingsPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/shinterface.json";
QJsonObject json;
if (QFile::exists(settingsPath)) {
QFile file(settingsPath);
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error == QJsonParseError::NoError) {
json = doc.object();
}
}
}
QJsonObject mqttJson = json["Mqtt"].toObject();
QString host = mqttJson["Host"].toString();
int port = mqttJson["Port"].toInt(1883);
// If MQTT is configured with a host, try to connect
static std::shared_ptr<MqttClient> mqttClient;
if (!host.isEmpty()) {
qDebug() << "MQTT configured:" << host << port;
mqttClient = std::make_shared<MqttClient>();
mqttClient->start(mqttJson);
// Give it a moment to connect
QTest::qWait(1000);
// Check if connected or connecting
auto qClient = mqttClient->getClient();
if (qClient && (qClient->state() == QMqttClient::Connected ||
qClient->state() == QMqttClient::Connecting)) {
qDebug() << "MQTT connected/connecting, using client";
MqttItem::client = mqttClient;
} else {
qDebug() << "MQTT connection failed, using UI_ONLY mode";
programMode = PROGRAM_MODE_UI_ONLY;
}
} else {
qDebug() << "No MQTT host configured, using UI_ONLY mode";
programMode = PROGRAM_MODE_UI_ONLY;
}
}
void testMqttItemCreation()
{
MqttItem item("test_mqtt", 0);
QCOMPARE(item.getName(), QString("test_mqtt"));
QVERIFY(item.getTopic().isEmpty());
QVERIFY(item.getValueKey() == "state");
}
void testMqttItemSetTopic()
{
MqttItem item("test_mqtt", 0);
item.setTopic("my_device");
item.setValueKey("state");
QVERIFY(item.getTopic() == "my_device");
QVERIFY(item.getValueKey() == "state");
}
void testMqttItemSetValueType()
{
MqttItem item("test_mqtt", 0);
// Default should be BOOL
QVERIFY(item.getValueType() == ITEM_VALUE_BOOL);
// Set to UINT
item.setValueType(ITEM_VALUE_UINT);
QVERIFY(item.getValueType() == ITEM_VALUE_UINT);
// Set to ENUM
item.setValueType(ITEM_VALUE_ENUM);
QVERIFY(item.getValueType() == ITEM_VALUE_ENUM);
}
void testMqttItemValueNames()
{
MqttItem item("test_mqtt", 0);
// Initially empty
QVERIFY(item.getValueNames().empty());
// Set value names
std::vector<QString> names = {"off", "heat", "cool"};
item.setValueNames(names);
auto storedNames = item.getValueNames();
QVERIFY(storedNames.size() == 3);
QVERIFY(storedNames[0] == "off");
QVERIFY(storedNames[1] == "heat");
QVERIFY(storedNames[2] == "cool");
}
void testValueNameConversion()
{
MqttItem item("test_mqtt", 0);
// Set value names for enum
std::vector<QString> names = {"off", "heat", "cool", "auto"};
item.setValueNames(names);
item.setValueType(ITEM_VALUE_ENUM);
// Test name to index
QVERIFY(item.valueNameToIndex("heat") == 1);
QVERIFY(item.valueNameToIndex("cool") == 2);
QVERIFY(item.valueNameToIndex("unknown") == -1);
// Test index to name
QVERIFY(item.indexToValueName(0) == "off");
QVERIFY(item.indexToValueName(3) == "auto");
QVERIFY(item.indexToValueName(99).isEmpty()); // Out of bounds
}
void testSetFromExposeBinary()
{
MqttItem item("test_mqtt", 0);
QJsonObject expose;
expose["type"] = "binary";
expose["property"] = "state";
expose["value_on"] = "ON";
expose["value_off"] = "OFF";
item.setFromExpose(expose);
QVERIFY(item.getValueType() == ITEM_VALUE_BOOL);
QVERIFY(item.getValueKey() == "state");
QVERIFY(item.getValueOn() == "ON");
QVERIFY(item.getValueOff() == "OFF");
}
void testSetFromExposeNumeric()
{
MqttItem item("test_mqtt", 0);
QJsonObject expose;
expose["type"] = "numeric";
expose["property"] = "brightness";
expose["value_min"] = 0;
expose["value_max"] = 254;
expose["value_step"] = 1;
item.setFromExpose(expose);
QVERIFY(item.getValueType() == ITEM_VALUE_UINT);
QVERIFY(item.getValueKey() == "brightness");
QVERIFY(item.getValueMin() == 0);
QVERIFY(item.getValueMax() == 254);
QVERIFY(item.getValueStep() == 1);
}
void testSetFromExposeEnum()
{
MqttItem item("test_mqtt", 0);
QJsonObject expose;
expose["type"] = "enum";
expose["property"] = "system_mode";
expose["values"] = QJsonArray{"off", "heat", "cool", "auto"};
item.setFromExpose(expose);
QVERIFY(item.getValueType() == ITEM_VALUE_ENUM);
QVERIFY(item.getValueKey() == "system_mode");
auto names = item.getValueNames();
QVERIFY(names.size() == 4);
QVERIFY(names[0] == "off");
QVERIFY(names[1] == "heat");
QVERIFY(names[2] == "cool");
QVERIFY(names[3] == "auto");
}
void testJsonSerialization()
{
MqttItem item("test_mqtt", 1);
item.setTopic("my_device");
item.setValueKey("state");
item.setValueOn("ON");
item.setValueOff("OFF");
QJsonObject json;
item.store(json);
QVERIFY(json["Type"] == "Mqtt");
QVERIFY(json["Topic"] == "my_device");
QVERIFY(json["ValueKey"] == "state");
QVERIFY(json["ValueOn"] == "ON");
QVERIFY(json["ValueOff"] == "OFF");
}
void testJsonDeserialization()
{
QJsonObject json;
json["Type"] = "Mqtt";
json["ItemId"] = 100;
json["Name"] = "loaded_mqtt";
json["Topic"] = "test_device";
json["ValueKey"] = "state";
json["ValueOn"] = "ON";
json["ValueOff"] = "OFF";
json["Value"] = 1;
MqttItem item;
item.load(json);
QVERIFY(item.getTopic() == "test_device");
QVERIFY(item.getValueKey() == "state");
QVERIFY(item.getValueOn() == "ON");
QVERIFY(item.getValueOff() == "OFF");
}
void testLoadExposeFromDevice()
{
// Create item with specific topic and valueKey
MqttItem item("test", 0);
item.setTopic("0xa4c138ef510950e3");
item.setValueKey("system_mode");
// Simulate device data from zigbee2mqtt/bridge/devices
QJsonObject device;
device["friendly_name"] = "0xa4c138ef510950e3";
device["ieee_address"] = "0xa4c138ef510950e3";
QJsonObject definition;
definition["model"] = "TS0601_thermostat";
definition["vendor"] = "Tuya";
definition["description"] = "Thermostat";
QJsonArray exposes;
// Binary expose
QJsonObject stateExpose;
stateExpose["type"] = "binary";
stateExpose["property"] = "state";
stateExpose["value_on"] = "ON";
stateExpose["value_off"] = "OFF";
exposes.append(stateExpose);
// Enum expose - the one we're looking for
QJsonObject systemModeExpose;
systemModeExpose["type"] = "enum";
systemModeExpose["property"] = "system_mode";
systemModeExpose["values"] = QJsonArray{"off", "heat", "cool", "auto"};
exposes.append(systemModeExpose);
// Numeric expose
QJsonObject tempExpose;
tempExpose["type"] = "numeric";
tempExpose["property"] = "current_temperature";
tempExpose["value_min"] = 0;
tempExpose["value_max"] = 100;
exposes.append(tempExpose);
definition["exposes"] = exposes;
device["definition"] = definition;
// Call the private method via public API - we need to test the logic
// Since loadExposeFromDevice is private, we test via setFromExpose
QJsonObject enumExpose;
enumExpose["type"] = "enum";
enumExpose["property"] = "system_mode";
enumExpose["values"] = QJsonArray{"off", "heat", "cool", "auto"};
item.setFromExpose(enumExpose);
QVERIFY(item.getValueType() == ITEM_VALUE_ENUM);
QVERIFY(item.getValueNames().size() == 4);
}
void cleanupTestCase()
{
// Cleanup after all tests
}
};
QTEST_APPLESS_MAIN(TestMqttItem)
#include "test_mqttitem.moc"