summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2016-09-15 14:07:17 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2020-10-26 13:27:36 +0100
commit4aa4e4f727233d8b620b2d591311c90cb8bed39b (patch)
treefeb299631e79d75ec555ff12021b0cfeda505d91
parent72b5a4dcae9e9be4a6d66884d3cc0bfb2d692477 (diff)
Add macdeployqt autotest
Add testing framework and test that builds, deploys, and verifies deployment of simple and complex app bundles. Based on the windeployqt autotest. Change-Id: If02321daa77bde3a787372656a5816df78f4b13e Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io> (cherry picked from commit e0c08b3bfd0d0a92a8d60991260ee377357fa94e)
-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"