From 33d48093a8d32f2a99c41dc1a35f9da7922b3b7f Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 13 Apr 2018 09:35:29 +0200 Subject: [PATCH 01/10] Update qbs submodule To HEAD of 1.11 branch. Change-Id: I8f82841f13a45e127864d7a5786f0e48d65a4a56 Reviewed-by: Joerg Bornemann --- src/shared/qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 6a62717271ed651f1c1467647499992d1104ca37 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Tue, 10 Apr 2018 14:21:59 +0200 Subject: [PATCH 02/10] Squish: Update tst_designer_goto_slot for Qt>=5.10 Change-Id: I29b3788f2daed5ca27069cc8ab9d972bdcee3c02 Reviewed-by: Christian Stenger --- tests/system/objects.map | 1 + .../tst_designer_goto_slot/test.py | 19 ++++++++++++++----- .../testdata/connections.tsv | 8 ++++---- 3 files changed, 19 insertions(+), 9 deletions(-) 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/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()" From 429c596395697ff3533e679a848ad26cd1ee97cb Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Sat, 17 Mar 2018 09:31:56 +0200 Subject: [PATCH 03/10] Android: Fix debugging on Android 8+ The new way is much reliable and now we can debug all the libs from very first start, including static constructors, JNI_OnLoad, etc. The downside is that the startup is a little bit slower then before. On a Ryzen 1700X is 2 to 5 seconds slower. Task-number: QTCREATORBUG-19081 Change-Id: Iacedf7b8aa84de5026f9c81eeca35dd377cf4640 Reviewed-by: BogDan Vatra Reviewed-by: Vikas Pachdha Reviewed-by: Eike Ziller Reviewed-by: hjk --- src/plugins/android/android.pro | 2 + src/plugins/android/android.qbs | 2 + src/plugins/android/androiddeployqtstep.cpp | 1 + src/plugins/android/androidmanager.cpp | 10 + src/plugins/android/androidmanager.h | 3 + src/plugins/android/androidrunnable.h | 1 + src/plugins/android/androidrunner.cpp | 595 +----------------- src/plugins/android/androidrunner.h | 4 +- src/plugins/android/androidrunnerworker.cpp | 558 ++++++++++++++++ src/plugins/android/androidrunnerworker.h | 134 ++++ .../qmakeandroidrunconfiguration.cpp | 4 +- 11 files changed, 731 insertions(+), 583 deletions(-) create mode 100644 src/plugins/android/androidrunnerworker.cpp create mode 100644 src/plugins/android/androidrunnerworker.h 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 ceede4e58d8..9aa7d0f7d10 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -166,6 +166,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()); diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 53a3f6ada4d..387b55dc372 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -240,6 +240,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 9839dada07b..c091214a5e1 100644 --- a/src/plugins/android/androidrunnable.h +++ b/src/plugins/android/androidrunnable.h @@ -40,6 +40,7 @@ struct ANDROID_EXPORT AndroidRunnable QStringList beforeStartAdbCommands; QStringList afterFinishAdbCommands; QString deviceSerialNumber; + 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 b2952b578e4..6e9277e9f96 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; @@ -126,556 +108,6 @@ 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 (!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) : RunWorker(runControl), m_target(runControl->runConfiguration()->target()) { @@ -693,6 +125,7 @@ AndroidRunner::AndroidRunner(RunControl *runControl) m_androidRunnable.packageName = m_androidRunnable.intentName.left( m_androidRunnable.intentName.indexOf(QLatin1Char('/'))); m_androidRunnable.deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target); + m_androidRunnable.apiLevel = AndroidManager::deviceApiLevel(m_target); auto androidRunConfig = qobject_cast(runControl->runConfiguration()); m_androidRunnable.amStartExtraArgs = androidRunConfig->amStartExtraArgs(); @@ -702,23 +135,26 @@ AndroidRunner::AndroidRunner(RunControl *runControl) for (QString shellCmd: androidRunConfig->postFinishShellCommands()) 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, @@ -822,6 +258,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; @@ -854,5 +291,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 c878f95e9a6..e4ad458621b 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 { @@ -85,7 +85,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..36a3d7a2ab5 --- /dev/null +++ b/src/plugins/android/androidrunnerworker.cpp @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** 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 (!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/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp b/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp index 5ae16c73c1a..fc9ac354806 100644 --- a/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp +++ b/src/plugins/qmakeandroidsupport/qmakeandroidrunconfiguration.cpp @@ -95,8 +95,10 @@ void QmakeAndroidRunConfiguration::updateDisplayName() const QmakeProjectManager::QmakeProFileNode *root = project->rootProjectNode(); if (root) { const QmakeProjectManager::QmakeProFileNode *node = root->findProFileFor(m_proFilePath); - if (node) // should always be found + if (node) { // should always be found + setDisplayName(node->displayName()); setDefaultDisplayName(node->displayName()); + } } } From a7cda9a54bce76467df5153770d2d9b783a38211 Mon Sep 17 00:00:00 2001 From: Andre Hartmann Date: Thu, 22 Mar 2018 18:08:38 +0100 Subject: [PATCH 04/10] ExternalToolsConfig: Fix tab order for path choosers Change-Id: I6d0d596eddea1c94f92d39f7cc827f54260d8aab Reviewed-by: Nazar Gerasymchuk Reviewed-by: Orgad Shaneh --- src/plugins/coreplugin/dialogs/externaltoolconfig.ui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui index 36d3247d261..3a1d4d1f613 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 From 54f024dba419b7680453c16bdfc1b84706d0209c Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 16 Apr 2018 09:45:01 +0200 Subject: [PATCH 05/10] AutoTest: Fix handling exceptions while environment setup If setting up the environment for gtest fails due to an exception we might end up having no reportable result but we get a return code of 1 for the test application. Inform the results pane instead of just ignoring the return code and explicitly send a fatal result for this. Task-number: QTCREATORBUG-20280 Change-Id: I05e522764d6302c5b0760c4bc10e01a2248a4494 Reviewed-by: David Schulz --- src/plugins/autotest/gtest/gtestoutputreader.cpp | 12 ++++++++---- src/plugins/autotest/testoutputreader.cpp | 8 ++++++++ src/plugins/autotest/testoutputreader.h | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index d3d915d96f2..bb4e13f6224 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: From fa11070977ea65e2e1d3a94e5c8c980494388294 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 13 Apr 2018 12:33:31 +0200 Subject: [PATCH 06/10] QMake: Make signalling of parsing state more robust Move the emitParsingStarted into a location that is called from both methods that had it before. Also add an QTC_ASSERT into incrementPending, which is triggered by the qmake parsing code directly. If something went wrong before, then the signal will be sent anyway and in the right sequence -- although the start signal is a bit late at that point. Task-number: QTCREATORBUG-20203 Change-Id: I64611e471d1e4959d5cfe0118223594a04238433 Reviewed-by: Eike Ziller --- src/plugins/qmakeprojectmanager/qmakeproject.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 44ebc927b66..1bc2feaa94e 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,7 +477,6 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile::AsyncUpdateDelay delay) return; } - emitParsingStarted(); rootProFile()->setParseInProgressRecursive(true); setAllBuildConfigurationsEnabled(false); @@ -501,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); } From 17d530d60db270e3c011296a4f2ddc444a2acdff Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 16 Apr 2018 13:42:43 +0200 Subject: [PATCH 07/10] Make sure we always quote the qmldebug command line arguments Change-Id: I5ce68c238e0ab05d19715659a9cc2161e51e1a7e Task-number: QTCREATORBUG-20260 Reviewed-by: hjk Reviewed-by: Christian Kandeler --- src/plugins/debugger/debuggerruncontrol.cpp | 8 +++--- .../qmlprofiler/qmlprofilerruncontrol.cpp | 5 ++-- .../tests/localqmlprofilerrunner_test.cpp | 26 +++++++++++++++++++ src/plugins/qnx/qnxanalyzesupport.cpp | 7 +++-- .../remotelinuxqmltoolingsupport.cpp | 7 +++-- 5 files changed, 39 insertions(+), 14 deletions(-) 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/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index ff828ae11e5..e813ce0e612 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -254,8 +255,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 007dac486a2..1f2dcce4b2f 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); From fdc4ac41d92a97ed31bb9c3297ceb9f7cae1c748 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Wed, 21 Mar 2018 22:21:56 +0100 Subject: [PATCH 08/10] Squish: Replace Qt 5.3 kit with Qt 5.10 This patch just changes the minimum for replacing the kits while keeping the tests running. Further updates to the tests should be done in separate patches. Task-number: QTCREATORBUG-19440 Change-Id: I25ce60ad0a47678dba4352a4b2601ca1cdd4741d Reviewed-by: Christian Stenger --- tests/system/README | 37 +++++------ .../mac/QtProject/qtcreator/profiles.xml | 2 +- .../mac/QtProject/qtcreator/qtversion.xml | 6 +- .../unix/QtProject/qtcreator/profiles.xml | 57 +++++++++-------- .../unix/QtProject/qtcreator/qtversion.xml | 24 +++---- .../windows/QtProject/qtcreator/profiles.xml | 62 +++++++++---------- .../windows/QtProject/qtcreator/qtversion.xml | 24 +++---- .../QtProject/qtcreator/toolchains.xml | 53 ++++++++-------- tests/system/shared/classes.py | 24 +++---- tests/system/shared/project.py | 13 ++-- tests/system/suite_CSUP/tst_CSUP06/test.py | 2 +- tests/system/suite_QMLS/tst_QMLS03/test.py | 2 +- tests/system/suite_WELP/tst_WELP03/test.py | 2 +- .../suite_debugger/tst_simple_analyze/test.py | 2 +- .../testdata/events_qt5.10.1.tsv | 10 +++ .../{events_qt5.tsv => events_qt5.6.1.tsv} | 0 .../tst_create_proj_wizard/test.py | 6 +- .../suite_general/tst_openqt_creator/test.py | 2 +- .../suite_general/tst_rename_file/test.py | 2 + .../tst_session_handling/test.py | 2 +- .../suite_qtquick/tst_qml_outline/test.py | 2 +- .../tst_qtquick_creation3/test.py | 10 +-- .../tst_qtquick_creation4/test.py | 2 +- 23 files changed, 177 insertions(+), 169 deletions(-) create mode 100644 tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt5.10.1.tsv rename tests/system/suite_debugger/tst_simple_analyze/testdata/{events_qt5.tsv => events_qt5.6.1.tsv} (100%) 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/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_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 From 87710540603ca2f26aa7956229c848c0f63148d7 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 16 Apr 2018 15:49:31 +0200 Subject: [PATCH 09/10] Squish: Check also for expected clang on macOS Change-Id: I32d7f5ebf02a559ca854553536874c6ec68f0ec5 Reviewed-by: Robert Loehning --- tests/system/suite_general/tst_default_settings/test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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: From 9a946decc3d516ad4210fb06cb2c93489a2a44c8 Mon Sep 17 00:00:00 2001 From: Vikas Pachdha Date: Mon, 16 Apr 2018 13:59:13 +0200 Subject: [PATCH 10/10] Android: Give preference to detected deploy errors Task-number: QTCREATORBUG-17414 Change-Id: I35171dc8649c9ecd790a1a508ccc3d4f690ee67b Reviewed-by: Tobias Hunger --- src/plugins/android/androiddeployqtstep.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 9aa7d0f7d10..8705f649e92 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -325,11 +325,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; }