QtBase  v6.3.1
tst_macdeployqt.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include <QtCore>
30 #include <QtTest>
31 
32 bool g_testDirectoryBuild = false; // toggle to keep build output for debugging.
38 
39 #if QT_CONFIG(process)
40 
41 static const QString msgProcessError(const QProcess &process, const QString &what)
42 {
44  QTextStream(&result) << what << ": \"" << process.program() << ' '
45  << process.arguments().join(QLatin1Char(' ')) << "\": " << process.errorString();
46  return result;
47 }
48 
49 static bool runProcess(const QString &binary,
50  const QStringList &arguments,
51  QString *errorMessage,
52  const QString &workingDir = QString(),
54  int timeOut = 10000,
55  QByteArray *stdOut = nullptr, QByteArray *stdErr = nullptr)
56 {
57  QProcess process;
58  if (!env.isEmpty())
59  process.setProcessEnvironment(env);
60  if (!workingDir.isEmpty())
61  process.setWorkingDirectory(workingDir);
62  process.start(binary, arguments, QIODevice::ReadOnly);
63  if (!process.waitForStarted()) {
64  *errorMessage = msgProcessError(process, "Failed to start");
65  return false;
66  }
67  if (!process.waitForFinished(timeOut)) {
68  *errorMessage = msgProcessError(process, "Timed out");
69  process.terminate();
70  if (!process.waitForFinished(300))
71  process.kill();
72  return false;
73  }
74  if (stdOut)
75  *stdOut = process.readAllStandardOutput();
76  if (stdErr)
77  *stdErr= process.readAllStandardError();
78  if (process.exitStatus() != QProcess::NormalExit) {
79  *errorMessage = msgProcessError(process, "Crashed");
80  return false;
81  }
82  if (process.exitCode() != QProcess::NormalExit) {
83  *errorMessage = msgProcessError(process, "Exit code " + QString::number(process.exitCode()));
84  return false;
85  }
86  return true;
87 }
88 
89 #else
90 
91 static bool runProcess(const QString &binary,
92  const QStringList &arguments,
94  const QString &workingDir = QString(),
96  int timeOut = 5000,
97  QByteArray *stdOut = Q_NULLPTR, QByteArray *stdErr = Q_NULLPTR)
98 {
102  Q_UNUSED(workingDir);
103  Q_UNUSED(env);
104  Q_UNUSED(timeOut);
105  Q_UNUSED(stdOut);
106  Q_UNUSED(stdErr);
107  return false;
108 }
109 
110 #endif
111 
113 {
114  return "source_" + name;
115 }
116 
118 {
120  return "build_" + name;
121  return g_temporaryDirectory->path() + "/build_" + name;
122 }
123 
124 bool qmake(const QString &source, const QString &destination, QString *errorMessage)
125 {
127  return runProcess(g_qmakeBinary, args, errorMessage, destination);
128 }
129 
130 bool make(const QString &destination, QString *errorMessage)
131 {
133  return runProcess(g_makeBinary, args, errorMessage, destination,
134  {}, 60000);
135 }
136 
137 void build(const QString &name)
138 {
139  // Build the app or framework according to the convention used
140  // by this test:
141  // source_name (source code)
142  // build_name (build artifacts)
143 
146  QString profile = name + ".pro";
147 
150 
151  // Clear/set up build dir
153  QVERIFY(QDir(buildPath).removeRecursively());
154  QVERIFY(QDir().mkdir(buildPath));
155  QVERIFY(QDir(buildPath).exists());
156 
157  // Build application
158  QString sourceProFile = QDir(sourcePath).canonicalPath() + '/' + profile;
159  QString errorMessage;
160  QVERIFY2(qmake(sourceProFile, buildPath, &errorMessage), qPrintable(errorMessage));
161  QVERIFY2(make(buildPath, &errorMessage), qPrintable(errorMessage));
162 }
163 
164 bool changeInstallName(const QString &path, const QString &binary, const QString &from, const QString &to)
165 {
166  QStringList args = QStringList() << binary << "-change" << from << to;
167  QString errorMessage;
168  return runProcess(g_installNameToolBinary, args, &errorMessage, path);
169 }
170 
171 bool deploy(const QString &name, const QStringList &options, QString *errorMessage)
172 {
173  QString bundle = name + ".app";
175  QStringList args = QStringList() << bundle << options;
176  return runProcess(g_macdeployqtBinary, args, errorMessage, path);
177 }
178 
179 bool debugDeploy(const QString &name, const QStringList &options, QString *errorMessage)
180 {
181  QString bundle = name + ".app";
183  QStringList args = QStringList() << bundle << options << "-verbose=3";
184  QByteArray stdOut;
185  QByteArray stdErr;
186  bool exitOK = runProcess(g_macdeployqtBinary, args, errorMessage, path, QProcessEnvironment(),
187  10000, &stdOut, &stdErr);
188 
189  qDebug() << "macdeployqt exit OK" << exitOK;
190  qDebug() << qPrintable(stdOut);
191  qDebug() << qPrintable(stdErr);
192 
193  return exitOK;
194 }
195 
196 bool run(const QString &name, QString *errorMessage)
197 {
200  QString binary = name + ".app/Contents/MacOS/" + name;
201  return runProcess(binary, args, errorMessage, path);
202 }
203 
204 bool runPrintLibraries(const QString &name, QString *errorMessage, QByteArray *stdErr)
205 {
206  QString binary = name + ".app/Contents/MacOS/" + name;
210  env.insert("DYLD_PRINT_LIBRARIES", "true");
211  QByteArray stdOut;
212  return runProcess(binary, args, errorMessage, path, env, 5000, &stdOut, stdErr);
213 }
214 
216 {
217  QString errorMessage;
218  // Verify that the application runs after deployment and that it loads binaries from
219  // the application bundle only.
220  QByteArray libraries;
221  QVERIFY2(runPrintLibraries(name, &errorMessage, &libraries), qPrintable(errorMessage));
222  const QList<QString> parts = QString::fromLocal8Bit(libraries).split("dyld: loaded:");
224  // Let assume Qt is not installed in system
225  foreach (QString part, parts) {
226  part = part.trimmed();
227  if (part.isEmpty())
228  continue;
229  QVERIFY(!parts.startsWith(qtPath));
230  }
231 }
232 
233 class tst_macdeployqt : public QObject
234 {
235  Q_OBJECT
236 private slots:
237  void initTestCase();
238  void cleanupTestCase();
239  void basicapp();
240  void plugins_data();
241  void plugins();
242 };
243 
244 void tst_macdeployqt::initTestCase()
245 {
246 #ifdef QT_NO_PROCESS
247  QSKIP("This test requires QProcess support");
248 #endif
249 
250  // Set up test-global unique temporary directory
253 
254  // Locate build and deployment tools
263 }
264 
265 void tst_macdeployqt::cleanupTestCase()
266 {
267  delete g_temporaryDirectory;
268 }
269 
270 // Verify that deployment of a basic Qt Gui application works
271 void tst_macdeployqt::basicapp()
272 {
273 #ifdef QT_NO_PROCESS
274  QSKIP("This test requires QProcess support");
275 #endif
276 
277  QString errorMessage;
278  QString name = "basicapp";
279 
280  // Build and verify that the application runs before deployment
281  build(name);
282  QVERIFY2(run(name, &errorMessage), qPrintable(errorMessage));
283 
284  // Deploy application
285  QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage));
286 
287  // Verify deployment
289 }
290 
291 void tst_macdeployqt::plugins_data()
292 {
293  QTest::addColumn<QString>("name");
294  QTest::newRow("sqlite") << "plugin_sqlite";
295  QTest::newRow("tls") << "plugin_tls";
296 }
297 
298 void tst_macdeployqt::plugins()
299 {
300  QFETCH(QString, name);
301 
302  build(name);
303 
304  // Verify that the test app runs before deployment.
305  QString errorMessage;
306  if (!run(name, &errorMessage)) {
307  qDebug() << qPrintable(errorMessage);
308  QSKIP("Could not run test application before deployment");
309  }
310 
311  QVERIFY2(deploy(name, QStringList(), &errorMessage), qPrintable(errorMessage));
313 }
314 
316 #include "tst_macdeployqt.moc"
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:55
QString canonicalPath() const
Definition: qdir.cpp:683
static QString path(LibraryPath p)
bool startsWith(parameter_type t) const
Definition: qlist.h:649
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
The QProcessEnvironment class holds the environment variables that can be passed to a program.
Definition: qprocess.h:66
void insert(const QString &name, const QString &value)
Definition: qprocess.cpp:317
static QString findExecutable(const QString &executableName, const QStringList &paths=QStringList())
The QString class provides a Unicode character string.
Definition: qstring.h:388
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:7672
static QString fromLocal8Bit(QByteArrayView ba)
Definition: qstring.cpp:5563
bool isEmpty() const
Definition: qstring.h:1216
static QString number(int, int base=10)
Definition: qstring.cpp:7538
The QStringList class provides a list of strings.
The QTemporaryDir class creates a unique directory for temporary use.
Definition: qtemporarydir.h:54
QString path() const
bool isValid() const
The QTextStream class provides a convenient interface for reading and writing text.
Definition: qtextstream.h:62
QList< QVariant > arguments
part
Definition: enum_inc.h:2
Q_TESTLIB_EXPORT QTestData & newRow(const char *dataTag)
Definition: qtestcase.cpp:2658
#define QString()
Definition: parse-defines.h:51
#define Q_NULLPTR
QList< QString > QStringList
Definition: qcontainerfwd.h:64
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
#define qDebug
[1]
Definition: qlogging.h:177
GLsizei GLsizei GLenum void * binary
GLuint name
GLsizei GLsizei GLchar * source
GLsizei const GLchar *const * path
Definition: qopenglext.h:4283
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
#define QTEST_MAIN(TestObject)
Definition: qtest.h:664
#define QSKIP(statement,...)
Definition: qtestcase.h:222
#define QFETCH(Type, name)
Definition: qtestcase.h:230
#define QVERIFY(statement)
Definition: qtestcase.h:64
#define QFINDTESTDATA(basepath)
Definition: qtestcase.h:251
#define QVERIFY2(statement, description)
Definition: qtestcase.h:76
#define Q_OBJECT
Definition: qtmetamacros.h:158
#define slots
Definition: qtmetamacros.h:76
Q_UNUSED(salary)
[21]
bool runProcess(const QString &binary, const QStringList &args, const QString &workingDirectory, unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr, QString *errorMessage)
Definition: utils.cpp:346
QString bundle
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
QString sourcePath(const QString &name)
bool changeInstallName(const QString &path, const QString &binary, const QString &from, const QString &to)
bool debugDeploy(const QString &name, const QStringList &options, QString *errorMessage)
void build(const QString &name)
bool runPrintLibraries(const QString &name, QString *errorMessage, QByteArray *stdErr)
void runVerifyDeployment(const QString &name)
QTemporaryDir * g_temporaryDirectory
QString g_makeBinary
bool qmake(const QString &source, const QString &destination, QString *errorMessage)
bool make(const QString &destination, QString *errorMessage)
bool deploy(const QString &name, const QStringList &options, QString *errorMessage)
bool run(const QString &name, QString *errorMessage)
bool g_testDirectoryBuild
QString g_installNameToolBinary
QString g_macdeployqtBinary
QString g_qmakeBinary
QString buildPath(const QString &name)