summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-10 03:03:01 +0100
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-10 03:03:02 +0100
commit701d1876209473c4c6e916f0c677b36829560cb5 (patch)
treea2e4d17e8a69042540cb90326f31a6ddde5d6a97 /tests
parentf1ab55285840068cf38fba6fdea667578dad7c5b (diff)
parent957d17b42b24e558fbee3f4f503712d18edf4953 (diff)
Merge remote-tracking branch 'origin/5.15' into dev
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/x509/tst_x509.cpp274
-rw-r--r--tests/auto/x509/x509.pro8
-rw-r--r--tests/manual/gds/gds.pro8
-rw-r--r--tests/manual/gds/tst_gds.cpp329
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/tests.pro3
7 files changed, 625 insertions, 3 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 9d8b7db..82a44df 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,10 +1,11 @@
TEMPLATE = subdirs
SUBDIRS += qopcuaclient connection clientSetupInCpp security
-QT_FOR_CONFIG += opcua-private
+QT_FOR_CONFIG += opcua-private core-private
# only build declarative tests if at least one backend was built
qtHaveModule(qmltest):qtConfig(open62541)|qtConfig(uacpp) {
SUBDIRS += declarative
}
+qtConfig(ssl):!darwin:!winrt: SUBDIRS += x509
diff --git a/tests/auto/x509/tst_x509.cpp b/tests/auto/x509/tst_x509.cpp
new file mode 100644
index 0000000..d00c3d9
--- /dev/null
+++ b/tests/auto/x509/tst_x509.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpcUa/QOpcUaProvider>
+#include <QtOpcUa/QOpcUaKeyPair>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopedPointer>
+#include <QOpcUaX509CertificateSigningRequest>
+#include <QOpcUaX509ExtensionSubjectAlternativeName>
+#include <QOpcUaX509ExtensionBasicConstraints>
+#include <QOpcUaX509ExtensionKeyUsage>
+#include <QOpcUaX509ExtensionExtendedKeyUsage>
+
+#include <QtTest/QSignalSpy>
+#include <QtTest/QtTest>
+
+#define defineDataMethod(name) void name()\
+{\
+ QTest::addColumn<QString>("backend");\
+ for (auto backend : m_backends) {\
+ const QString rowName = QString("%1").arg(backend); \
+ QTest::newRow(rowName.toLatin1().constData()) << backend ; \
+ }\
+}
+
+class Tst_QOpcUaSecurity: public QObject
+{
+ Q_OBJECT
+
+public:
+ Tst_QOpcUaSecurity();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ defineDataMethod(keyPairs_data)
+ void keyPairs();
+
+ defineDataMethod(certificateSigningRequest_data)
+ void certificateSigningRequest();
+
+private:
+ QStringList m_backends;
+ QOpcUaProvider m_opcUa;
+};
+
+QByteArray textifyCertificateRequest(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"req", "-text", "-noout"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+QByteArray textifyCertificate(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"x509", "-text", "-noout"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+QByteArray asn1dump(const QByteArray &data)
+{
+ QProcess p;
+ p.start("openssl", QStringList {"asn1parse", "-inform","PEM"});
+ p.waitForStarted();
+ p.write(data);
+ p.closeWriteChannel();
+ p.waitForFinished();
+ return p.readAllStandardOutput();
+}
+
+Tst_QOpcUaSecurity::Tst_QOpcUaSecurity()
+{
+ m_backends = QOpcUaProvider::availableBackends();
+}
+
+void Tst_QOpcUaSecurity::initTestCase()
+{
+}
+
+void Tst_QOpcUaSecurity::keyPairs()
+{
+ QFETCH(QString, backend);
+
+ QOpcUaKeyPair key;
+ QOpcUaKeyPair loadedKey;
+ QByteArray byteArray;
+
+ QVERIFY(key.hasPrivateKey() == false);
+
+ // Generate key
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+ QVERIFY(key.hasPrivateKey() == true);
+
+ // Export public key
+ byteArray = key.publicKeyToByteArray();
+ QVERIFY(byteArray.startsWith("-----BEGIN PUBLIC KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END PUBLIC KEY-----\n"));
+
+ // Load public key
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QVERIFY(loadedKey.hasPrivateKey() == false);
+
+ // Check unencrypted PEM export
+ byteArray = key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString());
+ QVERIFY(byteArray.startsWith("-----BEGIN PRIVATE KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END PRIVATE KEY-----\n"));
+
+ // Load private key from PEM data
+ QSignalSpy passwordSpy(&loadedKey, SIGNAL(passphraseNeeded(QString&,int,bool)));
+
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QVERIFY(loadedKey.hasPrivateKey() == true);
+ QCOMPARE(passwordSpy.count(), 0);
+ QCOMPARE(loadedKey.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()), byteArray);
+
+ // Check encrypted PEM export
+ byteArray = key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Aes128Cbc, QString("password"));
+ QVERIFY(byteArray.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----\n"));
+ QVERIFY(byteArray.endsWith("-----END ENCRYPTED PRIVATE KEY-----\n"));
+ QCOMPARE(passwordSpy.count(), 0);
+
+ // Setup password callback
+ QString passphraseToReturn;
+ connect(&loadedKey, &QOpcUaKeyPair::passphraseNeeded, this, [&passphraseToReturn](QString &passphrase, int maximumLength, bool writeOperation){
+ Q_UNUSED(maximumLength);
+ qDebug() << "Requested a passphrase for" << (writeOperation ? "write":"read") << "operation";
+ passphrase = passphraseToReturn;
+ });
+
+ // Load key with wrong password
+ qDebug() << "Trying to decrypt with wrong password; will cause an error";
+ passphraseToReturn = "WrongPassword";
+ QVERIFY(!loadedKey.loadFromPemData(byteArray));
+ QCOMPARE(passwordSpy.count(), 1);
+ QVERIFY(loadedKey.hasPrivateKey() == false);
+
+ // Load key with right password
+ qDebug() << "Trying to decrypt with right password; will cause no error";
+ passphraseToReturn = "password";
+ QVERIFY(loadedKey.loadFromPemData(byteArray));
+ QCOMPARE(passwordSpy.count(), 2);
+ QCOMPARE(loadedKey.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()),
+ key.privateKeyToByteArray(QOpcUaKeyPair::Cipher::Unencrypted, QString()));
+ QVERIFY(loadedKey.hasPrivateKey() == true);
+}
+
+void Tst_QOpcUaSecurity::certificateSigningRequest()
+{
+ QFETCH(QString, backend);
+
+ QOpcUaKeyPair key;
+
+ // Generate key
+ key.generateRsaKey(QOpcUaKeyPair::RsaKeyStrength::Bits1024);
+ QVERIFY(key.hasPrivateKey() == true);
+
+ QOpcUaX509CertificateSigningRequest csr;
+
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CommonName, "QtOpcUaViewer");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, "DE");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, "Berlin");
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, "The Qt Company");
+ csr.setSubject(dn);
+
+ QOpcUaX509ExtensionSubjectAlternativeName *san = new QOpcUaX509ExtensionSubjectAlternativeName;
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "foo.com");
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::DNS, "bla.com");
+ san->addEntry(QOpcUaX509ExtensionSubjectAlternativeName::Type::URI, "urn:foo.com:The%20Qt%20Company:QtOpcUaViewer");
+ san->setCritical(true);
+ csr.addExtension(san);
+
+ QOpcUaX509ExtensionBasicConstraints *bc = new QOpcUaX509ExtensionBasicConstraints;
+ bc->setCa(false);
+ bc->setCritical(true);
+ csr.addExtension(bc);
+
+ QOpcUaX509ExtensionKeyUsage *ku = new QOpcUaX509ExtensionKeyUsage;
+ ku->setCritical(true);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DigitalSignature);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::NonRepudiation);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::KeyEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::DataEncipherment);
+ ku->setKeyUsage(QOpcUaX509ExtensionKeyUsage::KeyUsage::CertificateSigning);
+ csr.addExtension(ku);
+
+ QOpcUaX509ExtensionExtendedKeyUsage *eku = new QOpcUaX509ExtensionExtendedKeyUsage;
+ eku->setCritical(true);
+ eku->setKeyUsage(QOpcUaX509ExtensionExtendedKeyUsage::KeyUsage::EmailProtection);
+ csr.addExtension(eku);
+
+ QByteArray csrData = csr.createRequest(key);
+ qDebug() << csrData;
+ QVERIFY(csrData.startsWith("-----BEGIN CERTIFICATE REQUEST-----\n"));
+ QVERIFY(csrData.endsWith("\n-----END CERTIFICATE REQUEST-----\n"));
+ qDebug().noquote() << textifyCertificateRequest(csrData);
+ qDebug().noquote() << asn1dump(csrData);
+
+ QByteArray certData = csr.createSelfSignedCertificate(key);
+ qDebug() << certData;
+ QVERIFY(certData.startsWith("-----BEGIN CERTIFICATE-----\n"));
+ QVERIFY(certData.endsWith("\n-----END CERTIFICATE-----\n"));
+ qDebug().noquote() << textifyCertificate(certData);
+ qDebug().noquote() << asn1dump(certData);
+}
+
+void Tst_QOpcUaSecurity::cleanupTestCase()
+{
+}
+
+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_QOpcUaSecurity tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_x509.moc"
+
diff --git a/tests/auto/x509/x509.pro b/tests/auto/x509/x509.pro
new file mode 100644
index 0000000..b1f1fc1
--- /dev/null
+++ b/tests/auto/x509/x509.pro
@@ -0,0 +1,8 @@
+TARGET = tst_x509
+
+QT += testlib opcua network
+QT -= gui
+CONFIG += testcase
+
+SOURCES += \
+ tst_x509.cpp
diff --git a/tests/manual/gds/gds.pro b/tests/manual/gds/gds.pro
new file mode 100644
index 0000000..525595f
--- /dev/null
+++ b/tests/manual/gds/gds.pro
@@ -0,0 +1,8 @@
+TARGET = tst_gds
+
+QT += testlib opcua network
+QT -= gui
+CONFIG += testcase
+
+SOURCES += \
+ tst_gds.cpp
diff --git a/tests/manual/gds/tst_gds.cpp b/tests/manual/gds/tst_gds.cpp
new file mode 100644
index 0000000..9e57238
--- /dev/null
+++ b/tests/manual/gds/tst_gds.cpp
@@ -0,0 +1,329 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt OPC UA module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtOpcUa/QOpcUaProvider>
+#include <QtOpcUa/QOpcUaGdsClient>
+#include <QtOpcUa/QOpcUaApplicationIdentity>
+#include <QtOpcUa/QOpcUaPkiConfiguration>
+#include <QtOpcUa/QOpcUaAuthenticationInformation>
+#include <QtOpcUa/QOpcUaEndpointDescription>
+#include <QtOpcUa/QOpcUaApplicationRecordDataType>
+#include <QtOpcUa/QOpcUaClient>
+#include <QtOpcUa/QOpcUaX509DistinguishedName>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QScopedPointer>
+#include <QHostInfo>
+
+#include <QtTest/QSignalSpy>
+#include <QtTest/QtTest>
+
+#define defineDataMethod(name) void name()\
+{\
+ QTest::addColumn<QString>("backend");\
+ for (auto backend : m_backends) {\
+ const QString rowName = QString("%1").arg(backend); \
+ QTest::newRow(rowName.toLatin1().constData()) << backend ; \
+ QVERIFY(!backend.isEmpty()); \
+ }\
+}
+
+static QOpcUaPkiConfiguration getPkiConfig()
+{
+ QOpcUaPkiConfiguration pkiConfig;
+ const QString pkiDir = QCoreApplication::applicationDirPath() + "/pki";
+
+ pkiConfig.setClientCertificateFile(pkiDir + "/own/certs/application.der");
+ pkiConfig.setPrivateKeyFile(pkiDir + "/own/private/application.pem");
+ pkiConfig.setTrustListDirectory(pkiDir + "/trusted/certs");
+ pkiConfig.setRevocationListDirectory(pkiDir + "/trusted/crl");
+ pkiConfig.setIssuerListDirectory(pkiDir + "/issuers/certs");
+ pkiConfig.setIssuerRevocationListDirectory(pkiDir + "/issuers/crl");
+ return pkiConfig;
+}
+
+static QOpcUaApplicationIdentity getAppIdentity()
+{
+ QOpcUaApplicationIdentity identity;
+
+ const QString applicationUri = QStringLiteral("urn:%1:%2:%3")
+ .arg(QHostInfo::localHostName())
+ .arg(QCoreApplication::organizationName())
+ .arg(QCoreApplication::applicationName());
+ const QString productUri = QStringLiteral("urn:%1:%2")
+ .arg(QCoreApplication::organizationName())
+ .arg(QCoreApplication::applicationName());
+
+ identity.setProductUri(productUri);
+ identity.setApplicationUri(applicationUri);
+ identity.setApplicationName(QCoreApplication::applicationName());
+ identity.setApplicationType(QOpcUaApplicationDescription::Client);
+ return identity;
+}
+
+static bool removeSettingsFile()
+{
+ QSettings settings;
+ QFile file(settings.fileName());
+ if (file.exists())
+ return file.remove();
+ return true;
+}
+
+static void provideCredentials(QOpcUaAuthenticationInformation &authInfo)
+{
+ qDebug() << "Authentication credentials provided";
+ authInfo.setUsernameAuthentication("root", "secret");
+}
+
+static void commonGdsClientSetup(QOpcUaGdsClient &gc, const QString &backend, const QOpcUaEndpointDescription endpoint)
+{
+ QObject::connect(&gc, &QOpcUaGdsClient::authenticationRequired, provideCredentials);
+
+ gc.setBackend(backend);
+ gc.setEndpoint(endpoint);
+ gc.setApplicationIdentity(getAppIdentity());
+ gc.setPkiConfiguration(getPkiConfig());
+
+ QOpcUaApplicationRecordDataType ar = gc.applicationRecord();
+ ar.setApplicationNames(QVector<QOpcUaLocalizedText>{QOpcUaLocalizedText("en", gc.applicationIdentity().applicationName())});
+ ar.setApplicationType(gc.applicationIdentity().applicationType());
+ ar.setApplicationUri(gc.applicationIdentity().applicationUri());
+ ar.setProductUri(gc.applicationIdentity().productUri());
+ ar.setDiscoveryUrls(QVector<QString>{QLatin1String("opc.tcp://localhost")});
+ gc.setApplicationRecord(ar);
+
+ QOpcUaX509DistinguishedName dn;
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::CountryName, QLatin1String("DE"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::LocalityName, QLatin1String("Berlin"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::StateOrProvinceName, QLatin1String("Berlin"));
+ dn.setEntry(QOpcUaX509DistinguishedName::Type::OrganizationName, QLatin1String("The Qt Company"));
+ gc.setCertificateSigningRequestPresets(dn, QLatin1String("foo.com"));
+}
+
+// The tests have are depending on each other and have to be executed in order.
+
+class Tst_QOpcUaGds: public QObject
+{
+ Q_OBJECT
+
+public:
+ Tst_QOpcUaGds();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ defineDataMethod(registerApplication_data)
+ void registerApplication();
+ defineDataMethod(reuseApplicationId_data)
+ void reuseApplicationId();
+ defineDataMethod(reuseRegisteredUri_data)
+ void reuseRegisteredUri();
+ defineDataMethod(serverForgotRegistration_data)
+ void serverForgotRegistration();
+private:
+ QStringList m_backends;
+ QOpcUaEndpointDescription m_endpoint;
+};
+
+Tst_QOpcUaGds::Tst_QOpcUaGds()
+{
+ m_backends = QOpcUaProvider::availableBackends();
+}
+
+void Tst_QOpcUaGds::initTestCase()
+{
+ QOpcUaEndpointDescription endpoint;
+ QOpcUaProvider provider;
+
+ QScopedPointer<QOpcUaClient> client(provider.createClient("open62541"));
+ QVERIFY(!client.isNull());
+ QSignalSpy endpointSpy(client.data(), &QOpcUaClient::endpointsRequestFinished);
+ client->requestEndpoints(QUrl("opc.tcp://172.17.0.1:48060"));
+ endpointSpy.wait(2000);
+ QCOMPARE(endpointSpy.size(), 1);
+
+ const QVector<QOpcUaEndpointDescription> desc = endpointSpy.at(0).at(0).value<QVector<QOpcUaEndpointDescription>>();
+ QVERIFY(desc.size() > 0);
+
+ for (const auto &i : qAsConst(desc)) {
+ if (i.securityPolicy().endsWith("#Basic256Sha256")
+ && i.securityMode() == QOpcUaEndpointDescription::MessageSecurityMode::SignAndEncrypt)
+ m_endpoint = i;
+ }
+
+ QVERIFY(m_endpoint.securityPolicy().endsWith("#Basic256Sha256"));
+}
+
+void Tst_QOpcUaGds::registerApplication()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ QVERIFY(removeSettingsFile());
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+ QSignalSpy certificateStatusSpy(&gc, &QOpcUaGdsClient::certificateUpdateRequired);
+ QSignalSpy certificateUpdatedSpy(&gc, &QOpcUaGdsClient::certificateUpdated);
+ QSignalSpy trustListUpdatedSpy(&gc, &QOpcUaGdsClient::trustListUpdated);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+
+ if (certificateStatusSpy.isEmpty())
+ certificateStatusSpy.wait();
+
+ if (certificateUpdatedSpy.isEmpty())
+ certificateUpdatedSpy.wait();
+
+ if (trustListUpdatedSpy.isEmpty())
+ trustListUpdatedSpy.wait();
+
+ QVERIFY(!certificateStatusSpy.isEmpty());
+ QVERIFY(!certificateUpdatedSpy.isEmpty());
+ QVERIFY(!trustListUpdatedSpy.isEmpty());
+}
+
+void Tst_QOpcUaGds::reuseApplicationId()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Keep settings file in order to reuse the existing registration
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+}
+
+void Tst_QOpcUaGds::reuseRegisteredUri()
+{
+ // This test assumes that a registratin at the server is still present.
+ // It will remove the information locally and has to reuse the
+ // registration from the server.
+
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Remove the registration data locally; the server will keep it.
+ QSettings settings("Unknown Organization", "tst_gds");
+
+ QString applicationId = settings.value(QLatin1String("gds/applicationId")).toString();
+ settings.remove(QLatin1String("gds/applicationId"));
+ settings.sync();
+
+ QVERIFY(removeSettingsFile());
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+ QVERIFY(gc.applicationId().size() > 10);
+ if (!applicationId.isEmpty())
+ QVERIFY(gc.applicationId() == applicationId);
+}
+
+void Tst_QOpcUaGds::serverForgotRegistration()
+{
+ QFETCH(QString, backend);
+ if (backend == "open62541")
+ QSKIP("Skipping open62541");
+
+ // Setting an invalid application ID simulates a previous registration which has been
+ // forgotten by the server
+ QSettings settings("Unknown Organization", "tst_gds");
+ settings.setValue(QLatin1String("gds/applicationId"), "ns=42;i=1111111");
+ settings.sync();
+
+ QOpcUaGdsClient gc;
+ commonGdsClientSetup(gc, backend, m_endpoint);
+
+ QSignalSpy registeredSpy(&gc, &QOpcUaGdsClient::applicationRegistered);
+ QSignalSpy certificateGroupsSpy(&gc, &QOpcUaGdsClient::certificateGroupsReceived);
+
+ gc.start();
+
+ QVERIFY(certificateGroupsSpy.wait());
+ QVERIFY(!certificateGroupsSpy.at(0).at(0).value<QStringList>().isEmpty());
+ QVERIFY(registeredSpy.wait());
+}
+
+void Tst_QOpcUaGds::cleanupTestCase()
+{
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QTEST_SET_MAIN_SOURCE_PATH
+
+ if (QOpcUaProvider::availableBackends().empty()) {
+ qDebug("No OPCUA backends found, skipping tests.");
+ return EXIT_SUCCESS;
+ }
+
+ Tst_QOpcUaGds tc;
+ return QTest::qExec(&tc, argc, argv);
+}
+
+#include "tst_gds.moc"
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 3ee3e86..6007738 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -1,3 +1,6 @@
TEMPLATE = subdirs
SUBDIRS += eventsubscription
+
+QT_FOR_CONFIG += opcua-private core-private
+qtConfig(ssl):!darwin:!winrt: SUBDIRS += gds
diff --git a/tests/tests.pro b/tests/tests.pro
index e7b0667..5719b55 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -1,6 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS += auto \
- manual
+SUBDIRS += auto
QT_FOR_CONFIG += opcua-private