File indexing completed on 2024-04-21 15:24:20

0001 /*
0002     SPDX-FileCopyrightText: 2012 Miha Čančula <miha@noughmad.eu>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "phpunitrunjob.h"
0008 #include "phpunittestsuite.h"
0009 #include "testdoxdelegate.h"
0010 #include "testproviderdebug.h"
0011 
0012 #include <QStandardPaths>
0013 
0014 #include <util/processlinemaker.h>
0015 #include <util/executecompositejob.h>
0016 #include <outputview/outputmodel.h>
0017 #include <interfaces/itestcontroller.h>
0018 #include <interfaces/iruncontroller.h>
0019 #include <interfaces/icore.h>
0020 #include <interfaces/ilauncher.h>
0021 #include <interfaces/ilaunchconfiguration.h>
0022 #include <interfaces/launchconfigurationtype.h>
0023 #include <interfaces/ilaunchmode.h>
0024 
0025 #include <KProcess>
0026 #include <KLocalizedString>
0027 #include <KConfigGroup>
0028 
0029 PhpUnitRunJob::PhpUnitRunJob(PhpUnitTestSuite* suite, const QStringList& cases, KDevelop::OutputJob::OutputJobVerbosity verbosity, QObject* parent)
0030 : KJob(parent)
0031 , m_process(nullptr)
0032 , m_suite(suite)
0033 , m_cases(cases)
0034 , m_job(nullptr)
0035 , m_outputJob(nullptr)
0036 , m_verbosity(verbosity)
0037 {
0038 }
0039 
0040 KJob* createTestJob(QString launchModeId, QStringList arguments )
0041 {
0042     KDevelop::LaunchConfigurationType* type = KDevelop::ICore::self()->runController()->launchConfigurationTypeForId( QStringLiteral("Script Application") );
0043     KDevelop::ILaunchMode* mode = KDevelop::ICore::self()->runController()->launchModeForId( launchModeId );
0044 
0045     qCDebug(TESTPROVIDER) << "got mode and type:" << type << type->id() << mode << mode->id();
0046     Q_ASSERT(type && mode);
0047 
0048     KDevelop::ILauncher* launcher = nullptr;
0049     foreach (KDevelop::ILauncher *l, type->launchers())
0050     {
0051         //qCDebug(TESTPROVIDER) << "available launcher" << l << l->id() << l->supportedModes();
0052         if (l->supportedModes().contains(mode->id())) {
0053             launcher = l;
0054             break;
0055         }
0056     }
0057     Q_ASSERT(launcher);
0058 
0059     KDevelop::ILaunchConfiguration* ilaunch = nullptr;
0060     QList<KDevelop::ILaunchConfiguration*> launchConfigurations = KDevelop::ICore::self()->runController()->launchConfigurations();
0061     foreach (KDevelop::ILaunchConfiguration *l, launchConfigurations) {
0062         if (l->type() == type && l->config().readEntry("ConfiguredByPhpUnit", false)) {
0063             ilaunch = l;
0064             break;
0065         }
0066     }
0067     if (!ilaunch) {
0068         ilaunch = KDevelop::ICore::self()->runController()->createLaunchConfiguration( type,
0069                                                 qMakePair( mode->id(), launcher->id() ),
0070                                                 nullptr, //TODO add project
0071                                                 i18n("PHPUnit") );
0072         ilaunch->config().writeEntry("ConfiguredByPhpUnit", true);
0073         //qCDebug(TESTPROVIDER) << "created config, launching";
0074     } else {
0075         //qCDebug(TESTPROVIDER) << "reusing generated config, launching";
0076     }
0077     type->configureLaunchFromCmdLineArguments( ilaunch->config(), arguments );
0078     return KDevelop::ICore::self()->runController()->execute(launchModeId, ilaunch);
0079 }
0080 
0081 void PhpUnitRunJob::start()
0082 {
0083     m_process = new KProcess(this);
0084     // TODO: Arguments from test cases
0085 
0086     QStringList args;
0087 
0088     if (m_cases != m_suite->cases())
0089     {
0090         args << QStringLiteral("--filter");
0091         args << '"' + m_cases.join(QStringLiteral("|")) + '"';
0092     }
0093 
0094     args << QStringLiteral("--testdox") << m_suite->name() << m_suite->url().toLocalFile();
0095 
0096     const QString exe = QStandardPaths::findExecutable(QStringLiteral("phpunit"));
0097     if (exe.isEmpty()) {
0098         KDevelop::ITestController* tc = KDevelop::ICore::self()->testController();
0099         tc->notifyTestRunFinished(m_suite, m_result);
0100         emitResult();
0101         return;
0102     }
0103 
0104     args.prepend(exe);
0105     args.prepend(QStringLiteral("php"));
0106 
0107     m_job = createTestJob(QStringLiteral("execute"), args);
0108 
0109     m_outputJob = qobject_cast<KDevelop::OutputJob*>(m_job);
0110     if (!m_outputJob) {
0111         if (UnprotectedExecuteCompositeJob* cjob = qobject_cast<UnprotectedExecuteCompositeJob*>(m_job)) {
0112             m_outputJob = qobject_cast<KDevelop::OutputJob*>(cjob->subjobs().last());
0113         }
0114     }
0115     Q_ASSERT(m_outputJob);
0116     if (m_outputJob) {
0117         m_outputJob->setVerbosity(m_verbosity);
0118         connect(m_outputJob->model(), &QAbstractItemModel::rowsInserted, this, &PhpUnitRunJob::rowsInserted);
0119     }
0120 
0121     connect(m_job, &KJob::finished, this, &PhpUnitRunJob::processFinished);
0122 }
0123 
0124 bool PhpUnitRunJob::doKill()
0125 {
0126     if (m_job)
0127     {
0128         m_job->kill();
0129     }
0130     return true;
0131 }
0132 
0133 void PhpUnitRunJob::processFinished(KJob* job)
0134 {
0135     if (job->error() == 1) {
0136         m_result.suiteResult = KDevelop::TestResult::Failed;
0137     } else if (job->error() == 0) {
0138         m_result.suiteResult = KDevelop::TestResult::Passed;
0139         foreach (KDevelop::TestResult::TestCaseResult result, m_result.testCaseResults)
0140         {
0141             if (result == KDevelop::TestResult::Failed)
0142             {
0143                 m_result.suiteResult = KDevelop::TestResult::Failed;
0144                 break;
0145             }
0146         }
0147     } else {
0148         m_result.suiteResult = KDevelop::TestResult::Error;
0149     }
0150 
0151     qCDebug(TESTPROVIDER) << m_result.suiteResult << m_result.testCaseResults;
0152     KDevelop::ICore::self()->testController()->notifyTestRunFinished(m_suite, m_result);
0153     emitResult();
0154 }
0155 
0156 void PhpUnitRunJob::rowsInserted(const QModelIndex &parent, int startRow, int endRow)
0157 {
0158     Q_ASSERT(m_outputJob);
0159     static QRegExp testResultLineExp = QRegExp("\\[([x\\s])\\]");
0160     for (int row = startRow; row <= endRow; ++row)
0161     {
0162         QString line = m_outputJob->model()->data(m_outputJob->model()->index(row, 0, parent), Qt::DisplayRole).toString();
0163 
0164         int i = testResultLineExp.indexIn(line);
0165         if (i > -1)
0166         {
0167             bool passed = testResultLineExp.cap(1) == QLatin1String("x");
0168             QString testCase = "test" + line.mid(i+4).toLower().remove(' ');
0169             qCDebug(TESTPROVIDER) << "Got result in " << line << " for " << testCase;
0170             if (m_cases.contains(testCase, Qt::CaseInsensitive))
0171             {
0172                 foreach (const QString& realCaseName, m_cases)
0173                 {
0174                     if (QString::compare(testCase, realCaseName, Qt::CaseInsensitive) == 0)
0175                     {
0176                         m_result.testCaseResults[testCase] = (passed ? KDevelop::TestResult::Passed : KDevelop::TestResult::Failed);
0177                         break;
0178                     }
0179                 }
0180             }
0181         }
0182         else
0183         {
0184             qCDebug(TESTPROVIDER) << line << testResultLineExp.pattern() << i;
0185         }
0186     }
0187 }
0188 
0189 #include "moc_phpunitrunjob.cpp"