diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 78e8038c05b..0b6c01e4140 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -111,6 +111,10 @@ #include #endif +#ifdef Q_OS_ANDROID +#include +#endif + #include QT_BEGIN_NAMESPACE @@ -1790,6 +1794,14 @@ static void initEnvironment() qputenv("QT_QTESTLIB_RUNNING", "1"); } +#ifdef Q_OS_ANDROID +static QFile androidExitCodeFile() +{ + const QString testHome = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + return QFile(testHome + "/qtest_last_exit_code"_L1); +} +#endif + /*! Executes tests declared in \a testObject. In addition, the private slots \c{initTestCase()}, \c{cleanupTestCase()}, \c{init()} and \c{cleanup()} @@ -1890,6 +1902,10 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) if (QBenchmarkGlobalData::current->mode() != QBenchmarkGlobalData::CallgrindParentProcess) #endif QTestLog::startLogging(); + +#ifdef Q_OS_ANDROID + androidExitCodeFile().remove(); +#endif } /*! \internal @@ -1984,7 +2000,19 @@ int QTest::qRun() #endif // make sure our exit code is never going above 127 // since that could wrap and indicate 0 test fails - return qMin(QTestLog::failCount(), 127); + const int exitCode = qMin(QTestLog::failCount(), 127); + +#ifdef Q_OS_ANDROID + QFile exitCodeFile = androidExitCodeFile(); + if (exitCodeFile.open(QIODevice::WriteOnly)) { + exitCodeFile.write(qPrintable(QString::number(exitCode))); + } else { + qWarning("Failed to open %s for writing test exit code: %s", + qPrintable(exitCodeFile.fileName()), qPrintable(exitCodeFile.errorString())); + } +#endif + + return exitCode; } /*! \internal diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index b800bee8c54..b81d7a73600 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -547,7 +547,7 @@ static QString runCommandAsUserArgs(const QString &cmd) return "run-as %1 --user %2 %3"_L1.arg(g_options.package, g_testInfo.userId, cmd); } -static bool pullFiles() +static bool pullResults() { bool ret = true; for (auto it = g_options.outFiles.constBegin(); it != g_options.outFiles.end(); ++it) { @@ -702,6 +702,19 @@ static QString getCurrentTimeString() return QString::fromUtf8(output.simplified()); } +static int testExitCode() +{ + QByteArray exitCodeOutput; + const QString exitCodeCmd = "cat files/qtest_last_exit_code 2> /dev/null"_L1; + if (!execAdbCommand({ "shell"_L1, runCommandAsUserArgs(exitCodeCmd) }, &exitCodeOutput)) + return 1; + + bool ok; + int exitCode = exitCodeOutput.toInt(&ok); + + return ok ? exitCode : 1; +} + static bool uninstallTestPackage() { return execAdbCommand({ "uninstall"_L1, g_options.package }, nullptr); @@ -814,24 +827,24 @@ int main(int argc, char *argv[]) const QString formattedTime = getCurrentTimeString(); // start the tests - bool success = execAdbCommand(g_options.amStarttestArgs, nullptr); + if (!execAdbCommand(g_options.amStarttestArgs, nullptr)) + return 1; waitForStartedAndFinished(); - if (success) { - success &= pullFiles(); - if (g_options.showLogcatOutput) - printLogcat(formattedTime); - } + int exitCode = testExitCode(); // If we have a failure, attempt to print both logcat and the crash buffer which // includes the crash stacktrace that is not included in the default logcat. - if (!success) { + if (exitCode != 0 || g_options.showLogcatOutput) printLogcat(formattedTime); + if (exitCode != 0) printLogcatCrashBuffer(formattedTime); - } - success &= uninstallTestPackage(); + exitCode = pullResults() ? exitCode : 1; + + if (!uninstallTestPackage()) + return 1; testRunnerLock.release(); @@ -840,5 +853,5 @@ int main(int argc, char *argv[]) return 1; } - return success ? 0 : 1; + return exitCode; }