File indexing completed on 2024-03-24 04:38:34
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)