File indexing completed on 2024-05-12 04:40:09

0001 /*
0002     SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "test_lldbformatters.h"
0008 
0009 #include "controllers/variable.h"
0010 #include "controllers/variablecontroller.h"
0011 #include "debugsession.h"
0012 #include "stringhelpers.h"
0013 #include "tests/debuggers-tests-config.h"
0014 #include "tests/testhelper.h"
0015 
0016 #include <execute/iexecuteplugin.h>
0017 #include <interfaces/icore.h>
0018 #include <interfaces/iplugincontroller.h>
0019 #include <tests/autotestshell.h>
0020 #include <tests/testcore.h>
0021 
0022 #include <KConfigGroup>
0023 #include <KSharedConfig>
0024 
0025 #include <QDebug>
0026 #include <QString>
0027 #include <QTest>
0028 #include <QUrl>
0029 
0030 #include <algorithm>
0031 #include <vector>
0032 
0033 #define WAIT_FOR_A_WHILE_AND_IDLE(session, ms) \
0034     do { if (!KDevMI::waitForAWhile((session), (ms), __FILE__, __LINE__)) return; \
0035          if (!KDevMI::waitForState((session), DebugSession::PausedState, __FILE__, __LINE__, true)) \
0036              return; \
0037     } while (0)
0038 
0039 #define VERIFY_LOCAL(row, name, summary, children) \
0040     do { \
0041         if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__)) \
0042             return; \
0043     } while (0)
0044 
0045 #define VERIFY_WATCH(row, name, summary, children) \
0046     do { \
0047         if (!verifyVariable((row), (name), (summary), (children), __FILE__, __LINE__, false)) \
0048             return; \
0049     } while (0)
0050 
0051 using namespace KDevelop;
0052 using namespace KDevMI::LLDB;
0053 using KDevMI::findExecutable;
0054 using KDevMI::findSourceFile;
0055 using KDevMI::findFile;
0056 using KDevMI::compareData;
0057 using KDevMI::TestLaunchConfiguration;
0058 
0059 class TestDebugSession : public DebugSession
0060 {
0061     Q_OBJECT
0062 public:
0063     TestDebugSession() : DebugSession()
0064     {
0065         setSourceInitFile(false);
0066         // explicit set formatter path to force use in-tree formatters, not the one installed in system.
0067         auto formatter = findFile(LLDB_SRC_DIR, "formatters/all.py");
0068         setFormatterPath(formatter);
0069 
0070         KDevelop::ICore::self()->debugController()->addSession(this);
0071 
0072         variableController()->setAutoUpdate(IVariableController::UpdateLocals);
0073     }
0074 };
0075 
0076 VariableCollection *LldbFormattersTest::variableCollection()
0077 {
0078     return m_core->debugController()->variableCollection();
0079 }
0080 
0081 QModelIndex LldbFormattersTest::watchVariableIndexAt(int i, int col)
0082 {
0083     auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0);
0084     return variableCollection()->index(i, col, watchRoot);
0085 }
0086 
0087 QModelIndex LldbFormattersTest::localVariableIndexAt(int i, int col)
0088 {
0089     auto localRoot = variableCollection()->indexForItem(variableCollection()->locals(), 0);
0090     return variableCollection()->index(i, col, localRoot);
0091 }
0092 
0093 // Note: line is zero-based
0094 KDevelop::Breakpoint* LldbFormattersTest::addCodeBreakpoint(const QUrl& location, int line)
0095 {
0096     return m_core->debugController()->breakpointModel()->addCodeBreakpoint(location, line);
0097 }
0098 
0099 // Called before the first testfunction is executed
0100 void LldbFormattersTest::initTestCase()
0101 {
0102     AutoTestShell::init({QStringLiteral("kdevlldb"), QStringLiteral("kdevexecute")});
0103     m_core = TestCore::initialize(Core::NoUi);
0104 
0105     m_iface = m_core->pluginController()
0106                 ->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))
0107                 ->extension<IExecutePlugin>();
0108     Q_ASSERT(m_iface);
0109 
0110     const QString lldbMiExecutable = QStandardPaths::findExecutable(QStringLiteral("lldb-mi"));
0111     if (lldbMiExecutable.isEmpty()) {
0112         QSKIP("Skipping, lldb-mi not available");
0113     }
0114 }
0115 
0116 // Called after the last testfunction was executed
0117 void LldbFormattersTest::cleanupTestCase()
0118 {
0119     TestCore::shutdown();
0120 }
0121 
0122 // Called before each testfunction is executed
0123 void LldbFormattersTest::init()
0124 {
0125     //remove all breakpoints - so we can set our own in the test
0126     KConfigGroup bpCfg = KSharedConfig::openConfig()->group("breakpoints");
0127     bpCfg.writeEntry("number", 0);
0128     bpCfg.sync();
0129 
0130     auto count = m_core->debugController()->breakpointModel()->rowCount();
0131     m_core->debugController()->breakpointModel()->removeRows(0, count);
0132 
0133     while (variableCollection()->watches()->childCount() > 0) {
0134         auto idx = watchVariableIndexAt(0);
0135         auto var = qobject_cast<LldbVariable*>(variableCollection()->itemForIndex(idx));
0136         if (!var) break;
0137         var->die();
0138     }
0139 
0140     m_session = new TestDebugSession;
0141 }
0142 
0143 void LldbFormattersTest::cleanup()
0144 {
0145     // Called after every testfunction
0146     if (m_session)
0147         m_session->stopDebugger();
0148     WAIT_FOR_STATE(m_session, DebugSession::EndedState);
0149     m_session.clear();
0150 }
0151 
0152 bool LldbFormattersTest::verifyVariable(int index, const QString &name,
0153                                         const QString &expectedSummary,
0154                                         QStringList expectedChildren,
0155                                         const char *file, int line,
0156                                         bool isLocal, bool useRE, bool unordered)
0157 {
0158     QList<QPair<QString, QString>> childrenPairs;
0159     childrenPairs.reserve(expectedChildren.size());
0160     if (unordered) {
0161         qDebug() << "useRE set to true when unordered = true";
0162         useRE = true;
0163         expectedChildren.sort();
0164         for (auto& c : expectedChildren) {
0165             childrenPairs << qMakePair(QStringLiteral(R"(^\[\d+\]$)"), c);
0166         }
0167     } else {
0168         for (int i = 0; i != expectedChildren.size(); ++i) {
0169             childrenPairs << qMakePair(QStringLiteral("[%0]").arg(i), expectedChildren[i]);
0170         }
0171     }
0172     return verifyVariable(index, name, expectedSummary, childrenPairs, file, line, isLocal, useRE, unordered);
0173 }
0174 
0175 bool LldbFormattersTest::verifyVariable(int index, const QString &name,
0176                                         const QString &expectedSummary,
0177                                         QList<QPair<QString, QString>> expectedChildren,
0178                                         const char *file, int line,
0179                                         bool isLocal, bool useRE, bool unordered)
0180 {
0181     QModelIndex varIdx, summaryIdx;
0182     if (isLocal) {
0183         varIdx = localVariableIndexAt(index, 0);
0184         summaryIdx = localVariableIndexAt(index, 1);
0185     } else {
0186         varIdx = watchVariableIndexAt(index, 0);
0187         summaryIdx = watchVariableIndexAt(index, 1);
0188     }
0189 
0190     if (!compareData(varIdx, name, file, line)) {
0191         return false;
0192     }
0193     if (!compareData(summaryIdx, expectedSummary, file, line, useRE)) {
0194         return false;
0195     }
0196 
0197     // fetch all children
0198     auto var = variableCollection()->itemForIndex(varIdx);
0199     auto childCount = 0;
0200     while (childCount != variableCollection()->rowCount(varIdx)) {
0201         childCount = variableCollection()->rowCount(varIdx);
0202         var->fetchMoreChildren();
0203         if (!waitForAWhile(m_session, 50, file, line))
0204             return false;
0205     }
0206     if (childCount != expectedChildren.length()) {
0207         QTest::qFail(qPrintable(QString("'%0' didn't match expected '%1' in %2:%3")
0208                                 .arg(childCount).arg(expectedChildren.length()).arg(file).arg(line)),
0209                      file, line);
0210         return false;
0211     }
0212 
0213     QVector<int> theOrder;
0214     theOrder.reserve(childCount);
0215     for (int i = 0; i != childCount; ++i) {
0216         theOrder.push_back(i);
0217     }
0218     if (unordered) {
0219         qDebug() << "actual list sorted for unordered compare";
0220         std::sort(theOrder.begin(), theOrder.end(), [&](int a, int b){
0221             auto indexA = variableCollection()->index(a, 1, varIdx);
0222             auto indexB = variableCollection()->index(b, 1, varIdx);
0223             return indexA.model()->data(indexA, Qt::DisplayRole).toString()
0224                 < indexB.model()->data(indexB, Qt::DisplayRole).toString();
0225         });
0226         std::sort(expectedChildren.begin(), expectedChildren.end(),
0227                   [](const QPair<QString, QString> &a, const QPair<QString, QString> &b){
0228             return a.second < b.second;
0229         });
0230         qDebug() << "sorted actual order" << theOrder;
0231         qDebug() << "sorted expectedChildren" << expectedChildren;
0232     }
0233 
0234     for (int i = 0; i != childCount; ++i) {
0235         if (!compareData(variableCollection()->index(theOrder[i], 0, varIdx),
0236                          expectedChildren[i].first, file, line, useRE)) {
0237             return false;
0238         }
0239 
0240         if (!compareData(variableCollection()->index(theOrder[i], 1, varIdx),
0241                          expectedChildren[i].second, file, line, useRE)) {
0242             return false;
0243         }
0244     }
0245     return true;
0246 }
0247 
0248 void LldbFormattersTest::testQChar()
0249 {
0250     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qchar"));
0251     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qchar.cpp")), 4);
0252 
0253     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0254     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0255 
0256     // Should be two rows ('auto', 'local')
0257     QCOMPARE(variableCollection()->rowCount(), 2);
0258 
0259     variableCollection()->expanded(localVariableIndexAt(0));
0260     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0261 
0262     QList<QPair<QString, QString>> children;
0263     children << qMakePair(QStringLiteral("ucs"), QStringLiteral("107"));
0264 
0265     VERIFY_LOCAL(0, "c", "'k'", children);
0266 }
0267 
0268 void LldbFormattersTest::testQString()
0269 {
0270     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qstring"));
0271     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qstring.cpp")), 4);
0272 
0273     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0274     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0275 
0276     // Should be two rows ('auto', 'local')
0277     QCOMPARE(variableCollection()->rowCount(), 2);
0278 
0279     variableCollection()->expanded(localVariableIndexAt(0));
0280     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0281 
0282     QString expected = QStringLiteral("test最后一个不是特殊字符'\"\\u6211");
0283     QStringList children;
0284     for (auto ch : qAsConst(expected)) {
0285         children << Utils::quote(ch, '\'');
0286     }
0287 
0288     VERIFY_LOCAL(0, "s", Utils::quote(expected), children);
0289 
0290     m_session->stepOver();
0291     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0292     QCOMPARE(m_session->currentLine(), 5);
0293 
0294     expected.append("x");
0295     children << QStringLiteral("'x'");
0296 
0297     VERIFY_LOCAL(0, "s", Utils::quote(expected), children);
0298 
0299     m_session->run();
0300     WAIT_FOR_STATE(m_session, DebugSession::EndedState);
0301 }
0302 
0303 void LldbFormattersTest::testQByteArray()
0304 {
0305     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qbytearray"));
0306     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qbytearray.cpp")), 4);
0307 
0308     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0309     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0310 
0311     // Should be two rows ('auto', 'local')
0312     QCOMPARE(variableCollection()->rowCount(), 2);
0313 
0314     variableCollection()->expanded(localVariableIndexAt(0));
0315     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0316 
0317     QStringList charlist {
0318         R"(-26 '\xe6')",
0319         R"(-104 '\x98')",
0320         R"(-81 '\xaf')",
0321         R"(39 ''')",
0322         R"(34 '"')",
0323         R"(92 '\')",
0324         R"(117 'u')",
0325         R"(54 '6')",
0326         R"(50 '2')",
0327         R"(49 '1')",
0328         R"(49 '1')",
0329     };
0330 
0331     VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211")", charlist);
0332 
0333     m_session->stepOver();
0334     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0335     QCOMPARE(m_session->currentLine(), 5);
0336 
0337     charlist << QStringLiteral("120 'x'");
0338     VERIFY_LOCAL(0, "ba", R"("\xe6\x98\xaf'\"\\u6211x")", charlist);
0339 
0340     m_session->run();
0341     WAIT_FOR_STATE(m_session, DebugSession::EndedState);
0342 }
0343 
0344 
0345 void LldbFormattersTest::testQListContainer_data()
0346 {
0347     QTest::addColumn<QString>("container");
0348     QTest::addColumn<bool>("unordered");
0349 
0350     QTest::newRow("QList") << "QList" << false;
0351     QTest::newRow("QQueue") << "QQueue" << false;
0352     QTest::newRow("QVector") << "QVector" << false;
0353     QTest::newRow("QStack") << "QStack" << false;
0354     QTest::newRow("QLinkedList") << "QLinkedList" << false;
0355     QTest::newRow("QSet") << "QSet" << true;
0356 }
0357 
0358 void LldbFormattersTest::testQListContainer()
0359 {
0360     QFETCH(QString, container);
0361     QFETCH(bool, unordered);
0362 
0363     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistcontainer"));
0364     cfg.config().writeEntry(KDevMI::Config::BreakOnStartEntry, true);
0365 
0366     auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0);
0367     variableCollection()->expanded(watchRoot);
0368     variableCollection()->variableWidgetShown();
0369 
0370     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0371     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0372 
0373     m_session->addUserCommand(QStringLiteral("break set --func doStuff<%1>()").arg(container));
0374     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0375 
0376     m_session->run();
0377     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0378 
0379     // <int>
0380     m_session->stepOver();
0381     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0382     QCOMPARE(m_session->currentLine(), 33); // line 34: intList << 10 << 20;
0383 
0384     auto var = variableCollection()->watches()->add(QStringLiteral("intList"));
0385     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0386 
0387     if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral("<size=0>"), QStringList{},
0388                         __FILE__, __LINE__, false, false, unordered)) {
0389         return;
0390     }
0391 
0392     m_session->stepOver();
0393     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0394     QCOMPARE(m_session->currentLine(), 34); // line 35: intList << 30;
0395 
0396     variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update.
0397     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0398 
0399     if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral("<size=2>"), QStringList{"10", "20"},
0400                         __FILE__, __LINE__, false, false, unordered)) {
0401         return;
0402     }
0403 
0404     m_session->stepOver();
0405     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0406     QCOMPARE(m_session->currentLine(), 36); // line 37: Container<QString> stringList;
0407 
0408     if (!verifyVariable(0, QStringLiteral("intList"), QStringLiteral("<size=3>"), QStringList{"10", "20", "30"},
0409                         __FILE__, __LINE__, false, false, unordered)) {
0410         return;
0411     }
0412     var->die();
0413 
0414     // <QString>
0415     m_session->stepOver();
0416     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0417     QCOMPARE(m_session->currentLine(), 37); // line 38: stringList << "a" << "bc";
0418 
0419     var = variableCollection()->watches()->add(QStringLiteral("stringList"));
0420     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0421 
0422     if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral("<size=0>"), QStringList{},
0423                         __FILE__, __LINE__, false, false, unordered)) {
0424         return;
0425     }
0426 
0427     m_session->stepOver();
0428     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0429     QCOMPARE(m_session->currentLine(), 38); // line 39: stringList << "d";
0430 
0431     variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update.
0432     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0433 
0434 
0435     if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral("<size=2>"), QStringList{"\"a\"", "\"bc\""},
0436                         __FILE__, __LINE__, false, false, unordered)) {
0437         return;
0438     }
0439 
0440     m_session->stepOver();
0441     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0442     QCOMPARE(m_session->currentLine(), 40); // line 41: Container<A> structList;
0443 
0444     if (!verifyVariable(0, QStringLiteral("stringList"), QStringLiteral("<size=3>"), QStringList{"\"a\"", "\"bc\"", "\"d\""},
0445                         __FILE__, __LINE__, false, false, unordered)) {
0446         return;
0447     }
0448     var->die();
0449 
0450     // <struct A>
0451     m_session->stepOver();
0452     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0453     QCOMPARE(m_session->currentLine(), 41); // line 42: structList << A(QStringLiteral("a"), QStringLiteral("b"), 100, -200);
0454 
0455     var = variableCollection()->watches()->add(QStringLiteral("structList"));
0456     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0457 
0458     if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral("<size=0>"), QStringList{},
0459                         __FILE__, __LINE__, false, false, unordered)) {
0460         return;
0461     }
0462 
0463     m_session->runUntil({}, 43);
0464     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0465     QCOMPARE(m_session->currentLine(), 42); // line 43: structList << A();
0466 
0467     variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update.
0468     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0469 
0470     if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral("<size=1>"), QStringList{"{...}"},
0471                         __FILE__, __LINE__, false, false, unordered)) {
0472         return;
0473     }
0474 
0475     m_session->stepOver();
0476     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0477     QCOMPARE(m_session->currentLine(), 44); // line 45: Container<int*> pointerList;
0478 
0479     if (!verifyVariable(0, QStringLiteral("structList"), QStringLiteral("<size=2>"), QStringList{"{...}", "{...}"},
0480                         __FILE__, __LINE__, false, false, unordered)) {
0481         return;
0482     }
0483     var->die();
0484 
0485     // <int*>
0486     m_session->stepOver();
0487     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0488     QCOMPARE(m_session->currentLine(), 45); // line 46: pointerList << new int(1) << new int(2);
0489 
0490     var = variableCollection()->watches()->add(QStringLiteral("pointerList"));
0491     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0492 
0493     if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral("<size=0>"), QStringList{},
0494                         __FILE__, __LINE__, false, false, unordered)) {
0495         return;
0496     }
0497 
0498     m_session->stepOver();
0499     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0500     QCOMPARE(m_session->currentLine(), 46); // line 47: pointerList << new int(3);
0501 
0502     variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update.
0503     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0504 
0505     if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral("<size=2>"), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$"},
0506                         __FILE__, __LINE__, false, true, unordered)) {
0507         return;
0508     }
0509 
0510     m_session->stepOver();
0511     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0512     QCOMPARE(m_session->currentLine(), 47); // line 48: qDeleteAll(pointerList);
0513 
0514     if (!verifyVariable(0, QStringLiteral("pointerList"), QStringLiteral("<size=3>"), QStringList{"^0x[0-9A-Fa-f]+$", "^0x[0-9A-Fa-f]+$",
0515                                                                   "^0x[0-9A-Fa-f]+$"},
0516                         __FILE__, __LINE__, false, true, unordered)) {
0517         return;
0518     }
0519     var->die();
0520     m_session->stepOver(); // step over qDeleteAll
0521 
0522     // <QPair<int, int>>
0523     m_session->stepOver();
0524     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0525     QCOMPARE(m_session->currentLine(), 50); // line 51: pairList << QPair<int, int>(1, 2) << qMakePair(2, 3);
0526 
0527     var = variableCollection()->watches()->add(QStringLiteral("pairList"));
0528     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0529 
0530     VERIFY_WATCH(0, "pairList", "<size=0>", QStringList{});
0531 
0532     m_session->stepOver();
0533     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0534     QCOMPARE(m_session->currentLine(), 51); // line 52: pairList << qMakePair(4, 5);
0535 
0536     variableCollection()->expanded(watchVariableIndexAt(0)); // expand this node for correct update.
0537     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0538 
0539     if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral("<size=2>"), QStringList{"{...}", "{...}"},
0540                         __FILE__, __LINE__, false, false, unordered)) {
0541         return;
0542     }
0543 
0544     m_session->stepOver();
0545     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0546     QCOMPARE(m_session->currentLine(), 54); // line 55: int i = 0;
0547 
0548     if (!verifyVariable(0, QStringLiteral("pairList"), QStringLiteral("<size=3>"), QStringList{"{...}", "{...}", "{...}"},
0549                         __FILE__, __LINE__, false, false, unordered)) {
0550         return;
0551     }
0552     var->die();
0553 }
0554 
0555 void LldbFormattersTest::testQListPOD()
0556 {
0557     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qlistpod"));
0558     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qlistpod.cpp")), 30);
0559 
0560     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0561     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0562 
0563     // Should be two rows ('auto', 'local')
0564     QCOMPARE(variableCollection()->rowCount(), 2);
0565 
0566     auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0);
0567     variableCollection()->expanded(watchRoot);
0568     variableCollection()->variableWidgetShown();
0569     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0570 
0571     variableCollection()->watches()->add(QStringLiteral("b"));
0572     variableCollection()->watches()->add(QStringLiteral("c"));
0573     variableCollection()->watches()->add(QStringLiteral("uc"));
0574     variableCollection()->watches()->add(QStringLiteral("s"));
0575     variableCollection()->watches()->add(QStringLiteral("us"));
0576     variableCollection()->watches()->add(QStringLiteral("i"));
0577     variableCollection()->watches()->add(QStringLiteral("ui"));
0578     variableCollection()->watches()->add(QStringLiteral("l"));
0579     variableCollection()->watches()->add(QStringLiteral("ul"));
0580     variableCollection()->watches()->add(QStringLiteral("i64"));
0581     variableCollection()->watches()->add(QStringLiteral("ui64"));
0582     variableCollection()->watches()->add(QStringLiteral("f"));
0583     variableCollection()->watches()->add(QStringLiteral("d"));
0584     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0585 
0586     VERIFY_WATCH(0, "b", "<size=1>", (QStringList{"false"}));
0587     VERIFY_WATCH(1, "c", "<size=1>", (QStringList{"50 '2'"}));
0588     VERIFY_WATCH(2, "uc", "<size=1>", (QStringList{"50 '2'"}));
0589 
0590     VERIFY_WATCH(3, "s", "<size=1>", (QStringList{"50"}));
0591     VERIFY_WATCH(4, "us", "<size=1>", (QStringList{"50"}));
0592 
0593     VERIFY_WATCH(5, "i", "<size=1>", (QStringList{"50"}));
0594     VERIFY_WATCH(6, "ui", "<size=1>", (QStringList{"50"}));
0595 
0596     VERIFY_WATCH(7, "l", "<size=1>", (QStringList{"50"}));
0597     VERIFY_WATCH(8, "ul", "<size=1>", (QStringList{"50"}));
0598 
0599     VERIFY_WATCH(9, "i64", "<size=1>", (QStringList{"50"}));
0600     VERIFY_WATCH(10, "ui64", "<size=1>", (QStringList{"50"}));
0601 
0602     VERIFY_WATCH(11, "f", "<size=1>", (QStringList{"50"}));
0603     VERIFY_WATCH(12, "d", "<size=1>", (QStringList{"50"}));
0604 }
0605 
0606 void LldbFormattersTest::testQMapInt()
0607 {
0608     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapint"));
0609     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapint.cpp")), 6);
0610 
0611     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0612     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0613 
0614     // Should be two rows ('auto', 'local')
0615     QCOMPARE(variableCollection()->rowCount(), 2);
0616 
0617     variableCollection()->expanded(localVariableIndexAt(0));
0618     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0619 
0620     VERIFY_LOCAL(0, "m", "<size=2>", (QStringList{"(10, 100)", "(20, 200)"}));
0621 
0622     m_session->stepOver();
0623     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0624     QCOMPARE(m_session->currentLine(), 7);
0625 
0626     VERIFY_LOCAL(0, "m", "<size=3>", (QStringList{"(10, 100)", "(20, 200)", "(30, 300)"}));
0627 }
0628 
0629 void LldbFormattersTest::testQMapString()
0630 {
0631     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstring"));
0632     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstring.cpp")), 7);
0633 
0634     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0635     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0636     QCOMPARE(m_session->currentLine(), 7); // line 8: m[QStringLiteral("30")] = QStringLiteral("300");
0637 
0638     // Should be two rows ('auto', 'local')
0639     QCOMPARE(variableCollection()->rowCount(), 2);
0640 
0641     variableCollection()->expanded(localVariableIndexAt(0));
0642     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0643 
0644     VERIFY_LOCAL(0, "m", "<size=2>", (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")"}));
0645 
0646     m_session->stepOver();
0647     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0648     QCOMPARE(m_session->currentLine(), 8); // line 9: return 0;
0649 
0650     VERIFY_LOCAL(0, "m", "<size=3>",
0651                  (QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"}));
0652 }
0653 
0654 void LldbFormattersTest::testQMapStringBool()
0655 {
0656     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qmapstringbool"));
0657     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qmapstringbool.cpp")), 7);
0658 
0659     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0660     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0661 
0662     // Should be two rows ('auto', 'local')
0663     QCOMPARE(variableCollection()->rowCount(), 2);
0664 
0665     variableCollection()->expanded(localVariableIndexAt(0));
0666     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0667 
0668     VERIFY_LOCAL(0, "m", "<size=2>", (QStringList{"(\"10\", true)", "(\"20\", false)"}));
0669 
0670     m_session->stepOver();
0671     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0672     QCOMPARE(m_session->currentLine(), 8);
0673 
0674     VERIFY_LOCAL(0, "m", "<size=3>", (QStringList{"(\"10\", true)", "(\"20\", false)", "(\"30\", true)"}));
0675 }
0676 
0677 
0678 void LldbFormattersTest::testQHashInt()
0679 {
0680     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashint"));
0681     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashint.cpp")), 6);
0682 
0683     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0684     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0685 
0686     // Should be two rows ('auto', 'local')
0687     QCOMPARE(variableCollection()->rowCount(), 2);
0688 
0689     variableCollection()->expanded(localVariableIndexAt(0));
0690     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0691 
0692     if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral("<size=2>"), QStringList{"(10, 100)", "(20, 200)"},
0693                         __FILE__, __LINE__, true, false, true)) {
0694         return;
0695     }
0696 
0697     m_session->stepOver();
0698     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0699     QCOMPARE(m_session->currentLine(), 7);
0700 
0701     if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral("<size=3>"), QStringList{"(10, 100)", "(20, 200)", "(30, 300)"},
0702                         __FILE__, __LINE__, true, false, true)) {
0703         return;
0704     }
0705 }
0706 
0707 void LldbFormattersTest::testQHashString()
0708 {
0709     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qhashstring"));
0710     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qhashstring.cpp")), 7);
0711 
0712     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0713     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0714 
0715     // Should be two rows ('auto', 'local')
0716     QCOMPARE(variableCollection()->rowCount(), 2);
0717 
0718     variableCollection()->expanded(localVariableIndexAt(0));
0719     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0720 
0721     if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral("<size=2>"), QStringList{"(\"10\", \"100\")", "(\"20\", \"200\")"},
0722                         __FILE__, __LINE__, true, false, true)) {
0723         return;
0724     }
0725 
0726     m_session->stepOver();
0727     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0728     QCOMPARE(m_session->currentLine(), 8);
0729 
0730     if (!verifyVariable(0, QStringLiteral("h"), QStringLiteral("<size=3>"),
0731                         {"(\"10\", \"100\")", "(\"20\", \"200\")", "(\"30\", \"300\")"},
0732                         __FILE__, __LINE__, true, false, true)) {
0733         return;
0734     }
0735 }
0736 
0737 void LldbFormattersTest::testQSetInt()
0738 {
0739     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetint"));
0740     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetint.cpp")), 6);
0741 
0742     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0743     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0744 
0745     // Should be two rows ('auto', 'local')
0746     QCOMPARE(variableCollection()->rowCount(), 2);
0747 
0748     variableCollection()->expanded(localVariableIndexAt(0));
0749     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0750 
0751     if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral("<size=2>"), QStringList{"10", "20"},
0752                         __FILE__, __LINE__, true, false, true)) {
0753         return;
0754     }
0755 
0756     m_session->stepOver();
0757     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0758     QCOMPARE(m_session->currentLine(), 7);
0759 
0760     if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral("<size=3>"), QStringList{"10", "20", "30"},
0761                         __FILE__, __LINE__, true, false, true)) {
0762         return;
0763     }
0764 }
0765 
0766 void LldbFormattersTest::testQSetString()
0767 {
0768     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qsetstring"));
0769     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qsetstring.cpp")), 7);
0770 
0771     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0772     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0773 
0774     // Should be two rows ('auto', 'local')
0775     QCOMPARE(variableCollection()->rowCount(), 2);
0776 
0777     variableCollection()->expanded(localVariableIndexAt(0));
0778     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0779 
0780     if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral("<size=2>"), QStringList{"\"10\"", "\"20\""},
0781                         __FILE__, __LINE__, true, false, true)) {
0782         return;
0783     }
0784 
0785     m_session->stepOver();
0786     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0787     QCOMPARE(m_session->currentLine(), 8);
0788 
0789     if (!verifyVariable(0, QStringLiteral("s"), QStringLiteral("<size=3>"),
0790                         {"\"10\"", "\"20\"", "\"30\""},
0791                         __FILE__, __LINE__, true, false, true)) {
0792         return;
0793     }
0794 }
0795 
0796 void LldbFormattersTest::testQDate()
0797 {
0798     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdate"));
0799     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdate.cpp")), 5);
0800 
0801     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0802     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0803 
0804     // Should be two rows ('auto', 'local')
0805     QCOMPARE(variableCollection()->rowCount(), 2);
0806 
0807     variableCollection()->expanded(localVariableIndexAt(0));
0808     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0809 
0810     QList<QPair<QString, QString>> children;
0811     children.append({QStringLiteral("jd"), QStringLiteral("2455217")});
0812     children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20\"")});
0813     children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent
0814     if (!verifyVariable(0, QStringLiteral("d"), QStringLiteral(".+"), children,
0815                         __FILE__, __LINE__, true, true, false)) {
0816         return;
0817     }
0818 }
0819 
0820 void LldbFormattersTest::testQTime()
0821 {
0822     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qtime"));
0823     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qtime.cpp")), 5);
0824 
0825     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0826     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0827 
0828     // Should be two rows ('auto', 'local')
0829     QCOMPARE(variableCollection()->rowCount(), 2);
0830 
0831     variableCollection()->expanded(localVariableIndexAt(0));
0832     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0833 
0834     QList<QPair<QString, QString>> children;
0835     children.append({QStringLiteral("mds"), QStringLiteral("55810123")});
0836     children.append({QStringLiteral("(ISO)"), QStringLiteral("\"15:30:10.000123\"")});
0837     children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale) and summary are locale dependent
0838     if (!verifyVariable(0, QStringLiteral("t"), QStringLiteral(".+"), children,
0839                         __FILE__, __LINE__, true, true, false)) {
0840         return;
0841     }
0842 }
0843 
0844 void LldbFormattersTest::testQDateTime()
0845 {
0846     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qdatetime"));
0847     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qdatetime.cpp")), 5);
0848 
0849     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0850     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0851 
0852     // Should be two rows ('auto', 'local')
0853     QCOMPARE(variableCollection()->rowCount(), 2);
0854 
0855     variableCollection()->expanded(localVariableIndexAt(0));
0856     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0857 
0858     QList<QPair<QString, QString>> children;
0859     children.append({QStringLiteral("toSecsSinceEpoch"), QStringLiteral("1264019473")});
0860     children.append({QStringLiteral("(ISO)"), QStringLiteral("\"2010-01-20 20:31:13\"")});
0861     children.append({QStringLiteral("(Locale)"), QStringLiteral("\".+\"")}); // (Locale), (UTC) and summary are locale dependent
0862     children.append({QStringLiteral("(UTC)"), QStringLiteral("\".+\"")});
0863     if (!verifyVariable(0, QStringLiteral("dt"), QStringLiteral(".+"), children,
0864                         __FILE__, __LINE__, true, true, false)) {
0865         return;
0866     }
0867 }
0868 
0869 void LldbFormattersTest::testQUrl()
0870 {
0871     TestLaunchConfiguration cfg(QStringLiteral("debuggee_qurl"));
0872     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("qurl.cpp")), 4);
0873 
0874     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0875     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0876 
0877     // Should be two rows ('auto', 'local')
0878     QCOMPARE(variableCollection()->rowCount(), 2);
0879 
0880     variableCollection()->expanded(localVariableIndexAt(0));
0881     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0882 
0883     QList<QPair<QString, QString>> children;
0884     children.append({QStringLiteral("(port)"), QStringLiteral("12345")});
0885     children.append({QStringLiteral("(scheme)"), QStringLiteral("\"http\"")});
0886     children.append({QStringLiteral("(userName)"), QStringLiteral("\"user\"")});
0887     children.append({QStringLiteral("(password)"), QStringLiteral("<Invalid>")});
0888     children.append({QStringLiteral("(host)"), QStringLiteral("\"www.kdevelop.org\"")});
0889     children.append({QStringLiteral("(path)"), QStringLiteral("\"/foo\"")});
0890     children.append({QStringLiteral("(query)"), QStringLiteral("\"xyz=bar\"")});
0891     children.append({QStringLiteral("(fragment)"), QStringLiteral("\"asdf\"")});
0892     VERIFY_LOCAL(0, "u", "http://user@www.kdevelop.org:12345/foo?xyz=bar#asdf", children);
0893 }
0894 
0895 void LldbFormattersTest::testQUuid()
0896 {
0897     TestLaunchConfiguration cfg(QStringLiteral("debuggee_quuid"));
0898     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("quuid.cpp")), 4);
0899 
0900     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0901     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0902 
0903     // Should be two rows ('auto', 'local')
0904     QCOMPARE(variableCollection()->rowCount(), 2);
0905 
0906     variableCollection()->expanded(localVariableIndexAt(0));
0907     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0908 
0909     VERIFY_LOCAL(0, "id", "QUuid({9ec3b70b-d105-42bf-b3b4-656e44d2e223})", (QStringList{}));
0910 }
0911 
0912 void LldbFormattersTest::testKTextEditorTypes()
0913 {
0914     TestLaunchConfiguration cfg(QStringLiteral("debuggee_ktexteditortypes"));
0915     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("ktexteditortypes.cpp")), 8);
0916 
0917     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0918     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0919 
0920     // Should be two rows ('auto', 'local')
0921     QCOMPARE(variableCollection()->rowCount(), 2);
0922 
0923     auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0);
0924     variableCollection()->expanded(watchRoot);
0925     variableCollection()->variableWidgetShown();
0926     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0927 
0928     variableCollection()->watches()->add(QStringLiteral("cursor"));
0929     variableCollection()->watches()->add(QStringLiteral("range"));
0930     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0931 
0932     QList<QPair<QString, QString>> cursorChildren;
0933     cursorChildren.append({QStringLiteral("m_line"), QStringLiteral("1")});
0934     cursorChildren.append({QStringLiteral("m_column"), QStringLiteral("1")});
0935 
0936     QList<QPair<QString, QString>> rangeChildren;
0937     rangeChildren.append({QStringLiteral("m_start"), QStringLiteral("(1, 1)")});
0938     rangeChildren.append({QStringLiteral("m_end"), QStringLiteral("(2, 2)")});
0939 
0940     VERIFY_WATCH(0, "cursor", "(1, 1)", cursorChildren);
0941     VERIFY_WATCH(1, "range", "[(1, 1) -> (2, 2)]", rangeChildren);
0942 }
0943 
0944 void LldbFormattersTest::testKDevelopTypes()
0945 {
0946     TestLaunchConfiguration cfg(QStringLiteral("debuggee_kdeveloptypes"));
0947     addCodeBreakpoint(QUrl::fromLocalFile(findSourceFile("kdeveloptypes.cpp")), 11);
0948 
0949     QVERIFY(m_session->startDebugging(&cfg, m_iface));
0950     WAIT_FOR_STATE_AND_IDLE(m_session, DebugSession::PausedState);
0951 
0952     // Should be two rows ('auto', 'local')
0953     QCOMPARE(variableCollection()->rowCount(), 2);
0954 
0955     auto watchRoot = variableCollection()->indexForItem(variableCollection()->watches(), 0);
0956     variableCollection()->expanded(watchRoot);
0957     variableCollection()->variableWidgetShown();
0958     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0959 
0960     variableCollection()->watches()->add(QStringLiteral("path1"));
0961     variableCollection()->watches()->add(QStringLiteral("path2"));
0962     WAIT_FOR_A_WHILE_AND_IDLE(m_session, 50);
0963 
0964     QList<QPair<QString, QString>> path1Children;
0965     path1Children.append({QStringLiteral("m_data"), QStringLiteral("<size=2>")});
0966 
0967     QList<QPair<QString, QString>> path2Children;
0968     path2Children.append({QStringLiteral("m_data"), QStringLiteral("<size=3>")});
0969 
0970     VERIFY_WATCH(0, "path1", "(\"tmp\", \"foo\")", path1Children);
0971     VERIFY_WATCH(1, "path2", "(\"http://www.test.com\", \"tmp\", \"asdf.txt\")", path2Children);
0972 }
0973 
0974 QTEST_MAIN(LldbFormattersTest)
0975 
0976 #include "test_lldbformatters.moc"
0977 #include "moc_test_lldbformatters.cpp"