Add tests for the tcp server/client

This commit is contained in:
Carl Philipp Klemm 2026-04-09 16:52:58 +02:00
parent 3fd27905ee
commit 0fd50eb227
3 changed files with 373 additions and 0 deletions

View file

@ -16,6 +16,8 @@ public:
TcpServer(QObject* parent = nullptr);
virtual bool launch(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) override;
virtual void sendJson(const QJsonObject& json) override;
quint16 getServerPort() const { return server.serverPort(); }
bool isListening() const { return server.isListening(); }
signals:
void sigRequestSave();

View file

@ -56,6 +56,16 @@ add_executable(test_sensor unit/sensors/test_sensor.cpp ${COMMON_TEST_SOURCES})
add_executable(test_actor unit/actors/test_actor.cpp ${COMMON_TEST_SOURCES})
add_executable(test_itemstore unit/items/test_itemstore.cpp ${COMMON_TEST_SOURCES})
add_executable(test_itemloadersource unit/items/test_itemloadersource.cpp ${COMMON_TEST_SOURCES})
add_executable(test_tcp unit/service/test_tcp.cpp ${COMMON_TEST_SOURCES}
../src/service/service.h
../src/service/service.cpp
../src/service/server.h
../src/service/server.cpp
../src/service/tcpserver.h
../src/service/tcpserver.cpp
../src/service/tcpclient.h
../src/service/tcpclient.cpp
)
# Link libraries for test_item
target_link_libraries(test_item
@ -132,9 +142,27 @@ target_include_directories(test_itemloadersource PRIVATE
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
)
# Link libraries for test_tcp
target_link_libraries(test_tcp
Qt6::Core
Qt6::Gui
Qt6::Widgets
Qt6::Multimedia
Qt6::Network
Qt6::WebSockets
Qt6::Test
)
# Include paths for source files
target_include_directories(test_tcp PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../src
${Qt6Gui_PRIVATE_INCLUDE_DIRS}
)
# Add tests to CTest
add_test(NAME test_item COMMAND test_item)
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_tcp COMMAND test_tcp)

View file

@ -0,0 +1,343 @@
#include <QtTest/QtTest>
#include <QTcpServer>
#include <QTcpSocket>
#include <QJsonDocument>
#include <QJsonArray>
#include <QSignalSpy>
#include "service/tcpserver.h"
#include "service/tcpclient.h"
#include "service/server.h"
#include "service/service.h"
#include "items/item.h"
class TestTcp : public QObject
{
Q_OBJECT
private slots:
void initTestCase()
{
// Setup for all tests
}
void testTcpServerCreation()
{
TcpServer server;
// Server should be created successfully
QVERIFY(true);
}
void testTcpServerLaunch()
{
TcpServer server;
// Launch server on any address, port 0 means dynamic port
bool result = server.launch(QHostAddress::Any, 0);
QVERIFY(result);
// Server should be listening after launch
QVERIFY(server.isListening());
}
void testTcpServerLaunchSpecificPort()
{
TcpServer server;
// Try to launch on a specific port
bool result = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(result);
// Server should be listening
QVERIFY(server.isListening());
}
void testTcpClientCreation()
{
TcpClient client;
// Client should be created successfully
QVERIFY(true);
}
void testTcpProtocolMessageFormat()
{
// Test the protocol message format: "MSG JSON LEN <size>\n<json>"
QJsonObject json;
json["MessageType"] = "TestMessage";
json["Data"] = QJsonArray();
QByteArray jsonData = QJsonDocument(json).toJson();
QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
// Verify message format
QVERIFY(message.startsWith("MSG JSON LEN "));
QVERIFY(message.contains("\n"));
// Parse the message size
QStringList parts = message.split("\n");
QVERIFY(parts.size() >= 1);
QString sizeStr = parts[0].mid(13); // Skip "MSG JSON LEN "
bool ok;
quint64 size = sizeStr.toUInt(&ok);
QVERIFY(ok);
QVERIFY(size == jsonData.size());
}
void testTcpProtocolParseCommand()
{
// Test parsing a command from the protocol
QByteArray command = "MSG JSON LEN 123\n";
QVERIFY(command.startsWith("MSG JSON LEN "));
QByteArray sizeStr = command.mid(13);
sizeStr.chop(1); // Remove newline
bool ok;
quint64 size = sizeStr.toLongLong(&ok);
QVERIFY(ok);
QVERIFY(size == 123);
}
void testTcpServerSendJson()
{
// Start server
TcpServer server;
bool serverResult = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(serverResult);
// Connect a client to trigger client list
QTcpSocket clientSocket;
clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
QVERIFY(clientSocket.waitForConnected(1000));
// Give server time to accept connection
QTest::qWait(200);
// Send JSON from server
QJsonObject json;
json["MessageType"] = "TestMessage";
json["Data"] = QJsonArray();
server.sendJson(json);
// Give more time for data to be sent
QTest::qWait(200);
// Client should receive data (or at least the connection should work)
// Note: This may fail due to timing in test environment
QVERIFY(clientSocket.state() == QTcpSocket::ConnectedState);
// Cleanup
clientSocket.close();
}
void testTcpServerProcessIncomingJson()
{
// Start server
TcpServer server;
bool serverResult = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(serverResult);
// Connect client
QTcpSocket clientSocket;
clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
QVERIFY(clientSocket.waitForConnected(1000));
// Give server time to accept connection
QTest::qWait(100);
// Send a message that the server can process
QJsonObject json;
json["MessageType"] = "GetItems";
json["Data"] = QJsonArray();
QByteArray jsonData = QJsonDocument(json).toJson();
QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
clientSocket.write(message.toUtf8());
clientSocket.flush();
// Give server time to process
QTest::qWait(100);
// Cleanup
clientSocket.close();
}
void testTcpServerMultipleClients()
{
// Start server
TcpServer server;
bool serverResult = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(serverResult);
// Connect multiple clients
QTcpSocket client1;
client1.connectToHost(QHostAddress::LocalHost, server.getServerPort());
QVERIFY(client1.waitForConnected(1000));
QTcpSocket client2;
client2.connectToHost(QHostAddress::LocalHost, server.getServerPort());
QVERIFY(client2.waitForConnected(1000));
// Give server time to accept connections
QTest::qWait(200);
// Send message to all clients
QJsonObject json;
json["MessageType"] = "TestMessage";
json["Data"] = QJsonArray();
server.sendJson(json);
// Give time for data to be sent
QTest::qWait(200);
// Both clients should be connected (timing may affect actual data receipt)
QVERIFY(client1.state() == QTcpSocket::ConnectedState);
QVERIFY(client2.state() == QTcpSocket::ConnectedState);
// Cleanup
client1.close();
client2.close();
}
void testItemUpdateMessageFormat()
{
// Test creating an item update message format
Item item(1, "test_item", 1);
QJsonObject itemJson;
item.store(itemJson);
QJsonArray items;
items.append(itemJson);
// Manually create the message (since createMessage is protected)
QJsonObject message;
message["MessageType"] = "ItemUpdate";
message["Data"] = items;
message["FullList"] = false;
QVERIFY(message["MessageType"].toString() == "ItemUpdate");
QVERIFY(message["Data"].toArray().size() == 1);
QVERIFY(message["FullList"].toBool() == false);
}
void testSensorUpdateMessageFormat()
{
// Test creating a sensor update message format
Sensor sensor(Sensor::TYPE_TEMPERATURE, 1, 25.0, "temp_sensor");
QJsonObject sensorJson;
sensor.store(sensorJson);
QJsonArray sensors;
sensors.append(sensorJson);
// Manually create the message (since createMessage is protected)
QJsonObject message;
message["MessageType"] = "SensorUpdate";
message["Data"] = sensors;
QVERIFY(message["MessageType"].toString() == "SensorUpdate");
QVERIFY(message["Data"].toArray().size() == 1);
}
void testTcpClientSocketState()
{
// Test that TcpClient creates a socket
TcpClient client;
// The client should have a valid socket (internal implementation detail)
// We can verify the client was created successfully
QVERIFY(true);
}
void testTcpServerClientConnection()
{
// Start server
TcpServer server;
bool serverResult = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(serverResult);
// Connect a client
QTcpSocket clientSocket;
clientSocket.connectToHost(QHostAddress::LocalHost, server.getServerPort());
bool waitResult = clientSocket.waitForConnected(1000);
QVERIFY(waitResult);
// Verify connection state
QVERIFY(clientSocket.state() == QTcpSocket::ConnectedState);
// Cleanup
clientSocket.close();
}
void testTcpServerDisconnection()
{
// Start server
TcpServer server;
bool serverResult = server.launch(QHostAddress::LocalHost, 0);
QVERIFY(serverResult);
// Connect a client
QTcpSocket* client = new QTcpSocket();
client->connectToHost(QHostAddress::LocalHost, server.getServerPort());
QVERIFY(client->waitForConnected(1000));
// Give server time to accept connection
QTest::qWait(100);
// Disconnect client
client->disconnectFromHost();
QTest::qWait(100);
// Cleanup
delete client;
}
void testTcpProtocolLargeMessage()
{
// Test the protocol with a larger message
QJsonObject json;
json["MessageType"] = "TestMessage";
// Create a large data array
QJsonArray data;
for (int i = 0; i < 100; i++) {
data.append(QJsonObject{{"id", i}, {"value", QString("item%1").arg(i)}});
}
json["Data"] = data;
QByteArray jsonData = QJsonDocument(json).toJson();
QString message = QString("MSG JSON LEN %1\n").arg(jsonData.size()) + QString::fromUtf8(jsonData);
// Verify message format
QVERIFY(message.startsWith("MSG JSON LEN "));
// Parse the message size
QStringList parts = message.split("\n");
QString sizeStr = parts[0].mid(13);
bool ok;
quint64 size = sizeStr.toUInt(&ok);
QVERIFY(ok);
QVERIFY(size == jsonData.size());
}
void cleanupTestCase()
{
// Cleanup after all tests
}
};
QTEST_APPLESS_MAIN(TestTcp)
#include "test_tcp.moc"