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 # Allow for a custom test runner script
android: isEmpty($(TESTRUNNER)) { android: isEmpty($(TESTRUNNER)) {
APK_PATH = $$shell_path($$OUT_PWD/android-build/$${TARGET}.apk)
qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner) qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner)
qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt) qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt)
isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json 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 += --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 += --adb \"$$shell_path($${ANDROID_SDK_ROOT}$${QMAKE_DIR_SEP}platform-tools$${QMAKE_DIR_SEP}adb$${extension})\"
$${type}.commands += --make \"$(MAKE) -f $(MAKEFILE)\" $${type}.commands += --make \"$(MAKE) -f $(MAKEFILE)\"
$${type}.commands += --apk $$APK_PATH
} else: $${type}.commands += $(TESTRUNNER) } else: $${type}.commands += $(TESTRUNNER)
unix { unix {

View File

@ -150,11 +150,18 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\sa setKey() \sa setKey()
*/ */
#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(QObject *parent) QSharedMemory::QSharedMemory(QObject *parent)
: QObject(*new QSharedMemoryPrivate, 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 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 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() \sa setKey(), create(), attach()
*/ */
#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(const QString &key, QObject *parent) QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent) : QObject(*new QSharedMemoryPrivate, parent)
{ {
setKey(key); 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 The destructor clears the key, which forces the shared memory object
@ -604,4 +619,6 @@ QString QSharedMemory::errorString() const
QT_END_NAMESPACE QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
#include "moc_qsharedmemory.cpp" #include "moc_qsharedmemory.cpp"
#endif

View File

@ -40,8 +40,14 @@
#ifndef QSHAREDMEMORY_H #ifndef QSHAREDMEMORY_H
#define 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 QT_BEGIN_NAMESPACE
@ -49,9 +55,14 @@ QT_BEGIN_NAMESPACE
class QSharedMemoryPrivate; 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 Q_OBJECT
#endif
Q_DECLARE_PRIVATE(QSharedMemory) Q_DECLARE_PRIVATE(QSharedMemory)
public: public:
@ -74,8 +85,17 @@ public:
UnknownError UnknownError
}; };
#ifndef QT_NO_QOBJECT
QSharedMemory(QObject *parent = nullptr); QSharedMemory(QObject *parent = nullptr);
QSharedMemory(const QString &key, 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(); ~QSharedMemory();
void setKey(const QString &key); void setKey(const QString &key);
@ -104,6 +124,9 @@ public:
private: private:
Q_DISABLE_COPY(QSharedMemory) Q_DISABLE_COPY(QSharedMemory)
#ifdef QT_NO_QOBJECT
QScopedPointer<QSharedMemoryPrivate> d_ptr;
#endif
}; };
#endif // QT_NO_SHAREDMEMORY #endif // QT_NO_SHAREDMEMORY

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,8 @@ SOURCES += \
../../corelib/kernel/qmetatype.cpp \ ../../corelib/kernel/qmetatype.cpp \
../../corelib/kernel/qvariant.cpp \ ../../corelib/kernel/qvariant.cpp \
../../corelib/kernel/qsystemerror.cpp \ ../../corelib/kernel/qsystemerror.cpp \
../../corelib/kernel/qsharedmemory.cpp \
../../corelib/kernel/qsystemsemaphore.cpp \
../../corelib/plugin/quuid.cpp \ ../../corelib/plugin/quuid.cpp \
../../corelib/serialization/qdatastream.cpp \ ../../corelib/serialization/qdatastream.cpp \
../../corelib/serialization/qjson.cpp \ ../../corelib/serialization/qjson.cpp \
@ -103,6 +105,12 @@ SOURCES += \
../../xml/sax/qxml.cpp ../../xml/sax/qxml.cpp
unix:SOURCES += ../../corelib/kernel/qcore_unix.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/qfilesystemengine_unix.cpp \
../../corelib/io/qfilesystemiterator_unix.cpp \ ../../corelib/io/qfilesystemiterator_unix.cpp \
../../corelib/io/qfsfileengine_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/qfilesystemiterator_win.cpp \
../../corelib/io/qfsfileengine_win.cpp \ ../../corelib/io/qfsfileengine_win.cpp \
../../corelib/kernel/qcoreapplication_win.cpp \ ../../corelib/kernel/qcoreapplication_win.cpp \
../../corelib/kernel/qsharedmemory_win.cpp \
../../corelib/kernel/qsystemsemaphore_win.cpp \
../../corelib/plugin/qsystemlibrary.cpp \ ../../corelib/plugin/qsystemlibrary.cpp \
mac { mac {
SOURCES += \ SOURCES += \
../../corelib/kernel/qcoreapplication_mac.cpp \ ../../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 += \ OBJECTIVE_SOURCES += \
../../corelib/global/qoperatingsystemversion_darwin.mm \ ../../corelib/global/qoperatingsystemversion_darwin.mm \
../../corelib/kernel/qcore_mac_objc.mm \ ../../corelib/kernel/qcore_mac_objc.mm \