diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 9d2803e551c..434966b0750 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -18,6 +18,7 @@ HEADERS += \ androiderrormessage.h \ androidglobal.h \ androidrunner.h \ + androidrunnerworker.h \ androiddebugsupport.h \ androidqtversionfactory.h \ androidqtversion.h \ @@ -66,6 +67,7 @@ SOURCES += \ androidtoolchain.cpp \ androiderrormessage.cpp \ androidrunner.cpp \ + androidrunnerworker.cpp \ androiddebugsupport.cpp \ androidqtversionfactory.cpp \ androidqtversion.cpp \ diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index 8f095013b58..18187f00e0c 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -93,6 +93,8 @@ Project { "androidrunnable.h", "androidrunner.cpp", "androidrunner.h", + "androidrunnerworker.cpp", + "androidrunnerworker.h", "androidsdkmanager.cpp", "androidsdkmanager.h", "androidsdkmanagerwidget.cpp", diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 5e264117e94..5cf723fe692 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -164,6 +164,7 @@ bool AndroidDeployQtStep::init(QList &earlierSteps) m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so"; AndroidManager::setDeviceSerialNumber(target(), m_serialNumber); + AndroidManager::setDeviceApiLevel(target(), info.sdk); QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit()); @@ -325,11 +326,12 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QFutureInter emit addOutput(tr("The process \"%1\" crashed.").arg(m_command), BuildStep::OutputFormat::ErrorMessage); } - if (exitCode == 0 && exitStatus == QProcess::NormalExit) { - if (deployError != NoError && m_uninstallPreviousPackageRun) { - deployError = Failure; - } - } else { + if (deployError != NoError) { + if (m_uninstallPreviousPackageRun) + deployError = Failure; // Even re-install failed. Set to Failure. + } else if (exitCode != 0 || exitStatus != QProcess::NormalExit) { + // Set the deployError to Failure when no deployError code was detected + // but the adb tool failed otherwise relay the detected deployError. deployError = Failure; } diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index c361ce24909..7a757d7f07a 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -241,6 +241,16 @@ void AndroidManager::setDeviceSerialNumber(ProjectExplorer::Target *target, cons target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber); } +int AndroidManager::deviceApiLevel(ProjectExplorer::Target *target) +{ + return target->namedSettings(ApiLevelKey).toInt(); +} + +void AndroidManager::setDeviceApiLevel(ProjectExplorer::Target *target, int level) +{ + target->setNamedSettings(ApiLevelKey, level); +} + QPair AndroidManager::apiLevelRange() { return qMakePair(9, 26); diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h index d6e175c66f7..b1ce3321317 100644 --- a/src/plugins/android/androidmanager.h +++ b/src/plugins/android/androidmanager.h @@ -56,6 +56,9 @@ public: static QString deviceSerialNumber(ProjectExplorer::Target *target); static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber); + static int deviceApiLevel(ProjectExplorer::Target *target); + static void setDeviceApiLevel(ProjectExplorer::Target *target, int level); + static QString buildTargetSDK(ProjectExplorer::Target *target); static bool signPackage(ProjectExplorer::Target *target); diff --git a/src/plugins/android/androidrunnable.h b/src/plugins/android/androidrunnable.h index b9cf920285a..6d6fdb8f904 100644 --- a/src/plugins/android/androidrunnable.h +++ b/src/plugins/android/androidrunnable.h @@ -42,6 +42,7 @@ struct ANDROID_EXPORT AndroidRunnable QString deviceSerialNumber; QString extraAppParams; Utils::Environment extraEnvVars; + int apiLevel = -1; QString displayName() const { return packageName; } static void *staticTypeId; diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 9e98789c8c3..b4e3d11e9a0 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -28,35 +28,17 @@ #include "androiddeployqtstep.h" #include "androidconfigurations.h" -#include "androidglobal.h" #include "androidrunconfiguration.h" #include "androidmanager.h" #include "androidavdmanager.h" +#include "androidrunnerworker.h" -#include #include #include -#include #include #include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace std::placeholders; using namespace ProjectExplorer; using namespace Utils; @@ -127,568 +109,10 @@ using namespace Utils; namespace Android { namespace Internal { -const int MIN_SOCKET_HANDSHAKE_PORT = 20001; -const int MAX_SOCKET_HANDSHAKE_PORT = 20999; -static const QString pidScript = QStringLiteral("for p in /proc/[0-9]*; " - "do cat <$p/cmdline && echo :${p##*/}; done"); -static const QString pidPollingScript = QStringLiteral("while true; do sleep 1; " - "cat /proc/%1/cmdline > /dev/null; done"); - -static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date - "\\s+" - "[0-9\\-:.]*"// time - "\\s*" - "(\\d*)" // pid 1. capture - "\\s+" - "\\d*" // unknown - "\\s+" - "(\\w)" // message type 2. capture - "\\s+" - "(.*): " // source 3. capture - "(.*)" // message 4. capture - "[\\n\\r]*" - ); -static int APP_START_TIMEOUT = 45000; - -static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, - int msecs = APP_START_TIMEOUT) -{ - bool timedOut = false; - auto end = chrono::high_resolution_clock::now(); - if (chrono::duration_cast(end-start).count() > msecs) - timedOut = true; - return timedOut; -} - -static qint64 extractPID(const QByteArray &output, const QString &packageName) -{ - qint64 pid = -1; - foreach (auto tuple, output.split('\n')) { - tuple = tuple.simplified(); - if (!tuple.isEmpty()) { - auto parts = tuple.split(':'); - QString commandName = QString::fromLocal8Bit(parts.first()); - if (parts.length() == 2 && commandName == packageName) { - pid = parts.last().toLongLong(); - break; - } - } - } - return pid; -} - -void findProcessPID(QFutureInterface &fi, const QString &adbPath, - QStringList selector, const QString &packageName) -{ - if (packageName.isEmpty()) - return; - - qint64 processPID = -1; - chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); - do { - QThread::msleep(200); - const QByteArray out = Utils::SynchronousProcess() - .runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript) - .allRawOutput(); - processPID = extractPID(out, packageName); - } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); - - if (!fi.isCanceled()) - fi.reportResult(processPID); -} - -static void deleter(QProcess *p) -{ - p->kill(); - p->waitForFinished(); - // Might get deleted from its own signal handler. - p->deleteLater(); -} - -class AndroidRunnerWorker : public QObject -{ - Q_OBJECT - - enum DebugHandShakeType { - PingPongFiles, - SocketHandShake - }; - -public: - AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable); - ~AndroidRunnerWorker(); - - void asyncStart(); - void asyncStop(); - - void setAndroidRunnable(const AndroidRunnable &runnable); - void handleRemoteDebuggerRunning(); - -signals: - void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid); - void remoteProcessFinished(const QString &errString = QString()); - - void remoteOutput(const QString &output); - void remoteErrorOutput(const QString &output); - -private: - void onProcessIdChanged(qint64 pid); - void logcatReadStandardError(); - void logcatReadStandardOutput(); - void adbKill(qint64 pid); - QStringList selector() const; - void forceStop(); - void findPs(); - void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError); - bool adbShellAmNeedsQuotes(); - bool runAdb(const QStringList &args, QString *exitMessage = nullptr, int timeoutS = 10); - - // Create the processes and timer in the worker thread, for correct thread affinity - std::unique_ptr m_adbLogcatProcess; - std::unique_ptr m_psIsAlive; - QScopedPointer m_socket; - - QByteArray m_stdoutBuffer; - QByteArray m_stderrBuffer; - - QFuture m_pidFinder; - qint64 m_processPID = -1; - bool m_useCppDebugger = false; - QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; - Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket. - QUrl m_qmlServer; - QString m_pingFile; - QString m_pongFile; - QString m_gdbserverPath; - QString m_gdbserverSocket; - QString m_adb; - QRegExp m_logCatRegExp; - DebugHandShakeType m_handShakeMethod = SocketHandShake; - bool m_customPort = false; - - AndroidRunnable m_androidRunnable; - int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT; -}; - -AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable) - : m_adbLogcatProcess(nullptr, deleter) - , m_psIsAlive(nullptr, deleter) - , m_logCatRegExp(regExpLogcat) - , m_androidRunnable(runnable) -{ - auto runConfig = runControl->runConfiguration(); - auto aspect = runConfig->extraAspect(); - Core::Id runMode = runControl->runMode(); - const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE; - m_useCppDebugger = debuggingMode && aspect->useCppDebugger(); - if (debuggingMode && aspect->useQmlDebugger()) - m_qmlDebugServices = QmlDebug::QmlDebuggerServices; - else if (runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE) - m_qmlDebugServices = QmlDebug::QmlProfilerServices; - else if (runMode == ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE) - m_qmlDebugServices = QmlDebug::QmlPreviewServices; - else - m_qmlDebugServices = QmlDebug::NoQmlDebugServices; - m_localGdbServerPort = Utils::Port(5039); - QTC_CHECK(m_localGdbServerPort.isValid()); - if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { - QTcpServer server; - QTC_ASSERT(server.listen(QHostAddress::LocalHost) - || server.listen(QHostAddress::LocalHostIPv6), - qDebug() << tr("No free ports available on host for QML debugging.")); - m_qmlServer.setScheme(Utils::urlTcpScheme()); - m_qmlServer.setHost(server.serverAddress().toString()); - m_qmlServer.setPort(server.serverPort()); - } - m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString(); - - QString packageDir = "/data/data/" + m_androidRunnable.packageName; - m_pingFile = packageDir + "/debug-ping"; - m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_androidRunnable.packageName; - m_gdbserverSocket = packageDir + "/debug-socket"; - const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion( - runConfig->target()->kit()); - if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0)) - m_gdbserverPath = packageDir + "/lib/libgdbserver.so"; - else - m_gdbserverPath = packageDir + "/lib/gdbserver"; - - if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0)) { - if (qEnvironmentVariableIsSet("QTC_ANDROID_USE_FILE_HANDSHAKE")) - m_handShakeMethod = PingPongFiles; - } else { - m_handShakeMethod = PingPongFiles; - } - - if (qEnvironmentVariableIsSet("QTC_ANDROID_SOCKET_HANDSHAKE_PORT")) { - QByteArray envData = qgetenv("QTC_ANDROID_SOCKET_HANDSHAKE_PORT"); - if (!envData.isEmpty()) { - bool ok = false; - int port = 0; - port = envData.toInt(&ok); - if (ok && port > 0 && port < 65535) { - m_socketHandShakePort = port; - m_customPort = true; - } - } - } -} - -AndroidRunnerWorker::~AndroidRunnerWorker() -{ - if (!m_pidFinder.isFinished()) - m_pidFinder.cancel(); -} - -void AndroidRunnerWorker::forceStop() -{ - runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, nullptr, 30); - - // try killing it via kill -9 - const QByteArray out = Utils::SynchronousProcess() - .runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript) - .allRawOutput(); - - qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName); - if (pid != -1) { - adbKill(pid); - } -} - -void AndroidRunnerWorker::asyncStart() -{ - forceStop(); - - // Start the logcat process before app starts. - std::unique_ptr logcatProcess(new QProcess, deleter); - connect(logcatProcess.get(), &QProcess::readyReadStandardOutput, - this, &AndroidRunnerWorker::logcatReadStandardOutput); - connect(logcatProcess.get(), &QProcess::readyReadStandardError, - this, &AndroidRunnerWorker::logcatReadStandardError); - // Its assumed that the device or avd returned by selector() is online. - logcatProcess->start(m_adb, selector() << "logcat"); - - QString errorMessage; - - if (m_useCppDebugger) - runAdb({"shell", "rm", m_pongFile}); // Remove pong file. - - for (const QString &entry: m_androidRunnable.beforeStartAdbCommands) - runAdb(entry.split(' ', QString::SkipEmptyParts)); - - QStringList args({"shell", "am", "start"}); - args << m_androidRunnable.amStartExtraArgs; - args << "-n" << m_androidRunnable.intentName; - - if (m_useCppDebugger) { - if (!runAdb({"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()})){ - QTC_CHECK(false); - } - if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(), - "localfilesystem:" + m_gdbserverSocket}, &errorMessage)) { - emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(errorMessage)); - return; - } - - const QString pingPongSocket(m_androidRunnable.packageName + ".ping_pong_socket"); - args << "-e" << "debug_ping" << "true"; - if (m_handShakeMethod == SocketHandShake) { - args << "-e" << "ping_socket" << pingPongSocket; - } else if (m_handShakeMethod == PingPongFiles) { - args << "-e" << "ping_file" << m_pingFile; - args << "-e" << "pong_file" << m_pongFile; - } - - QString gdbserverCommand = QString::fromLatin1(adbShellAmNeedsQuotes() ? "\"%1 --multi +%2\"" : "%1 --multi +%2") - .arg(m_gdbserverPath).arg(m_gdbserverSocket); - args << "-e" << "gdbserver_command" << gdbserverCommand; - args << "-e" << "gdbserver_socket" << m_gdbserverSocket; - - if (m_handShakeMethod == SocketHandShake) { - const QString port = QString("tcp:%1").arg(m_socketHandShakePort); - if (!runAdb({"forward", port, "localabstract:" + pingPongSocket}, &errorMessage)) { - emit remoteProcessFinished(tr("Failed to forward ping pong ports. Reason: %1.") - .arg(errorMessage)); - return; - } - } - } - - if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { - // currently forward to same port on device and host - const QString port = QString("tcp:%1").arg(m_qmlServer.port()); - if (!runAdb({"forward", port, port}, &errorMessage)) { - emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.") - .arg(errorMessage)); - return; - } - - args << "-e" << "qml_debug" << "true" - << "-e" << "qmljsdebugger" - << QString("port:%1,block,services:%2") - .arg(m_qmlServer.port()).arg(QmlDebug::qmlDebugServices(m_qmlDebugServices)); - } - - if (!m_androidRunnable.extraAppParams.isEmpty()) { - args << "-e" << "extraappparams" - << QString::fromLatin1(m_androidRunnable.extraAppParams.toUtf8().toBase64()); - } - - if (m_androidRunnable.extraEnvVars.size() > 0) { - args << "-e" << "extraenvvars" - << QString::fromLatin1(m_androidRunnable.extraEnvVars.toStringList().join('\t') - .toUtf8().toBase64()); - } - - if (!runAdb(args, &errorMessage)) { - emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.") - .arg(errorMessage)); - return; - } - - if (m_useCppDebugger) { - if (m_handShakeMethod == SocketHandShake) { - //Handling socket - bool wasSuccess = false; - const int maxAttempts = 20; //20 seconds - m_socket.reset(new QTcpSocket()); - for (int i = 0; i < maxAttempts; i++) { - - QThread::sleep(1); // give Android time to start process - m_socket->connectToHost(QHostAddress(QStringLiteral("127.0.0.1")), - m_socketHandShakePort); - if (!m_socket->waitForConnected()) - continue; - - if (!m_socket->waitForReadyRead()) { - m_socket->close(); - continue; - } - - const QByteArray pid = m_socket->readLine(); - if (pid.isEmpty()) { - m_socket->close(); - continue; - } - - wasSuccess = true; - - break; - } - - if (!m_customPort) { - // increment running port to avoid clash when using multiple - // debug sessions at the same time - m_socketHandShakePort++; - // wrap ports around to avoid overflow - if (m_socketHandShakePort == MAX_SOCKET_HANDSHAKE_PORT) - m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT; - } - - if (!wasSuccess) { - emit remoteProcessFinished(tr("Failed to contact debugging port.")); - return; - } - } else { - // Handling ping. - for (int i = 0; ; ++i) { - Utils::TemporaryFile tmp("pingpong"); - tmp.open(); - tmp.close(); - - runAdb({"pull", m_pingFile, tmp.fileName()}); - - QFile res(tmp.fileName()); - const bool doBreak = res.size(); - res.remove(); - if (doBreak) - break; - - if (i == 20) { - emit remoteProcessFinished(tr("Unable to start \"%1\".") - .arg(m_androidRunnable.packageName)); - return; - } - qDebug() << "WAITING FOR " << tmp.fileName(); - QThread::msleep(500); - } - } - - } - - QTC_ASSERT(!m_adbLogcatProcess, /**/); - m_adbLogcatProcess = std::move(logcatProcess); - m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(), - m_androidRunnable.packageName), - bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); - -} - -bool AndroidRunnerWorker::adbShellAmNeedsQuotes() -{ - // Between Android SDK Tools version 24.3.1 and 24.3.4 the quoting - // needs for the 'adb shell am start ...' parameters changed. - // Run a test to find out on what side of the fence we live. - // The command will fail with a complaint about the "--dummy" - // option on newer SDKs, and with "No intent supplied" on older ones. - // In case the test itself fails assume a new SDK. - Utils::SynchronousProcess adb; - adb.setTimeoutS(10); - Utils::SynchronousProcessResponse response - = adb.run(m_adb, selector() << "shell" << "am" << "start" - << "-e" << "dummy" << "dummy --dummy"); - if (response.result == Utils::SynchronousProcessResponse::StartFailed - || response.result != Utils::SynchronousProcessResponse::Finished) - return true; - - const QString output = response.allOutput(); - const bool oldSdk = output.contains("Error: No intent supplied"); - return !oldSdk; -} - -bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage, int timeoutS) -{ - Utils::SynchronousProcess adb; - adb.setTimeoutS(timeoutS); - Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args); - if (exitMessage) - *exitMessage = response.exitMessage(m_adb, timeoutS); - return response.result == Utils::SynchronousProcessResponse::Finished; -} - -void AndroidRunnerWorker::handleRemoteDebuggerRunning() -{ - if (m_useCppDebugger) { - if (m_handShakeMethod == SocketHandShake) { - m_socket->write("OK"); - m_socket->waitForBytesWritten(); - m_socket->close(); - } else { - Utils::TemporaryFile tmp("pingpong"); - tmp.open(); - - runAdb({"push", tmp.fileName(), m_pongFile}); - } - QTC_CHECK(m_processPID != -1); - } -// emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort); -} - -void AndroidRunnerWorker::asyncStop() -{ - if (!m_pidFinder.isFinished()) - m_pidFinder.cancel(); - - if (m_processPID != -1) { - forceStop(); - } -} - -void AndroidRunnerWorker::setAndroidRunnable(const AndroidRunnable &runnable) -{ - m_androidRunnable = runnable; -} - -void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError) -{ - QList lines = text.split('\n'); - // lines always contains at least one item - lines[0].prepend(buffer); - if (!lines.last().endsWith('\n')) { - // incomplete line - buffer = lines.last(); - lines.removeLast(); - } else { - buffer.clear(); - } - - QString pidString = QString::number(m_processPID); - foreach (const QByteArray &msg, lines) { - const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n'); - if (!line.contains(pidString)) - continue; - if (m_logCatRegExp.exactMatch(line)) { - // Android M - if (m_logCatRegExp.cap(1) == pidString) { - const QString &messagetype = m_logCatRegExp.cap(2); - QString output = line.mid(m_logCatRegExp.pos(2)); - - if (onlyError - || messagetype == QLatin1String("F") - || messagetype == QLatin1String("E") - || messagetype == QLatin1String("W")) - emit remoteErrorOutput(output); - else - emit remoteOutput(output); - } - } else { - if (onlyError || line.startsWith("F/") - || line.startsWith("E/") - || line.startsWith("W/")) - emit remoteErrorOutput(line); - else - emit remoteOutput(line); - } - } -} - -void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) -{ - // Don't write to m_psProc from a different thread - QTC_ASSERT(QThread::currentThread() == thread(), return); - m_processPID = pid; - if (pid == -1) { - emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.") - .arg(m_androidRunnable.packageName)); - // App died/killed. Reset log and monitor processes. - m_adbLogcatProcess.reset(); - m_psIsAlive.reset(); - - // Run adb commands after application quit. - for (const QString &entry: m_androidRunnable.afterFinishAdbCommands) - runAdb(entry.split(' ', QString::SkipEmptyParts)); - } else { - // In debugging cases this will be funneled to the engine to actually start - // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. - emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID); - logcatReadStandardOutput(); - QTC_ASSERT(!m_psIsAlive, /**/); - m_psIsAlive.reset(new QProcess); - m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); - connect(m_psIsAlive.get(), &QProcess::readyRead, [this](){ - if (!m_psIsAlive->readAll().simplified().isEmpty()) - onProcessIdChanged(-1); - }); - m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell") - << pidPollingScript.arg(m_processPID)); - } -} - -void AndroidRunnerWorker::logcatReadStandardError() -{ - if (m_processPID != -1) - logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true); -} - -void AndroidRunnerWorker::logcatReadStandardOutput() -{ - if (m_processPID != -1) - logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false); -} - -void AndroidRunnerWorker::adbKill(qint64 pid) -{ - runAdb({"shell", "kill", "-9", QString::number(pid)}); - runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)}); -} - -QStringList AndroidRunnerWorker::selector() const -{ - return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber); -} - -AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName, - const QString &extraAppParams, const Utils::Environment &extraEnvVars) +AndroidRunner::AndroidRunner(RunControl *runControl, + const QString &intentName, + const QString &extraAppParams, + const Utils::Environment &extraEnvVars) : RunWorker(runControl), m_target(runControl->runConfiguration()->target()) { setDisplayName("AndroidRunner"); @@ -709,6 +133,7 @@ AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName, m_androidRunnable.extraAppParams = extraAppParams; m_androidRunnable.extraEnvVars = extraEnvVars; m_androidRunnable.deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target); + m_androidRunnable.apiLevel = AndroidManager::deviceApiLevel(m_target); if (auto androidRunConfig = qobject_cast( runControl->runConfiguration())) { @@ -720,23 +145,26 @@ AndroidRunner::AndroidRunner(RunControl *runControl, const QString &intentName, m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); } - m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable)); + if (m_androidRunnable.apiLevel > 23) + m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable)); + else + m_worker.reset(new AndroidRunnerWorkerPreNougat(runControl, m_androidRunnable)); m_worker->moveToThread(&m_thread); - connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorker::asyncStart); - connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorker::asyncStop); + connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorkerBase::asyncStart); + connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorkerBase::asyncStop); connect(this, &AndroidRunner::androidRunnableChanged, - m_worker.data(), &AndroidRunnerWorker::setAndroidRunnable); + m_worker.data(), &AndroidRunnerWorkerBase::setAndroidRunnable); connect(this, &AndroidRunner::remoteDebuggerRunning, - m_worker.data(), &AndroidRunnerWorker::handleRemoteDebuggerRunning); + m_worker.data(), &AndroidRunnerWorkerBase::handleRemoteDebuggerRunning); - connect(m_worker.data(), &AndroidRunnerWorker::remoteProcessStarted, + connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteProcessStarted, this, &AndroidRunner::handleRemoteProcessStarted); - connect(m_worker.data(), &AndroidRunnerWorker::remoteProcessFinished, + connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteProcessFinished, this, &AndroidRunner::handleRemoteProcessFinished); - connect(m_worker.data(), &AndroidRunnerWorker::remoteOutput, + connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteOutput, this, &AndroidRunner::remoteOutput); - connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput, + connect(m_worker.data(), &AndroidRunnerWorkerBase::remoteErrorOutput, this, &AndroidRunner::remoteErrorOutput); connect(&m_outputParser, &QmlDebug::QmlOutputParser::waitingForConnectionOnPort, @@ -840,6 +268,7 @@ void AndroidRunner::launchAVD() m_target->project(), deviceAPILevel, targetArch); AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber); m_androidRunnable.deviceSerialNumber = info.serialNumber; + m_androidRunnable.apiLevel = info.sdk; emit androidRunnableChanged(m_androidRunnable); if (info.isValid()) { AndroidAvdManager avdManager; @@ -872,5 +301,3 @@ void AndroidRunner::checkAVD() } // namespace Internal } // namespace Android - -#include "androidrunner.moc" diff --git a/src/plugins/android/androidrunner.h b/src/plugins/android/androidrunner.h index 0032fc37204..fc6b4e64701 100644 --- a/src/plugins/android/androidrunner.h +++ b/src/plugins/android/androidrunner.h @@ -43,7 +43,7 @@ namespace Android { namespace Internal { -class AndroidRunnerWorker; +class AndroidRunnerWorkerBase; class AndroidRunner : public ProjectExplorer::RunWorker { @@ -88,7 +88,7 @@ private: QString m_launchedAVDName; QThread m_thread; QTimer m_checkAVDTimer; - QScopedPointer m_worker; + QScopedPointer m_worker; QPointer m_target; Utils::Port m_gdbServerPort; QUrl m_qmlServer; diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp new file mode 100644 index 00000000000..11ae7d3db81 --- /dev/null +++ b/src/plugins/android/androidrunnerworker.cpp @@ -0,0 +1,569 @@ +/**************************************************************************** +** +** Copyright (C) 2018 BogDan Vatra +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "androidconfigurations.h" +#include "androidrunnerworker.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace std::placeholders; +using namespace ProjectExplorer; + +namespace Android { +namespace Internal { + + +static const QString pidScript = "pidof -s \"%1\""; +static const QString pidScriptPreNougat = QStringLiteral("for p in /proc/[0-9]*; " + "do cat <$p/cmdline && echo :${p##*/}; done"); +static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done"); + +static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date + "\\s+" + "[0-9\\-:.]*"// time + "\\s*" + "(\\d*)" // pid 1. capture + "\\s+" + "\\d*" // unknown + "\\s+" + "(\\w)" // message type 2. capture + "\\s+" + "(.*): " // source 3. capture + "(.*)" // message 4. capture + "[\\n\\r]*" + ); +static int APP_START_TIMEOUT = 45000; + +static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, + int msecs = APP_START_TIMEOUT) +{ + bool timedOut = false; + auto end = chrono::high_resolution_clock::now(); + if (chrono::duration_cast(end-start).count() > msecs) + timedOut = true; + return timedOut; +} + +static qint64 extractPID(const QByteArray &output, const QString &packageName) +{ + qint64 pid = -1; + foreach (auto tuple, output.split('\n')) { + tuple = tuple.simplified(); + if (!tuple.isEmpty()) { + auto parts = tuple.split(':'); + QString commandName = QString::fromLocal8Bit(parts.first()); + if (parts.length() == 2 && commandName == packageName) { + pid = parts.last().toLongLong(); + break; + } + } + } + return pid; +} + +void findProcessPIDPreNougat(QFutureInterface &fi, const QString &adbPath, + QStringList selector, const QString &packageName) +{ + if (packageName.isEmpty()) + return; + + qint64 processPID = -1; + chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); + do { + QThread::msleep(200); + const QByteArray out = Utils::SynchronousProcess() + .runBlocking(adbPath, selector << QStringLiteral("shell") << pidScriptPreNougat) + .allRawOutput(); + processPID = extractPID(out, packageName); + } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); + + if (!fi.isCanceled()) + fi.reportResult(processPID); +} + +void findProcessPID(QFutureInterface &fi, const QString &adbPath, + QStringList selector, const QString &packageName) +{ + if (packageName.isEmpty()) + return; + + qint64 processPID = -1; + chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); + do { + QThread::msleep(200); + const QByteArray out = Utils::SynchronousProcess() + .runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript.arg(packageName)) + .allRawOutput(); + if (!out.isEmpty()) + processPID = out.trimmed().toLongLong(); + } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); + + if (!fi.isCanceled()) + fi.reportResult(processPID); +} + +AndroidRunnerWorkerBase::AndroidRunnerWorkerBase(RunControl *runControl, const AndroidRunnable &runnable) + : m_androidRunnable(runnable) + , m_adbLogcatProcess(nullptr, deleter) + , m_psIsAlive(nullptr, deleter) + , m_logCatRegExp(regExpLogcat) + , m_gdbServerProcess(nullptr, deleter) + , m_jdbProcess(nullptr, deleter) + +{ + auto runConfig = runControl->runConfiguration(); + auto aspect = runConfig->extraAspect(); + Core::Id runMode = runControl->runMode(); + const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE; + m_useCppDebugger = debuggingMode && aspect->useCppDebugger(); + if (debuggingMode && aspect->useQmlDebugger()) + m_qmlDebugServices = QmlDebug::QmlDebuggerServices; + else if (runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE) + m_qmlDebugServices = QmlDebug::QmlProfilerServices; + else if (runMode == ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE) + m_qmlDebugServices = QmlDebug::QmlPreviewServices; + else + m_qmlDebugServices = QmlDebug::NoQmlDebugServices; + m_localGdbServerPort = Utils::Port(5039); + QTC_CHECK(m_localGdbServerPort.isValid()); + if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { + QTcpServer server; + QTC_ASSERT(server.listen(QHostAddress::LocalHost) + || server.listen(QHostAddress::LocalHostIPv6), + qDebug() << tr("No free ports available on host for QML debugging.")); + m_qmlServer.setScheme(Utils::urlTcpScheme()); + m_qmlServer.setHost(server.serverAddress().toString()); + m_qmlServer.setPort(server.serverPort()); + } + m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString(); + m_localJdbServerPort = Utils::Port(5038); + QTC_CHECK(m_localJdbServerPort.isValid()); +} + +AndroidRunnerWorkerBase::~AndroidRunnerWorkerBase() +{ + if (m_processPID != -1) + forceStop(); + + if (!m_pidFinder.isFinished()) + m_pidFinder.cancel(); +} + +bool AndroidRunnerWorkerBase::adbShellAmNeedsQuotes() +{ + // Between Android SDK Tools version 24.3.1 and 24.3.4 the quoting + // needs for the 'adb shell am start ...' parameters changed. + // Run a test to find out on what side of the fence we live. + // The command will fail with a complaint about the "--dummy" + // option on newer SDKs, and with "No intent supplied" on older ones. + // In case the test itself fails assume a new SDK. + Utils::SynchronousProcess adb; + adb.setTimeoutS(10); + Utils::SynchronousProcessResponse response + = adb.run(m_adb, selector() << "shell" << "am" << "start" + << "-e" << "dummy" << "dummy --dummy"); + if (response.result == Utils::SynchronousProcessResponse::StartFailed + || response.result != Utils::SynchronousProcessResponse::Finished) + return true; + + const QString output = response.allOutput(); + const bool oldSdk = output.contains("Error: No intent supplied"); + return !oldSdk; +} + +bool AndroidRunnerWorkerBase::runAdb(const QStringList &args, int timeoutS) +{ + Utils::SynchronousProcess adb; + adb.setTimeoutS(timeoutS); + Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args); + m_lastRunAdbError = response.exitMessage(m_adb, timeoutS); + m_lastRunAdbRawOutput = response.allRawOutput(); + return response.result == Utils::SynchronousProcessResponse::Finished; +} + +void AndroidRunnerWorkerBase::adbKill(qint64 pid) +{ + runAdb({"shell", "kill", "-9", QString::number(pid)}); + runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)}); +} + + +QStringList AndroidRunnerWorkerBase::selector() const +{ + return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber); +} + +void AndroidRunnerWorkerBase::forceStop() +{ + runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, 30); + + // try killing it via kill -9 + const QByteArray out = Utils::SynchronousProcess() + .runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScriptPreNougat) + .allRawOutput(); + + qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName); + if (pid != -1) { + adbKill(pid); + } +} + +void AndroidRunnerWorkerBase::logcatReadStandardError() +{ + if (m_processPID != -1) + logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true); +} + +void AndroidRunnerWorkerBase::logcatReadStandardOutput() +{ + if (m_processPID != -1) + logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false); +} + +void AndroidRunnerWorkerBase::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError) +{ + QList lines = text.split('\n'); + // lines always contains at least one item + lines[0].prepend(buffer); + if (!lines.last().endsWith('\n')) { + // incomplete line + buffer = lines.last(); + lines.removeLast(); + } else { + buffer.clear(); + } + + QString pidString = QString::number(m_processPID); + foreach (const QByteArray &msg, lines) { + const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n'); + if (!line.contains(pidString)) + continue; + if (m_useCppDebugger) { + switch (m_jdbState) { + case JDBState::Idle: + if (msg.trimmed().endsWith("Sending WAIT chunk")) { + m_jdbState = JDBState::Waiting; + handleJdbWaiting(); + } + break; + case JDBState::Waiting: + if (msg.indexOf("debugger has settled") > 0) { + m_jdbState = JDBState::Settled; + handleJdbSettled(); + } + break; + default: + break; + } + } + if (m_logCatRegExp.exactMatch(line)) { + // Android M + if (m_logCatRegExp.cap(1) == pidString) { + const QString &messagetype = m_logCatRegExp.cap(2); + QString output = line.mid(m_logCatRegExp.pos(2)); + + if (onlyError + || messagetype == QLatin1String("F") + || messagetype == QLatin1String("E") + || messagetype == QLatin1String("W")) + emit remoteErrorOutput(output); + else + emit remoteOutput(output); + } + } else { + if (onlyError || line.startsWith("F/") + || line.startsWith("E/") + || line.startsWith("W/")) + emit remoteErrorOutput(line); + else + emit remoteOutput(line); + } + } +} + +void AndroidRunnerWorkerBase::setAndroidRunnable(const AndroidRunnable &runnable) +{ + m_androidRunnable = runnable; +} + +void AndroidRunnerWorkerBase::asyncStart() +{ + forceStop(); + + // Start the logcat process before app starts. + std::unique_ptr logcatProcess(new QProcess, deleter); + connect(logcatProcess.get(), &QProcess::readyReadStandardOutput, + this, &AndroidRunnerWorkerBase::logcatReadStandardOutput); + connect(logcatProcess.get(), &QProcess::readyReadStandardError, + this, &AndroidRunnerWorkerBase::logcatReadStandardError); + // Its assumed that the device or avd returned by selector() is online. + logcatProcess->start(m_adb, selector() << "logcat"); + QTC_ASSERT(!m_adbLogcatProcess, /**/); + m_adbLogcatProcess = std::move(logcatProcess); + + for (const QString &entry: m_androidRunnable.beforeStartAdbCommands) + runAdb(entry.split(' ', QString::SkipEmptyParts)); + + QStringList args({"shell", "am", "start"}); + args << m_androidRunnable.amStartExtraArgs; + args << "-n" << m_androidRunnable.intentName; + if (m_useCppDebugger) { + args << "-D"; + QString gdbServerSocket; + // run-as pwd fails on API 22 so route the pwd through shell. + if (!runAdb({"shell", "run-as", m_androidRunnable.packageName, "/system/bin/sh", "-c", "pwd"})) { + emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError)); + return; + } + gdbServerSocket = QString::fromUtf8(m_lastRunAdbRawOutput.trimmed()) + "/debug-socket"; + + QString gdbServerExecutable; + if (!runAdb({"shell", "run-as", m_androidRunnable.packageName, "ls", "lib/"})) { + emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError)); + return; + } + + for (const auto &line: m_lastRunAdbRawOutput.split('\n')) { + if (line.indexOf("gdbserver") != -1/* || line.indexOf("lldb-server") != -1*/) { + gdbServerExecutable = QString::fromUtf8(line.trimmed()); + break; + } + } + + if (gdbServerExecutable.isEmpty()) { + emit remoteProcessFinished(tr("Can't find C++ debugger.")); + return; + } + + runAdb({"shell", "run-as", m_androidRunnable.packageName, "killall", gdbServerExecutable}); + runAdb({"shell", "run-as", m_androidRunnable.packageName, "rm", gdbServerSocket}); + std::unique_ptr gdbServerProcess(new QProcess, deleter); + gdbServerProcess->start(m_adb, selector() << "shell" << "run-as" + << m_androidRunnable.packageName << "lib/" + gdbServerExecutable + << "--multi" << "+" + gdbServerSocket); + if (!gdbServerProcess->waitForStarted()) { + emit remoteProcessFinished(tr("Failed to start C++ debugger.")); + return; + } + m_gdbServerProcess = std::move(gdbServerProcess); + QStringList removeForward{"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()}; + runAdb(removeForward); + if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(), + "localfilesystem:" + gdbServerSocket})) { + emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(m_lastRunAdbError)); + return; + } + m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' ')); + } + + if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { + // currently forward to same port on device and host + const QString port = QString("tcp:%1").arg(m_qmlServer.port()); + QStringList removeForward{{"forward", "--remove", port}}; + runAdb(removeForward); + if (!runAdb({"forward", port, port})) { + emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.") + .arg(m_lastRunAdbError)); + return; + } + m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' ')); + + args << "-e" << "qml_debug" << "true" + << "-e" << "qmljsdebugger" + << QString("port:%1,block,services:%2") + .arg(m_qmlServer.port()).arg(QmlDebug::qmlDebugServices(m_qmlDebugServices)); + } + + if (!m_androidRunnable.extraAppParams.isEmpty()) { + args << "-e" << "extraappparams" + << QString::fromLatin1(m_androidRunnable.extraAppParams.toUtf8().toBase64()); + } + + if (m_androidRunnable.extraEnvVars.size() > 0) { + args << "-e" << "extraenvvars" + << QString::fromLatin1(m_androidRunnable.extraEnvVars.toStringList().join('\t') + .toUtf8().toBase64()); + } + + if (!runAdb(args)) { + emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.") + .arg(m_lastRunAdbError)); + return; + } +} + +void AndroidRunnerWorkerBase::asyncStop() +{ + if (!m_pidFinder.isFinished()) + m_pidFinder.cancel(); + + if (m_processPID != -1) + forceStop(); + + m_jdbProcess.reset(); + m_gdbServerProcess.reset(); +} + +void AndroidRunnerWorkerBase::handleRemoteDebuggerRunning() +{} + +void AndroidRunnerWorkerBase::handleJdbWaiting() +{ + QStringList removeForward{"forward", "--remove", "tcp:" + m_localJdbServerPort.toString()}; + runAdb(removeForward); + if (!runAdb({"forward", "tcp:" + m_localJdbServerPort.toString(), + "jdwp:" + QString::number(m_processPID)})) { + emit remoteProcessFinished(tr("Failed to forward jdb debugging ports. Reason: %1.").arg(m_lastRunAdbError)); + return; + } + m_androidRunnable.afterFinishAdbCommands.push_back(removeForward.join(' ')); + + auto jdbPath = AndroidConfigurations::currentConfig().openJDKLocation().appendPath("bin"); + if (Utils::HostOsInfo::isWindowsHost()) + jdbPath.appendPath("jdb.exe"); + else + jdbPath.appendPath("jdb"); + + std::unique_ptr jdbProcess(new QProcess, deleter); + jdbProcess->setProcessChannelMode(QProcess::MergedChannels); + jdbProcess->start(jdbPath.toString(), QStringList() << "-connect" << + QString("com.sun.jdi.SocketAttach:hostname=localhost,port=%1") + .arg(m_localJdbServerPort.toString())); + if (!jdbProcess->waitForStarted()) { + emit remoteProcessFinished(tr("Failed to start JDB")); + return; + } + m_jdbProcess = std::move(jdbProcess); +} + +void AndroidRunnerWorkerBase::handleJdbSettled() +{ + auto waitForCommand = [&]() { + for (int i= 0; i < 5 && m_jdbProcess->state() == QProcess::Running; ++i) { + m_jdbProcess->waitForReadyRead(500); + QByteArray lines = m_jdbProcess->readAll(); + for (const auto &line: lines.split('\n')) { + auto msg = line.trimmed(); + if (msg.startsWith(">")) + return true; + } + } + return false; + }; + if (waitForCommand()) { + m_jdbProcess->write("cont\n"); + if (m_jdbProcess->waitForBytesWritten(5000) && waitForCommand()) { + m_jdbProcess->write("exit\n"); + m_jdbProcess->waitForBytesWritten(5000); + if (!m_jdbProcess->waitForFinished(5000)) { + m_jdbProcess->terminate(); + if (!m_jdbProcess->waitForFinished(5000)) { + m_jdbProcess->kill(); + m_jdbProcess->waitForFinished(); + } + } else if (m_jdbProcess->exitStatus() == QProcess::NormalExit && m_jdbProcess->exitCode() == 0) { + return; + } + } + } + emit remoteProcessFinished(tr("Can't attach jdb to the running application").arg(m_lastRunAdbError)); +} + +void AndroidRunnerWorkerBase::onProcessIdChanged(qint64 pid) +{ + // Don't write to m_psProc from a different thread + QTC_ASSERT(QThread::currentThread() == thread(), return); + m_processPID = pid; + if (pid == -1) { + emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.") + .arg(m_androidRunnable.packageName)); + // App died/killed. Reset log, monitor, jdb & gdb processes. + m_adbLogcatProcess.reset(); + m_psIsAlive.reset(); + m_jdbProcess.reset(); + m_gdbServerProcess.reset(); + + // Run adb commands after application quit. + for (const QString &entry: m_androidRunnable.afterFinishAdbCommands) + runAdb(entry.split(' ', QString::SkipEmptyParts)); + } else { + // In debugging cases this will be funneled to the engine to actually start + // and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. + emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID); + logcatReadStandardOutput(); + QTC_ASSERT(!m_psIsAlive, /**/); + m_psIsAlive.reset(new QProcess); + m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); + connect(m_psIsAlive.get(), static_cast(&QProcess::finished), + this, bind(&AndroidRunnerWorkerBase::onProcessIdChanged, this, -1)); + m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell") + << pidPollingScript.arg(m_processPID)); + } +} + + +AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable) + : AndroidRunnerWorkerBase(runControl, runnable) +{ +} + +void AndroidRunnerWorker::asyncStart() +{ + AndroidRunnerWorkerBase::asyncStart(); + m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(), + m_androidRunnable.packageName), + bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); +} + +AndroidRunnerWorkerPreNougat::AndroidRunnerWorkerPreNougat(RunControl *runControl, const AndroidRunnable &runnable) + : AndroidRunnerWorkerBase(runControl, runnable) +{ +} + +void AndroidRunnerWorkerPreNougat::asyncStart() +{ + AndroidRunnerWorkerBase::asyncStart(); + m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPIDPreNougat, m_adb, selector(), + m_androidRunnable.packageName), + bind(&AndroidRunnerWorkerPreNougat::onProcessIdChanged, this, _1)); + +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h new file mode 100644 index 00000000000..7a5c6614b85 --- /dev/null +++ b/src/plugins/android/androidrunnerworker.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2018 BogDan Vatra +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + + +#pragma once + +#include + +#include +#include + +#include "androidrunnable.h" + +namespace ProjectExplorer { +class RunControl; +} + +namespace Android { +namespace Internal { + +const int MIN_SOCKET_HANDSHAKE_PORT = 20001; + +static void deleter(QProcess *p) +{ + p->terminate(); + if (!p->waitForFinished(1000)) { + p->kill(); + p->waitForFinished(); + } + // Might get deleted from its own signal handler. + p->deleteLater(); +} + +class AndroidRunnerWorkerBase : public QObject +{ + Q_OBJECT +public: + AndroidRunnerWorkerBase(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable); + ~AndroidRunnerWorkerBase() override; + bool adbShellAmNeedsQuotes(); + bool runAdb(const QStringList &args, int timeoutS = 10); + void adbKill(qint64 pid); + QStringList selector() const; + void forceStop(); + void logcatReadStandardError(); + void logcatReadStandardOutput(); + void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError); + void setAndroidRunnable(const AndroidRunnable &runnable); + + virtual void asyncStart(); + virtual void asyncStop(); + virtual void handleRemoteDebuggerRunning(); + virtual void handleJdbWaiting(); + virtual void handleJdbSettled(); + +signals: + void remoteProcessStarted(Utils::Port gdbServerPort, const QUrl &qmlServer, int pid); + void remoteProcessFinished(const QString &errString = QString()); + + void remoteOutput(const QString &output); + void remoteErrorOutput(const QString &output); + +protected: + enum class JDBState { + Idle, + Waiting, + Settled + }; + virtual void onProcessIdChanged(qint64 pid); + + // Create the processes and timer in the worker thread, for correct thread affinity + AndroidRunnable m_androidRunnable; + QString m_adb; + qint64 m_processPID = -1; + std::unique_ptr m_adbLogcatProcess; + std::unique_ptr m_psIsAlive; + QByteArray m_stdoutBuffer; + QByteArray m_stderrBuffer; + QRegExp m_logCatRegExp; + QFuture m_pidFinder; + bool m_useCppDebugger = false; + QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; + Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket. + QUrl m_qmlServer; + QByteArray m_lastRunAdbRawOutput; + QString m_lastRunAdbError; + JDBState m_jdbState = JDBState::Idle; + Utils::Port m_localJdbServerPort; + std::unique_ptr m_gdbServerProcess; + std::unique_ptr m_jdbProcess; +}; + + +class AndroidRunnerWorker : public AndroidRunnerWorkerBase +{ + Q_OBJECT +public: + AndroidRunnerWorker(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable); + void asyncStart() override; +}; + + +class AndroidRunnerWorkerPreNougat : public AndroidRunnerWorkerBase +{ + Q_OBJECT +public: + AndroidRunnerWorkerPreNougat(ProjectExplorer::RunControl *runControl, const AndroidRunnable &runnable); + void asyncStart() override; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index 4e65d020a8c..d2c126b8b7d 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -48,15 +48,19 @@ GTestOutputReader::GTestOutputReader(const QFutureInterface &futu , m_executable(testApplication ? testApplication->program() : QString()) , m_projectFile(projectFile) { - // on Windows abort() will result in normal termination, but exit code will be set to 3 - if (Utils::HostOsInfo::isWindowsHost()) { connect(m_testApplication, static_cast(&QProcess::finished), this, [this] (int exitCode, QProcess::ExitStatus /*exitStatus*/) { - if (exitCode == 3) + if (exitCode == 1 && !m_description.isEmpty()) { + // TODO tr() + const QString message{"Running test(s) failed\n" + m_description + + "\nExecutable: " + m_executable}; + createAndReportResult(message, Result::MessageFatal); + } + // on Windows abort() will result in normal termination, but exit code will be set to 3 + if (Utils::HostOsInfo::isWindowsHost() && exitCode == 3) reportCrash(); }); - } } void GTestOutputReader::processOutput(const QByteArray &outputLine) diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp index c4215b68521..a0e5807298d 100644 --- a/src/plugins/autotest/testoutputreader.cpp +++ b/src/plugins/autotest/testoutputreader.cpp @@ -74,6 +74,14 @@ void TestOutputReader::reportCrash() m_futureInterface.reportResult(result); } +void TestOutputReader::createAndReportResult(const QString &message, Result::Type type) +{ + TestResultPtr result = createDefaultResult(); + result->setDescription(message); + result->setResult(type); + reportResult(result); +} + void TestOutputReader::reportResult(const TestResultPtr &result) { m_futureInterface.reportResult(result); diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h index bcb5e4614ed..e4935a11497 100644 --- a/src/plugins/autotest/testoutputreader.h +++ b/src/plugins/autotest/testoutputreader.h @@ -45,6 +45,7 @@ public: virtual void processOutput(const QByteArray &outputLine) = 0; virtual void processStdError(const QByteArray &output); void reportCrash(); + void createAndReportResult(const QString &message, Result::Type type); bool hadValidOutput() const { return m_hadValidOutput; } signals: diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui index 90f95a0e5e0..24a75872ba1 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui @@ -294,7 +294,9 @@ removeButton revertButton description + executable arguments + workingDirectory outputBehavior errorOutputBehavior environmentButton diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 741a295af2d..b9c829a27d4 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -477,16 +477,16 @@ void DebuggerRunTool::setCoreFileName(const QString &coreFile, bool isSnapshot) void DebuggerRunTool::appendInferiorCommandLineArgument(const QString &arg) { - if (!m_runParameters.inferior.commandLineArguments.isEmpty()) - m_runParameters.inferior.commandLineArguments.append(' '); - m_runParameters.inferior.commandLineArguments.append(arg); + QtcProcess::addArg(&m_runParameters.inferior.commandLineArguments, arg, + device() ? device()->osType() : HostOsInfo::hostOs()); } void DebuggerRunTool::prependInferiorCommandLineArgument(const QString &arg) { if (!m_runParameters.inferior.commandLineArguments.isEmpty()) m_runParameters.inferior.commandLineArguments.prepend(' '); - m_runParameters.inferior.commandLineArguments.prepend(arg); + m_runParameters.inferior.commandLineArguments.prepend( + QtcProcess::quoteArg(arg, device() ? device()->osType() : HostOsInfo::hostOs())); } void DebuggerRunTool::addQmlServerInferiorCommandLineArgumentIfNeeded() diff --git a/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp b/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp index 08c2dd36998..3ed926659c1 100644 --- a/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp +++ b/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp @@ -82,8 +82,10 @@ void QmakeAndroidRunConfiguration::updateDisplayName() const QmakeProjectManager::QmakeProFileNode *root = project->rootProjectNode(); if (root) { const QmakeProjectManager::QmakeProFileNode *node = root->findProFileFor(proFilePath()); - if (node) // should always be found + if (node) { // should always be found + setDisplayName(node->displayName()); setDefaultDisplayName(node->displayName()); + } } } diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 1a46a290381..612e8a17bc0 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -421,7 +421,6 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile *file, QmakeProFile::AsyncUp return; } - emitParsingStarted(); file->setParseInProgressRecursive(true); setAllBuildConfigurationsEnabled(false); @@ -478,9 +477,6 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile::AsyncUpdateDelay delay) return; } - if (m_asyncUpdateState != Base) - emitParsingStarted(); - rootProFile()->setParseInProgressRecursive(true); setAllBuildConfigurationsEnabled(false); @@ -503,12 +499,15 @@ void QmakeProject::startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay) m_asyncUpdateTimer.stop(); m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(), delay == QmakeProFile::ParseLater ? UPDATE_INTERVAL : 0)); + if (!isParsing()) + emitParsingStarted(); m_asyncUpdateTimer.start(); } void QmakeProject::incrementPendingEvaluateFutures() { ++m_pendingEvaluateFuturesCount; + QTC_ASSERT(isParsing(), emitParsingStarted()); m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(), m_asyncUpdateFutureInterface->progressMaximum() + 1); } diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index 9774aa67319..98345f8e6f2 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -253,8 +254,8 @@ LocalQmlProfilerSupport::LocalQmlProfilerSupport(QmlProfilerTool *profilerTool, else QTC_CHECK(false); - QString arguments = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlProfilerServices, - code, true); + QString arguments = Utils::QtcProcess::quoteArg( + QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlProfilerServices, code, true)); if (!debuggee.commandLineArguments.isEmpty()) arguments += ' ' + debuggee.commandLineArguments; diff --git a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp index e4591ac30e7..2ee59069359 100644 --- a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp +++ b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -148,6 +149,31 @@ void LocalQmlProfilerRunnerTest::testRunner() runControl->initiateFinish(); QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); + + debuggee.commandLineArguments = QString("-test QmlProfiler,"); + serverUrl.setScheme(Utils::urlSocketScheme()); + { + Utils::TemporaryFile file("file with spaces"); + if (file.open()) + serverUrl.setPath(file.fileName()); + } + + runControl = new ProjectExplorer::RunControl(nullptr, + ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + runControl->setRunnable(debuggee); + profiler = new LocalQmlProfilerSupport(&tool, runControl, serverUrl); + connectRunner(); + runControl->initiateStart(); + + QTRY_VERIFY_WITH_TIMEOUT(running, 30000); + QTRY_VERIFY_WITH_TIMEOUT(!running, 30000); + QCOMPARE(startCount, 4); + QCOMPARE(stopCount, 4); + QCOMPARE(runCount, 3); + + runControl->initiateFinish(); + QTRY_VERIFY(runControl.isNull()); + QVERIFY(profiler.isNull()); } void LocalQmlProfilerRunnerTest::testFindFreePort() diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp index 8001327dedd..fa6a61b2672 100644 --- a/src/plugins/qnx/qnxanalyzesupport.cpp +++ b/src/plugins/qnx/qnxanalyzesupport.cpp @@ -75,11 +75,10 @@ void QnxQmlProfilerSupport::start() serverUrl.setScheme("tcp"); m_profiler->recordData("QmlServerUrl", serverUrl); - QString args = QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, qmlPort); auto r = runnable().as(); - if (!r.commandLineArguments.isEmpty()) - r.commandLineArguments.append(' '); - r.commandLineArguments += args; + QtcProcess::addArg(&r.commandLineArguments, + QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, qmlPort), + device()->osType()); setRunnable(r); diff --git a/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp b/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp index 836ab11fd56..933c91f4762 100644 --- a/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp +++ b/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp @@ -28,6 +28,7 @@ #include #include +#include #include using namespace ProjectExplorer; @@ -66,11 +67,9 @@ void RemoteLinuxQmlToolingSupport::start() serverUrl.setPort(qmlPort.number()); m_runworker->recordData("QmlServerUrl", serverUrl); - QString args = QmlDebug::qmlDebugTcpArguments(m_services, qmlPort); auto r = runnable().as(); - if (!r.commandLineArguments.isEmpty()) - r.commandLineArguments.append(' '); - r.commandLineArguments += args; + QtcProcess::addArg(&r.commandLineArguments, QmlDebug::qmlDebugTcpArguments(m_services, qmlPort), + device()->osType()); setRunnable(r); diff --git a/src/shared/qbs b/src/shared/qbs index 8da0e97f40a..fee6a037b7b 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 8da0e97f40abf19a089197c6aec8333a4e92d729 +Subproject commit fee6a037b7b28afdc2dfd6297eb089d3e5888ff7 diff --git a/tests/system/README b/tests/system/README index 94ddbbc4265..b309c607537 100644 --- a/tests/system/README +++ b/tests/system/README @@ -25,30 +25,25 @@ Second - some of the test suites/test cases expect a build of Qt 4.8.7 to be ava * Other: make -j -Third - some of the test suites/test cases expect Qt 5.3.1 (default toolchain), Qt 5.4.1 (gcc, Linux and Windows only) and Qt 5.6.1-1 (default toolchain) -installed in their default locations. On Linux/Mac this is ~/Qt5.x.1 and on Windows this is C:\Qt\Qt5.x.1. The default toolchains are gcc on Linux, -clang on Mac and MSVC2010 (Qt <= 5.5) or MSVC2013 (Qt > 5.5) on Windows. It's easiest to use default installations of the official opensource Qt packages. +Third - some of the test suites/test cases expect Qt versions to be installed in their default +locations. On Linux/macOS this is ~/Qt5.x.1 and on Windows this is C:\Qt\Qt5.x.1. It's easiest to +use installations of the official opensource Qt packages. Just install the Qt version for the +respective toolchain and if possible the additional module Qt Script. Do not install Qt WebEngine. +The exact versions and toolchains are: -On macOS it might be necessary to tweak some files of the Qt installation - depending on the installed Xcode and its installed SDKs. -If Xcode 8+ is installed you will need to update the file -~/Qt5.3.1/5.3/clang_64/mkspecs/features/mac/default_pre.prf -Change the line +Linux: +Qt 5.4.1 (gcc) +Qt 5.6.1-1 (gcc) +Qt 5.10.1 (gcc) - isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null"))) +macOS: +Qt 5.6.1-1 (clang) +Qt 5.10.1 (clang) -to - - isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null"))) - -Furthermore - depending on the installed SDK it might be necessary to update the file -~/Qt5.3.1/5.3/clang_64/mkspecs/qdevice.pri -Search for the following line - - !host_build:QMAKE_MAC_SDK = macosx10.8 - -Newer Xcode might miss the 10.8 SDK. Check -/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs -which SDKs are installed and update the version number accordingly. +Windows: +Qt 5.4.1 (gcc) +Qt 5.6.1-1 (MSVC2013, 32 bit) +Qt 5.10.1 (MSVC2015, 32 bit) Fourth - you'll have to provide some additional repositories (and for the hooking into subprocesses even some more Squish bundles, see below). These additional repositories are located inside ~/squish-data or C:\Users\\squish-data (depending on the OS you're on). diff --git a/tests/system/objects.map b/tests/system/objects.map index 388424259bd..fce477feb63 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -192,6 +192,7 @@ :Select a Git Commit.detailsText_QPlainTextEdit {name='detailsText' type='QPlainTextEdit' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} :Select a Git Commit.workingDirectoryEdit_QLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} :Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git__Internal__ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'} +:Select signal.signalList_QTreeView {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeView' visible='1'} :Select signal.signalList_QTreeWidget {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeWidget' visible='1'} :Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.Description:_QLabel {name='descriptionLabel' text='Description:' type='QLabel' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} diff --git a/tests/system/settings/mac/QtProject/qtcreator/profiles.xml b/tests/system/settings/mac/QtProject/qtcreator/profiles.xml index a3f0a20048a..c7548b95d16 100644 --- a/tests/system/settings/mac/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/mac/QtProject/qtcreator/profiles.xml @@ -101,7 +101,7 @@ :///DESKTOP/// {a1e860d1-c241-4abf-80fe-cf0c9f0a43b3} - Desktop 5.3.1 default + Desktop 5.10.1 default false diff --git a/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml b/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml index 77b68271abb..f9a0babe523 100644 --- a/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml @@ -16,7 +16,7 @@ QtVersion.1 9 - Desktop Qt 5.6.1 (SQUISH_DEFAULT_COMPILER) + Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) ~/Qt5.6.1/5.6/SQUISH_DEFAULT_COMPILER/bin/qmake Qt4ProjectManager.QtVersion.Desktop false @@ -26,8 +26,8 @@ QtVersion.2 11 - Desktop Qt 5.3.1 (SQUISH_DEFAULT_COMPILER) - ~/Qt5.3.1/5.3/SQUISH_DEFAULT_COMPILER/bin/qmake + Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) + ~/Qt5.10.1/5.10.1/SQUISH_DEFAULT_COMPILER/bin/qmake Qt4ProjectManager.QtVersion.Desktop false diff --git a/tests/system/settings/unix/QtProject/qtcreator/profiles.xml b/tests/system/settings/unix/QtProject/qtcreator/profiles.xml index 1f56742fe09..627451db8b3 100644 --- a/tests/system/settings/unix/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/unix/QtProject/qtcreator/profiles.xml @@ -1,6 +1,6 @@ - + Profile.0 @@ -50,29 +50,6 @@ Profile.2 - - false - - - - {70e26273-2c0b-4534-bbc0-eb6ca670821a} - Desktop Device - Desktop - - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - 11 - - :///DESKTOP/// - {e91398ba-6443-4b02-b416-782a70d9df90} - - Desktop 5.3.1 default - false - - - - - Profile.3 false @@ -96,7 +73,7 @@ - Profile.4 + Profile.3 false @@ -119,13 +96,41 @@ + + Profile.4 + + false + + + + + {70e26273-2c0b-4534-bbc0-eb6ca670821a} + Desktop Device + Desktop + + + + {7bfd4fd4-e64a-417f-b10f-20602e1719d1} + {c3f59b87-6997-4bd8-8067-ee04dc536371} + + + 17 + + + {0d32ea2d-f954-4011-8f7c-80da4977c94c} + + Desktop 5.10.1 default + false + + + Profile.Count 5 Profile.Default - {e91398ba-6443-4b02-b416-782a70d9df90} + {542217c7-ce0f-48f7-843b-d4fad339688d} Version diff --git a/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml b/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml index b6463a4f6e0..90e86a4f6a8 100644 --- a/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml @@ -1,6 +1,6 @@ - + QtVersion.0 @@ -14,16 +14,6 @@ QtVersion.1 - - 11 - Qt 5.3.1 (SQUISH_DEFAULT_COMPILER) - ~/Qt5.3.1/5.3/SQUISH_DEFAULT_COMPILER/bin/qmake - Qt4ProjectManager.QtVersion.Desktop - false - - - - QtVersion.2 13 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) @@ -33,7 +23,7 @@ - QtVersion.3 + QtVersion.2 15 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) @@ -42,6 +32,16 @@ false + + QtVersion.3 + + 17 + Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) + ~/Qt5.10.1/5.10.1/SQUISH_DEFAULT_COMPILER/bin/qmake + Qt4ProjectManager.QtVersion.Desktop + false + + Version 1 diff --git a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml index 70eda53be2e..ac1911748fb 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml @@ -1,6 +1,6 @@ - + Profile.0 @@ -32,34 +32,6 @@ Profile.1 - - false - - - - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} - Desktop Device - Desktop - - - ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2} - - {93e707bd-236f-4d8d-917d-814aa358024b} - ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2} - - - 20 - - :///DESKTOP/// - {6a95566e-8372-4372-8286-ef73af7de191} - - Desktop 5.3.1 default - false - - - - - Profile.2 false @@ -87,7 +59,7 @@ - Profile.3 + Profile.2 false @@ -114,13 +86,41 @@ + + Profile.3 + + false + + + + + {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} + Desktop Device + Desktop + + + + {c96cfaf3-fb8a-472b-b3c7-e94e8c490f17} + {ed856706-2a9d-4745-9d85-4e322b6f91d4} + + + 26 + + + {37fad24d-07f3-442e-8c65-d9ded3633f7e} + + Desktop 5.10.1 default + false + + + Profile.Count 4 Profile.Default - {6a95566e-8372-4372-8286-ef73af7de191} + {f9c7858c-d167-4b78-847a-91943bd0af07} Version diff --git a/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml b/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml index 713c9acea63..e9c384e6343 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml @@ -1,6 +1,6 @@ - + QtVersion.0 @@ -14,16 +14,6 @@ QtVersion.1 - - 20 - Qt 5.3.1 (msvc2010_opengl) - C:/Qt/Qt5.3.1/5.3/msvc2010_opengl/bin/qmake.exe - Qt4ProjectManager.QtVersion.Desktop - false - - - - QtVersion.2 22 Qt %{Qt:Version} (mingw491_32) @@ -33,7 +23,7 @@ - QtVersion.3 + QtVersion.2 24 Qt %{Qt:Version} (msvc2013) @@ -42,6 +32,16 @@ false + + QtVersion.3 + + 26 + Qt %{Qt:Version} (msvc2015) + C:/Qt/Qt5.10.1/5.10.1/msvc2015/bin/qmake.exe + Qt4ProjectManager.QtVersion.Desktop + false + + Version 1 diff --git a/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml b/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml index 0db49a0028b..1dea56573ee 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml @@ -4,17 +4,6 @@ ToolChain.0 - - x86-windows-msvc2010-pe-32bit - c:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/vcvarsall.bat - x86 - true - Microsoft Visual C++ Compiler 10.0 (x86) - ProjectExplorer.ToolChain.Msvc:{1186dad9-c485-4f69-b7e1-aff54c89ecb2} - - - - ToolChain.1 C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/g++.exe @@ -29,19 +18,7 @@ - ToolChain.2 - - x86-windows-msvc2010-pe-32bit - C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/vcvarsall.bat - x86 - true - Microsoft Visual C++ Compiler 10.0 (x86) - ProjectExplorer.ToolChain.Msvc:{93e707bd-236f-4d8d-917d-814aa358024b} - 1 - - - - ToolChain.3 + ToolChain.1 x86-windows-msvc2013-pe-32bit C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat @@ -53,7 +30,7 @@ - ToolChain.4 + ToolChain.2 i686-w64-mingw32 C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/gcc.exe @@ -69,6 +46,32 @@ 1 + + ToolChain.3 + + x86-windows-msvc2015-pe-32bit + C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat + x86 + true + Microsoft Visual C++ Compiler 14.0 (x86) + ProjectExplorer.ToolChain.Msvc:{c96cfaf3-fb8a-472b-b3c7-e94e8c490f17} + 1 + C + + + + ToolChain.4 + + x86-windows-msvc2015-pe-32bit + C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat + x86 + true + Microsoft Visual C++ Compiler 14.0 (x86) + ProjectExplorer.ToolChain.Msvc:{ed856706-2a9d-4745-9d85-4e322b6f91d4} + 2 + Cxx + + ToolChain.5 diff --git a/tests/system/shared/classes.py b/tests/system/shared/classes.py index 402181cfa54..d06398a159b 100644 --- a/tests/system/shared/classes.py +++ b/tests/system/shared/classes.py @@ -32,9 +32,9 @@ class Targets: (DESKTOP_4_8_7_DEFAULT, EMBEDDED_LINUX, - DESKTOP_5_3_1_DEFAULT, DESKTOP_5_4_1_GCC, - DESKTOP_5_6_1_DEFAULT) = ALL_TARGETS + DESKTOP_5_6_1_DEFAULT, + DESKTOP_5_10_1_DEFAULT) = ALL_TARGETS @staticmethod def availableTargetClasses(): @@ -62,12 +62,12 @@ class Targets: return "Desktop 4.8.7 default" elif target == Targets.EMBEDDED_LINUX: return "Embedded Linux" - elif target == Targets.DESKTOP_5_3_1_DEFAULT: - return "Desktop 5.3.1 default" elif target == Targets.DESKTOP_5_4_1_GCC: return "Desktop 5.4.1 GCC" elif target == Targets.DESKTOP_5_6_1_DEFAULT: return "Desktop 5.6.1 default" + elif target == Targets.DESKTOP_5_10_1_DEFAULT: + return "Desktop 5.10.1 default" else: return None @@ -83,7 +83,7 @@ class Targets: @staticmethod def getDefaultKit(): - return Targets.DESKTOP_5_3_1_DEFAULT + return Targets.DESKTOP_5_6_1_DEFAULT # this class holds some constants for easier usage inside the Projects view class ProjectSettings: @@ -169,7 +169,7 @@ class Qt5Path: @staticmethod def getPaths(pathSpec): - qt5targets = [Targets.DESKTOP_5_3_1_DEFAULT, Targets.DESKTOP_5_6_1_DEFAULT] + qt5targets = [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT] if platform.system() != 'Darwin': qt5targets.append(Targets.DESKTOP_5_4_1_GCC) if pathSpec == Qt5Path.DOCS: @@ -223,20 +223,20 @@ class Qt5Path: @staticmethod def examplesPath(target): qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target) - if qtMinorVersion == 2: - path = "examples" - else: + if qtMinorVersion < 10: path = "Examples/Qt-5.%d" % qtMinorVersion + else: + path = "Examples/Qt-5.%d.%d" % (qtMinorVersion, qtPatchVersion) return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path) @staticmethod def docsPath(target): qtMinorVersion, qtPatchVersion = Qt5Path.getQtMinorAndPatchVersion(target) - if qtMinorVersion == 2: - path = "doc" - else: + if qtMinorVersion < 10: path = "Docs/Qt-5.%d" % qtMinorVersion + else: + path = "Docs/Qt-5.%d.%d" % (qtMinorVersion, qtPatchVersion) return os.path.join(Qt5Path.__createPlatformQtPath__(qtMinorVersion), path) diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 1a35e81aa50..6004986a78d 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -315,7 +315,7 @@ def createNewQtQuickUI(workingDir, qtVersion = "5.6"): return projectName -def createNewQmlExtension(workingDir, targets=[Targets.DESKTOP_5_3_1_DEFAULT]): +def createNewQmlExtension(workingDir, targets=[Targets.DESKTOP_5_6_1_DEFAULT]): available = __createProjectOrFileSelectType__(" Library", "Qt Quick 2 Extension Plugin") if workingDir == None: workingDir = tempDir() @@ -639,10 +639,11 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False): version = res.group("version") else: version = None - if 'only available with Qt 5.6' in text: - result = [Targets.DESKTOP_5_6_1_DEFAULT] - elif 'available with Qt 5.7 and later' in text: - result = [] # FIXME we have currently no Qt5.7+ available in predefined settings + if templateName.startswith("Qt Quick Application - "): + if templateName == "Qt Quick Application - Empty": + result = [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT] + else: + result = [Targets.DESKTOP_5_10_1_DEFAULT] elif 'Supported Platforms' in text: supports = text[text.find('Supported Platforms'):].split(":")[1].strip().split(" ") result = [] @@ -651,7 +652,7 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False): result.append(Targets.DESKTOP_4_8_7_DEFAULT) if platform.system() in ("Linux", "Darwin"): result.append(Targets.EMBEDDED_LINUX) - result.extend([Targets.DESKTOP_5_3_1_DEFAULT, Targets.DESKTOP_5_6_1_DEFAULT]) + result.extend([Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT]) if platform.system() != 'Darwin': result.append(Targets.DESKTOP_5_4_1_GCC) elif 'Platform independent' in text: diff --git a/tests/system/suite_CSUP/tst_CSUP06/test.py b/tests/system/suite_CSUP/tst_CSUP06/test.py index 40d924d2c5b..baf42187775 100644 --- a/tests/system/suite_CSUP/tst_CSUP06/test.py +++ b/tests/system/suite_CSUP/tst_CSUP06/test.py @@ -165,7 +165,7 @@ def main(): with TestSection(getCodeModelString(useClang)): if not startCreator(useClang): continue - openQmakeProject(examplePath, [Targets.DESKTOP_5_3_1_DEFAULT]) + openQmakeProject(examplePath, [Targets.DESKTOP_5_6_1_DEFAULT]) checkCodeModelSettings(useClang) if not openDocument("cplusplus-tools.Sources.main\\.cpp"): earlyExit("Failed to open main.cpp.") diff --git a/tests/system/suite_QMLS/tst_QMLS03/test.py b/tests/system/suite_QMLS/tst_QMLS03/test.py index 11408d700b5..0287c352f36 100644 --- a/tests/system/suite_QMLS/tst_QMLS03/test.py +++ b/tests/system/suite_QMLS/tst_QMLS03/test.py @@ -80,7 +80,7 @@ def main(): if not startedWithoutPluginError(): return # open example project - openQmakeProject(examplePath, [Targets.DESKTOP_5_3_1_DEFAULT]) + openQmakeProject(examplePath, [Targets.DESKTOP_5_6_1_DEFAULT]) # open qml file openDocument("animation.Resources.animation\\.qrc./animation.basics.color-animation\\.qml") # get editor diff --git a/tests/system/suite_WELP/tst_WELP03/test.py b/tests/system/suite_WELP/tst_WELP03/test.py index 3347f34f373..34d0de76aa0 100644 --- a/tests/system/suite_WELP/tst_WELP03/test.py +++ b/tests/system/suite_WELP/tst_WELP03/test.py @@ -123,7 +123,7 @@ def main(): for p in proFiles: removePackagingDirectory(os.path.dirname(p)) examplesLineEdit = waitForObject(search %(expect[1][0], expect[1][1])) - example = openExample(examplesLineEdit, "address book", "Address Book.*", + example = openExample(examplesLineEdit, "address book", "(0000 )?Address Book.*", "Address Book Example") if example is not None: # close second example application diff --git a/tests/system/suite_debugger/tst_simple_analyze/test.py b/tests/system/suite_debugger/tst_simple_analyze/test.py index 07166a8bf4d..c366d535bc8 100644 --- a/tests/system/suite_debugger/tst_simple_analyze/test.py +++ b/tests/system/suite_debugger/tst_simple_analyze/test.py @@ -108,7 +108,7 @@ def performTest(workingDir, projectName, targetCount, availableConfigs): colMean, colMedian, colLongest, colShortest) = range(2, 11) model = waitForObject(":Events.QmlProfilerEventsTable_QmlProfiler::" "Internal::QmlProfilerStatisticsMainView").model() - compareEventsTab(model, "events_qt5.tsv") + compareEventsTab(model, "events_qt%s.tsv" % qtVersion) test.compare(dumpItems(model, column=colPercent)[0], '100.00 %') # cannot run following test on colShortest (unstable) for i in [colTotal, colMean, colMedian, colLongest]: diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv new file mode 100644 index 00000000000..4038c164c97 --- /dev/null +++ b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv @@ -0,0 +1,10 @@ +"0" "1" "6" "11" +"" "" "1" "Main Program" +"main.qml:15" "Handling Signal" "2" "onTriggered: { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" +"main.qml:15" "JavaScript" "2" "onTriggered" +"main.qml:4" "Creating" "2" "QtQuick.Window/Window" +"main.qml:0" "Compiling" "1" "main.qml" +"main.qml:10" "Creating" "2" "QtQuick/Timer" +"main.qml:14" "Binding" "3" "running: runCount < 2" +"main.qml:14" "JavaScript" "3" "expression for running" +"" "Binding" "1" "Source code not available" diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.6.1.tsv similarity index 100% rename from tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.tsv rename to tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.6.1.tsv diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index 99e41de9751..adfe3d60442 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -27,7 +27,6 @@ source("../../shared/qtcreator.py") def main(): global tmpSettingsDir, availableBuildSystems - qtVersionsForQuick = ["5.6"] availableBuildSystems = ["qmake", "Qbs"] if which("cmake"): availableBuildSystems.append("CMake") @@ -72,6 +71,7 @@ def main(): template = current.values()[0] displayedPlatforms = __createProject__(category, template) if template.startswith("Qt Quick Application - "): + qtVersionsForQuick = ["5.6", "5.10"] if template == "Qt Quick Application - Empty" else ["5.10"] for counter, qtVersion in enumerate(qtVersionsForQuick): def additionalFunc(displayedPlatforms, qtVersion): requiredQtVersion = __createProjectHandleQtQuickSelection__(qtVersion) @@ -128,9 +128,7 @@ def handleBuildSystemVerifyKits(category, template, kits, displayedPlatforms, test.log("Using build system '%s'" % buildSystem) selectFromCombo(combo, buildSystem) clickButton(waitForObject(":Next_QPushButton")) - if (template.startswith("Qt Quick Application - ") - and template != "Qt Quick Application - Empty"): - test.warning("No suitable Qt version available for '%s'" % template) + if template == "Qt Quick Application - Scroll": clickButton(waitForObject(":Next_QPushButton")) elif specialHandlingFunc: specialHandlingFunc(displayedPlatforms, *args) diff --git a/tests/system/suite_general/tst_default_settings/test.py b/tests/system/suite_general/tst_default_settings/test.py index a4230fe17fa..088d7246f6c 100644 --- a/tests/system/suite_general/tst_default_settings/test.py +++ b/tests/system/suite_general/tst_default_settings/test.py @@ -188,9 +188,10 @@ def __getExpectedCompilers__(): compilers.extend(findAllFilesInPATH("*g++*")) compilers.extend(findAllFilesInPATH("*gcc*")) if platform.system() == 'Darwin': - xcodeClang = getOutputFromCmdline(["xcrun", "--find", "clang++"]).strip("\n") - if xcodeClang and os.path.exists(xcodeClang) and xcodeClang not in expected: - expected.append(xcodeClang) + for compilerExe in ('clang++', 'clang'): + xcodeClang = getOutputFromCmdline(["xcrun", "--find", compilerExe]).strip("\n") + if xcodeClang and os.path.exists(xcodeClang) and xcodeClang not in expected: + expected.append(xcodeClang) for compiler in compilers: compilerPath = which(compiler) if compilerPath: diff --git a/tests/system/suite_general/tst_openqt_creator/test.py b/tests/system/suite_general/tst_openqt_creator/test.py index 6d401784704..391c3d0bef7 100644 --- a/tests/system/suite_general/tst_openqt_creator/test.py +++ b/tests/system/suite_general/tst_openqt_creator/test.py @@ -64,7 +64,7 @@ def main(): openGeneralMessages() # Verify messages appear once, from using default kit before configuring generalMessages = str(waitForObject(":Qt Creator_Core::OutputWindow").plainText) - test.compare(generalMessages.count("Project MESSAGE: Cannot build Qt Creator with Qt version 5.3.1."), 1, + test.compare(generalMessages.count("Project MESSAGE: Cannot build Qt Creator with Qt version 5.6.1."), 2, "Warning about outdated Qt shown?") test.compare(generalMessages.count("Project ERROR: Use at least Qt 5.6.2."), 2, "Minimum Qt version shown (once when parsing with default kit, once with selected)?") diff --git a/tests/system/suite_general/tst_rename_file/test.py b/tests/system/suite_general/tst_rename_file/test.py index 7bf2c882ea7..0063b638e59 100644 --- a/tests/system/suite_general/tst_rename_file/test.py +++ b/tests/system/suite_general/tst_rename_file/test.py @@ -53,6 +53,8 @@ def main(): ["Resources", "adding.qrc"], ["QML", "example.qml"]]: filenames = ["ABCD" + filename.upper(), "abcd" + filename.lower(), "test", "TEST", filename] + if (filename.endswith(".qrc") and JIRA.isBugStillOpen(20101)): + filenames.remove("ABCD" + filename.upper()) previous = filenames[-1] for filename in filenames: tempFiletype = filetype diff --git a/tests/system/suite_general/tst_session_handling/test.py b/tests/system/suite_general/tst_session_handling/test.py index 673aae739a6..bbce227979b 100644 --- a/tests/system/suite_general/tst_session_handling/test.py +++ b/tests/system/suite_general/tst_session_handling/test.py @@ -39,7 +39,7 @@ def main(): "Verifying window title contains created session name.") checkWelcomePage(sessionName, True) for project in projects: - openQmakeProject(project, [Targets.DESKTOP_5_3_1_DEFAULT]) + openQmakeProject(project, [Targets.DESKTOP_5_6_1_DEFAULT]) progressBarWait(20000) checkNavigator(52, "Verifying whether all projects have been opened.") openDocument("animation.Resources.animation\\.qrc./animation.basics.animators\\.qml") diff --git a/tests/system/suite_qtquick/tst_qml_outline/test.py b/tests/system/suite_qtquick/tst_qml_outline/test.py index f3ca4885ac1..e14c5eb5394 100644 --- a/tests/system/suite_qtquick/tst_qml_outline/test.py +++ b/tests/system/suite_qtquick/tst_qml_outline/test.py @@ -39,7 +39,7 @@ def main(): startApplication("qtcreator" + SettingsPath) if not startedWithoutPluginError(): return - openQmakeProject(os.path.join(templateDir, proFile), [Targets.DESKTOP_5_3_1_DEFAULT]) + openQmakeProject(os.path.join(templateDir, proFile), [Targets.DESKTOP_5_6_1_DEFAULT]) qmlFiles = [treebase + "focus\\.qml", treebase + "Core.ListMenu\\.qml"] checkOutlineFor(qmlFiles) testModify() diff --git a/tests/system/suite_qtquick/tst_qtquick_creation3/test.py b/tests/system/suite_qtquick/tst_qtquick_creation3/test.py index b47da5d58d7..24a43f18e75 100644 --- a/tests/system/suite_qtquick/tst_qtquick_creation3/test.py +++ b/tests/system/suite_qtquick/tst_qtquick_creation3/test.py @@ -35,14 +35,8 @@ def main(): # using a temporary directory won't mess up a potentially existing workingDir = tempDir() projectName = createNewQtQuickUI(workingDir, qtVersion) - kit = Targets.getStringForTarget(Targets.DESKTOP_5_6_1_DEFAULT) - if addAndActivateKit(Targets.DESKTOP_5_6_1_DEFAULT): - quick = "2.6" - else: - test.fatal("Failed to activate kit %s" % kit) - continue - test.log("Running project Qt Quick UI Prototype (%s)" % kit) - qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(2, 1, workingDir, projectName, 11223, quick) + quick = "2.6" + qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(1, 0, workingDir, projectName, 11223, quick) if qmlViewer!=None: qmlViewerPath = os.path.dirname(qmlViewer) qmlViewer = os.path.basename(qmlViewer) diff --git a/tests/system/suite_qtquick/tst_qtquick_creation4/test.py b/tests/system/suite_qtquick/tst_qtquick_creation4/test.py index a39ac6f8670..a1bda108ec2 100644 --- a/tests/system/suite_qtquick/tst_qtquick_creation4/test.py +++ b/tests/system/suite_qtquick/tst_qtquick_creation4/test.py @@ -29,7 +29,7 @@ def main(): startApplication("qtcreator" + SettingsPath) if not startedWithoutPluginError(): return - for target in [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_3_1_DEFAULT]: + for target in [Targets.DESKTOP_5_6_1_DEFAULT, Targets.DESKTOP_5_10_1_DEFAULT]: # using a temporary directory won't mess up a potentially existing createNewQmlExtension(tempDir(), [target]) # wait for parsing to complete diff --git a/tests/system/suite_tools/tst_designer_goto_slot/test.py b/tests/system/suite_tools/tst_designer_goto_slot/test.py index 56200fa3fdd..18aa5730b14 100644 --- a/tests/system/suite_tools/tst_designer_goto_slot/test.py +++ b/tests/system/suite_tools/tst_designer_goto_slot/test.py @@ -39,7 +39,8 @@ def main(): ":FormEditorStack_qdesigner_internal::FormWindow", 20, widgets[current], Qt.CopyAction) connections = [] for record in testData.dataset("connections.tsv"): - connections.append([testData.field(record, col) for col in ["widget", "signal", "slot"]]) + connections.append([testData.field(record, col) for col in ["widget", "baseclass", + "signal", "slot"]]) for con in connections: selectFromLocator("mainwindow.ui") openContextMenu(waitForObject(con[0]), 5, 5, 0) @@ -49,13 +50,21 @@ def main(): waitFor("macHackActivateContextMenuItem('Go to slot...', con[0])", 6000) else: activateItem(waitForObjectItem("{type='QMenu' unnamed='1' visible='1'}", "Go to slot...")) - waitForObjectItem(":Select signal.signalList_QTreeWidget", con[1]) - clickItem(":Select signal.signalList_QTreeWidget", con[1], 5, 5, 0, Qt.LeftButton) + try: + # Creator built with Qt <= 5.9 + signalWidgetObject = waitForObject(":Select signal.signalList_QTreeWidget", 5000) + signalName = con[2] + except: + # Creator built with Qt >= 5.10 + signalWidgetObject = waitForObject(":Select signal.signalList_QTreeView") + signalName = con[1] + "." + con[2] + waitForObjectItem(signalWidgetObject, signalName) + clickItem(signalWidgetObject, signalName, 5, 5, 0, Qt.LeftButton) clickButton(waitForObject(":Go to slot.OK_QPushButton")) editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget") type(editor, "") type(editor, "") - test.verify(waitFor('str(lineUnderCursor(editor)).strip() == con[2]', 1000), - 'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), con[2])) + test.verify(waitFor('str(lineUnderCursor(editor)).strip() == con[3]', 1000), + 'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), con[3])) invokeMenuItem("File", "Save All") invokeMenuItem("File", "Exit") diff --git a/tests/system/suite_tools/tst_designer_goto_slot/testdata/connections.tsv b/tests/system/suite_tools/tst_designer_goto_slot/testdata/connections.tsv index e222ef4544c..db3dc814d6e 100644 --- a/tests/system/suite_tools/tst_designer_goto_slot/testdata/connections.tsv +++ b/tests/system/suite_tools/tst_designer_goto_slot/testdata/connections.tsv @@ -1,4 +1,4 @@ -"widget" "signal" "slot" -":FormEditorStack.PushButton_QPushButton" "clicked()" "void MainWindow::on_pushButton_clicked()" -":FormEditorStack.CheckBox_QCheckBox" "toggled(bool)" "void MainWindow::on_checkBox_toggled(bool checked)" -":FormEditorStack.centralWidget_QDesignerWidget" "destroyed()" "void MainWindow::on_MainWindow_destroyed()" +"widget" "baseclass" "signal" "slot" +":FormEditorStack.PushButton_QPushButton" "QAbstractButton" "clicked()" "void MainWindow::on_pushButton_clicked()" +":FormEditorStack.CheckBox_QCheckBox" "QAbstractButton" "toggled(bool)" "void MainWindow::on_checkBox_toggled(bool checked)" +":FormEditorStack.centralWidget_QDesignerWidget" "QObject" "destroyed()" "void MainWindow::on_MainWindow_destroyed()"