Run prebuilt android test APKs

androidtestrunner now checks is the apk is build and if it is, it will skip the build phase.
Now we can build the apks in parallel (which takes most of the time) and run them sequentially.
This way running tests on Android is much faster.

Change-Id: I82f34723ac08f7728cc0daab3366e03821335eed
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
BogDan Vatra 2019-06-06 15:29:01 +03:00 committed by BogDan Vatra
parent d73497cf77
commit 079dafc42f
9 changed files with 143 additions and 22 deletions

View File

@ -54,6 +54,7 @@ debug_and_release:debug_and_release_target {
# Allow for a custom test runner script
android: isEmpty($(TESTRUNNER)) {
APK_PATH = $$shell_path($$OUT_PWD/android-build/$${TARGET}.apk)
qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner)
qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt)
isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json
@ -62,6 +63,7 @@ android: isEmpty($(TESTRUNNER)) {
$${type}.commands += --path \"$$OUT_PWD/android-build\"
$${type}.commands += --adb \"$$shell_path($${ANDROID_SDK_ROOT}$${QMAKE_DIR_SEP}platform-tools$${QMAKE_DIR_SEP}adb$${extension})\"
$${type}.commands += --make \"$(MAKE) -f $(MAKEFILE)\"
$${type}.commands += --apk $$APK_PATH
} else: $${type}.commands += $(TESTRUNNER)
unix {

View File

@ -150,11 +150,18 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\sa setKey()
*/
#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
{
}
#else
QSharedMemory::QSharedMemory()
: d_ptr(new QSharedMemoryPrivate)
{
}
#endif
/*!
Constructs a shared memory object with the given \a parent and with
its key set to \a key. Because its key is set, its create() and
@ -162,11 +169,19 @@ QSharedMemory::QSharedMemory(QObject *parent)
\sa setKey(), create(), attach()
*/
#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
{
setKey(key);
}
#else
QSharedMemory::QSharedMemory(const QString &key)
: d_ptr(new QSharedMemoryPrivate)
{
setKey(key);
}
#endif
/*!
The destructor clears the key, which forces the shared memory object
@ -604,4 +619,6 @@ QString QSharedMemory::errorString() const
QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
#include "moc_qsharedmemory.cpp"
#endif

View File

@ -40,8 +40,14 @@
#ifndef QSHAREDMEMORY_H
#define QSHAREDMEMORY_H
#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
#ifndef QT_NO_QOBJECT
# include <QtCore/qobject.h>
#else
# include <QtCore/qobjectdefs.h>
# include <QtCore/qscopedpointer.h>
# include <QtCore/qstring.h>
#endif
QT_BEGIN_NAMESPACE
@ -49,9 +55,14 @@ QT_BEGIN_NAMESPACE
class QSharedMemoryPrivate;
class Q_CORE_EXPORT QSharedMemory : public QObject
class Q_CORE_EXPORT QSharedMemory
#ifndef QT_NO_QOBJECT
: public QObject
#endif
{
#ifndef QT_NO_QOBJECT
Q_OBJECT
#endif
Q_DECLARE_PRIVATE(QSharedMemory)
public:
@ -74,8 +85,17 @@ public:
UnknownError
};
#ifndef QT_NO_QOBJECT
QSharedMemory(QObject *parent = nullptr);
QSharedMemory(const QString &key, QObject *parent = nullptr);
#else
QSharedMemory();
QSharedMemory(const QString &key);
static QString tr(const char * str)
{
return QString::fromLatin1(str);
}
#endif
~QSharedMemory();
void setKey(const QString &key);
@ -104,6 +124,9 @@ public:
private:
Q_DISABLE_COPY(QSharedMemory)
#ifdef QT_NO_QOBJECT
QScopedPointer<QSharedMemoryPrivate> d_ptr;
#endif
};
#endif // QT_NO_SHAREDMEMORY

View File

@ -67,7 +67,10 @@ namespace QSharedMemoryPrivate
#else
#include "qsystemsemaphore.h"
#include "private/qobject_p.h"
#ifndef QT_NO_QOBJECT
# include "private/qobject_p.h"
#endif
#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_RTEMS)
# include <sys/sem.h>
@ -107,9 +110,14 @@ private:
};
#endif // QT_NO_SYSTEMSEMAPHORE
class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
class Q_AUTOTEST_EXPORT QSharedMemoryPrivate
#ifndef QT_NO_QOBJECT
: public QObjectPrivate
#endif
{
#ifndef QT_NO_QOBJECT
Q_DECLARE_PUBLIC(QSharedMemory)
#endif
public:
QSharedMemoryPrivate();

View File

@ -64,8 +64,11 @@
#ifndef QT_NO_SHAREDMEMORY
QT_BEGIN_NAMESPACE
QSharedMemoryPrivate::QSharedMemoryPrivate()
: QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
QSharedMemoryPrivate::QSharedMemoryPrivate() :
#ifndef QT_NO_QOBJECT
QObjectPrivate(),
#endif
memory(0), size(0), error(QSharedMemory::NoError),
#ifndef QT_NO_SYSTEMSEMAPHORE
systemSemaphore(QString()), lockedByMe(false),
#endif

View File

@ -47,7 +47,10 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_SHAREDMEMORY
QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(),
QSharedMemoryPrivate::QSharedMemoryPrivate() :
#ifndef QT_NO_QOBJECT
QObjectPrivate(),
#endif
memory(0), size(0), error(QSharedMemory::NoError),
systemSemaphore(QString()), lockedByMe(false), hand(0)
{

View File

@ -209,6 +209,7 @@ struct Options
bool sectionsOnly;
bool protectedAuthenticationPath;
bool jarSigner;
QString apkPath;
// Gdbserver
TriState gdbServer;
@ -396,6 +397,11 @@ Options parseOptions()
options.helpRequested = true;
else
options.jdkPath = arguments.at(++i);
} else if (argument.compare(QLatin1String("--apk"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
options.helpRequested = true;
else
options.apkPath = arguments.at(++i);
} else if (argument.compare(QLatin1String("--sign"), Qt::CaseInsensitive) == 0) {
if (i + 2 >= arguments.size()) {
options.helpRequested = true;
@ -544,6 +550,7 @@ void printHelp()
" dependencies into the build directory and update the XML templates.\n"
" The project will not be built or installed.\n"
" --no-strip: Do not strip debug symbols from libraries.\n"
" --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.\n"
" --help: Displays this information.\n\n",
qPrintable(QCoreApplication::arguments().at(0))
);
@ -2389,6 +2396,14 @@ bool installApk(const Options &options)
return true;
}
bool copyPackage(const Options &options)
{
fflush(stdout);
auto from = apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK);
QFile::remove(options.apkPath);
return QFile::copy(from, options.apkPath);
}
bool copyStdCpp(Options *options)
{
if (options->verbose)
@ -2759,7 +2774,8 @@ enum ErrorCode
CannotSignPackage = 15,
CannotInstallApk = 16,
CannotGenerateAssetsFileList = 18,
CannotCopyAndroidExtraResources = 19
CannotCopyAndroidExtraResources = 19,
CannotCopyApk = 20
};
int main(int argc, char *argv[])
@ -2902,6 +2918,9 @@ int main(int argc, char *argv[])
if (!options.keyStore.isEmpty() && !signPackage(options))
return CannotSignPackage;
if (!options.apkPath.isEmpty() && !copyPackage(options))
return CannotCopyApk;
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Signed package\n", options.timer.elapsed());
}

View File

@ -31,6 +31,7 @@
#include <QDir>
#include <QHash>
#include <QRegExp>
#include <QSystemSemaphore>
#include <QXmlStreamReader>
#include <algorithm>
@ -60,6 +61,7 @@ struct Options
QStringList testArgsList;
QHash<QString, QString> outFiles;
QString testArgs;
QString apkPath;
QHash<QString, std::function<bool(const QByteArray &)>> checkFiles = {
{QStringLiteral("txt"), [](const QByteArray &data) -> bool {
return data.indexOf("\nFAIL! : ") < 0;
@ -227,6 +229,11 @@ static bool parseOptions()
g_options.helpRequested = true;
else
g_options.makeCommand = arguments.at(++i);
} else if (argument.compare(QStringLiteral("--apk"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.apkPath = arguments.at(++i);
} else if (argument.compare(QStringLiteral("--activity"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
@ -280,6 +287,8 @@ static void printHelp()
" Default is 5 minutes.\n"
" --make <make cmd>: make command, needed to install the qt library.\n"
" If make is missing make sure the --path is set.\n"
" --apk <apk path>: If the apk is specified and if exists, we'll skip\n"
" the package building.\n"
" -- arguments that will be passed to the test application.\n"
" --verbose: Prints out information during processing.\n"
" --help: Displays this information.\n\n",
@ -420,6 +429,19 @@ static bool pullFiles()
return ret;
}
struct RunnerLocker
{
RunnerLocker()
{
runner.acquire();
}
~RunnerLocker()
{
runner.release();
}
QSystemSemaphore runner{QStringLiteral("androidtestrunner"), 1, QSystemSemaphore::Open};
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
@ -428,19 +450,29 @@ int main(int argc, char *argv[])
return 1;
}
if (!g_options.makeCommand.isEmpty()) {
// we need to run make INSTALL_ROOT=path install to install the application file(s) first
if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install")
.arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) {
RunnerLocker lock; // do not install or run packages while another test is running
if (!g_options.apkPath.isEmpty() && QFile::exists(g_options.apkPath)) {
if (!execCommand(QStringLiteral("%1 install -r %2")
.arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) {
return 1;
}
} else {
if (!g_options.makeCommand.isEmpty()) {
// we need to run make INSTALL_ROOT=path install to install the application file(s) first
if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install")
.arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) {
return 1;
}
}
// Run androiddeployqt
static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral();
if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2 --apk %4").arg(g_options.androidDeployQtCommand,
g_options.buildPath,
verbose,
g_options.apkPath), nullptr, true)) {
return 1;
}
}
// Run androiddeployqt
static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral();
if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2").arg(g_options.androidDeployQtCommand,
g_options.buildPath,
verbose), nullptr, g_options.verbose)) {
return 1;
}
QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml");

View File

@ -60,6 +60,8 @@ SOURCES += \
../../corelib/kernel/qmetatype.cpp \
../../corelib/kernel/qvariant.cpp \
../../corelib/kernel/qsystemerror.cpp \
../../corelib/kernel/qsharedmemory.cpp \
../../corelib/kernel/qsystemsemaphore.cpp \
../../corelib/plugin/quuid.cpp \
../../corelib/serialization/qdatastream.cpp \
../../corelib/serialization/qjson.cpp \
@ -103,6 +105,12 @@ SOURCES += \
../../xml/sax/qxml.cpp
unix:SOURCES += ../../corelib/kernel/qcore_unix.cpp \
../../corelib/kernel/qsharedmemory_posix.cpp \
../../corelib/kernel/qsharedmemory_systemv.cpp \
../../corelib/kernel/qsharedmemory_unix.cpp \
../../corelib/kernel/qsystemsemaphore_posix.cpp \
../../corelib/kernel/qsystemsemaphore_systemv.cpp \
../../corelib/kernel/qsystemsemaphore_unix.cpp \
../../corelib/io/qfilesystemengine_unix.cpp \
../../corelib/io/qfilesystemiterator_unix.cpp \
../../corelib/io/qfsfileengine_unix.cpp
@ -112,12 +120,18 @@ win32:SOURCES += ../../corelib/global/qoperatingsystemversion_win.cpp \
../../corelib/io/qfilesystemiterator_win.cpp \
../../corelib/io/qfsfileengine_win.cpp \
../../corelib/kernel/qcoreapplication_win.cpp \
../../corelib/kernel/qsharedmemory_win.cpp \
../../corelib/kernel/qsystemsemaphore_win.cpp \
../../corelib/plugin/qsystemlibrary.cpp \
mac {
SOURCES += \
../../corelib/kernel/qcoreapplication_mac.cpp \
../../corelib/kernel/qcore_mac.cpp
../../corelib/kernel/qcore_mac.cpp \
../../corelib/io/qfilesystemengine_unix.cpp \
../../corelib/io/qfilesystemiterator_unix.cpp \
../../corelib/io/qfsfileengine_unix.cpp
OBJECTIVE_SOURCES += \
../../corelib/global/qoperatingsystemversion_darwin.mm \
../../corelib/kernel/qcore_mac_objc.mm \