summaryrefslogtreecommitdiffstats
path: root/tests/auto
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@qt.io>2021-11-18 14:52:19 +0100
committerJoerg Bornemann <joerg.bornemann@qt.io>2021-11-23 21:11:45 +0100
commit3f56950862181f4d50f30d66f577c933795522c3 (patch)
treed6d47e2693605b3b207e22614860563ee606aff8 /tests/auto
parent1e9f9a4b7d1efa7efd5d501754f2de000a507cc5 (diff)
Move macdeployqt and windeployqt from qttools to qtbase
Having all *deployqt tools in qtbase will allow us to couple deployment support more tightly with the build system. Change-Id: I299efdacfa6b66a303bb3996ff3ff84e723210a5 Reviewed-by: Kai Koehne <kai.koehne@qt.io>
Diffstat (limited to 'tests/auto')
-rw-r--r--tests/auto/tools/CMakeLists.txt9
-rw-r--r--tests/auto/tools/macdeployqt/CMakeLists.txt10
-rw-r--r--tests/auto/tools/macdeployqt/source_basicapp/basicapp.pro1
-rw-r--r--tests/auto/tools/macdeployqt/source_basicapp/main.cpp44
-rw-r--r--tests/auto/tools/macdeployqt/source_plugin_sqlite/main.cpp36
-rw-r--r--tests/auto/tools/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro2
-rw-r--r--tests/auto/tools/macdeployqt/source_plugin_tls/main.cpp35
-rw-r--r--tests/auto/tools/macdeployqt/source_plugin_tls/plugin_tls.pro2
-rw-r--r--tests/auto/tools/macdeployqt/tst_macdeployqt.cpp316
-rw-r--r--tests/auto/tools/windeployqt/CMakeLists.txt4
-rw-r--r--tests/auto/tools/windeployqt/test/CMakeLists.txt11
-rw-r--r--tests/auto/tools/windeployqt/testapp/CMakeLists.txt21
-rw-r--r--tests/auto/tools/windeployqt/testapp/main.cpp46
-rw-r--r--tests/auto/tools/windeployqt/tst_windeployqt.cpp181
14 files changed, 718 insertions, 0 deletions
diff --git a/tests/auto/tools/CMakeLists.txt b/tests/auto/tools/CMakeLists.txt
index f1a9ced60e..8a827d11e4 100644
--- a/tests/auto/tools/CMakeLists.txt
+++ b/tests/auto/tools/CMakeLists.txt
@@ -15,3 +15,12 @@ if(TARGET Qt::DBus)
add_subdirectory(qdbuscpp2xml)
add_subdirectory(qdbusxml2cpp)
endif()
+if(QT_FEATURE_process AND NOT CMAKE_CROSSCOMPILING)
+ if(QT_FEATURE_macdeployqt)
+ add_subdirectory(macdeployqt)
+ endif()
+ if(QT_FEATURE_windeployqt AND BUILD_SHARED_LIBS)
+ # windeployqt does not work with static Qt builds. See QTBUG-69427.
+ add_subdirectory(windeployqt)
+ endif()
+endif()
diff --git a/tests/auto/tools/macdeployqt/CMakeLists.txt b/tests/auto/tools/macdeployqt/CMakeLists.txt
new file mode 100644
index 0000000000..073c2a9e70
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Generated from macdeployqt.pro.
+
+#####################################################################
+## tst_macdeployqt Test:
+#####################################################################
+
+qt_internal_add_test(tst_macdeployqt
+ SOURCES
+ tst_macdeployqt.cpp
+)
diff --git a/tests/auto/tools/macdeployqt/source_basicapp/basicapp.pro b/tests/auto/tools/macdeployqt/source_basicapp/basicapp.pro
new file mode 100644
index 0000000000..bba41b9c12
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_basicapp/basicapp.pro
@@ -0,0 +1 @@
+SOURCES = main.cpp
diff --git a/tests/auto/tools/macdeployqt/source_basicapp/main.cpp b/tests/auto/tools/macdeployqt/source_basicapp/main.cpp
new file mode 100644
index 0000000000..093a882f32
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_basicapp/main.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QRasterWindow>
+#include <QScreen>
+#include <QTimer>
+
+// Simple test application just to verify that it comes up properly
+
+int main(int argc, char ** argv)
+{
+ QGuiApplication app(argc, argv);
+ QRasterWindow w;
+ w.setTitle("macdeployqt test application");
+ w.show();
+ QTimer::singleShot(200, &w, &QCoreApplication::quit);
+ return app.exec();
+}
diff --git a/tests/auto/tools/macdeployqt/source_plugin_sqlite/main.cpp b/tests/auto/tools/macdeployqt/source_plugin_sqlite/main.cpp
new file mode 100644
index 0000000000..31e2e8117c
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_plugin_sqlite/main.cpp
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtSql>
+
+int main(int argc, char ** argv)
+{
+ QCoreApplication app(argc, argv);
+ QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
+ return db.isValid() ? 0 : 1;
+}
diff --git a/tests/auto/tools/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro b/tests/auto/tools/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro
new file mode 100644
index 0000000000..e8183d3cee
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_plugin_sqlite/plugin_sqlite.pro
@@ -0,0 +1,2 @@
+SOURCES = main.cpp
+QT += sql
diff --git a/tests/auto/tools/macdeployqt/source_plugin_tls/main.cpp b/tests/auto/tools/macdeployqt/source_plugin_tls/main.cpp
new file mode 100644
index 0000000000..7d1070b2c5
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_plugin_tls/main.cpp
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtNetwork>
+
+int main(int argc, char ** argv)
+{
+ QCoreApplication app(argc, argv);
+ return QSslSocket::supportsSsl() ? 0 : 1;
+}
diff --git a/tests/auto/tools/macdeployqt/source_plugin_tls/plugin_tls.pro b/tests/auto/tools/macdeployqt/source_plugin_tls/plugin_tls.pro
new file mode 100644
index 0000000000..23954f5941
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/source_plugin_tls/plugin_tls.pro
@@ -0,0 +1,2 @@
+SOURCES = main.cpp
+QT += network
diff --git a/tests/auto/tools/macdeployqt/tst_macdeployqt.cpp b/tests/auto/tools/macdeployqt/tst_macdeployqt.cpp
new file mode 100644
index 0000000000..8922025570
--- /dev/null
+++ b/tests/auto/tools/macdeployqt/tst_macdeployqt.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtTest>
+
+bool g_testDirectoryBuild = false; // toggle to keep build output for debugging.
+QTemporaryDir *g_temporaryDirectory;
+QString g_macdeployqtBinary;
+QString g_qmakeBinary;
+QString g_makeBinary;
+QString g_installNameToolBinary;
+
+#if QT_CONFIG(process)
+
+static const QString msgProcessError(const QProcess &process, const QString &what)
+{
+ QString result;
+ QTextStream(&result) << what << ": \"" << process.program() << ' '
+ << process.arguments().join(QLatin1Char(' ')) << "\": " << process.errorString();
+ return result;
+}
+
+static bool runProcess(const QString &binary,
+ const QStringList &arguments,
+ QString *errorMessage,
+ const QString &workingDir = QString(),
+ const QProcessEnvironment &env = QProcessEnvironment(),
+ int timeOut = 10000,
+ QByteArray *stdOut = nullptr, QByteArray *stdErr = nullptr)
+{
+ QProcess process;
+ if (!env.isEmpty())
+ process.setProcessEnvironment(env);
+ if (!workingDir.isEmpty())
+ process.setWorkingDirectory(workingDir);
+ process.start(binary, arguments, QIODevice::ReadOnly);
+ if (!process.waitForStarted()) {
+ *errorMessage = msgProcessError(process, "Failed to start");
+ return false;
+ }
+ if (!process.waitForFinished(timeOut)) {
+ *errorMessage = msgProcessError(process, "Timed out");
+ process.terminate();
+ if (!process.waitForFinished(300))
+ process.kill();
+ return false;
+ }
+ if (stdOut)
+ *stdOut = process.readAllStandardOutput();
+ if (stdErr)
+ *stdErr= process.readAllStandardError();
+ if (process.exitStatus() != QProcess::NormalExit) {
+ *errorMessage = msgProcessError(process, "Crashed");
+ return false;
+ }
+ if (process.exitCode() != QProcess::NormalExit) {
+ *errorMessage = msgProcessError(process, "Exit code " + QString::number(process.exitCode()));
+ return false;
+ }
+ return true;
+}
+
+#else
+
+static bool runProcess(const QString &binary,
+ const QStringList &arguments,
+ QString *arguments,
+ const QString &workingDir = QString(),
+ const QProcessEnvironment &env = QProcessEnvironment(),
+ int timeOut = 5000,
+ QByteArray *stdOut = Q_NULLPTR, QByteArray *stdErr = Q_NULLPTR)
+{
+ Q_UNUSED(binary);
+ Q_UNUSED(arguments);
+ Q_UNUSED(arguments);
+ Q_UNUSED(workingDir);
+ Q_UNUSED(env);
+ Q_UNUSED(timeOut);
+ Q_UNUSED(stdOut);
+ Q_UNUSED(stdErr);
+ return false;
+}
+
+#endif
+
+QString sourcePath(const QString &name)
+{
+ return "source_" + name;
+}
+
+QString buildPath(const QString &name)
+{
+ if (g_testDirectoryBuild)
+ return "build_" + name;
+ return g_temporaryDirectory->path() + "/build_" + name;
+}
+
+bool qmake(const QString &source, const QString &destination, QString *errorMessage)
+{
+ QStringList args = QStringList() << source;
+ return runProcess(g_qmakeBinary, args, errorMessage, destination);
+}
+
+bool make(const QString &destination, QString *errorMessage)
+{
+ QStringList args;
+ return runProcess(g_makeBinary, args, errorMessage, destination,
+ {}, 60000);
+}
+
+void build(const QString &name)
+{
+ // Build the app or framework according to the convention used
+ // by this test:
+ // source_name (source code)
+ // build_name (build artifacts)
+
+ QString source = sourcePath(name);
+ QString build = buildPath(name);
+ QString profile = name + ".pro";
+
+ QString sourcePath = QFINDTESTDATA(source);
+ QVERIFY(!sourcePath.isEmpty());
+
+ // Clear/set up build dir
+ QString buildPath = build;
+ QVERIFY(QDir(buildPath).removeRecursively());
+ QVERIFY(QDir().mkdir(buildPath));
+ QVERIFY(QDir(buildPath).exists());
+
+ // Build application
+ QString sourceProFile = QDir(sourcePath).canonicalPath() + '/' + profile;
+ QString errorMessage;
+ QVERIFY2(qmake(sourceProFile, buildPath, &errorMessage), qPrintable(errorMessage));
+ QVERIFY2(make(buildPath, &errorMessage), qPrintable(errorMessage));
+}
+
+bool changeInstallName(const QString &path, const QString &binary, const QString &from, const QString &to)
+{
+ QStringList args = QStringList() << binary << "-change" << from << to;
+ QString errorMessage;
+ return runProcess(g_installNameToolBinary, args, &errorMessage, path);
+}
+
+bool deploy(const QString &name, const QStringList &options, QString *errorMessage)
+{
+ QString bundle = name + ".app";
+ QString path = buildPath(name);
+ QStringList args = QStringList() << bundle << options;
+ return runProcess(g_macdeployqtBinary, args, errorMessage, path);
+}
+
+bool debugDeploy(const QString &name, const QStringList &options, QString *errorMessage)
+{
+ QString bundle = name + ".app";
+ QString path = buildPath(name);
+ QStringList args = QStringList() << bundle << options << "-verbose=3";
+ QByteArray stdOut;
+ QByteArray stdErr;
+ bool exitOK = runProcess(g_macdeployqtBinary, args, errorMessage, path, QProcessEnvironment(),
+ 10000, &stdOut, &stdErr);
+
+ qDebug() << "macdeployqt exit OK" << exitOK;
+ qDebug() << qPrintable(stdOut);
+ qDebug() << qPrintable(stdErr);
+
+ return exitOK;
+}
+
+bool run(const QString &name, QString *errorMessage)
+{
+ QString path = buildPath(name);
+ QStringList args;
+ QString binary = name + ".app/Contents/MacOS/" + name;
+ return runProcess(binary, args, errorMessage, path);
+}
+
+bool runPrintLibraries(const QString &name, QString *errorMessage, QByteArray *stdErr)
+{
+ QString binary = name + ".app/Contents/MacOS/" + name;
+ QString path = buildPath(name);
+ QStringList args;
+ QProcessEnvironment env = QProcessEnvironment();
+ env.insert("DYLD_PRINT_LIBRARIES", "true");
+ QByteArray stdOut;
+ return runProcess(binary, args, errorMessage, path, env, 5000, &stdOut, stdErr);
+}
+
+void runVerifyDeployment(const QString &name)
+{
+ QString errorMessage;
+ // Verify that the application runs after deployment and that it loads binaries from
+ // the application bundle only.
+ QByteArray libraries;
+ QVERIFY2(runPrintLibraries(name, &errorMessage, &libraries), qPrintable(errorMessage));
+ const QList<QString> parts = QString::fromLocal8Bit(libraries).split("dyld: loaded:");
+ const QString qtPath = QLibraryInfo::path(QLibraryInfo::PrefixPath);
+ // Let assume Qt is not installed in system
+ foreach (QString part, parts) {
+ part = part.trimmed();
+ if (part.isEmpty())
+ continue;
+ QVERIFY(!parts.startsWith(qtPath));
+ }
+}
+
+class tst_macdeployqt : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void basicapp();
+ void plugins_data();
+ void plugins();
+};
+
+void tst_macdeployqt::initTestCase()
+{
+#ifdef QT_NO_PROCESS
+ QSKIP("This test requires QProcess support");
+#endif
+
+ // Set up test-global unique temporary directory
+ g_temporaryDirectory = new QTemporaryDir();
+ QVERIFY(g_temporaryDirectory->isValid());
+
+ // Locate build and deployment tools
+ g_macdeployqtBinary = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/macdeployqt";
+ QVERIFY(!g_macdeployqtBinary.isEmpty());
+ g_qmakeBinary = QLibraryInfo::path(QLibraryInfo::BinariesPath) + "/qmake";
+ QVERIFY(!g_qmakeBinary.isEmpty());
+ g_makeBinary = QStandardPaths::findExecutable("make");
+ QVERIFY(!g_makeBinary.isEmpty());
+ g_installNameToolBinary = QStandardPaths::findExecutable("install_name_tool");
+ QVERIFY(!g_installNameToolBinary.isEmpty());
+}
+
+void tst_macdeployqt::cleanupTestCase()
+{
+ delete g_temporaryDirectory;
+}
+
+// Verify that deployment of a basic Qt Gui application works
+void tst_macdeployqt::basicapp()
+{
+#ifdef QT_NO_PROCESS
+ QSKIP("This test requires QProcess support");
+#endif
+
+ QString errorMessage;
+ QString name = "basicapp";
+
+ // Build and verify that the application runs before deployment
+ build(name);
+ QVERIFY2(run(name, &errorMessage), qPrintable(errorMessage));
+
+ // Deploy application
+ QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage));
+
+ // Verify deployment
+ runVerifyDeployment(name);
+}
+
+void tst_macdeployqt::plugins_data()
+{
+ QTest::addColumn<QString>("name");
+ QTest::newRow("sqlite") << "plugin_sqlite";
+ QTest::newRow("tls") << "plugin_tls";
+}
+
+void tst_macdeployqt::plugins()
+{
+ QFETCH(QString, name);
+
+ build(name);
+
+ // Verify that the test app runs before deployment.
+ QString errorMessage;
+ if (!run(name, &errorMessage)) {
+ qDebug() << qPrintable(errorMessage);
+ QSKIP("Could not run test application before deployment");
+ }
+
+ QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage));
+ runVerifyDeployment(name);
+}
+
+QTEST_MAIN(tst_macdeployqt)
+#include "tst_macdeployqt.moc"
diff --git a/tests/auto/tools/windeployqt/CMakeLists.txt b/tests/auto/tools/windeployqt/CMakeLists.txt
new file mode 100644
index 0000000000..5eae1ca164
--- /dev/null
+++ b/tests/auto/tools/windeployqt/CMakeLists.txt
@@ -0,0 +1,4 @@
+# Generated from windeployqt.pro.
+
+add_subdirectory(testapp)
+add_subdirectory(test)
diff --git a/tests/auto/tools/windeployqt/test/CMakeLists.txt b/tests/auto/tools/windeployqt/test/CMakeLists.txt
new file mode 100644
index 0000000000..d412249a7f
--- /dev/null
+++ b/tests/auto/tools/windeployqt/test/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Generated from test.pro.
+
+#####################################################################
+## tst_windeployqt Test:
+#####################################################################
+
+qt_internal_add_test(tst_windeployqt
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" # special case
+ SOURCES
+ ../tst_windeployqt.cpp
+)
diff --git a/tests/auto/tools/windeployqt/testapp/CMakeLists.txt b/tests/auto/tools/windeployqt/testapp/CMakeLists.txt
new file mode 100644
index 0000000000..83851dae65
--- /dev/null
+++ b/tests/auto/tools/windeployqt/testapp/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Generated from testapp.pro.
+
+#####################################################################
+## testapp Binary:
+#####################################################################
+
+qt_internal_add_executable(windeploy_testapp # special case
+ GUI
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/"
+ SOURCES
+ main.cpp
+ PUBLIC_LIBRARIES
+ Qt::Gui
+)
+
+# special case begin
+set_target_properties(windeploy_testapp
+ PROPERTIES
+ OUTPUT_NAME testapp
+)
+# special case end
diff --git a/tests/auto/tools/windeployqt/testapp/main.cpp b/tests/auto/tools/windeployqt/testapp/main.cpp
new file mode 100644
index 0000000000..4dc03cdad4
--- /dev/null
+++ b/tests/auto/tools/windeployqt/testapp/main.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QRasterWindow>
+#include <QScreen>
+#include <QTimer>
+
+// Simple test application just to verify that it comes up properly
+
+int main(int argc, char ** argv)
+{
+ QGuiApplication app(argc, argv);
+ QRasterWindow w;
+ w.setTitle("windeployqt test application");
+ const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
+ w.resize(availableGeometry.size() / 4);
+ w.show();
+ QTimer::singleShot(200, &w, &QCoreApplication::quit);
+ return app.exec();
+}
diff --git a/tests/auto/tools/windeployqt/tst_windeployqt.cpp b/tests/auto/tools/windeployqt/tst_windeployqt.cpp
new file mode 100644
index 0000000000..38403c90fc
--- /dev/null
+++ b/tests/auto/tools/windeployqt/tst_windeployqt.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QProcess>
+#include <QtCore/QProcessEnvironment>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QTextStream>
+#include <QtTest/QtTest>
+
+static const QString msgProcessError(const QProcess &process, const QString &what,
+ const QByteArray &stdOut = QByteArray(),
+ const QByteArray &stdErr = QByteArray())
+{
+ QString result;
+ QTextStream str(&result);
+ str << what << ": \"" << process.program() << ' '
+ << process.arguments().join(QLatin1Char(' ')) << "\": " << process.errorString();
+ if (!stdOut.isEmpty())
+ str << "\nStandard output:\n" << stdOut;
+ if (!stdErr.isEmpty())
+ str << "\nStandard error:\n" << stdErr;
+ return result;
+}
+
+static bool runProcess(const QString &binary,
+ const QStringList &arguments,
+ QString *errorMessage,
+ const QString &workingDir = QString(),
+ const QProcessEnvironment &env = QProcessEnvironment(),
+ int timeOut = 5000,
+ QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr)
+{
+ QProcess process;
+ if (!env.isEmpty())
+ process.setProcessEnvironment(env);
+ if (!workingDir.isEmpty())
+ process.setWorkingDirectory(workingDir);
+ qDebug().noquote().nospace() << "Running: " << QDir::toNativeSeparators(binary)
+ << ' ' << arguments.join(QLatin1Char(' '));
+ process.start(binary, arguments, QIODevice::ReadOnly);
+ if (!process.waitForStarted()) {
+ *errorMessage = msgProcessError(process, "Failed to start");
+ return false;
+ }
+ if (!process.waitForFinished(timeOut)) {
+ *errorMessage = msgProcessError(process, "Timed out");
+ process.terminate();
+ if (!process.waitForFinished(300))
+ process.kill();
+ return false;
+ }
+ const QByteArray stdOut = process.readAllStandardOutput();
+ const QByteArray stdErr = process.readAllStandardError();
+ if (stdOutIn)
+ *stdOutIn = stdOut;
+ if (stdErrIn)
+ *stdErrIn = stdErr;
+ if (process.exitStatus() != QProcess::NormalExit) {
+ *errorMessage = msgProcessError(process, "Crashed", stdOut, stdErr);
+ return false;
+ }
+ if (process.exitCode() != QProcess::NormalExit) {
+ *errorMessage = msgProcessError(process, "Exit code " + QString::number(process.exitCode()),
+ stdOut, stdErr);
+ return false;
+ }
+ return true;
+}
+
+class tst_windeployqt : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void help();
+ void deploy();
+
+private:
+ QString m_windeployqtBinary;
+ QString m_testApp;
+ QString m_testAppBinary;
+};
+
+void tst_windeployqt::initTestCase()
+{
+ m_windeployqtBinary = QStandardPaths::findExecutable("windeployqt");
+ QVERIFY(!m_windeployqtBinary.isEmpty());
+ m_testApp = QFINDTESTDATA("testapp");
+ QVERIFY(!m_testApp.isEmpty());
+ const QFileInfo testAppBinary(m_testApp + QLatin1String("/testapp.exe"));
+ QVERIFY2(testAppBinary.isFile(), qPrintable(testAppBinary.absoluteFilePath()));
+ m_testAppBinary = testAppBinary.absoluteFilePath();
+}
+
+void tst_windeployqt::help()
+{
+ QString errorMessage;
+ QByteArray stdOut;
+ QByteArray stdErr;
+ QVERIFY2(runProcess(m_windeployqtBinary, QStringList("--help"), &errorMessage,
+ QString(), QProcessEnvironment(), 5000, &stdOut, &stdErr),
+ qPrintable(errorMessage));
+ QVERIFY2(!stdOut.isEmpty(), stdErr);
+}
+
+// deploy(): Deploys the test application and launches it with Qt removed from the environment
+// to verify it runs stand-alone.
+
+void tst_windeployqt::deploy()
+{
+ QString errorMessage;
+ // Deploy application
+ QStringList deployArguments;
+ deployArguments << QLatin1String("--no-translations") << QDir::toNativeSeparators(m_testAppBinary);
+ QVERIFY2(runProcess(m_windeployqtBinary, deployArguments, &errorMessage, QString(), QProcessEnvironment(), 20000),
+ qPrintable(errorMessage));
+
+ // Create environment with Qt and all "lib" paths removed.
+ const QString qtBinDir = QDir::toNativeSeparators(QLibraryInfo::path(QLibraryInfo::BinariesPath));
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ const QString pathKey = QLatin1String("PATH");
+ const QChar pathSeparator(QLatin1Char(';')); // ### fixme: Qt 5.6: QDir::listSeparator()
+ const QString origPath = env.value(pathKey);
+ QString newPath;
+ const QStringList pathElements = origPath.split(pathSeparator, Qt::SkipEmptyParts);
+ for (const QString &pathElement : pathElements) {
+ if (pathElement.compare(qtBinDir, Qt::CaseInsensitive)
+ && !pathElement.contains(QLatin1String("\\lib"), Qt::CaseInsensitive)) {
+ if (!newPath.isEmpty())
+ newPath.append(pathSeparator);
+ newPath.append(pathElement);
+ }
+ }
+ if (newPath == origPath)
+ qWarning() << "Unable to remove Qt from PATH";
+ env.insert(pathKey, newPath);
+
+ // Create qt.conf to enforce usage of local plugins
+ QFile qtConf(QFileInfo(m_testAppBinary).absolutePath() + QLatin1String("/qt.conf"));
+ QVERIFY2(qtConf.open(QIODevice::WriteOnly | QIODevice::Text),
+ qPrintable(qtConf.fileName() + QLatin1String(": ") + qtConf.errorString()));
+ QVERIFY(qtConf.write("[Paths]\nPrefix = .\n"));
+ qtConf.close();
+
+ // Verify that application still runs
+ QVERIFY2(runProcess(m_testAppBinary, QStringList(), &errorMessage, QString(), env, 10000),
+ qPrintable(errorMessage));
+}
+
+QTEST_MAIN(tst_windeployqt)
+#include "tst_windeployqt.moc"