summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/macdeployqt/macdeployqt.pro4
-rw-r--r--tests/auto/macdeployqt/source_basicapp/basicapp.pro1
-rw-r--r--tests/auto/macdeployqt/source_basicapp/main.cpp44
-rw-r--r--tests/auto/macdeployqt/tst_macdeployqt.cpp289
5 files changed, 342 insertions, 2 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 0d3966a7e..742075325 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -11,7 +11,8 @@ SUBDIRS=\
cmake \
installed_cmake \
qtdiag \
- windeployqt
+ windeployqt \
+ macdeployqt
installed_cmake.depends = cmake
@@ -29,5 +30,6 @@ cross_compile:SUBDIRS -= qhelpcontentmodel qhelpenginecore qhelpindexmodel qhelp
qhelpindexmodel \
qhelpprojectdata \
-!qtConfig(process): SUBDIRS -= qtattributionsscanner linguist qtdiag windeployqt
+!qtConfig(process): SUBDIRS -= qtattributionsscanner linguist qtdiag windeployqt macdeployqt
!win32|winrt: SUBDIRS -= windeployqt
+!macos: SUBDIRS -= macdeployqt
diff --git a/tests/auto/macdeployqt/macdeployqt.pro b/tests/auto/macdeployqt/macdeployqt.pro
new file mode 100644
index 000000000..bec1d1ce4
--- /dev/null
+++ b/tests/auto/macdeployqt/macdeployqt.pro
@@ -0,0 +1,4 @@
+CONFIG += testcase
+QT = core testlib
+TARGET = tst_macdeployqt
+SOURCES += tst_macdeployqt.cpp
diff --git a/tests/auto/macdeployqt/source_basicapp/basicapp.pro b/tests/auto/macdeployqt/source_basicapp/basicapp.pro
new file mode 100644
index 000000000..bba41b9c1
--- /dev/null
+++ b/tests/auto/macdeployqt/source_basicapp/basicapp.pro
@@ -0,0 +1 @@
+SOURCES = main.cpp
diff --git a/tests/auto/macdeployqt/source_basicapp/main.cpp b/tests/auto/macdeployqt/source_basicapp/main.cpp
new file mode 100644
index 000000000..093a882f3
--- /dev/null
+++ b/tests/auto/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/macdeployqt/tst_macdeployqt.cpp b/tests/auto/macdeployqt/tst_macdeployqt.cpp
new file mode 100644
index 000000000..3ce60768c
--- /dev/null
+++ b/tests/auto/macdeployqt/tst_macdeployqt.cpp
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** 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);
+}
+
+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::location(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 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::location(QLibraryInfo::BinariesPath) + "/macdeployqt";
+ QVERIFY(!g_macdeployqtBinary.isEmpty());
+ g_qmakeBinary = QLibraryInfo::location(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);
+}
+
+QTEST_MAIN(tst_macdeployqt)
+#include "tst_macdeployqt.moc"