Fix running qml tests on Android

First and foremost the condition was wrong ...
In order to help running qml test cases on Android without any android
specific hack on
user's .pro files we copy the entire project folder to assets. I copy the
entire folder
and not only the tst_*.qml files because it might contain data which is
needed by the tests to run.

Change-Id: I06323d9d52904317410dd2f440de65a0766a48b5
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
This commit is contained in:
BogDan Vatra 2020-04-08 14:07:21 +03:00 committed by Assam Boudjelthia
parent 556a7e7318
commit 45b56d86bb
3 changed files with 70 additions and 58 deletions

View File

@ -5,8 +5,12 @@
contains(TEMPLATE, vc.*): DEFINES += QUICK_TEST_SOURCE_DIR=\"$$_PRO_FILE_PWD_\" contains(TEMPLATE, vc.*): DEFINES += QUICK_TEST_SOURCE_DIR=\"$$_PRO_FILE_PWD_\"
else: DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\"$$_PRO_FILE_PWD_\") else: DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\"$$_PRO_FILE_PWD_\")
} else { } else {
!isEmpty(RESOURCES): warning("The RESOURCES qmake variable is empty, the test will probably fail to run") isEmpty(RESOURCES) {
DEFINES += QUICK_TEST_SOURCE_DIR=\":/\" ANDROID_EXTRA_PLUGINS *= $$_PRO_FILE_PWD_
DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\"assets:/$$basename(_PRO_FILE_PWD_)\")
} else {
DEFINES += QUICK_TEST_SOURCE_DIR=$$shell_quote(\":/\")
}
} }
} else { } else {

View File

@ -63,33 +63,41 @@ 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) build_pass {
qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner) APK_PATH = $$shell_path($$OUT_PWD/android-build/$${TARGET}.apk)
qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt) qtPrepareTool(ANDROIDTESTRUNNER, androidtestrunner)
isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json qtPrepareTool(ANDROIDDEPLOYQT, androiddeployqt)
contains(QMAKE_HOST.os, Windows): extension = .exe isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json
$${type}.commands = $$ANDROIDTESTRUNNER --androiddeployqt \"$$ANDROIDDEPLOYQT --input $$ANDROID_DEPLOYMENT_SETTINGS_FILE\" contains(QMAKE_HOST.os, Windows): extension = .exe
$${type}.commands += --path \"$$OUT_PWD/android-build\" $${type}.commands = $$ANDROIDTESTRUNNER --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) apk\"
$${type}.commands += --apk $$APK_PATH $${type}.commands += --apk $$APK_PATH
} else: $${type}.commands += $(TESTRUNNER) # Allow for custom arguments to tests
!catch: $${type}.commands += $(TESTARGS)
unix { $${type}.commands += "--"
isEmpty(TEST_TARGET_DIR): TEST_TARGET_DIR = . for(import, IMPORTPATH): $${type}.commands *= -import \"assets:/$$basename(_PRO_FILE_PWD_)/$$import\"
} else {
app_bundle: \ $${type}.commands = "echo \"Pass\""
$${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET).app/Contents/MacOS/$(QMAKE_TARGET) }
else: !android: \
$${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET)
} else { } else {
# Windows $${type}.commands += $(TESTRUNNER)
!isEmpty(TEST_TARGET_DIR): TEST_TARGET_DIR = $$shell_path($$TEST_TARGET_DIR)$${QMAKE_DIR_SEP}
$${type}.commands += $${TEST_TARGET_DIR}$(TARGET)
}
# Allow for custom arguments to tests unix {
!catch: $${type}.commands += $(TESTARGS) isEmpty(TEST_TARGET_DIR): TEST_TARGET_DIR = .
app_bundle: \
$${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET).app/Contents/MacOS/$(QMAKE_TARGET)
else: !android: \
$${type}.commands += $${TEST_TARGET_DIR}/$(QMAKE_TARGET)
} else {
# Windows
!isEmpty(TEST_TARGET_DIR): TEST_TARGET_DIR = $$shell_path($$TEST_TARGET_DIR)$${QMAKE_DIR_SEP}
$${type}.commands += $${TEST_TARGET_DIR}$(TARGET)
}
# Allow for custom arguments to tests
!catch: $${type}.commands += $(TESTARGS)
}
!isEmpty(TESTRUN_CWD):!contains(TESTRUN_CWD, ^\\./?): \ !isEmpty(TESTRUN_CWD):!contains(TESTRUN_CWD, ^\\./?): \
$${type}.commands = $$QMAKE_CD $$shell_path($$TESTRUN_CWD) && $$eval($${type}.commands) $${type}.commands = $$QMAKE_CD $$shell_path($$TESTRUN_CWD) && $$eval($${type}.commands)

View File

@ -53,7 +53,6 @@ struct Options
bool verbose = false; bool verbose = false;
bool skipAddInstallRoot = false; bool skipAddInstallRoot = false;
std::chrono::seconds timeout{300}; // 5minutes std::chrono::seconds timeout{300}; // 5minutes
QString androidDeployQtCommand;
QString buildPath; QString buildPath;
QString adbCommand{QStringLiteral("adb")}; QString adbCommand{QStringLiteral("adb")};
QString makeCommand; QString makeCommand;
@ -110,11 +109,11 @@ static Options g_options;
static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false) static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false)
{ {
if (verbose) if (verbose)
fprintf(stdout, "Execute %s\n", command.toUtf8().constData()); fprintf(stdout, "Execute %s.\n", command.toUtf8().constData());
FILE *process = popen(command.toUtf8().constData(), QT_POPEN_READ); FILE *process = popen(command.toUtf8().constData(), QT_POPEN_READ);
if (!process) { if (!process) {
fprintf(stderr, "Cannot execute command %s", qPrintable(command)); fprintf(stderr, "Cannot execute command %s.\n", qPrintable(command));
return false; return false;
} }
char buffer[512]; char buffer[512];
@ -204,12 +203,7 @@ static bool parseOptions()
int i = 1; int i = 1;
for (; i < arguments.size(); ++i) { for (; i < arguments.size(); ++i) {
const QString &argument = arguments.at(i); const QString &argument = arguments.at(i);
if (argument.compare(QStringLiteral("--androiddeployqt"), Qt::CaseInsensitive) == 0) { if (argument.compare(QStringLiteral("--adb"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size())
g_options.helpRequested = true;
else
g_options.androidDeployQtCommand = arguments.at(++i).trimmed();
} else if (argument.compare(QStringLiteral("--adb"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size()) if (i + 1 == arguments.size())
g_options.helpRequested = true; g_options.helpRequested = true;
else else
@ -255,7 +249,7 @@ static bool parseOptions()
for (;i < arguments.size(); ++i) for (;i < arguments.size(); ++i)
g_options.testArgsList << arguments.at(i); g_options.testArgsList << arguments.at(i);
if (g_options.helpRequested || g_options.androidDeployQtCommand.isEmpty() || g_options.buildPath.isEmpty()) if (g_options.helpRequested || g_options.buildPath.isEmpty() || g_options.apkPath.isEmpty())
return false; return false;
QString serial = qEnvironmentVariable("ANDROID_DEVICE_SERIAL"); QString serial = qEnvironmentVariable("ANDROID_DEVICE_SERIAL");
@ -272,20 +266,18 @@ static void printHelp()
" runs it on the default emulator/device or on the one specified by\n" " runs it on the default emulator/device or on the one specified by\n"
" \"ANDROID_DEVICE_SERIAL\" environment variable.\n\n" " \"ANDROID_DEVICE_SERIAL\" environment variable.\n\n"
" Mandatory arguments:\n" " Mandatory arguments:\n"
" --androiddeployqt <androiddeployqt cmd>: The androiddeployqt:\n" " --path <path>: The path where androiddeployqt builds the android package.\n"
" path including its additional arguments.\n" " --apk <apk path>: The test apk path. The apk has to exist already, if it"
" --path <path>: The path where androiddeployqt will build the .apk.\n" " does not exist the make command must be provided for building the apk.\n\n"
" Optional arguments:\n" " Optional arguments:\n"
" --make <make cmd>: make command, needed to install the qt library.\n"
" For Qt 5.14+ this can be \"make apk\".\n"
" --adb <adb cmd>: The Android ADB command. If missing the one from\n" " --adb <adb cmd>: The Android ADB command. If missing the one from\n"
" $PATH will be used.\n" " $PATH will be used.\n"
" --activity <acitvity>: The Activity to run. If missing the first\n" " --activity <acitvity>: The Activity to run. If missing the first\n"
" activity from AndroidManifest.qml file will be used.\n" " activity from AndroidManifest.qml file will be used.\n"
" --timeout <seconds>: Timeout to run the test.\n" " --timeout <seconds>: Timeout to run the test.\n"
" Default is 5 minutes.\n" " 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"
" --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\n" " --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\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"
@ -342,6 +334,8 @@ static bool parseTestArgs()
QString unhandledArgs; QString unhandledArgs;
for (int i = 0; i < g_options.testArgsList.size(); ++i) { for (int i = 0; i < g_options.testArgsList.size(); ++i) {
const QString &arg = g_options.testArgsList[i].trimmed(); const QString &arg = g_options.testArgsList[i].trimmed();
if (arg == QStringLiteral("--"))
continue;
if (arg == QStringLiteral("-o")) { if (arg == QStringLiteral("-o")) {
if (i >= g_options.testArgsList.size() - 1) if (i >= g_options.testArgsList.size() - 1)
return false; // missing file argument return false; // missing file argument
@ -452,14 +446,15 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
RunnerLocker lock; // do not install or run packages while another test is running if (!QFile::exists(g_options.apkPath)) {
if (!g_options.apkPath.isEmpty() && QFile::exists(g_options.apkPath)) { if (g_options.makeCommand.isEmpty()) {
if (!execCommand(QStringLiteral("%1 install -r %2") fprintf(stderr,
.arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) { "No apk found at \"%s\". Provide a make command with the \"--make\" parameter "
"to generate it first.\n",
qPrintable(g_options.apkPath));
return 1; return 1;
} }
} else { if (!execCommand(g_options.makeCommand, nullptr, g_options.verbose)) {
if (!g_options.makeCommand.isEmpty()) {
if (!g_options.skipAddInstallRoot) { if (!g_options.skipAddInstallRoot) {
// we need to run make INSTALL_ROOT=path install to install the application file(s) first // we need to run make INSTALL_ROOT=path install to install the application file(s) first
if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install") if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install")
@ -473,15 +468,20 @@ int main(int argc, char *argv[])
} }
} }
} }
}
// Run androiddeployqt if (!QFile::exists(g_options.apkPath)) {
static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QString(); fprintf(stderr,
if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2 --apk %4").arg(g_options.androidDeployQtCommand, "No apk \"%s\" found after running the make command. Check the provided path and "
g_options.buildPath, "the make command.\n",
verbose, qPrintable(g_options.apkPath));
g_options.apkPath), nullptr, true)) { return 1;
return 1; }
}
RunnerLocker lock; // do not install or run packages while another test is running
if (!execCommand(QStringLiteral("%1 install -r %2")
.arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) {
return 1;
} }
QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml"); QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml");