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