5#include <QCoreApplication>
8#include <QRegularExpression>
9#include <QSystemSemaphore>
10#include <QXmlStreamReader>
21#define QT_POPEN_READ "rb"
24#define QT_POPEN_READ "r"
30 QXmlStreamReader reader{
data};
31 while (!reader.atEnd()) {
34 if (!reader.isStartElement())
48 return !reader.hasError();
52 if (
data.indexOf(
"\nFAIL! : "_L1) >= 0)
54 if (
data.indexOf(
"\nXPASS : "_L1) >= 0)
70 QXmlStreamReader reader{
data};
71 while (!reader.atEnd()) {
74 const bool isIncident = (reader.name() ==
QStringLiteral(
"Incident"));
75 if (reader.isStartElement() && isIncident) {
82 return !reader.hasError();
95 if (
data.indexOf(
"' message='Failure! |[Loc: ") >= 0)
100 return lines.
last().startsWith(
"##teamcity[testSuiteFinished "_L1);
106 if (
data.indexOf(
"\nnot ok ") >= 0)
152 fprintf(stderr,
"Cannot execute command %s.\n",
qPrintable(command));
160 fprintf(stdout,
"%s",
buffer);
166 return pclose(process) == 0;
234 fprintf(stderr,
"Syntax: %s <options> -- [TESTARGS] \n"
236 " Creates an Android package in a temp directory <destination> and\n"
237 " runs it on the default emulator/device or on the one specified by\n"
238 " \"ANDROID_DEVICE_SERIAL\" environment variable.\n"
240 " Mandatory arguments:\n"
241 " --path <path>: The path where androiddeployqt builds the android package.\n"
243 " --apk <apk path>: The test apk path. The apk has to exist already, if it\n"
244 " does not exist the make command must be provided for building the apk.\n"
246 " Optional arguments:\n"
247 " --make <make cmd>: make command, needed to install the qt library.\n"
248 " For Qt 5.14+ this can be \"make apk\".\n"
250 " --adb <adb cmd>: The Android ADB command. If missing the one from\n"
251 " $PATH will be used.\n"
253 " --activity <acitvity>: The Activity to run. If missing the first\n"
254 " activity from AndroidManifest.qml file will be used.\n"
256 " --timeout <seconds>: Timeout to run the test. Default is 5 minutes.\n"
258 " --skip-install-root: Do not append INSTALL_ROOT=... to the make command.\n"
260 " --show-logcat: Print Logcat output to stdout.\n"
262 " -- Arguments that will be passed to the test application.\n"
264 " --verbose: Prints out information during processing.\n"
266 " --help: Displays this information.\n\n",
273 QFile androidManifestXml(androidManifestPath);
275 QXmlStreamReader reader(&androidManifestXml);
276 while (!reader.atEnd()) {
278 if (reader.isStartElement() && reader.name() ==
QStringLiteral(
"manifest"))
279 return reader.attributes().value(
QStringLiteral(
"package")).toString();
287 QFile androidManifestXml(androidManifestPath);
289 QXmlStreamReader reader(&androidManifestXml);
290 while (!reader.atEnd()) {
292 if (reader.isStartElement() && reader.name() ==
QStringLiteral(
"activity"))
293 return reader.attributes().value(
QStringLiteral(
"android:name")).toString();
326 const auto match = newLoggingFormat.match(filePath);
327 if (!
match.hasMatch()) {
330 const auto capturedTexts =
match.capturedTexts();
335 if (
match.hasMatch()) {
336 logType =
match.capturedTexts().at(1);
370 using clock = std::chrono::system_clock;
371 auto start = clock::now();
374 std::this_thread::sleep_for(std::chrono::milliseconds(100));
375 if ((clock::now() -
start) > std::chrono::seconds{10})
390 "Unable to obtain the PID of the running unit test. Command \"%s\" "
399 std::this_thread::sleep_for(std::chrono::milliseconds(250));
421 "Unable to obtain the SDK version of the target. Command \"%s\" "
451 fprintf(stderr,
"Failed to get the test output from the target. Either the output "
452 "is empty or androidtestrunner failed to retrieve it.\n");
459 fprintf(stdout,
"%s",
output.constData());
481 QSystemSemaphore
runner{ QSystemSemaphore::platformSafeKey(u
"androidtestrunner"_s),
482 1, QSystemSemaphore::Open };
485int main(
int argc,
char *argv[])
495 "It is required to provide a make command with the \"--make\" parameter "
496 "to generate the apk.\n");
516 "No apk \"%s\" found after running the make command. Check the provided path and "
517 "the make command.\n",
547 fprintf(stderr,
"Cannot show logcat output on Android 23 and below.\n");
550 fprintf(stdout,
"Logcat output:\n");
555 fprintf(stdout,
"End Logcat output.\n");
QByteArray & prepend(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QStringList arguments()
static QString toNativeSeparators(const QString &pathName)
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
bool hasMatch() const
Returns true if the regular expression matched against the subject string, or false otherwise.
\inmodule QtCore \reentrant
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to match the regular expression against the given subject string, starting at the position o...
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
QString trimmed() const &
QByteArray toUtf8() const &
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
QSet< QString >::iterator it
QList< QVariant > arguments
GLboolean GLboolean GLboolean GLboolean a
[7]
GLbitfield GLuint64 timeout
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
#define qPrintable(string)
#define QStringLiteral(str)
static bool waitToFinish()
static void setOutputFile(QString file, QString format)
static void obtainSDKVersion()
static bool parseTestArgs()
static QString packageNameFromAndroidManifest(const QString &androidManifestPath)
static bool checkJunit(const QByteArray &data)
static bool checkLightxml(const QByteArray &data)
static bool checkXml(const QByteArray &data)
static QString activityFromAndroidManifest(const QString &androidManifestPath)
static bool checkTap(const QByteArray &data)
static bool checkCsv(const QByteArray &data)
static bool checkTeamcity(const QByteArray &data)
static bool parseOptions()
static bool execCommand(const QString &command, QByteArray *output=nullptr, bool verbose=false)
static bool checkTxt(const QByteArray &data)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QT_BEGIN_NAMESPACE typedef uchar * output
static QString shellQuote(const QString &arg)
QTextStream out(stdout)
[7]
const QHash< QString, std::function< bool(const QByteArray &)> > checkFiles
std::chrono::seconds timeout
QHash< QString, QString > outFiles