Commit 3889462d authored by Miha Čančula's avatar Miha Čančula Committed by Milian Wolff

Improve the CTestFinder

parent 7c5d48d0
[Project]
Name=KDevelop
Manager=KDevCMakeManager
Name=kdevelop
......@@ -30,6 +30,7 @@ set( cmakecommon_SRCS
cmakeutils.cpp
cmakebuilddirchooser.cpp
cmakemodelitems.cpp
)
set( cmakecommon_UI
......
......@@ -677,7 +677,7 @@ KDevelop::ReferencedTopDUContext CMakeManager::includeScript(const QString& file
m_watchers[project]->addPath(file);
QString profile = CMake::currentEnvironment(project);
const KDevelop::EnvironmentGroupList env( KGlobal::config() );
return CMakeParserUtils::includeScript( file, parent, &m_projectsData[project], dir, env.variables(profile));
return CMakeParserUtils::includeScript( file, parent, &m_projectsData[project], dir, env.variables(profile), project);
}
......
......@@ -31,6 +31,11 @@
#include <ktempdir.h>
#include <cmakeprojectdata.h>
#include <interfaces/iproject.h>
#include <interfaces/icore.h>
#include <interfaces/iplugincontroller.h>
#include "testing/ctestfinder/ictestprovider.h"
namespace CMakeParserUtils
{
QList<int> parseVersion(const QString& version, bool* ok)
......@@ -149,7 +154,7 @@ namespace CMakeParserUtils
}
KDevelop::ReferencedTopDUContext includeScript(const QString& file, KDevelop::ReferencedTopDUContext parent,
CMakeProjectData* data, const QString& sourcedir, const QMap<QString,QString>& env)
CMakeProjectData* data, const QString& sourcedir, const QMap<QString,QString>& env, KDevelop::IProject* project)
{
kDebug(9042) << "Running cmake script: " << file;
CMakeFileContent f = CMakeListsParser::readCMakeFile(file);
......@@ -181,6 +186,28 @@ namespace CMakeParserUtils
data->includeDirectories=v.includeDirectories();
data->targets=v.targets();
data->properties=v.properties();
KDevelop::IPlugin* testProviderPlugin = KDevelop::ICore::self()->pluginController()->loadPlugin("kdevctestfinder");
if (testProviderPlugin)
{
kDebug(9042) << "Found test provider plugin" << testProviderPlugin->extensions();
ICTestProvider* testProvider = testProviderPlugin->extension<ICTestProvider>();
if (testProvider)
{
kDebug(9042) << "Test provider supports the correct interface, creating" <<
v.testSuites().size() << "test suites for" << file;
QMultiMap<QString, QString>::const_iterator it = v.testSuites().constBegin();
QMultiMap<QString, QString>::const_iterator end = v.testSuites().constEnd();
for (; it != end; ++it)
{
testProvider->createTestSuite(it.value(), it.key(), project);
}
}
}
else
{
kDebug(9042) << "No test provider plugin loaded";
}
//printSubdirectories(data->subdirectories);
......
......@@ -32,6 +32,10 @@
struct CMakeProjectData;
class VariableMap;
namespace KDevelop {
class IProject;
}
namespace CMakeParserUtils
{
/**
......@@ -54,8 +58,7 @@ namespace CMakeParserUtils
/** Runs the process specified by @p execName with @p args */
KDEVCMAKECOMMON_EXPORT QString executeProcess(const QString& execName, const QStringList& args=QStringList());
KDEVCMAKECOMMON_EXPORT KDevelop::ReferencedTopDUContext includeScript( const QString& file, KDevelop::ReferencedTopDUContext parent, CMakeProjectData* data,
const QString& sourcedir, const QMap< QString, QString >& env);
KDEVCMAKECOMMON_EXPORT KDevelop::ReferencedTopDUContext includeScript( const QString& file, KDevelop::ReferencedTopDUContext parent, CMakeProjectData* data, const QString& sourcedir, const QMap< QString, QString >& env, KDevelop::IProject* project = 0);
}
#endif
......
......@@ -50,6 +50,12 @@ using namespace KDevelop;
void debugMsgs(const QString& message) { kDebug(9032) << "message:" << message; }
bool isGenerated(const QString& name)
{
return name.indexOf("#[")>=0;
}
CMakeProjectVisitor::message_callback CMakeProjectVisitor::s_msgcallback=debugMsgs;
CMakeProjectVisitor::CMakeProjectVisitor(const QString& root, ReferencedTopDUContext parent)
......@@ -270,7 +276,7 @@ int CMakeProjectVisitor::visit(const CMakeAst *ast)
int CMakeProjectVisitor::visit( const AddTestAst * test)
{
Q_UNUSED(test);
m_testSuites.insert(test->exeName(), test->testName());
return 1;
}
......@@ -2334,11 +2340,6 @@ void CMakeProjectVisitor::setVariableMap(VariableMap * vars)
m_vars=vars;
}
bool isGenerated(const QString& name)
{
return name.indexOf("#[")>=0;
}
QStringList CMakeProjectVisitor::dependees(const QString& s) const
{
QStringList ret;
......
......@@ -38,6 +38,7 @@ namespace KDevelop
{
class TopDUContext;
class Declaration;
class ITestController;
}
class KDEVCMAKECOMMON_EXPORT CMakeProjectVisitor : CMakeAstVisitor
......@@ -115,6 +116,7 @@ class KDEVCMAKECOMMON_EXPORT CMakeProjectVisitor : CMakeAstVisitor
QList<Target> targets() const { return m_targetForId.values(); }
QStringList resolveDependencies(const QStringList& target) const;
QStringList includeDirectories() const { return m_includeDirectories; }
QMultiMap<QString, QString> testSuites() const { return m_testSuites; }
int walk(const CMakeFileContent& fc, int line, bool isClean=false);
......@@ -202,6 +204,8 @@ class KDEVCMAKECOMMON_EXPORT CMakeProjectVisitor : CMakeAstVisitor
KDevelop::ReferencedTopDUContext m_parentCtx;
bool m_hitBreak;
QMap<QString, QString> m_environmentProfile;
QMultiMap<QString, QString> m_testSuites;
};
#endif
......@@ -21,6 +21,5 @@ target_link_libraries(
install(TARGETS kdevctestfinder DESTINATION ${PLUGIN_INSTALL_DIR} )
########### install files ###############
install( FILES ictestprovider.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevelop/ctest COMPONENT Devel)
install( FILES kdevctestfinder.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
......@@ -30,11 +30,12 @@
#include <interfaces/itestcontroller.h>
#include <project/interfaces/ibuildsystemmanager.h>
#include <project/projectmodel.h>
#include <interfaces/iplugincontroller.h>
#include <KPluginFactory>
#include <KAboutData>
#include <KLocale>
#include <QFile>
#include <KDebug>
K_PLUGIN_FACTORY(CTestFinderFactory, registerPlugin<CTestFinder>(); )
K_EXPORT_PLUGIN(CTestFinderFactory(KAboutData("kdevctestfinder","kdevctestfinder", ki18n("CTest Finder"), "0.1", ki18n("Finds CTest unit tests"), KAboutData::License_GPL)))
......@@ -44,11 +45,10 @@ using namespace KDevelop;
CTestFinder::CTestFinder(QObject* parent, const QList<QVariant>& args): IPlugin(CTestFinderFactory::componentData(), parent)
{
Q_UNUSED(args);
foreach (IProject* project, core()->projectController()->projects())
{
findTestsForProject(project);
}
connect (core()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)), SLOT(findTestsForProject(KDevelop::IProject*)));
KDEV_USE_EXTENSION_INTERFACE( ICTestProvider )
m_controller = ICore::self()->pluginController()->pluginForExtension("org.kdevelop.ITestController")->extension<ITestController>();
}
CTestFinder::~CTestFinder()
......@@ -56,78 +56,20 @@ CTestFinder::~CTestFinder()
}
void CTestFinder::findTestsForProject(IProject* project)
void CTestFinder::createTestSuite(const QString& name, const QString& executable, IProject* project, const QStringList& arguments)
{
IBuildSystemManager* bm = project->buildSystemManager();
if (!bm)
QString exe = executable;
if (exe.startsWith("#[bin_dir]"))
{
return;
exe.remove("#[bin_dir]");
}
findTestsInDirectory(bm->buildDirectory(project->projectItem()));
KUrl exeUrl = project->buildSystemManager()->buildDirectory(project->projectItem());
exeUrl.addPath(exe);
Q_ASSERT(exeUrl.isLocalFile());
kDebug() << exeUrl << exeUrl.toLocalFile();
CTestSuite* suite = new CTestSuite(name, exeUrl, project, arguments);
suite->setTestController(m_controller);
suite->loadCases();
}
void CTestFinder::findTestsInDirectory(const KUrl& directory)
{
KUrl fileUrl = directory;
fileUrl.addPath("CTestTestfile.cmake");
QFile file(fileUrl.toLocalFile());
if (!file.open(QIODevice::ReadOnly))
{
return;
}
while (!file.atEnd())
{
QString line = file.readLine();
if (line.startsWith('#'))
{
continue;
}
else if (line.startsWith("SUBDIRS("))
{
line = line.trimmed();
line.remove("SUBDIRS(");
line.remove(')');
KUrl subDirUrl = directory;
subDirUrl.cd(line);
findTestsInDirectory(subDirUrl);
}
else if (line.startsWith("ADD_TEST("))
{
line = line.trimmed();
line.remove("ADD_TEST(");
line.remove(')');
int firstSpace = line.indexOf(' ');
if (firstSpace == -1)
{
continue;
}
QString name = line.mid(0, firstSpace);
line.remove(0, firstSpace);
QStringList args = line.split('\"');
QMutableStringListIterator it(args);
while(it.hasNext())
{
QString arg = it.next().trimmed();
if (arg.isEmpty())
{
it.remove();
}
}
int n = args.size();
if (n < 1)
{
continue;
}
QString exe = args.takeFirst();
if (!exe.startsWith(directory.toLocalFile()))
{
exe = directory.toLocalFile(KUrl::AddTrailingSlash) + exe;
}
CTestSuite* suite = new CTestSuite(name, exe, args);
core()->testController()->addTestSuite(suite);
}
}
}
......@@ -20,26 +20,29 @@
#ifndef CTESTFINDER_H
#define CTESTFINDER_H
#include "ictestprovider.h"
#include <interfaces/iplugin.h>
class QVariant;
class KUrl;
namespace KDevelop
{
class IProject;
class IProject;
class ITestController;
}
class CTestFinder : public KDevelop::IPlugin
class CTestFinder : public KDevelop::IPlugin, public ICTestProvider
{
Q_OBJECT
Q_OBJECT
Q_INTERFACES(ICTestProvider)
public:
CTestFinder(QObject* parent, const QList<QVariant>& args);
virtual ~CTestFinder();
private slots:
void findTestsForProject(KDevelop::IProject* project);
void findTestsInDirectory(const KUrl& directory);
virtual void createTestSuite(const QString& name, const QString& executable, KDevelop::IProject* project, const QStringList& arguments = QStringList());
private:
KDevelop::ITestController* m_controller;
};
#endif // CTESTFINDER_H
......@@ -26,6 +26,7 @@
#include <KConfigGroup>
#include <KIcon>
#include "ctestlauncher.h"
#include <interfaces/iplugincontroller.h>
using namespace KDevelop;
......@@ -41,7 +42,8 @@ CTestLaunchConfigurationType::~CTestLaunchConfigurationType()
bool CTestLaunchConfigurationType::canLaunch(const KUrl& file) const
{
return ICore::self()->testController()->testSuiteForUrl(file);
ITestController* controller = ICore::self()->pluginController()->pluginForExtension("org.kdevelop.ITestController")->extension<ITestController>();
return controller->testSuiteForUrl(file);
}
void CTestLaunchConfigurationType::configureLaunchFromCmdLineArguments(KConfigGroup config, const QStringList& args) const
......
......@@ -27,46 +27,58 @@
#include <interfaces/ilaunchconfiguration.h>
#include <KProcess>
#include <KDebug>
#include <QFileInfo>
#include <interfaces/itestcontroller.h>
#include <interfaces/iproject.h>
using namespace KDevelop;
CTestSuite::CTestSuite(const QString& name, const KUrl& executable, const QStringList& args) :
CTestSuite::CTestSuite(const QString& name, const KUrl& executable, IProject* project, const QStringList& args): QObject(),
m_url(executable),
m_name(name),
m_args(args)
m_args(args),
m_project(project),
m_controller(0)
{
m_launchType = new CTestLaunchConfigurationType();
loadCases();
Q_ASSERT(project);
kDebug() << name << executable << project->name();
}
CTestSuite::~CTestSuite()
{
m_controller->removeTestSuite(this);
}
void CTestSuite::loadCases()
{
kDebug() << "Loading test cases for suite" << m_name << m_url;
m_cases.clear();
if (!m_args.isEmpty())
{
m_cases.clear();
m_cases << QString();
m_cases << name();
m_controller->addTestSuite(this);
return;
}
KProcess process;
process.setOutputChannelMode(KProcess::OnlyStdoutChannel);
process.setProgram(m_url.toLocalFile(), QStringList() << "-functions");
process.start();
if (!process.waitForFinished())
QFileInfo info(m_url.toLocalFile());
if (info.exists() && info.isExecutable())
{
return;
m_process = new KProcess;
m_process->setOutputChannelMode(KProcess::OnlyStdoutChannel);
m_process->setProgram(m_url.toLocalFile(), QStringList() << "-functions");
connect (m_process, SIGNAL(finished(int)), this, SLOT(loadCasesProcessFinished(int)));
connect (m_process, SIGNAL(started()), this, SLOT(readFromProcess()));
connect (m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(readFromProcess()));
connect (m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromProcess()));
kDebug() << "Starting a process to determine the test cases for" << m_name;
m_process->start();
}
while(!process.atEnd())
else
{
QString line = process.readLine().trimmed();
line.remove('(');
line.remove(')');
m_cases << line;
m_cases << m_url.toLocalFile(); // TODO: Remove
m_controller->addTestSuite(this);
}
}
......@@ -77,12 +89,19 @@ KDevelop::ILaunchConfiguration* CTestSuite::launchCase(const QString& testCase)
KDevelop::ILaunchConfiguration* CTestSuite::launchCases(const QStringList& testCases) const
{
ILaunchConfiguration* launch = ICore::self()->runController()->createLaunchConfiguration(m_launchType, qMakePair<QString, QString>("test", "CTestLauncher"));
ILaunchConfiguration* launch = ICore::self()->runController()->createLaunchConfiguration(m_launchType, qMakePair<QString, QString>("test", "CTestLauncher"));
KConfigGroup group = launch->config();
group.writeEntry("TestExecutable", m_url);
group.writeEntry("TestCases", testCases);
group.writeEntry("TestRunArguments", m_args);
if (!m_cases.isEmpty())
{
group.writeEntry("TestCases", testCases);
}
else if (!m_args.isEmpty())
{
group.writeEntry("TestRunArguments", m_args);
}
group.sync();
return launch;
......@@ -107,3 +126,46 @@ QString CTestSuite::name() const
{
return m_name;
}
KDevelop::IProject* CTestSuite::project() const
{
return m_project;
}
QStringList CTestSuite::arguments()
{
return m_args;
}
void CTestSuite::setTestController(ITestController* controller)
{
m_controller = controller;
}
void CTestSuite::loadCasesProcessFinished(int exitCode)
{
kDebug() << exitCode;
if (!m_process)
{
kWarning() << "Loading cases finished but process is 0";
return;
}
while(!m_process->atEnd())
{
QString line = m_process->readLine().trimmed();
line.remove('(');
line.remove(')');
m_cases << line;
}
kDebug() << m_url << m_cases;
m_controller->addTestSuite(this);
m_process->deleteLater();
m_process = 0;
}
void CTestSuite::readFromProcess()
{
kDebug() << m_process->errorString();
}
......@@ -24,13 +24,18 @@
#include <KUrl>
class KProcess;
namespace KDevelop {
class ITestController;
}
class CTestLaunchConfigurationType;
class CTestSuite : public QObject, public KDevelop::ITestSuite
{
Q_OBJECT
public:
CTestSuite(const QString& name, const KUrl& executable, const QStringList& args = QStringList());
CTestSuite(const QString& name, const KUrl& executable, KDevelop::IProject* project, const QStringList& args = QStringList());
virtual ~CTestSuite();
virtual KDevelop::ILaunchConfiguration* launchCase(const QString& testCase) const;
......@@ -39,14 +44,26 @@ public:
virtual KUrl url() const;
virtual QStringList cases() const;
virtual QString name() const;
virtual KDevelop::IProject* project() const;
void loadCases();
void setTestController(KDevelop::ITestController* controller);
QStringList arguments();
private:
KUrl m_url;
QString m_name;
QStringList m_cases;
QStringList m_args;
KDevelop::IProject* m_project;
CTestLaunchConfigurationType* m_launchType;
KDevelop::ITestController* m_controller;
KProcess* m_process;
private slots:
void loadCasesProcessFinished(int exitCode);
public slots:
void readFromProcess();
};
#endif // CTESTSUITE_H
......@@ -2,15 +2,18 @@
Type=Service
Icon=cmake
Exec=blubb
Comment=This plugin finds CTest unit tests
Comment=Finds and executes CTest unit tests
Name=CTest Finder
GenericName=CTest Finder
ServiceTypes=KDevelop/Plugin
X-KDE-Library=kdevctestfinder
X-KDevelop-Version=14
X-KDevelop-Category=Global
X-KDE-PluginInfo-Name=kdevctestfinder
X-KDE-PluginInfo-Author=Miha Čančula
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-Category=Testing
X-KDE-PluginInfo-Interfaces=org.kdevelop.ICTestProvider
X-KDE-PluginInfo-IRequired=org.kdevelop.ITestController
X-KDevelop-Version=14
X-KDevelop-Category=Global
X-KDevelop-Mode=NoGUI
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment