1 /*
2 This file is part of KDevelop PHP support
3 SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "phpunitrunjob.h"
9 #include "phpunittestsuite.h"
10 #include "testdoxdelegate.h"
11
12 #include <util/processlinemaker.h>
13 #include <util/executecompositejob.h>
14 #include <outputview/outputmodel.h>
15 #include <interfaces/itestcontroller.h>
16 #include <interfaces/icore.h>
17 #include <interfaces/ilauncher.h>
18 #include <interfaces/ilaunchconfiguration.h>
19 #include <interfaces/launchconfigurationtype.h>
20 #include <interfaces/ilaunchmode.h>
21
22 #include <KProcess>
23 #include <KStandardDirs>
24 #include <KDebug>
25 #include <KLocalizedString>
26 #include <KConfigGroup>
27
PhpUnitRunJob(PhpUnitTestSuite * suite,const QStringList & cases,KDevelop::OutputJob::OutputJobVerbosity verbosity,QObject * parent)28 PhpUnitRunJob::PhpUnitRunJob(PhpUnitTestSuite* suite, const QStringList& cases, KDevelop::OutputJob::OutputJobVerbosity verbosity, QObject* parent)
29 : KJob(parent)
30 , m_process(0)
31 , m_suite(suite)
32 , m_cases(cases)
33 , m_job(0)
34 , m_outputJob(0)
35 , m_verbosity(verbosity)
36 {
37 }
38
createTestJob(QString launchModeId,QStringList arguments)39 KJob* createTestJob(QString launchModeId, QStringList arguments )
40 {
41 KDevelop::LaunchConfigurationType* type = KDevelop::ICore::self()->runController()->launchConfigurationTypeForId( "Script Application" );
42 KDevelop::ILaunchMode* mode = KDevelop::ICore::self()->runController()->launchModeForId( launchModeId );
43
44 kDebug() << "got mode and type:" << type << type->id() << mode << mode->id();
45 Q_ASSERT(type && mode);
46
47 KDevelop::ILauncher* launcher = 0;
48 foreach (KDevelop::ILauncher *l, type->launchers())
49 {
50 //kDebug() << "available launcher" << l << l->id() << l->supportedModes();
51 if (l->supportedModes().contains(mode->id())) {
52 launcher = l;
53 break;
54 }
55 }
56 Q_ASSERT(launcher);
57
58 KDevelop::ILaunchConfiguration* ilaunch = 0;
59 QList<KDevelop::ILaunchConfiguration*> launchConfigurations = KDevelop::ICore::self()->runController()->launchConfigurations();
60 foreach (KDevelop::ILaunchConfiguration *l, launchConfigurations) {
61 if (l->type() == type && l->config().readEntry("ConfiguredByPhpUnit", false)) {
62 ilaunch = l;
63 break;
64 }
65 }
66 if (!ilaunch) {
67 ilaunch = KDevelop::ICore::self()->runController()->createLaunchConfiguration( type,
68 qMakePair( mode->id(), launcher->id() ),
69 0, //TODO add project
70 i18n("PHPUnit") );
71 ilaunch->config().writeEntry("ConfiguredByPhpUnit", true);
72 //kDebug() << "created config, launching";
73 } else {
74 //kDebug() << "reusing generated config, launching";
75 }
76 type->configureLaunchFromCmdLineArguments( ilaunch->config(), arguments );
77 return KDevelop::ICore::self()->runController()->execute(launchModeId, ilaunch);
78 }
79
start()80 void PhpUnitRunJob::start()
81 {
82 m_process = new KProcess(this);
83 // TODO: Arguments from test cases
84
85 QStringList args;
86
87 if (m_cases != m_suite->cases())
88 {
89 args << "--filter";
90 args << '"' + m_cases.join("|") + '"';
91 }
92
93 args << "--testdox" << m_suite->name() << m_suite->url().toLocalFile();
94
95 const QString exe = KStandardDirs::findExe("phpunit");
96 if (exe.isEmpty()) {
97 KDevelop::ITestController* tc = KDevelop::ICore::self()->testController();
98 tc->notifyTestRunFinished(m_suite, m_result);
99 emitResult();
100 return;
101 }
102
103 args.prepend(exe);
104 args.prepend("php");
105
106 m_job = createTestJob("execute", args);
107
108 m_outputJob = qobject_cast<KDevelop::OutputJob*>(m_job);
109 if (!m_outputJob) {
110 if (KDevelop::ExecuteCompositeJob* cjob = qobject_cast<KDevelop::ExecuteCompositeJob*>(m_job)) {
111 m_outputJob = qobject_cast<KDevelop::OutputJob*>(cjob->subjobs().last());
112 }
113 }
114 Q_ASSERT(m_outputJob);
115 if (m_outputJob) {
116 m_outputJob->setVerbosity(m_verbosity);
117 connect(m_outputJob->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int)));
118 }
119
120 connect(m_job, SIGNAL(finished(KJob*)), SLOT(processFinished(KJob*)));
121 }
122
doKill()123 bool PhpUnitRunJob::doKill()
124 {
125 if (m_job)
126 {
127 m_job->kill();
128 }
129 return true;
130 }
131
processFinished(KJob * job)132 void PhpUnitRunJob::processFinished(KJob* job)
133 {
134 if (job->error() == 1) {
135 m_result.suiteResult = KDevelop::TestResult::Failed;
136 } else if (job->error() == 0) {
137 m_result.suiteResult = KDevelop::TestResult::Passed;
138 foreach (KDevelop::TestResult::TestCaseResult result, m_result.testCaseResults)
139 {
140 if (result == KDevelop::TestResult::Failed)
141 {
142 m_result.suiteResult = KDevelop::TestResult::Failed;
143 break;
144 }
145 }
146 } else {
147 m_result.suiteResult = KDevelop::TestResult::Error;
148 }
149
150 kDebug() << m_result.suiteResult << m_result.testCaseResults;
151 KDevelop::ICore::self()->testController()->notifyTestRunFinished(m_suite, m_result);
152 emitResult();
153 }
154
rowsInserted(const QModelIndex & parent,int startRow,int endRow)155 void PhpUnitRunJob::rowsInserted(const QModelIndex &parent, int startRow, int endRow)
156 {
157 Q_ASSERT(m_outputJob);
158 static QRegExp testResultLineExp = QRegExp("\\[([x\\s])\\]");
159 for (int row = startRow; row <= endRow; ++row)
160 {
161 QString line = m_outputJob->model()->data(m_outputJob->model()->index(row, 0, parent), Qt::DisplayRole).toString();
162
163 int i = testResultLineExp.indexIn(line);
164 if (i > -1)
165 {
166 bool passed = testResultLineExp.cap(1) == "x";
167 QString testCase = "test" + line.mid(i+4).toLower().remove(' ');
168 kDebug() << "Got result in " << line << " for " << testCase;
169 if (m_cases.contains(testCase, Qt::CaseInsensitive))
170 {
171 foreach (const QString& realCaseName, m_cases)
172 {
173 if (QString::compare(testCase, realCaseName, Qt::CaseInsensitive) == 0)
174 {
175 m_result.testCaseResults[testCase] = (passed ? KDevelop::TestResult::Passed : KDevelop::TestResult::Failed);
176 break;
177 }
178 }
179 }
180 }
181 else
182 {
183 kDebug() << line << testResultLineExp.pattern() << i;
184 }
185 }
186 }
187