Revision control

Copy as Markdown

// Copyright (C) 2024 basysKom GmbH, opensource@basyskom.com
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <generated/codegentestbinarydeencoder.h>
#include <generated/codegentestdatatypes.h>
#include <generated/codegentestnodeids.h>
#include <generated2/csvonlytestnodeids.h>
#include <QtOpcUa/QOpcUaClient>
#include <QtOpcUa/QOpcUaNode>
#include <QtOpcUa/QOpcUaProvider>
#include <QtCore/QCoreApplication>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QElapsedTimer>
#include <QtCore/QProcess>
#include <QtCore/QScopedPointer>
#include <QtNetwork/QTcpServer>
#include <QtNetwork//QTcpSocket>
#include <QtTest/QSignalSpy>
#include <QTest>
using namespace Qt::Literals::StringLiterals;
const auto signalSpyTimeout = std::chrono::milliseconds(10000);
class OpcuaConnector
{
public:
OpcuaConnector(QOpcUaClient *client, const QOpcUaEndpointDescription &endPoint)
: opcuaClient(client)
{
QVERIFY(opcuaClient != nullptr);
QSignalSpy connectedSpy(opcuaClient, &QOpcUaClient::connected);
QSignalSpy disconnectedSpy(opcuaClient, &QOpcUaClient::disconnected);
QSignalSpy stateSpy(opcuaClient, &QOpcUaClient::stateChanged);
opcuaClient->connectToEndpoint(endPoint);
stateSpy.wait();
if (stateSpy.size() != 2)
stateSpy.wait();
QCOMPARE(connectedSpy.size(), 1); // one connected signal fired
QCOMPARE(disconnectedSpy.size(), 0); // zero disconnected signals fired
QCOMPARE(stateSpy.size(), 2);
QCOMPARE(stateSpy.at(0).at(0), QOpcUaClient::ClientState::Connecting);
QCOMPARE(stateSpy.at(1).at(0), QOpcUaClient::ClientState::Connected);
QCOMPARE(stateSpy.at(0).at(0).value<QOpcUaClient::ClientState>(),
QOpcUaClient::ClientState::Connecting);
QCOMPARE(stateSpy.at(1).at(0).value<QOpcUaClient::ClientState>(),
QOpcUaClient::ClientState::Connected);
stateSpy.clear();
connectedSpy.clear();
disconnectedSpy.clear();
QVERIFY(opcuaClient->endpoint() == endPoint);
}
~OpcuaConnector()
{
QSignalSpy connectedSpy(opcuaClient, &QOpcUaClient::connected);
QSignalSpy disconnectedSpy(opcuaClient, &QOpcUaClient::disconnected);
QSignalSpy stateSpy(opcuaClient, &QOpcUaClient::stateChanged);
QSignalSpy errorSpy(opcuaClient, &QOpcUaClient::errorChanged);
QVERIFY(opcuaClient != nullptr);
if (opcuaClient->state() == QOpcUaClient::Connected) {
QSignalSpy stateSpy(opcuaClient, &QOpcUaClient::stateChanged);
opcuaClient->disconnectFromEndpoint();
stateSpy.wait(signalSpyTimeout);
// Once the test has failed, QSignalSpy::wait() returns right away without actually waiting for anything.
// Processing events manually satisfies the checks below and prevents all following tests from failing
// because of an unexpected initial client state.
if (stateSpy.size() < 2) {
QElapsedTimer t;
t.start();
do {
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} while (t.durationElapsed() < signalSpyTimeout && stateSpy.size() < 2);
}
QVERIFY(opcuaClient->state() == QOpcUaClient::Disconnected);
QCOMPARE(connectedSpy.size(), 0);
QCOMPARE(disconnectedSpy.size(), 1);
QCOMPARE(stateSpy.size(), 2);
QCOMPARE(stateSpy.at(0).at(0).value<QOpcUaClient::ClientState>(),
QOpcUaClient::ClientState::Closing);
QCOMPARE(stateSpy.at(1).at(0).value<QOpcUaClient::ClientState>(),
QOpcUaClient::ClientState::Disconnected);
QCOMPARE(errorSpy.size(), 0);
}
opcuaClient = nullptr;
}
QOpcUaClient *opcuaClient;
};
#define defineDataMethod(name) void name()\
{\
QTest::addColumn<QOpcUaClient *>("opcuaClient");\
for (auto *client : std::as_const(m_clients))\
QTest::newRow(client->backend().toLatin1().constData()) << client;\
}
class Tst_GeneratedDataTypes : public QObject
{
Q_OBJECT
public:
Tst_GeneratedDataTypes();
private slots:
void initTestCase();
void cleanupTestCase();
defineDataMethod(checkGeneratedDecoding_data)
void checkGeneratedDecoding();
defineDataMethod(checkGeneratedEncoding_data)
void checkGeneratedEncoding();
defineDataMethod(testNodeIdsOnly_data)
defineDataMethod(checkRecursionLimit_data)
void checkRecursionLimit();
void testNodeIdsOnly();
private:
QString envOrDefault(const char *env, QString def)
{
return qEnvironmentVariableIsSet(env) ? QString::fromUtf8(qgetenv(env).constData()) : def;
}
QString makeTestModelId(TestModelNodeId id)
{
return QOpcUa::nodeIdFromInteger(4, static_cast<int>(id));
}
QUrl m_discoveryEndpoint;
QOpcUaProvider m_opcUa;
QStringList m_backends;
QList<QOpcUaClient *> m_clients;
QProcess m_serverProcess;
QString m_testServerPath;
QOpcUaEndpointDescription m_endpoint;
};
#define READ_VARIABLE_NODE_VALUE(NODE) \
{ \
QSignalSpy attributeReadSpy(NODE.data(), &QOpcUaNode::attributeRead);\
NODE->readAttributes(QOpcUa::NodeAttribute::Value); \
attributeReadSpy.wait(signalSpyTimeout); \
QCOMPARE(attributeReadSpy.count(), 1); \
QCOMPARE(attributeReadSpy.at(0).at(0).value<QOpcUa::NodeAttributes>(), QOpcUa::NodeAttribute::Value); \
QCOMPARE(NODE->attributeError(QOpcUa::NodeAttribute::Value), QOpcUa::UaStatusCode::Good); \
}
#define WRITE_VALUE_ATTRIBUTE(NODE, VALUE, TYPE) \
{ \
QSignalSpy resultSpy(NODE.data(), &QOpcUaNode::attributeWritten); \
NODE->writeAttribute(QOpcUa::NodeAttribute::Value, VALUE, TYPE); \
resultSpy.wait(signalSpyTimeout); \
QCOMPARE(resultSpy.size(), 1); \
QCOMPARE(resultSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value); \
QCOMPARE(resultSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); \
}
Tst_GeneratedDataTypes::Tst_GeneratedDataTypes()
{
m_backends = QOpcUaProvider::availableBackends();
}
void Tst_GeneratedDataTypes::initTestCase()
{
#if defined(Q_OS_MACOS)
const bool runsOnCI = qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci");
const auto osVer = QOperatingSystemVersion::current();
if (runsOnCI && osVer >= QOperatingSystemVersion::MacOSTahoe)
QSKIP("The tests fails on macOS 26 in CI: QTBUG-139354");
#endif
const quint16 defaultPort = 43344;
const QHostAddress defaultHost(QHostAddress::LocalHost);
for (const auto &backend: std::as_const(m_backends)) {
QVariantMap backendOptions;
QOpcUaClient *client = m_opcUa.createClient(backend, backendOptions);
QVERIFY2(client != nullptr,
u"Loading backend failed: %1"_s.arg(backend).toLatin1().data());
client->setParent(this);
qDebug() << "Using SDK plugin:" << client->backend();
m_clients.append(client);
}
if (qEnvironmentVariableIsEmpty("OPCUA_HOST") && qEnvironmentVariableIsEmpty("OPCUA_PORT")) {
m_testServerPath = qApp->applicationDirPath()
#if defined(Q_OS_MACOS)
+ "/../../open62541-testserver/open62541-testserver.app/Contents/MacOS/open62541-testserver"_L1
#else
#if defined(Q_OS_WIN) && !defined(TESTS_CMAKE_SPECIFIC_PATH)
+ "/.."_L1
#endif
+ "/../../open62541-testserver/open62541-testserver"_L1
#ifdef Q_OS_WIN
+ ".exe"_L1
#endif
#endif
;
if (!QFile::exists(m_testServerPath)) {
qDebug() << "Server Path:" << m_testServerPath;
QSKIP("all auto tests rely on an open62541-based test-server");
}
// In this case the test is supposed to open its own server.
// Unfortunately there is no way to check if the server has started up successfully
// because of missing error handling.
// This checks will detect other servers blocking the port.
// Check for running server
QTcpSocket socket;
socket.connectToHost(defaultHost, defaultPort);
QVERIFY2(socket.waitForConnected(1500) == false, "Server is already running");
// Check for running server which does not respond
QTcpServer server;
QVERIFY2(server.listen(defaultHost, defaultPort) == true, "Port is occupied by another process. Check for defunct server.");
server.close();
m_serverProcess.start(m_testServerPath);
QVERIFY2(m_serverProcess.waitForStarted(), qPrintable(m_serverProcess.errorString()));
// Check if server is up and running
QVERIFY(m_serverProcess.state() == QProcess::Running);
QTest::qSleep(100);
socket.connectToHost(defaultHost, defaultPort);
if (!socket.waitForConnected(5000))
{
bool success = false;
for (int i = 0; i < 50; ++i) {
QTest::qSleep(100);
socket.connectToHost(defaultHost, defaultPort);
if (socket.waitForConnected(5000)) {
success = true;
break;
}
}
if (!success)
QFAIL("Server does not run");
}
socket.disconnectFromHost();
}
QString host = envOrDefault("OPCUA_HOST", defaultHost.toString());
QString port = envOrDefault("OPCUA_PORT", QString::number(defaultPort));
m_discoveryEndpoint.setUrl("opc.tcp://%1:%2"_L1.arg(host, port));
qDebug() << "Using endpoint:" << m_discoveryEndpoint;
QOpcUaClient *client = m_clients.first();
if (client) {
QSignalSpy endpointSpy(m_clients.first(), &QOpcUaClient::endpointsRequestFinished);
client->requestEndpoints(m_discoveryEndpoint);
endpointSpy.wait(signalSpyTimeout);
QCOMPARE(endpointSpy.size(), 1);
const auto desc = endpointSpy.at(0).at(0).value<QList<QOpcUaEndpointDescription>>();
QVERIFY(desc.size() > 0);
QCOMPARE(endpointSpy.at(0).at(2).value<QUrl>(), m_discoveryEndpoint);
m_endpoint = desc.first();
}
}
void Tst_GeneratedDataTypes::checkGeneratedDecoding()
{
QFETCH(QOpcUaClient *, opcuaClient);
OpcuaConnector connector(opcuaClient, m_endpoint);
// Check against the default values of the test nodes on the server
// They must not have been modified by previous test runs if OPCUA_HOST and OPCUA_PORT is used
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_NestedStruct)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtTestStructType>(success);
QVERIFY(success);
QCOMPARE(decoded.stringMember(), u"TestString"_s);
QCOMPARE(decoded.localizedTextMember(), QOpcUaLocalizedText(u"en"_s, u"TestText"_s));
QCOMPARE(decoded.qualifiedNameMember(), QOpcUaQualifiedName(1, u"TestName"_s));
QCOMPARE(decoded.int64ArrayMember(), QList<qint64>({ std::numeric_limits<qint64>::max(),
std::numeric_limits<qint64>::max() - 1,
std::numeric_limits<qint64>::min() }));
QCOMPARE(decoded.nestedStructMember().doubleSubtypeMember(), 42);
QCOMPARE(decoded.nestedStructArrayMember().size(), 2);
QCOMPARE(decoded.nestedStructArrayMember().at(0).doubleSubtypeMember(), 23);
QCOMPARE(decoded.nestedStructArrayMember().at(1).doubleSubtypeMember(), 42);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithFirstMember)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtTestUnionType>(success);
QVERIFY(success);
QCOMPARE(decoded.switchField(), CodegenTestQtTestUnionType::SwitchField::Member1);
QCOMPARE(decoded.member1(), 42);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithSecondMember)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtTestUnionType>(success);
QVERIFY(success);
QCOMPARE(decoded.switchField(), CodegenTestQtTestUnionType::SwitchField::Member2);
QCOMPARE(decoded.member2().doubleSubtypeMember(), 23);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithOptionalField)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtStructWithOptionalFieldType>(success);
QVERIFY(success);
QCOMPARE(decoded.mandatoryMember(), 42);
QVERIFY(decoded.optionalMemberSpecified());
QCOMPARE(decoded.optionalMember(), 23);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWihoutOptionalField)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtStructWithOptionalFieldType>(success);
QVERIFY(success);
QCOMPARE(decoded.mandatoryMember(), 42);
QVERIFY(!decoded.optionalMemberSpecified());
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDiagnosticInfo)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructWithDiagnosticInfo_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtTestStructWithDiagnosticInfo>(success);
QVERIFY(success);
QCOMPARE(decoded.diagnosticInfoMember().hasSymbolicId(), true);
QCOMPARE(decoded.diagnosticInfoMember().symbolicId(), 1);
QCOMPARE(decoded.diagnosticInfoMember().hasNamespaceUri(), true);
QCOMPARE(decoded.diagnosticInfoMember().namespaceUri(), 2);
QCOMPARE(decoded.diagnosticInfoMember().hasLocalizedText(), true);
QCOMPARE(decoded.diagnosticInfoMember().localizedText(), 4); // FIXME Swapped with locale due to a bug in open62541
QCOMPARE(decoded.diagnosticInfoMember().hasLocale(), true);
QCOMPARE(decoded.diagnosticInfoMember().locale(), 3); // FIXME Swapped with localizedText due to a bug in open62541
QCOMPARE(decoded.diagnosticInfoMember().hasAdditionalInfo(), true);
QCOMPARE(decoded.diagnosticInfoMember().additionalInfo(), u"My additional info"_s);
QCOMPARE(decoded.diagnosticInfoMember().hasInnerStatusCode(), true);
QCOMPARE(decoded.diagnosticInfoMember().innerStatusCode(), QOpcUa::UaStatusCode::BadInternalError);
QCOMPARE(decoded.diagnosticInfoMember().hasInnerDiagnosticInfo(), true);
QCOMPARE(decoded.diagnosticInfoMember().innerDiagnosticInfo().hasAdditionalInfo(), true);
QCOMPARE(decoded.diagnosticInfoMember().innerDiagnosticInfo().additionalInfo(), u"My inner additional info"_s);
QCOMPARE(decoded.diagnosticInfoArrayMember().size(), 2);
QCOMPARE(decoded.diagnosticInfoArrayMember().at(0), decoded.diagnosticInfoMember());
QCOMPARE(decoded.diagnosticInfoArrayMember().at(1).hasLocale(), true);
QCOMPARE(decoded.diagnosticInfoArrayMember().at(1).locale(), 1);
QCOMPARE(decoded.diagnosticInfoArrayMember().at(1).hasInnerStatusCode(), true);
QCOMPARE(decoded.diagnosticInfoArrayMember().at(1).innerStatusCode(), QOpcUa::UaStatusCode::BadTypeMismatch);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDataValue)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructWithDataValue_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtTestStructWithDataValue>(success);
QVERIFY(success);
const QStringList expectedValue = {
u"TestString 1"_s, u"TestString 2"_s,
u"TestString 3"_s, u"TestString 4"_s
};
const auto variant = decoded.dataValueMember().value().value<QOpcUaVariant>();
QCOMPARE(variant.isArray(), true);
QList<qint32> expectedDimensions = {2, 2};
QCOMPARE(variant.arrayDimensions(), expectedDimensions);
QCOMPARE(variant.type(), QOpcUaVariant::ValueType::String);
QCOMPARE(variant.value(), expectedValue);
QCOMPARE(decoded.dataValueMember().statusCode(), QOpcUa::UaStatusCode::BadInternalError);
QCOMPARE(decoded.dataValueMember().serverTimestamp(), QDateTime::fromMSecsSinceEpoch(1698655307000));
QCOMPARE(decoded.dataValueMember().sourceTimestamp(), QDateTime::fromMSecsSinceEpoch(1698655306000));
QCOMPARE(decoded.dataValueMember().serverPicoseconds(), 23);
QCOMPARE(decoded.dataValueMember().sourcePicoseconds(), 42);
QCOMPARE(decoded.variantMember().isArray(), false);
QVERIFY(decoded.variantMember().arrayDimensions().empty());
QCOMPARE(decoded.variantMember().type(), QOpcUaVariant::ValueType::UInt64);
QCOMPARE(decoded.variantMember().value().value<quint64>(), 42);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_RecursiveStruct)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext.encodingTypeId(), makeTestModelId(TestModelNodeId::QtRecursiveTestStruct_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder codec(ext);
bool success = false;
const auto decoded = codec.decode<CodegenTestQtRecursiveTestStruct>(success);
QVERIFY(success);
QCOMPARE(decoded.stringMember(), u"Outer string"_s);
QCOMPARE(decoded.recursiveArrayMember().size(), 2);
QCOMPARE(decoded.recursiveArrayMember().at(0).stringMember(), u"Nested string 1"_s);
QCOMPARE(decoded.recursiveArrayMember().at(0).recursiveArrayMember().size(), 1);
QCOMPARE(decoded.recursiveArrayMember().at(0).recursiveArrayMember().at(0).stringMember(), u"Innermost string"_s);
QCOMPARE(decoded.recursiveArrayMember().at(0).recursiveArrayMember().at(0).recursiveArrayMember().size(), 0);
QCOMPARE(decoded.recursiveArrayMember().at(1).stringMember(), u"Nested string 2"_s);
QCOMPARE(decoded.recursiveArrayMember().at(1).recursiveArrayMember().size(), 0);
}
}
void Tst_GeneratedDataTypes::checkGeneratedEncoding()
{
QFETCH(QOpcUaClient *, opcuaClient);
OpcuaConnector connector(opcuaClient, m_endpoint);
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_NestedStruct)));
QVERIFY(node != nullptr);
CodegenTestQtTestStructType data;
data.setStringMember(u"TestString"_s);
data.setLocalizedTextMember(QOpcUaLocalizedText(u"en"_s, u"TestText"_s));
data.setQualifiedNameMember(QOpcUaQualifiedName(1, u"TestName"_s));
data.setInt64ArrayMember({ std::numeric_limits<qint64>::max(),
std::numeric_limits<qint64>::max() - 1,
std::numeric_limits<qint64>::min() });
CodegenTestQtInnerTestStructType i1, i2;
i1.setDoubleSubtypeMember(24.0);
i2.setDoubleSubtypeMember(43.0);
data.setNestedStructMember(i2);
data.setNestedStructArrayMember({ i1, i2 });
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtTestStructType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtTestStructType>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_NestedStruct)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtTestStructType>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithFirstMember)));
QVERIFY(node != nullptr);
CodegenTestQtTestUnionType data;
data.setMember1(43);
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtTestUnionType>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithFirstMember)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtTestUnionType>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithSecondMember)));
QVERIFY(node != nullptr);
CodegenTestQtInnerTestStructType inner;
inner.setDoubleSubtypeMember(43);
CodegenTestQtTestUnionType data;
data.setMember2(inner);
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtTestUnionType>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_UnionWithSecondMember)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestUnionType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtTestUnionType>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithOptionalField)));
QVERIFY(node != nullptr);
CodegenTestQtStructWithOptionalFieldType data;
data.setMandatoryMember(24);
data.setOptionalMemberSpecified(true);
data.setOptionalMember(43);
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtStructWithOptionalFieldType>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithOptionalField)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtStructWithOptionalFieldType>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWihoutOptionalField)));
QVERIFY(node != nullptr);
CodegenTestQtStructWithOptionalFieldType data;
data.setMandatoryMember(24);
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtStructWithOptionalFieldType>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWihoutOptionalField)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtStructWithOptionalFieldType_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtStructWithOptionalFieldType>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDiagnosticInfo)));
QVERIFY(node != nullptr);
CodegenTestQtTestStructWithDiagnosticInfo data;
QOpcUaDiagnosticInfo di1;
di1.setHasSymbolicId(true);
di1.setSymbolicId(2);
di1.setHasNamespaceUri(true);
di1.setNamespaceUri(3);
di1.setHasLocalizedText(true);
di1.setLocalizedText(4);
di1.setHasLocale(true);
di1.setLocale(5);
di1.setHasAdditionalInfo(true);
di1.setAdditionalInfo(u"My additional info"_s);
di1.setHasInnerStatusCode(true);
di1.setInnerStatusCode(QOpcUa::UaStatusCode::BadInternalError);
di1.setHasInnerDiagnosticInfo(true);
QOpcUaDiagnosticInfo inner;
inner.setHasAdditionalInfo(true);
inner.setAdditionalInfo(u"My inner additional info"_s);
di1.setInnerDiagnosticInfo(inner);
QOpcUaDiagnosticInfo di2;
di2.setHasLocale(true);
di2.setLocale(1);
di2.setHasInnerStatusCode(true);
di2.setInnerStatusCode(QOpcUa::UaStatusCode::BadAlreadyExists);
data.setDiagnosticInfoMember(di1);
data.setDiagnosticInfoArrayMember({ di1, di2 });
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtTestStructWithDiagnosticInfo_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtTestStructWithDiagnosticInfo>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDiagnosticInfo)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructWithDiagnosticInfo_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtTestStructWithDiagnosticInfo>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDataValue)));
QVERIFY(node != nullptr);
CodegenTestQtTestStructWithDataValue data;
QOpcUaDataValue dv;
dv.setValue(QOpcUaVariant(QOpcUaVariant::ValueType::Double, 23.0));
dv.setStatusCode(QOpcUa::UaStatusCode::UncertainInitialValue);
dv.setSourceTimestamp(QDateTime::currentDateTime());
dv.setSourcePicoseconds(24);
dv.setServerTimestamp(QDateTime::currentDateTime());
dv.setServerPicoseconds(43);
data.setDataValueMember(dv);
QOpcUaVariant var;
var.setArrayDimensions({ 3, 3});
var.setValue(QOpcUaVariant::ValueType::Double,
QVariant::fromValue(QList<double>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }));
data.setVariantMember(var);
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtTestStructWithDataValue_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtTestStructWithDataValue>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_StructWithDataValue)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtTestStructWithDataValue_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtTestStructWithDataValue>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
{
QScopedPointer<QOpcUaNode> node(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_RecursiveStruct)));
QVERIFY(node != nullptr);
CodegenTestQtRecursiveTestStruct data;
CodegenTestQtRecursiveTestStruct nested1;
CodegenTestQtRecursiveTestStruct innermost;
CodegenTestQtRecursiveTestStruct nested2;
data.setStringMember(u"My outer string"_s);
nested1.setStringMember(u"My nested string 1"_s);
nested2.setStringMember(u"My nested string 2"_s);
innermost.setStringMember(u"My innermost string"_s);
nested2.setRecursiveArrayMember({ innermost });
data.setRecursiveArrayMember({ nested1, nested2 });
QOpcUaExtensionObject ext(makeTestModelId(TestModelNodeId::QtRecursiveTestStruct_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder encoder(ext);
QCOMPARE(encoder.encode<CodegenTestQtRecursiveTestStruct>(data), true);
WRITE_VALUE_ATTRIBUTE(node, ext, QOpcUa::Types::ExtensionObject);
node.reset(opcuaClient->node(makeTestModelId(TestModelNodeId::DecoderTestNodes_RecursiveStruct)));
QVERIFY(node != nullptr);
READ_VARIABLE_NODE_VALUE(node);
auto ext2 = node->valueAttribute().value<QOpcUaExtensionObject>();
QCOMPARE(ext2.encodingTypeId(), makeTestModelId(TestModelNodeId::QtRecursiveTestStruct_Encoding_DefaultBinary));
CodegenTestBinaryDeEncoder decoder(ext2);
bool success = false;
const auto decoded = decoder.decode<CodegenTestQtRecursiveTestStruct>(success);
QVERIFY(success);
QCOMPARE(decoded, data);
}
}
void Tst_GeneratedDataTypes::checkRecursionLimit()
{
CodegenTestQtRecursiveTestStruct outer;
for (int i = 0; i < 499; ++i) {
CodegenTestQtRecursiveTestStruct next;
next.setRecursiveArrayMember({ outer });
outer = next;
}
// 500 levels, should be ok
{
QByteArray data;
CodegenTestBinaryDeEncoder decoder(&data);
bool success = decoder.encode(outer);
QVERIFY(success);
const auto decoded = decoder.decode<CodegenTestQtRecursiveTestStruct>(success);
QVERIFY(success);
QCOMPARE(decoded, outer);
}
// 501 levels, failure expected
{
CodegenTestQtRecursiveTestStruct next;
next.setRecursiveArrayMember({ outer });
outer = next;
QByteArray data;
CodegenTestBinaryDeEncoder decoder(&data);
bool success = decoder.encode(outer);
QVERIFY(success);
const auto decoded = decoder.decode<CodegenTestQtRecursiveTestStruct>(success);
QVERIFY(!success);
QCOMPARE(decoded, {});
}
}
void Tst_GeneratedDataTypes::testNodeIdsOnly()
{
QCOMPARE(static_cast<int>(CsvOnlyTestNodeId::Unknown), 0);
QCOMPARE(static_cast<int>(CsvOnlyTestNodeId::TestId1), 1);
QCOMPARE(static_cast<int>(CsvOnlyTestNodeId::TestId2), 2);
QCOMPARE(static_cast<int>(CsvOnlyTestNodeId::TestId3), 3);
}
void Tst_GeneratedDataTypes::cleanupTestCase()
{
if (m_serverProcess.state() == QProcess::Running) {
m_serverProcess.kill();
m_serverProcess.waitForFinished(2000);
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTEST_SET_MAIN_SOURCE_PATH
// run tests for all available backends
QStringList availableBackends = QOpcUaProvider::availableBackends();
if (availableBackends.empty()) {
qDebug("No OPCUA backends found, skipping tests.");
return EXIT_SUCCESS;
}
Tst_GeneratedDataTypes tc;
return QTest::qExec(&tc, argc, argv);
}
#include "tst_codegen.moc"