1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
/*
* Common helpers for MI debugger unit tests
* Copyright 2016 Aetf <aetf@unlimitedcodeworks.xyz>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "testhelper.h"
#include "debuggers-tests-config.h"
#include "midebugsession.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QRegularExpression>
#include <QTest>
namespace KDevMI {
QUrl findExecutable(const QString& name)
{
QString exeExtension;
#ifdef Q_OS_WIN
exeExtension = QStringLiteral(".exe");
#endif
QFileInfo info(QString::fromLocal8Bit(DEBUGGEE_BIN_DIR), name + exeExtension);
Q_ASSERT_X(info.exists(), "findExecutable", info.filePath().toLocal8Bit());
Q_ASSERT(info.isExecutable());
return QUrl::fromLocalFile(info.canonicalFilePath());
}
QString findSourceFile(const QString& name)
{
return findFile(DEBUGGEE_SRC_DIR, name);
}
QString findFile(const char* dir, const QString& name)
{
QFileInfo info(QString::fromLocal8Bit(dir), name);
Q_ASSERT_X(info.exists(), "findFile", info.filePath().toLocal8Bit());
return info.canonicalFilePath();
}
bool isAttachForbidden(const char *file, int line)
{
// if on linux, ensure we can actually attach
QFile canRun(QStringLiteral("/proc/sys/kernel/yama/ptrace_scope"));
if (canRun.exists()) {
if (!canRun.open(QIODevice::ReadOnly)) {
QTest::qFail("Something is wrong: /proc/sys/kernel/yama/ptrace_scope exists but cannot be read", file, line);
return true;
}
if (canRun.read(1).toInt() != 0) {
QTest::qSkip("ptrace attaching not allowed, skipping test. To enable it, set /proc/sys/kernel/yama/ptrace_scope to 0.", file, line);
return true;
}
}
return false;
}
bool compareData(const QModelIndex& index, const QString& expected, const char *file, int line, bool useRE)
{
QString s = index.model()->data(index, Qt::DisplayRole).toString();
bool matched = true;
if (useRE) {
QRegularExpression re(expected);
matched = re.match(s).hasMatch();
} else {
matched = s == expected;
}
return QTest::qVerify(matched, "Comparison of data", qPrintable(QString("'%0' didn't match expected '%1' in %2:%3")
.arg(s, expected, file).arg(line)),
file, line);
}
bool waitForAWhile(MIDebugSession *session, int ms, const char *file, int line)
{
QPointer<MIDebugSession> s(session); //session can get deleted in DebugController
QTest::qWait(ms);
if (!s) {
QTest::qFail("Session ended while waiting", file, line);
return false;
}
return true;
}
bool waitForState(MIDebugSession *session, KDevelop::IDebugSession::DebuggerState state,
const char *file, int line, bool waitForIdle)
{
QPointer<MIDebugSession> s(session); //session can get deleted in DebugController
QElapsedTimer stopWatch;
stopWatch.start();
// legacy behavior for tests that implicitly may require waiting for idle,
// but which were written before waitForIdle was added
waitForIdle = waitForIdle || state != MIDebugSession::EndedState;
while (s && (s->state() != state
|| (waitForIdle && s->debuggerStateIsOn(s_dbgBusy)))) {
if (stopWatch.elapsed() > 10000) {
qWarning() << "current state" << s->state() << "waiting for" << state;
QTest::qFail(qPrintable(QString("Timeout before reaching state %0").arg(state)),
file, line);
return false;
}
QTest::qWait(20);
}
// NOTE: don't wait anymore after leaving the loop. Waiting reenters event loop and
// may change session state.
if (!s && state != MIDebugSession::EndedState) {
QTest::qFail(qPrintable(QString("Session ended before reaching state %0").arg(state)),
file, line);
return false;
}
qDebug() << "Reached state " << state << " in " << file << ':' << line;
return true;
}
TestWaiter::TestWaiter(MIDebugSession * session_, const char * condition_, const char * file_, int line_)
: session(session_)
, condition(condition_)
, file(file_)
, line(line_)
{
stopWatch.start();
}
bool TestWaiter::waitUnless(bool ok)
{
if (ok) {
qDebug() << "Condition " << condition << " reached in " << file << ':' << line;
return false;
}
if (stopWatch.elapsed() > 5000) {
QTest::qFail(qPrintable(QString("Timeout before reaching condition %0").arg(condition)),
file, line);
return false;
}
QTest::qWait(100);
if (!session) {
QTest::qFail(qPrintable(QString("Session ended without reaching condition %0").arg(condition)),
file, line);
return false;
}
return true;
}
} // end of namespace KDevMI
|