File indexing completed on 2024-04-28 08:27:07

0001 /*
0002  * XDebug Debugger Support
0003  *
0004  * Copyright 2009 Niko Sams <niko.sams@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of the
0009  * License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public
0017  * License along with this program; if not, write to the
0018  * Free Software Foundation, Inc.,
0019  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
0020  */
0021 
0022 #include "connectiontest.h"
0023 
0024 #include <iostream>
0025 #include <QtTest/QSignalSpy>
0026 #include <QtCore/QTemporaryFile>
0027 #include <QtCore/QDir>
0028 #include <QStandardPaths>
0029 #include <QPointer>
0030 
0031 #include <QDebug>
0032 #include <KProcess>
0033 #include <KConfig>
0034 #include <QTest>
0035 
0036 #include <shell/shellextension.h>
0037 #include <interfaces/idebugcontroller.h>
0038 #include <debugger/breakpoint/breakpointmodel.h>
0039 #include <debugger/breakpoint/breakpoint.h>
0040 #include <interfaces/ilaunchconfiguration.h>
0041 #include <tests/testcore.h>
0042 #include <tests/autotestshell.h>
0043 #include <debugger/interfaces/iframestackmodel.h>
0044 #include <debugger/variable/variablecollection.h>
0045 #include <debugger/interfaces/ivariablecontroller.h>
0046 
0047 #include "connection.h"
0048 #include "debugsession.h"
0049 #include "launchconfig.h"
0050 #include "debugjob.h"
0051 #include "debuggerdebug.h"
0052 
0053 using namespace XDebug;
0054 namespace KParts {
0055 class MainWindow;
0056 }
0057 namespace Sublime {
0058 class Controller;
0059 }
0060 namespace KDevelop {
0061 class IToolViewFactory;
0062 }
0063 
0064 namespace {
0065 inline QString phpExecutable()
0066 {
0067     return QStandardPaths::findExecutable("php");
0068 }
0069 }
0070 
0071 class TestLaunchConfiguration
0072     : public KDevelop::ILaunchConfiguration
0073 {
0074 public:
0075     TestLaunchConfiguration(const QUrl& script)
0076     {
0077         c = new KConfig();
0078         cfg = c->group("launch");
0079         cfg.writeEntry("isExecutable", true);
0080         cfg.writeEntry("Executable", script);
0081         cfg.writeEntry("Interpreter", phpExecutable());
0082     }
0083     ~TestLaunchConfiguration() override
0084     {
0085         delete c;
0086     }
0087     const KConfigGroup config() const override { return cfg; }
0088     KConfigGroup config() override { return cfg; }
0089     QString name() const override { return QString("Test-Launch"); }
0090     KDevelop::IProject* project() const override { return nullptr; }
0091     KDevelop::LaunchConfigurationType* type() const override { return nullptr; }
0092 
0093 private:
0094     KConfigGroup cfg;
0095     KConfig* c;
0096 };
0097 
0098 #define COMPARE_DATA(index, expected) \
0099     QCOMPARE(index.model()->data(index, Qt::DisplayRole).toString(), QString(expected))
0100 
0101 void ConnectionTest::initTestCase()
0102 {
0103     // check whether the php-xdebug module is installed at all
0104     const int rc = QProcess::execute(phpExecutable(), {"-r", "exit(extension_loaded('xdebug') ? 0 : 1);"});
0105     if (rc != 0) {
0106         QSKIP("This test requires the php-xdebug module to be installed!");
0107     }
0108 }
0109 
0110 void ConnectionTest::init()
0111 {
0112     qRegisterMetaType<DebugSession*>("DebugSession*");
0113 
0114     KDevelop::AutoTestShell::init({
0115         QStringLiteral("kdevxdebug"),
0116         QStringLiteral("kdevexecutebrowser"),
0117         QStringLiteral("kdevexecutescript")
0118     });
0119     m_core = new KDevelop::TestCore();
0120     m_core->initialize(KDevelop::Core::NoUi);
0121 
0122     //remove all breakpoints - so we can set our own in the test
0123     KDevelop::BreakpointModel* m = KDevelop::ICore::self()->debugController()->breakpointModel();
0124     m->removeRows(0, m->rowCount());
0125 
0126     /*
0127        KDevelop::VariableCollection *vc = KDevelop::ICore::self()->debugController()->variableCollection();
0128        for (int i=0; i < vc->watches()->childCount(); ++i) {
0129         delete vc->watches()->child(i);
0130        }
0131        vc->watches()->clear();
0132      */
0133 }
0134 
0135 void ConnectionTest::cleanup()
0136 {
0137     m_core->cleanup();
0138     delete m_core;
0139 }
0140 
0141 void ConnectionTest::testStdOutput()
0142 {
0143     QStringList contents;
0144     contents << "<?php"
0145              << "$i = 0;"
0146              << "echo \"foo\\n\";"
0147              << "$i++;"
0148              << "echo \"foo\";"
0149              << "echo \"bar\";"
0150              << "echo \"\\n\";";
0151     QTemporaryFile file("xdebugtest");
0152     file.open();
0153     const auto url = QUrl::fromLocalFile(file.fileName());
0154     file.write(contents.join("\n").toUtf8());
0155     file.close();
0156 
0157     auto session = new DebugSession;
0158     KDevelop::ICore::self()->debugController()->addSession(session);
0159 
0160     TestLaunchConfiguration cfg(url);
0161     XDebugJob job(session, &cfg);
0162 
0163     QSignalSpy outputLineSpy(session, SIGNAL(outputLine(QString)));
0164     QSignalSpy outputSpy(session, SIGNAL(output(QString)));
0165 
0166     job.start();
0167 
0168     session->waitForConnected();
0169     session->waitForFinished();
0170     {
0171         QCOMPARE(outputSpy.count(), 4);
0172         QList<QVariant> arguments = outputSpy.takeFirst();
0173         QCOMPARE(arguments.count(), 1);
0174         QCOMPARE(arguments.first().toString(), QString("foo\n"));
0175     }
0176     {
0177         QCOMPARE(outputLineSpy.count(), 2);
0178         QList<QVariant> arguments = outputLineSpy.takeFirst();
0179         QCOMPARE(arguments.count(), 1);
0180         QCOMPARE(arguments.at(0).toString(), QString("foo"));
0181         arguments = outputLineSpy.takeFirst();
0182         QCOMPARE(arguments.count(), 1);
0183         QCOMPARE(arguments.at(0).toString(), QString("foobar"));
0184     }
0185 }
0186 
0187 void ConnectionTest::testShowStepInSource()
0188 {
0189     QStringList contents;
0190     contents << "<?php"
0191              << "$i = 0;"
0192              << "$i++;";
0193     QTemporaryFile file("xdebugtest");
0194     file.open();
0195     const auto url = QUrl::fromLocalFile(file.fileName());
0196     file.write(contents.join("\n").toUtf8());
0197     file.close();
0198 
0199     auto session = new DebugSession;
0200     KDevelop::ICore::self()->debugController()->addSession(session);
0201 
0202     TestLaunchConfiguration cfg(url);
0203     XDebugJob job(session, &cfg);
0204 
0205     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0206 
0207     QSignalSpy showStepInSourceSpy(session, SIGNAL(showStepInSource(QUrl,int,QString)));
0208 
0209     qCDebug(KDEV_PHP_DEBUGGER) << "************************************************************************************";
0210     job.start();
0211     session->waitForConnected();
0212 
0213     session->waitForState(DebugSession::PausedState);
0214     QTest::qWait(100);
0215     session->stepInto();
0216     session->waitForState(DebugSession::PausedState);
0217     QTest::qWait(100);
0218     session->run();
0219     session->waitForFinished();
0220 
0221     {
0222         QCOMPARE(showStepInSourceSpy.count(), 2);
0223         QList<QVariant> arguments = showStepInSourceSpy.takeFirst();
0224         QCOMPARE(arguments.first().value<QUrl>(), url);
0225         QCOMPARE(arguments.at(1).toInt(), 1);
0226 
0227         arguments = showStepInSourceSpy.takeFirst();
0228         QCOMPARE(arguments.first().value<QUrl>(), url);
0229         QCOMPARE(arguments.at(1).toInt(), 2);
0230     }
0231 }
0232 
0233 void ConnectionTest::testMultipleSessions()
0234 {
0235     QStringList contents;
0236     contents << "<?php"
0237              << "$i = 0;"
0238              << "$i++;";
0239     QTemporaryFile file("xdebugtest");
0240     file.open();
0241     const auto url = QUrl::fromLocalFile(file.fileName());
0242     file.write(contents.join("\n").toUtf8());
0243     file.close();
0244 
0245     for (int i = 0; i < 10; ++i) {
0246         auto session = new DebugSession;
0247         KDevelop::ICore::self()->debugController()->addSession(session);
0248 
0249         TestLaunchConfiguration cfg(url);
0250         XDebugJob job(session, &cfg);
0251 
0252         job.start();
0253         session->waitForConnected();
0254         session->waitForFinished();
0255     }
0256 }
0257 
0258 void ConnectionTest::testStackModel()
0259 {
0260     QStringList contents;
0261     contents << "<?php"         // 1
0262              << "function x() {" // 2
0263              << "  echo 'x';"   // 3
0264              << "}"             // 4
0265              << "x();"          // 5
0266              << "echo 'y';";    // 6
0267     QTemporaryFile file("xdebugtest");
0268     file.open();
0269     const auto url = QUrl::fromLocalFile(file.fileName());
0270     file.write(contents.join("\n").toUtf8());
0271     file.close();
0272 
0273     auto session = new DebugSession;
0274     KDevelop::ICore::self()->debugController()->addSession(session);
0275 
0276     TestLaunchConfiguration cfg(url);
0277     XDebugJob job(session, &cfg);
0278 
0279     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0280 
0281     job.start();
0282     session->waitForConnected();
0283     session->waitForState(DebugSession::PausedState);
0284 
0285     //step into function
0286     for (int i = 0; i < 2; ++i) {
0287         session->stepInto();
0288         session->waitForState(DebugSession::PausedState);
0289     }
0290 
0291     QTest::qWait(100);
0292 
0293     KDevelop::IFrameStackModel* stackModel = session->frameStackModel();
0294 
0295     QCOMPARE(stackModel->rowCount(QModelIndex()), 1); //one fake thread
0296 
0297     QModelIndex tIdx = stackModel->index(0, 0);
0298     QCOMPARE(stackModel->rowCount(tIdx), 2);
0299     QCOMPARE(stackModel->columnCount(tIdx), 3);
0300     COMPARE_DATA(tIdx.child(0, 0), "0");
0301     COMPARE_DATA(tIdx.child(0, 1), "x");
0302     COMPARE_DATA(tIdx.child(0, 2), url.toLocalFile() + ":3");
0303     COMPARE_DATA(tIdx.child(1, 0), "1");
0304     COMPARE_DATA(tIdx.child(1, 1), "{main}");
0305     COMPARE_DATA(tIdx.child(1, 2), url.toLocalFile() + ":5");
0306 
0307     session->stepInto();
0308     session->waitForState(DebugSession::PausedState);
0309     session->stepInto();
0310     session->waitForState(DebugSession::PausedState);
0311     QTest::qWait(100);
0312     QCOMPARE(stackModel->rowCount(tIdx), 1);
0313 
0314     session->run();
0315     session->waitForFinished();
0316 }
0317 
0318 void ConnectionTest::testBreakpoint()
0319 {
0320     QStringList contents;
0321     contents << "<?php"         // 1
0322              << "function x() {" // 2
0323              << "  echo 'x';"   // 3
0324              << "}"             // 4
0325              << "x();"          // 5
0326              << "echo 'y';";    // 6
0327     QTemporaryFile file("xdebugtest");
0328     file.open();
0329     const auto url = QUrl::fromLocalFile(file.fileName());
0330     file.write(contents.join("\n").toUtf8());
0331     file.close();
0332 
0333     auto session = new DebugSession;
0334     KDevelop::ICore::self()->debugController()->addSession(session);
0335 
0336     TestLaunchConfiguration cfg(url);
0337     XDebugJob job(session, &cfg);
0338 
0339     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 3);
0340 
0341     job.start();
0342     session->waitForConnected();
0343 
0344     QSignalSpy showStepInSourceSpy(session, SIGNAL(showStepInSource(QUrl,int,QString)));
0345 
0346     session->waitForState(DebugSession::PausedState);
0347 
0348     QTest::qWait(100);
0349     session->run();
0350     session->waitForFinished();
0351     {
0352         QCOMPARE(showStepInSourceSpy.count(), 1);
0353         QList<QVariant> arguments = showStepInSourceSpy.takeFirst();
0354         QCOMPARE(arguments.first().value<QUrl>(), url);
0355         QCOMPARE(arguments.at(1).toInt(), 3);
0356     }
0357 }
0358 
0359 void ConnectionTest::testDisableBreakpoint()
0360 {
0361     QStringList contents;
0362     contents << "<?php"         // 1
0363              << "function x() {" // 2
0364              << "  echo 'x';"   // 3
0365              << "  echo 'z';"   // 4
0366              << "}"             // 5
0367              << "x();"          // 6
0368              << "x();"          // 7
0369              << "echo 'y';";    // 8
0370     QTemporaryFile file("xdebugtest");
0371     file.open();
0372     const auto url = QUrl::fromLocalFile(file.fileName());
0373     file.write(contents.join("\n").toUtf8());
0374     file.close();
0375 
0376     auto session = new DebugSession;
0377     KDevelop::ICore::self()->debugController()->addSession(session);
0378 
0379     TestLaunchConfiguration cfg(url);
0380     XDebugJob job(session, &cfg);
0381 
0382     KDevelop::BreakpointModel* breakpoints = KDevelop::ICore::self()->debugController()->breakpointModel();
0383     KDevelop::Breakpoint* b;
0384 
0385     //add disabled breakpoint before startProgram
0386     b = breakpoints->addCodeBreakpoint(url, 2);
0387     b->setData(KDevelop::Breakpoint::EnableColumn, false);
0388 
0389     b = breakpoints->addCodeBreakpoint(url, 3);
0390 
0391     job.start();
0392     session->waitForConnected();
0393 
0394     session->waitForState(DebugSession::PausedState);
0395 
0396     //disable existing breakpoint
0397     b->setData(KDevelop::Breakpoint::EnableColumn, false);
0398 
0399     //add another disabled breakpoint
0400     b = breakpoints->addCodeBreakpoint(url, 7);
0401     QTest::qWait(300);
0402     b->setData(KDevelop::Breakpoint::EnableColumn, false);
0403 
0404     session->run();
0405     session->waitForFinished();
0406 }
0407 
0408 void ConnectionTest::testChangeLocationBreakpoint()
0409 {
0410     QStringList contents;
0411     contents << "<?php"         // 1
0412              << "function x() {" // 2
0413              << "  echo 'x';"   // 3
0414              << "  echo 'z';"   // 4
0415              << "}"             // 5
0416              << "x();"          // 6
0417              << "x();"          // 7
0418              << "echo 'y';";    // 8
0419     QTemporaryFile file("xdebugtest");
0420     file.open();
0421     const auto url = QUrl::fromLocalFile(file.fileName());
0422     file.write(contents.join("\n").toUtf8());
0423     file.close();
0424 
0425     auto session = new DebugSession;
0426     KDevelop::ICore::self()->debugController()->addSession(session);
0427 
0428     TestLaunchConfiguration cfg(url);
0429     XDebugJob job(session, &cfg);
0430 
0431     KDevelop::BreakpointModel* breakpoints = KDevelop::ICore::self()->debugController()->breakpointModel();
0432 
0433     KDevelop::Breakpoint* b = breakpoints->addCodeBreakpoint(url, 5);
0434 
0435     job.start();
0436     session->waitForConnected();
0437 
0438     session->waitForState(DebugSession::PausedState);
0439 
0440     b->setLine(7);
0441     QTest::qWait(100);
0442     session->run();
0443 
0444     QTest::qWait(100);
0445     session->waitForState(DebugSession::PausedState);
0446 
0447     session->run();
0448     session->waitForFinished();
0449 }
0450 
0451 void ConnectionTest::testDeleteBreakpoint()
0452 {
0453     QStringList contents;
0454     contents << "<?php"         // 1
0455              << "function x() {" // 2
0456              << "  echo 'x';"   // 3
0457              << "  echo 'z';"   // 4
0458              << "}"             // 5
0459              << "x();"          // 6
0460              << "x();"          // 7
0461              << "echo 'y';";    // 8
0462     QTemporaryFile file("xdebugtest");
0463     file.open();
0464     const auto url = QUrl::fromLocalFile(file.fileName());
0465     file.write(contents.join("\n").toUtf8());
0466     file.close();
0467 
0468     auto session = new DebugSession;
0469     KDevelop::ICore::self()->debugController()->addSession(session);
0470 
0471     TestLaunchConfiguration cfg(url);
0472     XDebugJob job(session, &cfg);
0473 
0474     KDevelop::BreakpointModel* breakpoints = KDevelop::ICore::self()->debugController()->breakpointModel();
0475 
0476     QCOMPARE(KDevelop::ICore::self()->debugController()->breakpointModel()->rowCount(), 0);
0477     //add breakpoint before startProgram
0478     breakpoints->addCodeBreakpoint(url, 5);
0479     QCOMPARE(KDevelop::ICore::self()->debugController()->breakpointModel()->rowCount(), 1);
0480     breakpoints->removeRow(0);
0481     QCOMPARE(KDevelop::ICore::self()->debugController()->breakpointModel()->rowCount(), 0);
0482 
0483     breakpoints->addCodeBreakpoint(url, 2);
0484     QCOMPARE(KDevelop::ICore::self()->debugController()->breakpointModel()->rowCount(), 1);
0485 
0486     job.start();
0487     session->waitForConnected();
0488 
0489     session->waitForState(DebugSession::PausedState);
0490 
0491     breakpoints->removeRow(0);
0492 
0493     QTest::qWait(100);
0494     session->run();
0495     session->waitForFinished();
0496 }
0497 
0498 void ConnectionTest::testConditionalBreakpoint()
0499 {
0500     QStringList contents;
0501     contents << "<?php"        // 1
0502              << "$i = 0;"      // 2
0503              << "$i++;"        // 3
0504              << "$i++;"        // 4
0505              << "$i++;"        // 5
0506              << "echo 'y';";   // 6
0507     QTemporaryFile file("xdebugtest");
0508     file.open();
0509     const auto url = QUrl::fromLocalFile(file.fileName());
0510     file.write(contents.join("\n").toUtf8());
0511     file.close();
0512 
0513     auto session = new DebugSession;
0514     KDevelop::ICore::self()->debugController()->addSession(session);
0515 
0516     TestLaunchConfiguration cfg(url);
0517     XDebugJob job(session, &cfg);
0518 
0519     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2)
0520     ->setCondition("1==2");
0521     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 5)
0522     ->setCondition("1==1");
0523 
0524     job.start();
0525     session->waitForConnected();
0526 
0527     QSignalSpy showStepInSourceSpy(session, SIGNAL(showStepInSource(QUrl,int,QString)));
0528 
0529     session->waitForState(DebugSession::PausedState);
0530 
0531     QTest::qWait(100);
0532     session->run();
0533     session->waitForFinished();
0534     {
0535         QCOMPARE(showStepInSourceSpy.count(), 1);
0536         QList<QVariant> arguments = showStepInSourceSpy.takeFirst();
0537         QCOMPARE(arguments.first().value<QUrl>(), url);
0538         QCOMPARE(arguments.at(1).toInt(), 5);
0539     }
0540 }
0541 
0542 void ConnectionTest::testBreakpointError()
0543 {
0544     QStringList contents;
0545     contents << "<?php"        // 1
0546              << "$i = 0;"      // 2
0547              << "$i++;";        // 3
0548     QTemporaryFile file("xdebugtest");
0549     file.open();
0550     const auto url = QUrl::fromLocalFile(file.fileName());
0551     file.write(contents.join("\n").toUtf8());
0552     file.close();
0553 
0554     auto session = new DebugSession;
0555     KDevelop::ICore::self()->debugController()->addSession(session);
0556 
0557     TestLaunchConfiguration cfg(url);
0558     XDebugJob job(session, &cfg);
0559 
0560     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2);
0561 
0562     KDevelop::Breakpoint* b = KDevelop::ICore::self()->debugController()->breakpointModel()
0563                               ->addCodeBreakpoint(QUrl(""), 2);
0564     QVERIFY(b->errorText().isEmpty());
0565 
0566     job.start();
0567     session->waitForConnected();
0568     session->waitForState(DebugSession::PausedState);
0569     qCDebug(KDEV_PHP_DEBUGGER) << b->errorText();
0570     QVERIFY(!b->errorText().isEmpty());
0571 
0572     session->run();
0573     session->waitForFinished();
0574 }
0575 
0576 KDevelop::VariableCollection* variableCollection()
0577 {
0578     return KDevelop::ICore::self()->debugController()->variableCollection();
0579 }
0580 
0581 void ConnectionTest::testVariablesLocals()
0582 {
0583     QStringList contents;
0584     contents << "<?php"                 // 1
0585              << "$foo = 'foo';"         // 2
0586              << "$bar = 123;"           // 3
0587              << "$baz = array(1, 2, 5);" // 4
0588              << "echo '';";             // 5
0589 /*
0590    <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="context_get" transaction_id="9" context="0">
0591     <property name="foo" fullname="$foo" address="13397880" type="string" size="3" encoding="base64">
0592         <![CDATA[Zm9v]]>
0593     </property>
0594     <property name="bar" fullname="$bar" address="13397840" type="int">
0595         <![CDATA[123]]>
0596     </property>
0597     <property name="baz" fullname="$baz" address="13403224" type="array" children="1" numchildren="3">
0598         <property name="0" fullname="$baz[0]" address="13397800" type="int"><![CDATA[1]]></property>
0599         <property name="1" fullname="$baz[1]" address="13402872" type="int"><![CDATA[2]]></property>
0600         <property name="2" fullname="$baz[2]" address="13403000" type="int"><![CDATA[3]]></property>
0601     </property>
0602    </response>
0603  */
0604     QTemporaryFile file("xdebugtest");
0605     file.open();
0606     const auto url = QUrl::fromLocalFile(file.fileName());
0607     file.write(contents.join("\n").toUtf8());
0608     file.close();
0609 
0610     auto session = new DebugSession;
0611     KDevelop::ICore::self()->debugController()->addSession(session);
0612 
0613     TestLaunchConfiguration cfg(url);
0614     XDebugJob job(session, &cfg);
0615 
0616     session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals);
0617     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 4);
0618 
0619     job.start();
0620     session->waitForConnected();
0621 
0622     session->waitForState(DebugSession::PausedState);
0623     QTest::qWait(1000);
0624 
0625     QVERIFY(variableCollection()->rowCount() >= 2);
0626     QModelIndex i = variableCollection()->index(1, 0);
0627     COMPARE_DATA(i, "Locals");
0628     QCOMPARE(variableCollection()->rowCount(i), 3);
0629     COMPARE_DATA(variableCollection()->index(2, 0, i), "$foo");
0630     COMPARE_DATA(variableCollection()->index(2, 1, i), "foo");
0631     COMPARE_DATA(variableCollection()->index(0, 0, i), "$bar");
0632     COMPARE_DATA(variableCollection()->index(0, 1, i), "123");
0633     COMPARE_DATA(variableCollection()->index(1, 0, i), "$baz");
0634     COMPARE_DATA(variableCollection()->index(1, 1, i), "");
0635     i = variableCollection()->index(1, 0, i);
0636     QCOMPARE(variableCollection()->rowCount(i), 3);
0637     COMPARE_DATA(variableCollection()->index(0, 0, i), "0");
0638     COMPARE_DATA(variableCollection()->index(0, 1, i), "1");
0639     COMPARE_DATA(variableCollection()->index(2, 0, i), "2");
0640     COMPARE_DATA(variableCollection()->index(2, 1, i), "5");
0641     session->run();
0642     session->waitForFinished();
0643 }
0644 
0645 void ConnectionTest::testVariablesSuperglobals()
0646 {
0647     QStringList contents;
0648     contents << "<?php"                 // 1
0649              << "$foo = 'foo';";        // 2
0650 
0651     QTemporaryFile file("xdebugtest");
0652     file.open();
0653     const auto url = QUrl::fromLocalFile(file.fileName());
0654     file.write(contents.join("\n").toUtf8());
0655     file.close();
0656 
0657     auto session = new DebugSession;
0658     KDevelop::ICore::self()->debugController()->addSession(session);
0659 
0660     TestLaunchConfiguration cfg(url);
0661     XDebugJob job(session, &cfg);
0662 
0663     session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals);
0664     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0665 
0666     job.start();
0667     session->waitForConnected();
0668 
0669     session->waitForState(DebugSession::PausedState);
0670     QTest::qWait(1000);
0671 
0672     QVERIFY(variableCollection()->rowCount() >= 3);
0673     QModelIndex i = variableCollection()->index(2, 0);
0674     COMPARE_DATA(i, "Superglobals");
0675     QVERIFY(variableCollection()->rowCount(i) > 4);
0676     COMPARE_DATA(variableCollection()->index(0, 0, i), "$_COOKIE");
0677     COMPARE_DATA(variableCollection()->index(0, 1, i), "");
0678     COMPARE_DATA(variableCollection()->index(1, 0, i), "$_ENV");
0679     COMPARE_DATA(variableCollection()->index(1, 1, i), "");
0680     COMPARE_DATA(variableCollection()->index(6, 0, i), "$_SERVER");
0681     COMPARE_DATA(variableCollection()->index(6, 1, i), "");
0682     i = variableCollection()->index(6, 0, i);
0683     QVERIFY(variableCollection()->rowCount(i) > 5);
0684     session->run();
0685     session->waitForFinished();
0686 }
0687 void ConnectionTest::testVariableExpanding()
0688 {
0689     QStringList contents;
0690     contents << "<?php"                 // 1
0691              << "$foo = array(array(array(1, 2, 5)));" // 2
0692              << "echo '';";             // 3
0693 
0694     QTemporaryFile file("xdebugtest");
0695     file.open();
0696     const auto url = QUrl::fromLocalFile(file.fileName());
0697     file.write(contents.join("\n").toUtf8());
0698     file.close();
0699 
0700     auto session = new DebugSession;
0701     KDevelop::ICore::self()->debugController()->addSession(session);
0702 
0703     TestLaunchConfiguration cfg(url);
0704     XDebugJob job(session, &cfg);
0705 
0706     session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals);
0707     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2);
0708 
0709     job.start();
0710     session->waitForConnected();
0711 
0712     session->waitForState(DebugSession::PausedState);
0713     QTest::qWait(1000);
0714 
0715     QVERIFY(variableCollection()->rowCount() >= 2);
0716     QModelIndex i = variableCollection()->index(1, 0);
0717     COMPARE_DATA(i, "Locals");
0718     QCOMPARE(variableCollection()->rowCount(i), 1);
0719     COMPARE_DATA(variableCollection()->index(0, 0, i), "$foo");
0720     COMPARE_DATA(variableCollection()->index(0, 1, i), "");
0721     i = i.child(0, 0);
0722     variableCollection()->expanded(i);
0723     QTest::qWait(1000);
0724     QCOMPARE(variableCollection()->rowCount(i), 1);
0725     i = i.child(0, 0);
0726     variableCollection()->expanded(i);
0727     QTest::qWait(1000);
0728     QCOMPARE(variableCollection()->rowCount(i), 1);
0729     i = i.child(0, 0);
0730     variableCollection()->expanded(i);
0731     QTest::qWait(1000);
0732     QCOMPARE(variableCollection()->rowCount(i), 3);
0733     COMPARE_DATA(variableCollection()->index(0, 0, i), "0");
0734     COMPARE_DATA(variableCollection()->index(0, 1, i), "1");
0735     COMPARE_DATA(variableCollection()->index(2, 0, i), "2");
0736     COMPARE_DATA(variableCollection()->index(2, 1, i), "5");
0737     session->run();
0738     session->waitForFinished();
0739 }
0740 
0741 class TestTooltipRoot
0742     : public KDevelop::TreeItem
0743 {
0744 public:
0745     TestTooltipRoot(KDevelop::TreeModel* model)
0746         : KDevelop::TreeItem(model)
0747     {}
0748 
0749     void init(KDevelop::Variable* var)
0750     {
0751         appendChild(var);
0752     }
0753 
0754     void fetchMoreChildren() override {}
0755 };
0756 void ConnectionTest::testTooltipVariable()
0757 {
0758     QStringList contents;
0759     contents << "<?php"      // 1
0760              << "$foo = 123;" // 2
0761              << "echo '';";  // 3
0762 
0763     QTemporaryFile file("xdebugtest");
0764     file.open();
0765     const auto url = QUrl::fromLocalFile(file.fileName());
0766     file.write(contents.join("\n").toUtf8());
0767     file.close();
0768 
0769     auto session = new DebugSession;
0770     KDevelop::ICore::self()->debugController()->addSession(session);
0771 
0772     TestLaunchConfiguration cfg(url);
0773     XDebugJob job(session, &cfg);
0774 
0775     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2);
0776 
0777     job.start();
0778     session->waitForConnected();
0779 
0780     session->waitForState(DebugSession::PausedState);
0781     QTest::qWait(1000);
0782 
0783     KDevelop::TreeModel* model = new KDevelop::TreeModel(QVector<QString>() << "Name" << "Value", this);
0784     TestTooltipRoot* tr = new TestTooltipRoot(model);
0785     model->setRootItem(tr);
0786 
0787     KDevelop::Variable* var = session->variableController()->createVariable(model, tr, "$foo");
0788     tr->init(var);
0789     var->attachMaybe();
0790     QTest::qWait(1000);
0791 
0792     QCOMPARE(model->rowCount(), 1);
0793     COMPARE_DATA(model->index(0, 0), "$foo");
0794     COMPARE_DATA(model->index(0, 1), "123");
0795     session->run();
0796     session->waitForFinished();
0797 }
0798 
0799 void ConnectionTest::testInvalidTooltipVariable()
0800 {
0801     QStringList contents;
0802     contents << "<?php"      // 1
0803              << "$foo = 123;" // 2
0804              << "echo '';";  // 3
0805 
0806     QTemporaryFile file("xdebugtest");
0807     file.open();
0808     const auto url = QUrl::fromLocalFile(file.fileName());
0809     file.write(contents.join("\n").toUtf8());
0810     file.close();
0811 
0812     auto session = new DebugSession;
0813     KDevelop::ICore::self()->debugController()->addSession(session);
0814 
0815     TestLaunchConfiguration cfg(url);
0816     XDebugJob job(session, &cfg);
0817 
0818     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2);
0819 
0820     job.start();
0821     session->waitForConnected();
0822 
0823     session->waitForState(DebugSession::PausedState);
0824     QTest::qWait(1000);
0825 
0826     KDevelop::TreeModel* model = new KDevelop::TreeModel(QVector<QString>() << "Name" << "Value", this);
0827     TestTooltipRoot* tr = new TestTooltipRoot(model);
0828     model->setRootItem(tr);
0829 
0830     KDevelop::Variable* var = session->variableController()->createVariable(model, tr, "blah");
0831     tr->init(var);
0832     var->attachMaybe();
0833     QTest::qWait(1000);
0834 
0835     QCOMPARE(model->rowCount(), 1);
0836     COMPARE_DATA(model->index(0, 0), "blah");
0837     COMPARE_DATA(model->index(0, 1), "");
0838     session->run();
0839     session->waitForFinished();
0840 }
0841 
0842 void ConnectionTest::testPhpCrash()
0843 {
0844     QStringList contents;
0845     contents << "<?php"
0846              << "$i = 0;"
0847              << "$i++;";
0848     QTemporaryFile file("xdebugtest");
0849     file.open();
0850     const auto url = QUrl::fromLocalFile(file.fileName());
0851     file.write(contents.join("\n").toUtf8());
0852     file.close();
0853 
0854     QPointer<DebugSession> session(new DebugSession);
0855     KDevelop::ICore::self()->debugController()->addSession(session);
0856 
0857     TestLaunchConfiguration cfg(url);
0858     XDebugJob* job = new XDebugJob(session, &cfg);
0859 
0860     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0861 
0862     job->start();
0863     session->waitForConnected();
0864 
0865     session->waitForState(DebugSession::PausedState);
0866     job->process()->kill(); //simulate crash
0867     QTest::qWait(1000);
0868 
0869     QVERIFY(!session);
0870 
0871     //job seems to gets deleted automatically
0872 }
0873 
0874 void ConnectionTest::testConnectionClosed()
0875 {
0876     QStringList contents;
0877     contents << "<?php"
0878              << "$i = 0;"
0879              << "$i++;";
0880     QTemporaryFile file("xdebugtest");
0881     file.open();
0882     const auto url = QUrl::fromLocalFile(file.fileName());
0883     file.write(contents.join("\n").toUtf8());
0884     file.close();
0885 
0886     QPointer<DebugSession> session(new DebugSession);
0887     KDevelop::ICore::self()->debugController()->addSession(session);
0888 
0889     TestLaunchConfiguration cfg(url);
0890     XDebugJob* job = new XDebugJob(session, &cfg);
0891 
0892     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0893 
0894     job->start();
0895     session->waitForConnected();
0896 
0897     session->waitForState(DebugSession::PausedState);
0898     session->connection()->close(); //simulate eg webserver restart
0899     QTest::qWait(1000);
0900     QVERIFY(!session);
0901 
0902     //job seems to gets deleted automatically
0903 }
0904 
0905 //see bug 235061
0906 void ConnectionTest::testMultipleConnectionsClosed()
0907 {
0908     QStringList contents;
0909     contents << "<?php echo 123;"
0910              << "$i = 0;"
0911              << "$i++;";
0912     QTemporaryFile file("xdebugtest");
0913     file.open();
0914     const auto url = QUrl::fromLocalFile(file.fileName());
0915     file.write(contents.join("\n").toUtf8());
0916     file.close();
0917 
0918     QPointer<DebugSession> session(new DebugSession);
0919     KDevelop::ICore::self()->debugController()->addSession(session);
0920     session->setAcceptMultipleConnections(true);
0921 
0922     TestLaunchConfiguration cfg(url);
0923     XDebugJob* job = new XDebugJob(session, &cfg);
0924 
0925     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 1);
0926 
0927     job->start();
0928     session->waitForConnected();
0929     Connection* firstConnection = session->connection();
0930 
0931     session->waitForState(DebugSession::PausedState);
0932 
0933     //start a second process, as XDebugBrowserJob does
0934     KProcess secondProcess;
0935     secondProcess.setProgram(job->process()->program());
0936     secondProcess.setEnvironment(job->process()->environment());
0937     secondProcess.setOutputChannelMode(KProcess::ForwardedChannels);
0938     secondProcess.start();
0939 
0940     QTest::qWait(1000);
0941 
0942     QVERIFY(session->connection() != firstConnection); //must be a different connection
0943 
0944     session->connection()->close(); //close second connection
0945     QTest::qWait(1000);
0946 
0947     QCOMPARE(session->state(), DebugSession::NotStartedState); //well, it should be EndedState in reality, but this works too
0948 
0949     //job seems to gets deleted automatically
0950 }
0951 
0952 void ConnectionTest::testVariableUpdates()
0953 {
0954     QStringList contents;
0955     contents << "<?php"                 // 1
0956              << "$foo = 1;"         // 2
0957              << "$foo++;"           // 3
0958              << "$foo++;";           // 4
0959 
0960     QTemporaryFile file("xdebugtest");
0961     file.open();
0962     const auto url = QUrl::fromLocalFile(file.fileName());
0963     file.write(contents.join("\n").toUtf8());
0964     file.close();
0965 
0966     auto session = new DebugSession;
0967     KDevelop::ICore::self()->debugController()->addSession(session);
0968 
0969     TestLaunchConfiguration cfg(url);
0970     XDebugJob job(session, &cfg);
0971 
0972     session->variableController()->setAutoUpdate(KDevelop::IVariableController::UpdateLocals);
0973     KDevelop::ICore::self()->debugController()->breakpointModel()->addCodeBreakpoint(url, 2);
0974 
0975     job.start();
0976     session->waitForConnected();
0977 
0978     session->waitForState(DebugSession::PausedState);
0979     QTest::qWait(1000);
0980 
0981     QVERIFY(variableCollection()->rowCount() >= 2);
0982     QModelIndex i = variableCollection()->index(1, 0);
0983     COMPARE_DATA(i, "Locals");
0984     QCOMPARE(variableCollection()->rowCount(i), 1);
0985     COMPARE_DATA(variableCollection()->index(0, 0, i), "$foo");
0986     COMPARE_DATA(variableCollection()->index(0, 1, i), "1");
0987 
0988     session->stepInto();
0989     QTest::qWait(1000);
0990     QVERIFY(variableCollection()->rowCount() >= 2);
0991     i = variableCollection()->index(1, 0);
0992     COMPARE_DATA(i, "Locals");
0993     QCOMPARE(variableCollection()->rowCount(i), 1);
0994     COMPARE_DATA(variableCollection()->index(0, 0, i), "$foo");
0995     COMPARE_DATA(variableCollection()->index(0, 1, i), "2");
0996 
0997     session->run();
0998     session->waitForFinished();
0999 }
1000 
1001 //     controller.connection()->sendCommand("eval -i 124", QStringList(), "eval(\"function test124() { return rand(); } return test124();\")");
1002 //     controller.connection()->sendCommand("eval -i 126", QStringList(), "test124();");
1003 
1004 QTEST_MAIN(ConnectionTest)