diff --git a/kdevplatform/shell/plugincontroller.cpp b/kdevplatform/shell/plugincontroller.cpp index 579f734eed..7efd358b0d 100644 --- a/kdevplatform/shell/plugincontroller.cpp +++ b/kdevplatform/shell/plugincontroller.cpp @@ -1,788 +1,782 @@ /* This file is part of the KDE project Copyright 2004, 2007 Alexander Dymo Copyright 2006 Matt Rogers Based on code from Kopete Copyright (c) 2002-2003 Martijn Klingens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugincontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "shellextension.h" #include "runcontroller.h" #include "debugcontroller.h" #include "documentationcontroller.h" #include "sourceformattercontroller.h" #include "projectcontroller.h" #include "ktexteditorpluginintegration.h" #include "debug.h" namespace { inline QString KEY_Plugins() { return QStringLiteral("Plugins"); } inline QString KEY_Suffix_Enabled() { return QStringLiteral("Enabled"); } inline QString KEY_LoadMode() { return QStringLiteral("X-KDevelop-LoadMode"); } inline QString KEY_Category() { return QStringLiteral("X-KDevelop-Category"); } inline QString KEY_Mode() { return QStringLiteral("X-KDevelop-Mode"); } inline QString KEY_Version() { return QStringLiteral("X-KDevelop-Version"); } inline QString KEY_Interfaces() { return QStringLiteral("X-KDevelop-Interfaces"); } inline QString KEY_Required() { return QStringLiteral("X-KDevelop-IRequired"); } inline QString KEY_Optional() { return QStringLiteral("X-KDevelop-IOptional"); } +inline QString KEY_KPlugin() { return QStringLiteral("KPlugin"); } +inline QString KEY_EnabledByDefault() { return QStringLiteral("EnabledByDefault"); } inline QString KEY_Global() { return QStringLiteral("Global"); } inline QString KEY_Project() { return QStringLiteral("Project"); } inline QString KEY_Gui() { return QStringLiteral("GUI"); } inline QString KEY_AlwaysOn() { return QStringLiteral("AlwaysOn"); } inline QString KEY_UserSelectable() { return QStringLiteral("UserSelectable"); } bool isUserSelectable( const KPluginMetaData& info ) { QString loadMode = info.value(KEY_LoadMode()); return loadMode.isEmpty() || loadMode == KEY_UserSelectable(); } bool isGlobalPlugin( const KPluginMetaData& info ) { return info.value(KEY_Category()) == KEY_Global(); } bool hasMandatoryProperties( const KPluginMetaData& info ) { QString mode = info.value(KEY_Mode()); if (mode.isEmpty()) { return false; } // when the plugin is installed into the versioned plugin path, it's good to go if (info.fileName().contains(QLatin1String("/kdevplatform/" QT_STRINGIFY(KDEVELOP_PLUGIN_VERSION) "/"))) { return true; } // the version property is only required when the plugin is not installed into the right directory QVariant version = info.rawData().value(KEY_Version()).toVariant(); if (version.isValid() && version.value() == KDEVELOP_PLUGIN_VERSION) { return true; } return false; } bool constraintsMatch( const KPluginMetaData& info, const QVariantMap& constraints) { for (auto it = constraints.begin(); it != constraints.end(); ++it) { const auto property = info.rawData().value(it.key()).toVariant(); if (!property.isValid()) { return false; } else if (property.canConvert()) { QSet values = property.toStringList().toSet(); QSet expected = it.value().toStringList().toSet(); if (!values.contains(expected)) { return false; } } else if (it.value() != property) { return false; } } return true; } struct Dependency { explicit Dependency(const QString &dependency) : interface(dependency) { if (dependency.contains('@')) { const auto list = dependency.split('@', QString::SkipEmptyParts); if (list.size() == 2) { interface = list.at(0); pluginName = list.at(1); } } } QString interface; QString pluginName; }; } namespace KDevelop { class PluginControllerPrivate { public: QVector plugins; //map plugin infos to currently loaded plugins typedef QHash InfoToPluginMap; InfoToPluginMap loadedPlugins; // The plugin manager's mode. The mode is StartingUp until loadAllPlugins() // has finished loading the plugins, after which it is set to Running. // ShuttingDown and DoneShutdown are used during shutdown by the // async unloading of plugins. enum CleanupMode { Running /**< the plugin manager is running */, CleaningUp /**< the plugin manager is cleaning up for shutdown */, CleanupDone /**< the plugin manager has finished cleaning up */ }; CleanupMode cleanupMode; bool canUnload(const KPluginMetaData& plugin) { qCDebug(SHELL) << "checking can unload for:" << plugin.name() << plugin.value(KEY_LoadMode()); if (plugin.value(KEY_LoadMode()) == KEY_AlwaysOn()) { return false; } const QStringList interfaces = KPluginMetaData::readStringList(plugin.rawData(), KEY_Interfaces()); qCDebug(SHELL) << "checking dependencies:" << interfaces; foreach (const KPluginMetaData& info, loadedPlugins.keys()) { if (info.pluginId() != plugin.pluginId()) { QStringList dependencies = KPluginMetaData::readStringList(plugin.rawData(), KEY_Required()); dependencies += KPluginMetaData::readStringList(plugin.rawData(), KEY_Optional()); foreach (const QString& dep, dependencies) { Dependency dependency(dep); if (!dependency.pluginName.isEmpty() && dependency.pluginName != plugin.pluginId()) { continue; } if (interfaces.contains(dependency.interface) && !canUnload(info)) { return false; } } } } return true; } KPluginMetaData infoForId( const QString& id ) const { foreach (const KPluginMetaData& info, plugins) { if (info.pluginId() == id) { return info; } } return KPluginMetaData(); } /** * Iterate over all cached plugin infos, and call the functor for every enabled plugin. * * If an extension and/or pluginName is given, the functor will only be called for * those plugins matching this information. * * The functor should return false when the iteration can be stopped, and true if it * should be continued. */ template void foreachEnabledPlugin(F func, const QString &extension = {}, const QVariantMap& constraints = QVariantMap(), const QString &pluginName = {}) { foreach (const auto& info, plugins) { if ((pluginName.isEmpty() || info.pluginId() == pluginName) && (extension.isEmpty() || KPluginMetaData::readStringList(info.rawData(), KEY_Interfaces()).contains(extension)) && constraintsMatch(info, constraints) && isEnabled(info)) { if (!func(info)) { break; } } } } /** * Decide whether a plugin is enabled */ bool isEnabled(const KPluginMetaData& info) const { + // first check black listing from environment static const QStringList disabledPlugins = QString::fromLatin1(qgetenv("KDEV_DISABLE_PLUGINS")).split(';'); if (disabledPlugins.contains(info.pluginId())) { return false; } if (!isUserSelectable( info )) return true; - // in case there's a user preference, prefer that + // read stored user preference const KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); const QString pluginEnabledKey = info.pluginId() + KEY_Suffix_Enabled(); if (grp.hasKey(pluginEnabledKey)) { return grp.readEntry(pluginEnabledKey, true); } - // in all other cases: figure out if we want to load that plugin by default - const auto defaultPlugins = ShellExtension::getInstance()->defaultPlugins(); - const bool isDefaultPlugin = defaultPlugins.isEmpty() || defaultPlugins.contains(info.pluginId()); - if (isDefaultPlugin) { - return true; - } - - if (!isGlobalPlugin( info )) { - QJsonValue enabledByDefault = info.rawData()[QStringLiteral("KPlugin")].toObject()[QStringLiteral("EnabledByDefault")]; - return enabledByDefault.isNull() || enabledByDefault.toBool(); //We consider plugins enabled until specified otherwise - } - return false; } Core *core; }; PluginController::PluginController(Core *core) : IPluginController(), d(new PluginControllerPrivate) { setObjectName(QStringLiteral("PluginController")); d->core = core; QSet foundPlugins; auto newPlugins = KPluginLoader::findPlugins(QStringLiteral("kdevplatform/" QT_STRINGIFY(KDEVELOP_PLUGIN_VERSION)), [&](const KPluginMetaData& meta) { if (meta.serviceTypes().contains(QStringLiteral("KDevelop/Plugin"))) { foundPlugins.insert(meta.pluginId()); return true; } else { qCWarning(SHELL) << "Plugin" << meta.fileName() << "is installed into the kdevplatform plugin directory, but does not have" " \"KDevelop/Plugin\" set as the service type. This plugin will not be loaded."; return false; } }); qCDebug(SHELL) << "Found" << newPlugins.size() << " plugins:" << foundPlugins; d->plugins = newPlugins; KTextEditorIntegration::initialize(); const QVector ktePlugins = KPluginLoader::findPlugins(QStringLiteral("ktexteditor"), [](const KPluginMetaData & md) { return md.serviceTypes().contains(QStringLiteral("KTextEditor/Plugin")) && md.serviceTypes().contains(QStringLiteral("KDevelop/Plugin")); }); foundPlugins.clear(); std::for_each(ktePlugins.cbegin(), ktePlugins.cend(), [&foundPlugins](const KPluginMetaData& data) { foundPlugins << data.pluginId(); }); qCDebug(SHELL) << "Found" << ktePlugins.size() << " KTextEditor plugins:" << foundPlugins; foreach (const auto& info, ktePlugins) { auto data = info.rawData(); // add some KDevelop specific JSON data data[KEY_Category()] = KEY_Global(); data[KEY_Mode()] = KEY_Gui(); data[KEY_Version()] = KDEVELOP_PLUGIN_VERSION; d->plugins.append({data, info.fileName(), info.metaDataFileName()}); } d->cleanupMode = PluginControllerPrivate::Running; // Register the KDevelop::IPlugin* metatype so we can properly unload it qRegisterMetaType( "KDevelop::IPlugin*" ); } PluginController::~PluginController() { if ( d->cleanupMode != PluginControllerPrivate::CleanupDone ) { qCWarning(SHELL) << "Destructing plugin controller without going through the shutdown process!"; } } KPluginMetaData PluginController::pluginInfo( const IPlugin* plugin ) const { return d->loadedPlugins.key(const_cast(plugin)); } void PluginController::cleanup() { if(d->cleanupMode != PluginControllerPrivate::Running) { //qCDebug(SHELL) << "called when not running. state =" << d->cleanupMode; return; } d->cleanupMode = PluginControllerPrivate::CleaningUp; // Ask all plugins to unload while ( !d->loadedPlugins.isEmpty() ) { //Let the plugin do some stuff before unloading unloadPlugin(d->loadedPlugins.begin().value(), Now); } d->cleanupMode = PluginControllerPrivate::CleanupDone; } IPlugin* PluginController::loadPlugin( const QString& pluginName ) { return loadPluginInternal( pluginName ); } -bool PluginController::isEnabled( const KPluginMetaData& info ) const -{ - return d->isEnabled(info); -} - void PluginController::initialize() { QElapsedTimer timer; timer.start(); QMap pluginMap; if( ShellExtension::getInstance()->defaultPlugins().isEmpty() ) { foreach( const KPluginMetaData& pi, d->plugins ) { - pluginMap.insert( pi.pluginId(), true ); + QJsonValue enabledByDefaultValue = pi.rawData()[KEY_KPlugin()].toObject()[KEY_EnabledByDefault()]; + // plugins enabled until explicitly specified otherwise + const bool enabledByDefault = (enabledByDefaultValue.isNull() || enabledByDefaultValue.toBool()); + pluginMap.insert(pi.pluginId(), enabledByDefault); } } else { // Get the default from the ShellExtension foreach( const QString& s, ShellExtension::getInstance()->defaultPlugins() ) { pluginMap.insert( s, true ); } } KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); QMap entries = grp.entryMap(); QMap::Iterator it; for ( it = entries.begin(); it != entries.end(); ++it ) { const QString key = it.key(); if (key.endsWith(KEY_Suffix_Enabled())) { const QString pluginid = key.left(key.length() - 7); const bool defValue = pluginMap.value( pluginid, false ); const bool enabled = grp.readEntry(key, defValue); pluginMap.insert( pluginid, enabled ); } } - foreach( const KPluginMetaData& pi, d->plugins ) - { - if( isGlobalPlugin( pi ) ) - { - QMap::const_iterator it = pluginMap.constFind( pi.pluginId() ); - if( it != pluginMap.constEnd() && ( it.value() || !isUserSelectable( pi ) ) ) - { - // Plugin is mentioned in pluginmap and the value is true, so try to load it - loadPluginInternal( pi.pluginId() ); - if(!grp.hasKey(pi.pluginId() + KEY_Suffix_Enabled())) { - if( isUserSelectable( pi ) ) - { - // If plugin isn't listed yet, add it with true now - grp.writeEntry(pi.pluginId() + KEY_Suffix_Enabled(), true); - } - } else if( grp.hasKey( pi.pluginId() + "Disabled" ) && !isUserSelectable( pi ) ) - { - // Remove now-obsolete entries - grp.deleteEntry( pi.pluginId() + "Disabled" ); - } + // store current known set of enabled plugins + foreach (const KPluginMetaData& pi, d->plugins) { + if (isUserSelectable(pi)) { + auto it = pluginMap.constFind(pi.pluginId()); + if (it != pluginMap.constEnd() && (it.value())) { + grp.writeEntry(pi.pluginId() + KEY_Suffix_Enabled(), true); } + } else { + // Backward compat: Remove any now-obsolete entries + grp.deleteEntry(pi.pluginId() + QLatin1String("Disabled")); } } // Synchronize so we're writing out to the file. grp.sync(); + // load global plugins + foreach (const KPluginMetaData& pi, d->plugins) { + if (isGlobalPlugin(pi)) { + loadPluginInternal(pi.pluginId()); + } + } + qCDebug(SHELL) << "Done loading plugins - took:" << timer.elapsed() << "ms"; } QList PluginController::loadedPlugins() const { return d->loadedPlugins.values(); } bool PluginController::unloadPlugin( const QString & pluginId ) { IPlugin *thePlugin = plugin( pluginId ); bool canUnload = d->canUnload( d->infoForId( pluginId ) ); qCDebug(SHELL) << "Unloading plugin:" << pluginId << "?" << thePlugin << canUnload; if( thePlugin && canUnload ) { return unloadPlugin(thePlugin, Later); } return (canUnload && thePlugin); } bool PluginController::unloadPlugin(IPlugin* plugin, PluginDeletion deletion) { qCDebug(SHELL) << "unloading plugin:" << plugin << pluginInfo( plugin ).name(); emit unloadingPlugin(plugin); plugin->unload(); emit pluginUnloaded(plugin); //Remove the plugin from our list of plugins so we create a new //instance when we're asked for it again. //This is important to do right here, not later when the plugin really //vanishes. For example project re-opening might try to reload the plugin //and then would get the "old" pointer which will be deleted in the next //event loop run and thus causing crashes. for ( PluginControllerPrivate::InfoToPluginMap::Iterator it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it ) { if ( it.value() == plugin ) { d->loadedPlugins.erase( it ); break; } } if (deletion == Later) plugin->deleteLater(); else delete plugin; return true; } KPluginMetaData PluginController::infoForPluginId( const QString &pluginId ) const { foreach (const KPluginMetaData& info, d->plugins) { if (info.pluginId() == pluginId) { return info; } } return KPluginMetaData(); } IPlugin *PluginController::loadPluginInternal( const QString &pluginId ) { QElapsedTimer timer; timer.start(); KPluginMetaData info = infoForPluginId( pluginId ); if ( !info.isValid() ) { qCWarning(SHELL) << "Unable to find a plugin named '" << pluginId << "'!" ; return nullptr; } if ( IPlugin* plugin = d->loadedPlugins.value( info ) ) { return plugin; } - if ( !isEnabled( info ) ) { + if (!d->isEnabled(info)) { // Do not load disabled plugins qCWarning(SHELL) << "Not loading plugin named" << pluginId << "because it has been disabled!"; return nullptr; } if ( !hasMandatoryProperties( info ) ) { qCWarning(SHELL) << "Unable to load plugin named" << pluginId << "because not all mandatory properties are set."; return nullptr; } if ( info.value(KEY_Mode()) == KEY_Gui() && Core::self()->setupFlags() == Core::NoUi ) { qCDebug(SHELL) << "Not loading plugin named" << pluginId << "- Running in No-Ui mode, but the plugin says it needs a GUI"; return nullptr; } qCDebug(SHELL) << "Attempting to load" << pluginId << "- name:" << info.name(); emit loadingPlugin( info.pluginId() ); // first, ensure all dependencies are available and not disabled // this is unrelated to whether they are loaded already or not. // when we depend on e.g. A and B, but B cannot be found, then we // do not want to load A first and then fail on B and leave A loaded. // this would happen if we'd skip this step here and directly loadDependencies. QStringList missingInterfaces; if ( !hasUnresolvedDependencies( info, missingInterfaces ) ) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "some of its required dependencies could not be fulfilled:" << missingInterfaces.join(QStringLiteral(",")); return nullptr; } // now ensure all dependencies are loaded QString failedDependency; if( !loadDependencies( info, failedDependency ) ) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "because a required dependency could not be loaded:" << failedDependency; return nullptr; } // same for optional dependencies, but don't error out if anything fails loadOptionalDependencies( info ); // now we can finally load the plugin itself KPluginLoader loader(info.fileName()); auto factory = loader.factory(); if (!factory) { qCWarning(SHELL) << "Can't load plugin" << pluginId << "because a factory to load the plugin could not be obtained:" << loader.errorString(); return nullptr; } // now create it auto plugin = factory->create(d->core); if (!plugin) { if (auto katePlugin = factory->create(d->core, QVariantList() << info.pluginId())) { plugin = new KTextEditorIntegration::Plugin(katePlugin, d->core); } else { qCWarning(SHELL) << "Creating plugin" << pluginId << "failed."; return nullptr; } } KConfigGroup group = Core::self()->activeSession()->config()->group(KEY_Plugins()); // runtime errors such as missing executables on the system or such get checked now if (plugin->hasError()) { qCWarning(SHELL) << "Could not load plugin" << pluginId << ", it reported the error:" << plugin->errorDescription() << "Disabling the plugin now."; group.writeEntry(info.pluginId() + KEY_Suffix_Enabled(), false); // do the same as KPluginInfo did group.sync(); unloadPlugin(pluginId); return nullptr; } // yay, it all worked - the plugin is loaded d->loadedPlugins.insert(info, plugin); group.writeEntry(info.pluginId() + KEY_Suffix_Enabled(), true); // do the same as KPluginInfo did group.sync(); qCDebug(SHELL) << "Successfully loaded plugin" << pluginId << "from" << loader.fileName() << "- took:" << timer.elapsed() << "ms"; emit pluginLoaded( plugin ); return plugin; } IPlugin* PluginController::plugin( const QString& pluginId ) { KPluginMetaData info = infoForPluginId( pluginId ); if ( !info.isValid() ) return nullptr; return d->loadedPlugins.value( info ); } bool PluginController::hasUnresolvedDependencies( const KPluginMetaData& info, QStringList& missing ) const { QSet required = KPluginMetaData::readStringList(info.rawData(), KEY_Required()).toSet(); if (!required.isEmpty()) { d->foreachEnabledPlugin([&required] (const KPluginMetaData& plugin) -> bool { foreach (const QString& iface, KPluginMetaData::readStringList(plugin.rawData(), KEY_Interfaces())) { required.remove(iface); required.remove(iface + '@' + plugin.pluginId()); } return !required.isEmpty(); }); } // if we found all dependencies required should be empty now if (!required.isEmpty()) { missing = required.toList(); return false; } return true; } void PluginController::loadOptionalDependencies( const KPluginMetaData& info ) { const QStringList dependencies = KPluginMetaData::readStringList(info.rawData(), KEY_Optional()); foreach (const QString& dep, dependencies) { Dependency dependency(dep); if (!pluginForExtension(dependency.interface, dependency.pluginName)) { qCDebug(SHELL) << "Couldn't load optional dependency:" << dep << info.pluginId(); } } } bool PluginController::loadDependencies( const KPluginMetaData& info, QString& failedDependency ) { const QStringList dependencies = KPluginMetaData::readStringList(info.rawData(), KEY_Required()); foreach (const QString& value, dependencies) { Dependency dependency(value); if (!pluginForExtension(dependency.interface, dependency.pluginName)) { failedDependency = value; return false; } } return true; } IPlugin *PluginController::pluginForExtension(const QString &extension, const QString &pluginName, const QVariantMap& constraints) { IPlugin* plugin = nullptr; d->foreachEnabledPlugin([this, &plugin] (const KPluginMetaData& info) -> bool { plugin = d->loadedPlugins.value( info ); if( !plugin ) { plugin = loadPluginInternal( info.pluginId() ); } return !plugin; }, extension, constraints, pluginName); return plugin; } QList PluginController::allPluginsForExtension(const QString &extension, const QVariantMap& constraints) { //qCDebug(SHELL) << "Finding all Plugins for Extension:" << extension << "|" << constraints; QList plugins; d->foreachEnabledPlugin([this, &plugins] (const KPluginMetaData& info) -> bool { IPlugin* plugin = d->loadedPlugins.value( info ); if( !plugin) { plugin = loadPluginInternal( info.pluginId() ); } if (plugin && !plugins.contains(plugin)) { plugins << plugin; } return true; }, extension, constraints); return plugins; } QVector PluginController::queryExtensionPlugins(const QString& extension, const QVariantMap& constraints) const { QVector plugins; d->foreachEnabledPlugin([&plugins] (const KPluginMetaData& info) -> bool { plugins << info; return true; }, extension, constraints); return plugins; } QStringList PluginController::allPluginNames() { QStringList names; Q_FOREACH( const KPluginMetaData& info , d->plugins ) { names << info.pluginId(); } return names; } QList PluginController::queryPluginsForContextMenuExtensions(KDevelop::Context* context, QWidget* parent) const { // This fixes random order of extension menu items between different runs of KDevelop. // Without sorting we have random reordering of "Analyze With" submenu for example: // 1) "Cppcheck" actions, "Vera++" actions - first run // 2) "Vera++" actions, "Cppcheck" actions - some other run. QMultiMap sortedPlugins; for (auto it = d->loadedPlugins.constBegin(); it != d->loadedPlugins.constEnd(); ++it) { sortedPlugins.insert(it.key().name(), it.value()); } QList exts; foreach (IPlugin* plugin, sortedPlugins) { exts << plugin->contextMenuExtension(context, parent); } exts << Core::self()->debugControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->documentationControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->sourceFormatterControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->runControllerInternal()->contextMenuExtension(context, parent); exts << Core::self()->projectControllerInternal()->contextMenuExtension(context, parent); return exts; } QStringList PluginController::projectPlugins() { QStringList names; foreach (const KPluginMetaData& info, d->plugins) { if (info.value(KEY_Category()) == KEY_Project()) { names << info.pluginId(); } } return names; } void PluginController::loadProjectPlugins() { Q_FOREACH( const QString& name, projectPlugins() ) { loadPluginInternal( name ); } } void PluginController::unloadProjectPlugins() { Q_FOREACH( const QString& name, projectPlugins() ) { unloadPlugin( name ); } } QVector PluginController::allPluginInfos() const { return d->plugins; } void PluginController::updateLoadedPlugins() { QStringList defaultPlugins = ShellExtension::getInstance()->defaultPlugins(); KConfigGroup grp = Core::self()->activeSession()->config()->group( KEY_Plugins() ); foreach( const KPluginMetaData& info, d->plugins ) { if( isGlobalPlugin( info ) ) { bool enabled = grp.readEntry(info.pluginId() + KEY_Suffix_Enabled(), ( defaultPlugins.isEmpty() || defaultPlugins.contains( info.pluginId() ) ) ) || !isUserSelectable( info ); bool loaded = d->loadedPlugins.contains( info ); if( loaded && !enabled ) { qCDebug(SHELL) << "unloading" << info.pluginId(); if( !unloadPlugin( info.pluginId() ) ) { grp.writeEntry( info.pluginId() + KEY_Suffix_Enabled(), false ); } } else if( !loaded && enabled ) { loadPluginInternal( info.pluginId() ); } } + // TODO: what about project plugins? what about dependency plugins? } } void PluginController::resetToDefaults() { KSharedConfigPtr cfg = Core::self()->activeSession()->config(); cfg->deleteGroup( KEY_Plugins() ); cfg->sync(); KConfigGroup grp = cfg->group( KEY_Plugins() ); QStringList plugins = ShellExtension::getInstance()->defaultPlugins(); if( plugins.isEmpty() ) { foreach( const KPluginMetaData& info, d->plugins ) { - plugins << info.pluginId(); + if (!isUserSelectable(info)) { + continue; + } + + QJsonValue enabledByDefault = info.rawData()[KEY_KPlugin()].toObject()[KEY_EnabledByDefault()]; + // plugins enabled until explicitly specified otherwise + if (enabledByDefault.isNull() || enabledByDefault.toBool()) { + plugins << info.pluginId(); + } } } foreach( const QString& s, plugins ) { grp.writeEntry(s + KEY_Suffix_Enabled(), true); } grp.sync(); } } diff --git a/kdevplatform/shell/plugincontroller.h b/kdevplatform/shell/plugincontroller.h index 99e9cd689c..8a39a5135b 100644 --- a/kdevplatform/shell/plugincontroller.h +++ b/kdevplatform/shell/plugincontroller.h @@ -1,182 +1,181 @@ /* This file is part of the KDE project Copyright 2007 Andreas Pakulat Copyright 2004, 2007 Alexander Dymo Copyright 2006 Matt Rogers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_PLUGINCONTROLLER_H #define KDEVPLATFORM_PLUGINCONTROLLER_H #include #include "shellexport.h" namespace KDevelop { class Core; class CorePrivate; class IPlugin; class PluginControllerPrivate; /** * The KDevelop plugin controller. * The Plugin controller is responsible for querying, loading and unloading * available plugins. */ class KDEVPLATFORMSHELL_EXPORT PluginController: public IPluginController { Q_OBJECT friend class Core; friend class CorePrivate; public: explicit PluginController(Core *core); ~PluginController() override; /** * Get the plugin instance based on the ID. The ID should be whatever is * in X-KDE-PluginInfo-Name */ IPlugin* plugin( const QString& ); /** * Get the plugin info for a loaded plugin */ KPluginMetaData pluginInfo( const IPlugin* ) const override; /** * Find the KPluginMetaData structure for the given @p pluginId. */ KPluginMetaData infoForPluginId(const QString &pluginId) const override; /** * Get a list of currently loaded plugins */ QList loadedPlugins() const override; /** * Returns a uniquely specified plugin. If it isn't already loaded, it will be. * @param pluginName the name of the plugin, as given in the X-KDE-PluginInfo-Name property * @returns a pointer to the plugin instance or 0 */ IPlugin * loadPlugin( const QString & pluginName ) override; /** * @brief Unloads the plugin specified by @p plugin * * @param plugin The name of the plugin as specified by the * X-KDE-PluginInfo-Name key of the .desktop file for the plugin */ bool unloadPlugin( const QString & plugin ) override; enum PluginDeletion { Now, Later }; /** * retrieve all plugin infos */ QVector allPluginInfos() const; /** * loads not-yet-loaded plugins and unloads plugins * depending on the configuration in the session\ */ void updateLoadedPlugins(); /** * Queries for the plugin which supports given extension interface. * * All already loaded plugins will be queried and the first one to support the extension interface * will be returned. Any plugin can be an extension, only "ServiceTypes=..." entry is * required in .desktop file for that plugin. * * @param extension The extension interface * @param pluginName The name of the plugin to load if multiple plugins for the extension exist, corresponds to the X-KDE-PluginInfo-Name * @return A KDevelop extension plugin for given service type or 0 if no plugin supports it */ IPlugin *pluginForExtension(const QString &extension, const QString &pluginName = {}, const QVariantMap& constraints = QVariantMap()) override; QList allPluginsForExtension(const QString &extension, const QVariantMap& constraints = QVariantMap()) override; QStringList allPluginNames(); QVector queryExtensionPlugins(const QString& extension, const QVariantMap& constraints = QVariantMap()) const override; QList queryPluginsForContextMenuExtensions(KDevelop::Context* context, QWidget* parent) const override; QStringList projectPlugins(); void loadProjectPlugins(); void unloadProjectPlugins(); void resetToDefaults(); - bool isEnabled(const KPluginMetaData& info) const; private: /** * Directly unload the given \a plugin, either deleting it now or \a deletion. * * \param plugin plugin to unload * \param deletion if true, delete the plugin later, if false, delete it now. */ bool unloadPlugin(IPlugin* plugin, PluginDeletion deletion); /** * @internal * * The internal method for loading plugins. * Called by @ref loadPlugin directly or through the queue for async plugin * loading. */ IPlugin* loadPluginInternal( const QString &pluginId ); /** * Check whether the plugin identified by @p info has unresolved dependencies. * * Assume a plugin depends on the interfaces Foo and Bar. Then, all available enabled * plugins are queried to check whether any fulfills the interfaces. If any of the * interfaces is not found, then it is inserted into @p missing and this method returns * true. Otherwise, @p missing is empty and this method returns true, indicating that all * dependencies can be fulfilled. * * @return true when there are unresolved dependencies, false otherwise. */ bool hasUnresolvedDependencies( const KPluginMetaData& info, QStringList& missing ) const; bool loadDependencies(const KPluginMetaData&, QString& failedPlugin); void loadOptionalDependencies(const KPluginMetaData& info); void cleanup(); virtual void initialize(); private: const QScopedPointer d; }; } #endif diff --git a/kdevplatform/shell/settings/pluginpreferences.cpp b/kdevplatform/shell/settings/pluginpreferences.cpp index d2b6321858..b62ca10acb 100644 --- a/kdevplatform/shell/settings/pluginpreferences.cpp +++ b/kdevplatform/shell/settings/pluginpreferences.cpp @@ -1,110 +1,108 @@ /* KDevelop Project Settings * * Copyright 2008 Andreas Pakulat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "pluginpreferences.h" #include #include #include #include #include "../core.h" #include "../plugincontroller.h" #include "debug.h" namespace KDevelop { PluginPreferences::PluginPreferences(QWidget* parent) : ConfigPage(nullptr, nullptr, parent) { QVBoxLayout* lay = new QVBoxLayout(this ); lay->setMargin(0); selector = new KPluginSelector( this ); lay->addWidget( selector ); QMap> plugins; const QMap categories = { { "Core", i18nc("@title:group", "Core") }, { "Project Management", i18nc("@title:group", "Project Management") }, { "Version Control", i18nc("@title:group", "Version Control") }, { "Utilities", i18nc("@title:group", "Utilities") }, { "Documentation", i18nc("@title:group", "Documentation") }, { "Language Support", i18nc("@title:group", "Language Support") }, { "Debugging", i18nc("@title:group", "Debugging") }, { "Testing", i18nc("@title:group", "Testing") }, { "Analyzers", i18nc("@title:group", "Analyzers") }, { "Runtimes", i18nc("@title:group", "Runtimes") }, { "Other", i18nc("@title:group", "Other") } }; foreach (const KPluginMetaData& info, Core::self()->pluginControllerInternal()->allPluginInfos()) { const QString loadMode = info.value(QStringLiteral("X-KDevelop-LoadMode")); if( loadMode.isEmpty() || loadMode == QLatin1String("UserSelectable") ) { QString category = info.category(); if (!categories.contains(category)) { if (!category.isEmpty()) { qCWarning(SHELL) << "unknown category for plugin" << info.name() << ":" << info.category(); } category = QStringLiteral("Other"); } KPluginInfo kpi(info); - kpi.setPluginEnabled(Core::self()->pluginControllerInternal()->isEnabled(info)); plugins[category] << kpi; } else qCDebug(SHELL) << "skipping..." << info.pluginId() << info.value(QStringLiteral("X-KDevelop-Category")) << loadMode; } for (auto it = plugins.constBegin(), end = plugins.constEnd(); it != end; ++it) { selector->addPlugins(it.value(), KPluginSelector::ReadConfigFile, categories.value(it.key()), // no filter by category key, we did it ourselves above & will not work with "Other" QString(), Core::self()->activeSession()->config()); } connect(selector, &KPluginSelector::changed, this, &PluginPreferences::changed); - selector->load(); } void PluginPreferences::defaults() { Core::self()->pluginControllerInternal()->resetToDefaults(); selector->load(); } void PluginPreferences::apply() { selector->save(); qCDebug(SHELL) << "Plugins before apply: " << Core::self()->pluginControllerInternal()->allPluginNames(); Core::self()->pluginControllerInternal()->updateLoadedPlugins(); qCDebug(SHELL) << "Plugins after apply: " << Core::self()->pluginControllerInternal()->allPluginNames(); selector->load(); // Some plugins may have failed to load, they must be unchecked. } void PluginPreferences::reset() { selector->load(); } } diff --git a/kdevplatform/shell/shellextension.h b/kdevplatform/shell/shellextension.h index 3190c32c04..5bae84b290 100644 --- a/kdevplatform/shell/shellextension.h +++ b/kdevplatform/shell/shellextension.h @@ -1,83 +1,85 @@ /*************************************************************************** * Copyright 2004 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_SHELLEXTENSION_H #define KDEVPLATFORM_SHELLEXTENSION_H #include #include "shellexport.h" namespace KDevelop { /**Default area parameters collection.*/ struct AreaParams { /**Unique name for the area.*/ QString name; /**User-visible area title.*/ QString title; }; /** Shell extension. Provides application-dependent and shell-independent functionality. Shell uses extensions to perform application dependent actions. */ class KDEVPLATFORMSHELL_EXPORT ShellExtension { public: virtual ~ShellExtension() {} /**Returns an instance of a shell. Subclasses must create an instance of a shell by themselves. For example they could provide static init() method like: @code static void init() { s_instance = new MyExtension(); } @endcode*/ static ShellExtension *getInstance(); /**Reimplement to return the path to the executable that needs to be executed for new sessions.*/ virtual QString executableFilePath() = 0; /**Reimplement to return the name of KXMLGUI resource file for an application.*/ virtual QString xmlFile() = 0; /**Reimplement to return the name of the default ui area.*/ virtual AreaParams defaultArea() = 0; /**Reimplement to return the filename extension for project files.*/ virtual QString projectFileExtension() = 0; /**Reimplement to return the description for project files.*/ virtual QString projectFileDescription() = 0; /** * Reimplement to return the list of plugins that should - * automatically be loaded + * loaded by default. + * If an empty list is returned, instead the plugin metadata is fallen back to, + * by reading the bool value KPlugin/EnabledByDefault (default: true). */ virtual QStringList defaultPlugins() = 0; protected: ShellExtension(); static ShellExtension *s_instance; }; } #endif diff --git a/kdevplatform/shell/tests/CMakeLists.txt b/kdevplatform/shell/tests/CMakeLists.txt index 247fd52c9b..edcec1adbb 100644 --- a/kdevplatform/shell/tests/CMakeLists.txt +++ b/kdevplatform/shell/tests/CMakeLists.txt @@ -1,54 +1,66 @@ ecm_add_test(test_documentcontroller.cpp LINK_LIBRARIES Qt5::Test KDev::Tests) ecm_add_test(test_uicontroller.cpp LINK_LIBRARIES Qt5::Test KDev::Tests) ecm_add_test(test_shellbuddy.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) ecm_add_test(test_shelldocumentoperation.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) ecm_add_test(test_projectcontroller.cpp TEST_NAME test_projectcontroller LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Sublime KDev::Project KDev::Interfaces) ecm_add_test(test_sessioncontroller.cpp LINK_LIBRARIES Qt5::Test KF5::KIOWidgets KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) set(TEST_PLUGIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/testplugindir") configure_file("testfilepaths.h.cmake" "testfilepaths.h" ESCAPE_QUOTES) -kdevplatform_add_plugin(kdevnonguiinterfaceplugin JSON kdevnonguiinterfaceplugin.json SKIP_INSTALL SOURCES nonguiinterfaceplugin.cpp) -target_link_libraries(kdevnonguiinterfaceplugin Qt5::Core KDev::Interfaces) -set_target_properties(kdevnonguiinterfaceplugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TEST_PLUGIN_DIR}/kdevplatform/${KDEV_PLUGIN_VERSION}") + +macro(KDEVSHELL_ADD_TEST_PLUGIN _PLUGIN_NAME) + kdevplatform_add_plugin(${_PLUGIN_NAME} JSON plugins/${_PLUGIN_NAME}.testpluginjson SKIP_INSTALL SOURCES plugins/${_PLUGIN_NAME}.cpp) + target_link_libraries(${_PLUGIN_NAME} Qt5::Core KDev::Interfaces) + set_target_properties(${_PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${TEST_PLUGIN_DIR}/kdevplatform/${KDEV_PLUGIN_VERSION}") +endmacro() + +kdevshell_add_test_plugin(nonguiinterfaceplugin) +kdevshell_add_test_plugin(projectdefaultplugin) +kdevshell_add_test_plugin(projectnondefaultplugin) +kdevshell_add_test_plugin(globaldefaultplugin) +kdevshell_add_test_plugin(globalnondefaultplugin) ecm_add_test(test_plugincontroller.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) +ecm_add_test(test_pluginenabling.cpp + LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) + ecm_add_test(test_testcontroller.cpp LINK_LIBRARIES Qt5::Test KDev::Tests) ecm_add_test(test_ktexteditorpluginintegration.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Interfaces KDev::Sublime) ecm_add_test(test_detectedproblem.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell KDev::Serialization) ecm_add_test(test_problemmodelset.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) ecm_add_test(test_problemstorenode.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) ecm_add_test(test_problemstore.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) ecm_add_test(test_filteredproblemstore.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) ecm_add_test(test_problemmodel.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) ecm_add_test(test_checkerstatus.cpp LINK_LIBRARIES Qt5::Test KDev::Tests KDev::Shell) diff --git a/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json b/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json deleted file mode 100644 index deedff0c44..0000000000 --- a/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "KPlugin": { - "Description": "This plugin is purely for unit-test", - "Description[ca@valencia]": "Este connector és exclusivament per a unitats de proves", - "Description[ca]": "Aquest connector és exclusivament per a unitats de proves", - "Description[de]": "Die ist eine Modul nur für Unit-Tests", - "Description[es]": "Este complemento es exclusivamente para pruebas unitarias", - "Description[fi]": "Tämä liitännäinen on puhtaasti yksikkötestausta varten", - "Description[gl]": "Este complemento é soamente para probas unitarias.", - "Description[nl]": "Deze plugin is alleen voor eenheid-testen", - "Description[pl]": "Ta wtyczka służy tylko do testów jednostek", - "Description[pt]": "Este 'plugin' serve apenas para testes unitários", - "Description[pt_BR]": "Este plugin é apenas para testes unitários", - "Description[sk]": "Tento plugin je čisto na unit testy", - "Description[sl]": "Ta vstavek je samo za preizkušanje enot", - "Description[sv]": "Insticksprogrammet är enbart avsett för enhetstest", - "Description[tr]": "Bu eklenti tamamen birim-testleri içindir", - "Description[uk]": "Цей додаток призначено лише для тестування модулів", - "Description[x-test]": "xxThis plugin is purely for unit-testxx", - "Description[zh_CN]": "此插件只用于 unit 测试", - "Id": "kdevnonguiinterface", - "License": "LGPL", - "Name": "KDevNonGuiInterface", - "Name[bs]": "KDevNonGUI sučelje", - "Name[nds]": "KDevKeenBöversiet", - "Name[sv]": "KDevelop icke grafiskt användargränssnitt", - "Name[x-test]": "xxKDevNonGuiInterfacexx", - "ServiceTypes": [ - "KDevelop/Plugin" - ] - }, - "X-KDevelop-Interfaces": [ - "org.kdevelop.ITestNonGuiInterface" - ], - "X-KDevelop-Mode": "NoGUI" -} diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/plugins/globaldefaultplugin.cpp similarity index 60% copy from kdevplatform/shell/tests/nonguiinterfaceplugin.h copy to kdevplatform/shell/tests/plugins/globaldefaultplugin.cpp index e7db4a8c2a..b3bce7ef2d 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/plugins/globaldefaultplugin.cpp @@ -1,47 +1,41 @@ /* * This file is part of KDevelop * - * Copyright 2007 Hamish Rodda + * Copyright 2017 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN - #include -#include +#include -class ITestNonGuiInterface +class GlobalDefaultPlugin : public KDevelop::IPlugin { + Q_OBJECT public: - virtual ~ITestNonGuiInterface() {} + explicit GlobalDefaultPlugin(QObject* parent, const QVariantList&); }; -Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) - - -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface +GlobalDefaultPlugin::GlobalDefaultPlugin(QObject* parent, const QVariantList&) + : IPlugin(QStringLiteral("globaldefaultplugin"), parent) { - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; +} -#endif +K_PLUGIN_FACTORY_WITH_JSON(GlobalDefaultPluginFactory, "globaldefaultplugin.testpluginjson", + registerPlugin();) +#include "globaldefaultplugin.moc" diff --git a/kdevplatform/shell/tests/plugins/globaldefaultplugin.testpluginjson b/kdevplatform/shell/tests/plugins/globaldefaultplugin.testpluginjson new file mode 100644 index 0000000000..777f10a28d --- /dev/null +++ b/kdevplatform/shell/tests/plugins/globaldefaultplugin.testpluginjson @@ -0,0 +1,13 @@ +{ + "KPlugin": { + "Description": "This plugin is purely for unit-test", + "Id": "test_globaldefault", + "License": "LGPL", + "Name": "GlobalDefaultPlugin", + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/plugins/globalnondefaultplugin.cpp similarity index 59% copy from kdevplatform/shell/tests/nonguiinterfaceplugin.h copy to kdevplatform/shell/tests/plugins/globalnondefaultplugin.cpp index e7db4a8c2a..279485cfab 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/plugins/globalnondefaultplugin.cpp @@ -1,47 +1,41 @@ /* * This file is part of KDevelop * - * Copyright 2007 Hamish Rodda + * Copyright 2017 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN - #include -#include +#include -class ITestNonGuiInterface +class GlobalNonDefaultPlugin : public KDevelop::IPlugin { + Q_OBJECT public: - virtual ~ITestNonGuiInterface() {} + explicit GlobalNonDefaultPlugin(QObject* parent, const QVariantList&); }; -Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) - - -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface +GlobalNonDefaultPlugin::GlobalNonDefaultPlugin(QObject* parent, const QVariantList&) + : IPlugin(QStringLiteral("globalnondefaultplugin"), parent) { - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; +} -#endif +K_PLUGIN_FACTORY_WITH_JSON(GlobalNonDefaultPluginFactory, "globalnondefaultplugin.testpluginjson", + registerPlugin();) +#include "globalnondefaultplugin.moc" diff --git a/kdevplatform/shell/tests/plugins/globalnondefaultplugin.testpluginjson b/kdevplatform/shell/tests/plugins/globalnondefaultplugin.testpluginjson new file mode 100644 index 0000000000..609ec0402f --- /dev/null +++ b/kdevplatform/shell/tests/plugins/globalnondefaultplugin.testpluginjson @@ -0,0 +1,14 @@ +{ + "KPlugin": { + "Description": "This plugin is purely for unit-test", + "Id": "test_globalnondefault", + "License": "LGPL", + "Name": "GlobalNonDefaultPlugin", + "EnabledByDefault": false, + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Global", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/plugins/nonguiinterface.h similarity index 74% copy from kdevplatform/shell/tests/nonguiinterfaceplugin.h copy to kdevplatform/shell/tests/plugins/nonguiinterface.h index e7db4a8c2a..dd300b71b0 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/plugins/nonguiinterface.h @@ -1,47 +1,37 @@ /* * This file is part of KDevelop * * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN +#ifndef ITESTNONGUIINTERFACE +#define ITESTNONGUIINTERFACE -#include - -#include +#include class ITestNonGuiInterface { public: virtual ~ITestNonGuiInterface() {} }; Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface -{ - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; - #endif diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.cpp b/kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.cpp similarity index 58% rename from kdevplatform/shell/tests/nonguiinterfaceplugin.cpp rename to kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.cpp index c94553e72d..c6743ecda9 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.cpp +++ b/kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.cpp @@ -1,34 +1,45 @@ /* * This file is part of KDevelop * * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "nonguiinterfaceplugin.h" +#include "nonguiinterface.h" -#include +#include -K_PLUGIN_FACTORY_WITH_JSON(KDevProblemReporterFactory, "kdevnonguiinterfaceplugin.json", registerPlugin();) +#include -NonGuiInterfacePlugin::NonGuiInterfacePlugin( QObject* parent, const QVariantList& ) - : IPlugin( QStringLiteral("kdevnonguiinterfaceplugin"), parent ) +class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface +{ + Q_OBJECT + Q_INTERFACES(ITestNonGuiInterface) +public: + explicit NonGuiInterfacePlugin(QObject* parent, const QVariantList&); +}; + +NonGuiInterfacePlugin::NonGuiInterfacePlugin(QObject* parent, const QVariantList&) + : IPlugin(QStringLiteral("nonguiinterfaceplugin"), parent) { } +K_PLUGIN_FACTORY_WITH_JSON(NonGuiInterfacePluginFactory, "nonguiinterfaceplugin.testpluginjson", + registerPlugin();) + #include "nonguiinterfaceplugin.moc" diff --git a/kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.testpluginjson b/kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.testpluginjson new file mode 100644 index 0000000000..1b69a2ca73 --- /dev/null +++ b/kdevplatform/shell/tests/plugins/nonguiinterfaceplugin.testpluginjson @@ -0,0 +1,15 @@ +{ + "KPlugin": { + "Description": "This plugin is purely for unit-test", + "Id": "test_nonguiinterface", + "License": "LGPL", + "Name": "NonGuiInterface", + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Interfaces": [ + "org.kdevelop.ITestNonGuiInterface" + ], + "X-KDevelop-Mode": "NoGUI" +} diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/plugins/projectdefaultplugin.cpp similarity index 60% copy from kdevplatform/shell/tests/nonguiinterfaceplugin.h copy to kdevplatform/shell/tests/plugins/projectdefaultplugin.cpp index e7db4a8c2a..34334d3c9d 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/plugins/projectdefaultplugin.cpp @@ -1,47 +1,41 @@ /* * This file is part of KDevelop * - * Copyright 2007 Hamish Rodda + * Copyright 2017 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN - #include -#include +#include -class ITestNonGuiInterface +class ProjectDefaultPlugin : public KDevelop::IPlugin { + Q_OBJECT public: - virtual ~ITestNonGuiInterface() {} + explicit ProjectDefaultPlugin(QObject* parent, const QVariantList&); }; -Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) - - -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface +ProjectDefaultPlugin::ProjectDefaultPlugin(QObject* parent, const QVariantList&) + : IPlugin(QStringLiteral("projectdefaultplugin"), parent) { - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; +} -#endif +K_PLUGIN_FACTORY_WITH_JSON(ProjectDefaultPluginFactory, "projectdefaultplugin.testpluginjson", + registerPlugin();) +#include "projectdefaultplugin.moc" diff --git a/kdevplatform/shell/tests/plugins/projectdefaultplugin.testpluginjson b/kdevplatform/shell/tests/plugins/projectdefaultplugin.testpluginjson new file mode 100644 index 0000000000..5c5d23303f --- /dev/null +++ b/kdevplatform/shell/tests/plugins/projectdefaultplugin.testpluginjson @@ -0,0 +1,13 @@ +{ + "KPlugin": { + "Description": "This plugin is purely for unit-test", + "Id": "test_projectdefault", + "License": "LGPL", + "Name": "ProjectDefaultPlugin", + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Project", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/plugins/projectnondefaultplugin.cpp similarity index 59% copy from kdevplatform/shell/tests/nonguiinterfaceplugin.h copy to kdevplatform/shell/tests/plugins/projectnondefaultplugin.cpp index e7db4a8c2a..880e12cecd 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/plugins/projectnondefaultplugin.cpp @@ -1,47 +1,41 @@ /* * This file is part of KDevelop * - * Copyright 2007 Hamish Rodda + * Copyright 2017 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN - #include -#include +#include -class ITestNonGuiInterface +class ProjectNonDefaultPlugin : public KDevelop::IPlugin { + Q_OBJECT public: - virtual ~ITestNonGuiInterface() {} + explicit ProjectNonDefaultPlugin(QObject* parent, const QVariantList&); }; -Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) - - -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface +ProjectNonDefaultPlugin::ProjectNonDefaultPlugin(QObject* parent, const QVariantList&) + : IPlugin(QStringLiteral("projectnondefaultplugin"), parent) { - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; +} -#endif +K_PLUGIN_FACTORY_WITH_JSON(ProjectNonDefaultPluginFactory, "projectnondefaultplugin.testpluginjson", + registerPlugin();) +#include "projectnondefaultplugin.moc" diff --git a/kdevplatform/shell/tests/plugins/projectnondefaultplugin.testpluginjson b/kdevplatform/shell/tests/plugins/projectnondefaultplugin.testpluginjson new file mode 100644 index 0000000000..624d50620a --- /dev/null +++ b/kdevplatform/shell/tests/plugins/projectnondefaultplugin.testpluginjson @@ -0,0 +1,14 @@ +{ + "KPlugin": { + "Description": "This plugin is purely for unit-test", + "Id": "test_projectnondefault", + "License": "LGPL", + "Name": "ProjectNonDefaultPlugin", + "EnabledByDefault": false, + "ServiceTypes": [ + "KDevelop/Plugin" + ] + }, + "X-KDevelop-Category": "Project", + "X-KDevelop-Mode": "NoGUI" +} diff --git a/kdevplatform/shell/tests/test_plugincontroller.cpp b/kdevplatform/shell/tests/test_plugincontroller.cpp index 3c930bfbba..0047d9b122 100644 --- a/kdevplatform/shell/tests/test_plugincontroller.cpp +++ b/kdevplatform/shell/tests/test_plugincontroller.cpp @@ -1,100 +1,100 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "test_plugincontroller.h" -#include "nonguiinterfaceplugin.h" #include "testfilepaths.h" +#include "plugins/nonguiinterface.h" #include #include #include #include #include "../core.h" #include "../plugincontroller.h" using namespace KDevelop; void TestPluginController::initTestCase() { qApp->addLibraryPath(QStringLiteral(TEST_PLUGIN_DIR)); - AutoTestShell::init({QStringLiteral("kdevnonguiinterface")}); + AutoTestShell::init({QStringLiteral("test_nonguiinterface")}); TestCore::initialize( Core::NoUi ); m_pluginCtrl = Core::self()->pluginControllerInternal(); } void TestPluginController::cleanupTestCase() { TestCore::shutdown(); } void TestPluginController::pluginInfo() { - IPlugin* plugin = m_pluginCtrl->loadPlugin( QStringLiteral("kdevnonguiinterface") ); + IPlugin* plugin = m_pluginCtrl->loadPlugin(QStringLiteral("test_nonguiinterface")); QVERIFY(plugin); KPluginMetaData pluginInfo = m_pluginCtrl->pluginInfo(plugin); - QCOMPARE(pluginInfo.pluginId(), QStringLiteral("kdevnonguiinterface")); + QCOMPARE(pluginInfo.pluginId(), QStringLiteral("test_nonguiinterface")); } void TestPluginController::loadUnloadPlugin() { QSignalSpy spy(m_pluginCtrl, SIGNAL(pluginLoaded(KDevelop::IPlugin*))); QSignalSpy spyloading(m_pluginCtrl, SIGNAL(loadingPlugin(QString))); QVERIFY(spy.isValid()); QVERIFY(spyloading.isValid()); - m_pluginCtrl->loadPlugin( QStringLiteral( "kdevnonguiinterface" ) ); - QVERIFY( m_pluginCtrl->plugin( QStringLiteral( "kdevnonguiinterface" ) ) ); + m_pluginCtrl->loadPlugin(QStringLiteral("test_nonguiinterface")); + QVERIFY( m_pluginCtrl->plugin(QStringLiteral("test_nonguiinterface")) ); QCOMPARE(spy.size(), 1); QCOMPARE(spyloading.size(), 1); QList args = spyloading.takeFirst(); - QCOMPARE( args.at(0).toString(), QStringLiteral( "kdevnonguiinterface" ) ); + QCOMPARE( args.at(0).toString(), QStringLiteral("test_nonguiinterface") ); QSignalSpy spy2(m_pluginCtrl, SIGNAL(pluginUnloaded(KDevelop::IPlugin*)) ); QSignalSpy spy3(m_pluginCtrl, SIGNAL(unloadingPlugin(KDevelop::IPlugin*)) ); QVERIFY(spy2.isValid()); QVERIFY(spy3.isValid()); - m_pluginCtrl->unloadPlugin( QStringLiteral("kdevnonguiinterface") ); - QVERIFY( !m_pluginCtrl->plugin( QStringLiteral( "kdevnonguiinterface" ) ) ); + m_pluginCtrl->unloadPlugin(QStringLiteral("test_nonguiinterface")); + QVERIFY( !m_pluginCtrl->plugin(QStringLiteral("test_nonguiinterface")) ); QCOMPARE(spy2.size(), 1); QCOMPARE(spy3.size(), 1); } void TestPluginController::loadFromExtension() { IPlugin* plugin = m_pluginCtrl->pluginForExtension( QStringLiteral("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin ); QVERIFY( plugin->inherits("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin->extension()); } void TestPluginController::benchPluginForExtension() { QBENCHMARK { IPlugin* plugin = m_pluginCtrl->pluginForExtension( QStringLiteral("org.kdevelop.ITestNonGuiInterface") ); QVERIFY( plugin ); } } QTEST_MAIN( TestPluginController) diff --git a/kdevplatform/shell/tests/test_pluginenabling.cpp b/kdevplatform/shell/tests/test_pluginenabling.cpp new file mode 100644 index 0000000000..66a1a6e95c --- /dev/null +++ b/kdevplatform/shell/tests/test_pluginenabling.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2017 Friedrich W. H. Kossebau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "test_pluginenabling.h" + +#include "testfilepaths.h" + +#include +#include + +#include + +#include +#include +#include + +#include "../core.h" +#include "../plugincontroller.h" + +using namespace KDevelop; + +void TestPluginEnabling::initTestCase() +{ + qApp->addLibraryPath(QStringLiteral(TEST_PLUGIN_DIR)); +} + +void TestPluginEnabling::loadPlugin(const QString& pluginId, bool shouldBeEnabled) +{ + // check config storage + KConfigGroup grp = Core::self()->activeSession()->config()->group(QStringLiteral("Plugins")); + + const QString pluginEnabledKey = pluginId + QLatin1String("Enabled"); + // logic in kdevelop + const bool enabled = grp.hasKey(pluginEnabledKey) ? grp.readEntry(pluginEnabledKey, true) : false; + + QCOMPARE(enabled, shouldBeEnabled); + + // check plugin loading + QCOMPARE((m_pluginCtrl->loadPlugin(pluginId) != nullptr), shouldBeEnabled); + QCOMPARE((m_pluginCtrl->plugin(pluginId) != nullptr), shouldBeEnabled); + + if (shouldBeEnabled) { + m_pluginCtrl->unloadPlugin(pluginId); + QVERIFY(!m_pluginCtrl->plugin(pluginId)); + } + + // switch enabled state + const bool shouldNowBeEnabled = !shouldBeEnabled; + grp.writeEntry(pluginEnabledKey, shouldNowBeEnabled); + + // check plugin loading again + QCOMPARE((m_pluginCtrl->loadPlugin(pluginId) != nullptr), shouldNowBeEnabled); + QCOMPARE((m_pluginCtrl->plugin(pluginId) != nullptr), shouldNowBeEnabled); + + if (shouldNowBeEnabled) { + m_pluginCtrl->unloadPlugin(pluginId); + QVERIFY(!m_pluginCtrl->plugin(pluginId)); + } + +} + +void TestPluginEnabling::loadPluginCustomDefaults_data() +{ + QTest::addColumn("pluginId"); + QTest::addColumn("shouldBeEnabled"); + + QTest::newRow("test_globaldefault") << "test_globaldefault" << false; + QTest::newRow("test_globalnondefault") << "test_globalnondefault" << true; + QTest::newRow("test_projectdefault") << "test_projectdefault" << false; + QTest::newRow("test_projectnondefault") << "test_projectnondefault" << true; +} + +void TestPluginEnabling::loadPluginCustomDefaults() +{ + QFETCH(QString, pluginId); + QFETCH(bool, shouldBeEnabled); + + AutoTestShell::init({ + // set those as default which would not be by own metadata + // and do not set those which otherwise would, so both + QStringLiteral("test_globalnondefault"), + QStringLiteral("test_projectnondefault") + }); + // TODO: somehow currently the clean-up of the previous session is not yet done + // on the next data item test run, so the session with the name is still locked + // so we work-around that for now by using a custom session name per session + // TODO: consider adding a new bool temporarySession = true to TestCore::initialize() + TestCore::initialize(Core::NoUi, QStringLiteral("test_pluginenabling_custom_")+pluginId); + TestCore::self()->activeSession()->setTemporary(true); + m_pluginCtrl = Core::self()->pluginControllerInternal(); + + loadPlugin(pluginId, shouldBeEnabled); + + TestCore::shutdown(); +} + +void TestPluginEnabling::loadPluginNormalDefaults_data() +{ + QTest::addColumn("pluginId"); + QTest::addColumn("shouldBeEnabled"); + + QTest::newRow("test_globaldefault") << "test_globaldefault" << true; + QTest::newRow("test_globalnondefault") << "test_globalnondefault" << false; + QTest::newRow("test_projectdefault") << "test_projectdefault" << true; + QTest::newRow("test_projectnondefault") << "test_projectnondefault" << false; +} + +void TestPluginEnabling::loadPluginNormalDefaults() +{ + QFETCH(QString, pluginId); + QFETCH(bool, shouldBeEnabled); + + AutoTestShell::init(); + // see TODO in loadPluginCustomDefaults() + TestCore::initialize(Core::NoUi, QStringLiteral("test_pluginenabling_normal_")+pluginId); + TestCore::self()->activeSession()->setTemporary(true); + m_pluginCtrl = Core::self()->pluginControllerInternal(); + + // check plugin metadata + const auto pluginInfo = m_pluginCtrl->infoForPluginId(pluginId); + // logic in kdevelop different from KPluginMetaData::isEnabledByDefault(), here defaults to true + const auto enabledByDefaultValue = pluginInfo.rawData()["KPlugin"].toObject()["EnabledByDefault"]; + const bool enabledByDefault = (enabledByDefaultValue.isNull() || enabledByDefaultValue.toBool()); + QCOMPARE(enabledByDefault, shouldBeEnabled); + + loadPlugin(pluginId, shouldBeEnabled); + + TestCore::shutdown(); +} + +QTEST_MAIN(TestPluginEnabling) diff --git a/kdevplatform/shell/tests/nonguiinterfaceplugin.h b/kdevplatform/shell/tests/test_pluginenabling.h similarity index 54% rename from kdevplatform/shell/tests/nonguiinterfaceplugin.h rename to kdevplatform/shell/tests/test_pluginenabling.h index e7db4a8c2a..b9435e1189 100644 --- a/kdevplatform/shell/tests/nonguiinterfaceplugin.h +++ b/kdevplatform/shell/tests/test_pluginenabling.h @@ -1,47 +1,49 @@ /* - * This file is part of KDevelop - * - * Copyright 2007 Hamish Rodda + * Copyright 2017 Friedrich W. H. Kossebau * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef NONGUIINTERFACEPLUGIN -#define NONGUIINTERFACEPLUGIN +#ifndef KDEVPLATFORM_TEST_PLUGINENABLING_H +#define KDEVPLATFORM_TEST_PLUGINENABLING_H -#include +#include -#include +namespace KDevelop +{ +class PluginController; +} -class ITestNonGuiInterface +class TestPluginEnabling : public QObject { -public: - virtual ~ITestNonGuiInterface() {} -}; + Q_OBJECT -Q_DECLARE_INTERFACE( ITestNonGuiInterface, "org.kdevelop.ITestNonGuiInterface" ) +private Q_SLOTS: + void initTestCase(); + void loadPluginCustomDefaults_data(); + void loadPluginCustomDefaults(); + void loadPluginNormalDefaults_data(); + void loadPluginNormalDefaults(); -class NonGuiInterfacePlugin : public KDevelop::IPlugin, ITestNonGuiInterface -{ - Q_OBJECT - Q_INTERFACES(ITestNonGuiInterface) -public: - explicit NonGuiInterfacePlugin( QObject* parent, const QVariantList& = QVariantList() ); -}; +private: + void loadPlugin(const QString& pluginId, bool shouldBeEnabled); -#endif +private: + KDevelop::PluginController* m_pluginCtrl; +}; +#endif // KDEVPLATFORM_TEST_PLUGINENABLING_H diff --git a/plugins/cmake/tests/test_cmakemanager.cpp b/plugins/cmake/tests/test_cmakemanager.cpp index 3ba9675e91..eeb7678c9c 100644 --- a/plugins/cmake/tests/test_cmakemanager.cpp +++ b/plugins/cmake/tests/test_cmakemanager.cpp @@ -1,393 +1,393 @@ /* This file is part of KDevelop Copyright 2010 Esben Mose Hansen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "test_cmakemanager.h" #include "testhelpers.h" #include "cmakemodelitems.h" #include "cmakeutils.h" #include "cmakeimportjsonjob.h" #include #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(TestCMakeManager) using namespace KDevelop; void TestCMakeManager::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\n")); - AutoTestShell::init({"kdevcmakemanager"}); + AutoTestShell::init({"KDevCMakeManager", "KDevCMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView"}); TestCore::initialize(); cleanup(); } void TestCMakeManager::cleanupTestCase() { TestCore::shutdown(); } void TestCMakeManager::cleanup() { foreach(IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); } QVERIFY(ICore::self()->projectController()->projects().isEmpty()); } void TestCMakeManager::testWithBuildDirProject() { loadProject(QStringLiteral("with_build_dir")); } void TestCMakeManager::testIncludePaths() { IProject* project = loadProject(QStringLiteral("single_subdirectory")); Path sourceDir = project->path(); Path fooCpp(sourceDir, QStringLiteral("subdir/foo.cpp")); QVERIFY(QFile::exists(fooCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(fooCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the target, once the plain file ProjectBaseItem* fooCppItem = items.first(); Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem); QVERIFY(includeDirs.size() >= 3); Path buildDir(project->buildSystemManager()->buildDirectory(fooCppItem)); QVERIFY(includeDirs.contains(buildDir)); Path subBuildDir(buildDir, QStringLiteral("subdir/")); QVERIFY(includeDirs.contains(subBuildDir)); Path subDir(sourceDir, QStringLiteral("subdir/")); QVERIFY(includeDirs.contains(subDir)); } void TestCMakeManager::testRelativePaths() { IProject* project = loadProject(QStringLiteral("relative_paths"), QStringLiteral("/out")); Path codeCpp(project->path(), QStringLiteral("../src/code.cpp")); QVERIFY(QFile::exists( codeCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(codeCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Abort); QCOMPARE(items.size(), 1); // once in the target ProjectBaseItem* fooCppItem = items.first(); Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem); Path incDir(project->path(), QStringLiteral("../inc/")); QVERIFY(includeDirs.contains( incDir )); } void TestCMakeManager::testTargetIncludePaths() { IProject* project = loadProject(QStringLiteral("target_includes")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundInTarget = false; foreach(ProjectBaseItem* mainCppItem, items) { ProjectBaseItem* mainContainer = mainCppItem->parent(); Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem); if (mainContainer->target()) { foundInTarget = true; Path targetIncludesDir(project->path(), QStringLiteral("includes/")); QVERIFY(includeDirs.contains(targetIncludesDir)); } } QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QVERIFY(foundInTarget); } void TestCMakeManager::testTargetIncludeDirectories() { IProject* project = loadProject(QStringLiteral("target_include_directories")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundInTarget = false; foreach(ProjectBaseItem* mainCppItem, items) { ProjectBaseItem* mainContainer = mainCppItem->parent(); Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem); if (mainContainer->target()) { foundInTarget = true; QVERIFY(includeDirs.contains(Path(project->path(), "includes/"))); QVERIFY(includeDirs.contains(Path(project->path(), "libincludes/"))); } } QEXPECT_FAIL("", "files aren't being added to the target", Continue); QVERIFY(foundInTarget); } void TestCMakeManager::testQt5App() { IProject* project = loadProject(QStringLiteral("qt5_app")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundCore = false, foundGui = false, foundWidgets = false; foreach(ProjectBaseItem* mainCppItem, items) { Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem); foreach(const Path& include, includeDirs) { QString filename = include.lastPathSegment(); foundCore |= filename == QLatin1String("QtCore"); foundGui |= filename == QLatin1String("QtGui"); foundWidgets |= filename == QLatin1String("QtWidgets"); } } QVERIFY(foundCore); QVERIFY(foundGui); QVERIFY(foundWidgets); } void TestCMakeManager::testQt5AppOld() { IProject* project = loadProject(QStringLiteral("qt5_app_old")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundCore = false, foundGui = false, foundWidgets = false; foreach(ProjectBaseItem* mainCppItem, items) { Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem); foreach(const Path& include, includeDirs) { QString filename = include.lastPathSegment(); foundCore |= filename == QLatin1String("QtCore"); foundGui |= filename == QLatin1String("QtGui"); foundWidgets |= filename == QLatin1String("QtWidgets"); } } QVERIFY(foundCore); QVERIFY(foundGui); QVERIFY(foundWidgets); } void TestCMakeManager::testKF5App() { IProject* project = loadProject(QStringLiteral("kf5_app")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundCore = false, foundGui = false, foundWidgets = false, foundWidgetsAddons = false; foreach(ProjectBaseItem* mainCppItem, items) { Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem); qDebug() << "xxxxxxxxx" << includeDirs; foreach(const Path& include, includeDirs) { QString filename = include.lastPathSegment(); foundCore |= filename == QLatin1String("QtCore"); foundGui |= filename == QLatin1String("QtGui"); foundWidgets |= filename == QLatin1String("QtWidgets"); foundWidgetsAddons |= filename == QLatin1String("KWidgetsAddons"); } } QVERIFY(foundCore); QVERIFY(foundGui); QVERIFY(foundWidgets); QVERIFY(foundWidgetsAddons); } void TestCMakeManager::testDefines() { IProject* project = loadProject(QStringLiteral("defines")); Path mainCpp(project->path(), QStringLiteral("main.cpp")); QVERIFY(QFile::exists(mainCpp.toLocalFile())); QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl())); QEXPECT_FAIL("", "Will fix soon, hopefully", Continue); QCOMPARE(items.size(), 2); // once the plain file, once the target bool foundInTarget = false; foreach(ProjectBaseItem* mainCppItem, items) { QHash defines = project->buildSystemManager()->defines(mainCppItem); QCOMPARE(defines.value("B", QStringLiteral("not found")), QString()); QCOMPARE(defines.value("BV", QStringLiteral("not found")), QStringLiteral("1")); QCOMPARE(defines.value("BV2", QStringLiteral("not found")), QStringLiteral("2")); // QCOMPARE(defines.value("BAR", QStringLiteral("not found")), QStringLiteral("foo")); // QCOMPARE(defines.value("FOO", QStringLiteral("not found")), QStringLiteral("bar")); // QCOMPARE(defines.value("BLA", QStringLiteral("not found")), QStringLiteral("blub")); QCOMPARE(defines.value("ASDF", QStringLiteral("not found")), QStringLiteral("asdf")); QCOMPARE(defines.value("XYZ", QStringLiteral("not found")), QString()); QCOMPARE(defines.value("A", QStringLiteral("not found")), QString()); QCOMPARE(defines.value("AV", QStringLiteral("not found")), QStringLiteral("1")); QCOMPARE(defines.value("AV2", QStringLiteral("not found")), QStringLiteral("2")); QCOMPARE(defines.value("C", QStringLiteral("not found")), QString()); QCOMPARE(defines.value("CV", QStringLiteral("not found")), QStringLiteral("1")); QCOMPARE(defines.value("CV2", QStringLiteral("not found")), QStringLiteral("2")); QCOMPARE(defines.size(), 13); foundInTarget = true; } QVERIFY(foundInTarget); } void TestCMakeManager::testCustomTargetSources() { IProject* project = loadProject(QStringLiteral("custom_target_sources")); QList targets = project->buildSystemManager()->targets(project->projectItem()); QVERIFY(targets.size() == 1); QEXPECT_FAIL("", "Will fix soon, hopefully", Abort); ProjectTargetItem *target = targets.first(); QCOMPARE(target->fileList().size(), 1); QCOMPARE(target->fileList().first()->baseName(), QStringLiteral("foo.cpp")); } void TestCMakeManager::testConditionsInSubdirectoryBasedOnRootVariables() { IProject* project = loadProject(QStringLiteral("conditions_in_subdirectory_based_on_root_variables")); Path rootFooCpp(project->path(), QStringLiteral("foo.cpp")); QVERIFY(QFile::exists(rootFooCpp.toLocalFile())); QList< ProjectBaseItem* > rootFooItems = project->itemsForPath(IndexedString(rootFooCpp.pathOrUrl())); QEXPECT_FAIL("", "files aren't being added to the target", Continue); QCOMPARE(rootFooItems.size(), 4); // three items for the targets, one item for the plain file Path subdirectoryFooCpp(project->path(), QStringLiteral("subdirectory/foo.cpp")); QVERIFY(QFile::exists(subdirectoryFooCpp.toLocalFile())); QList< ProjectBaseItem* > subdirectoryFooItems = project->itemsForPath(IndexedString(subdirectoryFooCpp.pathOrUrl())); QEXPECT_FAIL("", "files aren't being added to the target", Continue); QCOMPARE(subdirectoryFooItems.size(), 4); // three items for the targets, one item for the plain file } void TestCMakeManager::testEnumerateTargets() { QString tempDir = QDir::tempPath(); QTemporaryFile targetDirectoriesFile; QTemporaryDir subdir; auto opened = targetDirectoriesFile.open(); QVERIFY(opened); QVERIFY(subdir.isValid()); const QString targetDirectoriesContent = tempDir + "/CMakeFiles/first_target.dir\n" + tempDir + "/CMakeFiles/second_target.dir\r\n" + tempDir + "/" + subdir.path() + "/CMakeFiles/third_target.dir"; targetDirectoriesFile.write(targetDirectoriesContent.toLatin1()); targetDirectoriesFile.close(); QHash targets = CMake::enumerateTargets(Path(targetDirectoriesFile.fileName()), tempDir, Path(tempDir)); QCOMPARE(targets.value(Path(tempDir)).value(0), QStringLiteral("first_target")); QCOMPARE(targets.value(Path(tempDir)).value(1), QStringLiteral("second_target")); QCOMPARE(targets.value(Path(tempDir + "/" + subdir.path())).value(0), QStringLiteral("third_target")); } void TestCMakeManager::testReload() { IProject* project = loadProject(QStringLiteral("tiny_project")); const Path sourceDir = project->path(); auto fmp = dynamic_cast(project->projectFileManager()); QVERIFY(fmp); auto projectItem = project->projectItem(); auto targets = projectItem->targetList(); auto job = fmp->createImportJob(project->projectItem()); project->setReloadJob(job); QSignalSpy spy(job, &KJob::finished); job->start(); QVERIFY(spy.wait()); QCOMPARE(spy.count(), 1); QCOMPARE(projectItem, project->projectItem()); QCOMPARE(targets, projectItem->targetList()); } void TestCMakeManager::testFaultyTarget() { loadProject(QStringLiteral("faulty_target")); } void TestCMakeManager::testParenthesesInTestArguments() { IProject* project = loadProject(QStringLiteral("parentheses_in_test_arguments")); auto job = new CMakeImportJsonJob(project, this); QVERIFY(job->exec()); } void TestCMakeManager::testExecutableOutputPath() { const auto prevSuitesCount = ICore::self()->testController()->testSuites().count(); qRegisterMetaType("KDevelop::ITestSuite*"); QSignalSpy spy(ICore::self()->testController(), &ITestController::testSuiteAdded); IProject* project = loadProject(QStringLiteral("randomexe")); const auto targets = project->projectItem()->targetList(); QCOMPARE(targets.count(), 1); const auto target = targets.first()->executable(); QVERIFY(target); const KDevelop::Path exePath(target->executable()->builtUrl()); QCOMPARE(exePath, KDevelop::Path(project->buildSystemManager()->buildDirectory(project->projectItem()), QLatin1String("randomplace/mytest"))); QVERIFY(spy.count() || spy.wait(100000)); auto suites = ICore::self()->testController()->testSuites(); QCOMPARE(suites.count(), prevSuitesCount + 1); const CTestSuite* suite = static_cast(ICore::self()->testController()->findTestSuite(project, "mytest")); QCOMPARE(suite->executable(), exePath); } diff --git a/plugins/cmake/tests/test_ctestfindsuites.cpp b/plugins/cmake/tests/test_ctestfindsuites.cpp index 03499b65fd..144ecb2fee 100644 --- a/plugins/cmake/tests/test_ctestfindsuites.cpp +++ b/plugins/cmake/tests/test_ctestfindsuites.cpp @@ -1,116 +1,116 @@ /* KDevelop CMake Support * * Copyright 2012 Miha Čančula * Copyright 2017 Kevin Funk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "test_ctestfindsuites.h" #include "testhelpers.h" #include "cmakeutils.h" #include "cmake-test-paths.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; void waitForSuites(IProject* project, int count, int max) { auto testController = ICore::self()->testController(); for(int i = 0; testController->testSuitesForProject(project).size() < count && i < max * 10; ++i) { QSignalSpy spy(testController, &ITestController::testSuiteAdded); QVERIFY(spy.wait(1000)); } } void TestCTestFindSuites::initTestCase() { - AutoTestShell::init({"kdevcmakemanager"}); + AutoTestShell::init({"KDevCMakeManager", "KDevCMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView"}); TestCore::initialize(); cleanup(); } void TestCTestFindSuites::cleanup() { foreach(IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); } QVERIFY(ICore::self()->projectController()->projects().isEmpty()); } void TestCTestFindSuites::cleanupTestCase() { TestCore::shutdown(); } void TestCTestFindSuites::testCTestSuite() { IProject* project = loadProject( "unit_tests" ); QVERIFY2(project, "Project was not opened"); waitForSuites(project, 5, 10); QList suites = ICore::self()->testController()->testSuitesForProject(project); QCOMPARE(suites.size(), 5); DUChainReadLocker locker(DUChain::lock()); foreach (auto suite, suites) { qDebug() << "checking suite" << suite->name(); QCOMPARE(suite->cases(), QStringList()); QVERIFY(!suite->declaration().isValid()); CTestSuite* ctestSuite = static_cast(suite); const auto buildDir = Path(CMake::allBuildDirs(project).at(0)); QString exeSubdir = buildDir.relativePath(ctestSuite->executable().parent()); QCOMPARE(exeSubdir, ctestSuite->name() == "fail" ? QStringLiteral("bin") : QString() ); QString willFail; const QString workingDirectory = ctestSuite->properties().value(QLatin1String("WORKING_DIRECTORY"), QString()); if (ctestSuite->name() == QLatin1String("fail")) { willFail = QLatin1String("TRUE"); QCOMPARE(workingDirectory, QLatin1String("/bar/baz")); QCOMPARE(ctestSuite->properties().value(QLatin1String("FOO"), QString()), QLatin1String("foo")); QCOMPARE(ctestSuite->properties().value(QLatin1String("BAR"), QString()), QLatin1String("TRUE")); QCOMPARE(ctestSuite->properties().value(QLatin1String("MULTILINE"), QString()), QLatin1String("this is \na multi\nline property")); QCOMPARE(ctestSuite->properties().value(QLatin1String("QUOTES"), QString()), QLatin1String("\"\\\\\"\\\\\\")); } else if (ctestSuite->name() == QLatin1String("test_three")) QCOMPARE(workingDirectory, QLatin1String("/foo")); else if (ctestSuite->name() == QLatin1String("test_three")) QCOMPARE(workingDirectory, QLatin1String("/foo")); else if (ctestSuite->name() == QLatin1String("test_five")) QCOMPARE(workingDirectory, QString(buildDir.path() + QLatin1String("/bin"))); else QCOMPARE(workingDirectory, QString()); QCOMPARE(ctestSuite->properties().value(QLatin1String("WILL_FAIL")), willFail); } } QTEST_MAIN(TestCTestFindSuites) diff --git a/plugins/custom-buildsystem/tests/test_custombuildsystemplugin.cpp b/plugins/custom-buildsystem/tests/test_custombuildsystemplugin.cpp index 41ee840574..94ba0d8b72 100644 --- a/plugins/custom-buildsystem/tests/test_custombuildsystemplugin.cpp +++ b/plugins/custom-buildsystem/tests/test_custombuildsystemplugin.cpp @@ -1,112 +1,112 @@ /************************************************************************ * KDevelop4 Custom Buildsystem Support * * * * Copyright 2010 Andreas Pakulat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 or version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ************************************************************************/ #include "test_custombuildsystemplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "testconfig.h" using KDevelop::Core; using KDevelop::ICore; using KDevelop::IProject; using KDevelop::TestCore; using KDevelop::AutoTestShell; using KDevelop::KDevSignalSpy; using KDevelop::Path; QTEST_MAIN(TestCustomBuildSystemPlugin) void TestCustomBuildSystemPlugin::cleanupTestCase() { TestCore::shutdown(); } void TestCustomBuildSystemPlugin::initTestCase() { - AutoTestShell::init({"kdevcustombuildsystem"}); + AutoTestShell::init({"KDevCustomBuildSystem", "KDevStandardOutputView"}); TestCore::initialize(); } void TestCustomBuildSystemPlugin::loadSimpleProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/simpleproject/simpleproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( QStringLiteral("SimpleProject") ); QVERIFY( project ); QCOMPARE( project->buildSystemManager()->buildDirectory( project->projectItem() ), Path( "file:///home/andreas/projects/testcustom/build/" ) ); } void TestCustomBuildSystemPlugin::buildDirProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/builddirproject/builddirproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( QStringLiteral("BuilddirProject") ); QVERIFY( project ); Path currentBuilddir = project->buildSystemManager()->buildDirectory( project->projectItem() ); QCOMPARE( currentBuilddir, Path( projecturl ).parent() ); } void TestCustomBuildSystemPlugin::loadMultiPathProject() { QUrl projecturl = QUrl::fromLocalFile( PROJECTS_SOURCE_DIR"/multipathproject/multipathproject.kdev4" ); KDevSignalSpy* projectSpy = new KDevSignalSpy( ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)) ); ICore::self()->projectController()->openProject( projecturl ); // Wait for the project to be opened QVERIFY(projectSpy->wait(10000)); IProject* project = ICore::self()->projectController()->findProjectByName( QStringLiteral("MultiPathProject") ); QVERIFY( project ); KDevelop::ProjectBaseItem* mainfile = nullptr; for (const auto& file: project->fileSet() ) { for (auto i: project->filesForPath(file)) { if( i->text() == QLatin1String("main.cpp") ) { mainfile = i; break; } } } QVERIFY(mainfile); QCOMPARE( project->buildSystemManager()->buildDirectory( mainfile ), Path( "file:///home/andreas/projects/testcustom/build2/src" ) ); } diff --git a/plugins/qmakemanager/tests/test_qmakeproject.cpp b/plugins/qmakemanager/tests/test_qmakeproject.cpp index da4fedbf6d..e4306bb322 100644 --- a/plugins/qmakemanager/tests/test_qmakeproject.cpp +++ b/plugins/qmakemanager/tests/test_qmakeproject.cpp @@ -1,145 +1,145 @@ /* KDevelop QMake Support * * Copyright 2011 Julien Desgats * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "test_qmakeproject.h" #include "../qmakeconfig.h" #include "qmaketestconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTEST_MAIN(TestQMakeProject); using namespace KDevelop; TestQMakeProject::TestQMakeProject(QObject* parent) : QObject(parent) { qRegisterMetaType(); } TestQMakeProject::~TestQMakeProject() { } void TestQMakeProject::initTestCase() { - AutoTestShell::init({ "kdevqmakemanager" }); + AutoTestShell::init({ "KDevQMakeManager", "KDevQMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView" }); TestCore::initialize(); } void TestQMakeProject::cleanupTestCase() { Core::self()->cleanup(); } void TestQMakeProject::testBuildDirectory_data() { QTest::addColumn("projectName"); // name of the project (both directory and .kde4 file) QTest::addColumn("target"); // directory to compile from project root QTest::addColumn("expected"); // expected build directory from build dir QTest::newRow("Basic Project") << "basic_project" << "" << ""; QTest::newRow("Subdirs Project (root)") << "subdirs_project" << "" << ""; QTest::newRow("Subdirs Project (dir_a)") << "subdirs_project" << "dir_a" << "dir_a"; } void TestQMakeProject::testBuildDirectory() { QFETCH(QString, projectName); QFETCH(QString, target); QFETCH(QString, expected); const QString buildDir = QStringLiteral("/tmp/some/path"); // some dummy directory to build (nothing will be built anyway) foreach (IProject* p, ICore::self()->projectController()->projects()) { ICore::self()->projectController()->closeProject(p); } // setup project config, to avoid build dir chooser dialog popping up { // note: all checks from QMakeProjectManager::projectNeedsConfiguration must be satisfied const QString fileName = QStringLiteral("%1/%2/.kdev4/%3.kdev4").arg(QMAKE_TESTS_PROJECTS_DIR).arg(projectName).arg(projectName); KConfig cfg(fileName); KConfigGroup group(&cfg, QMakeConfig::CONFIG_GROUP); group.writeEntry(QMakeConfig::BUILD_FOLDER, buildDir); group.writeEntry(QMakeConfig::QMAKE_EXECUTABLE, QMAKE_TESTS_QMAKE_EXECUTABLE); group.sync(); /// create subgroup for one build dir KConfigGroup buildDirGroup = KConfigGroup(&cfg, QMakeConfig::CONFIG_GROUP).group(buildDir); buildDirGroup.writeEntry(QMakeConfig::QMAKE_EXECUTABLE, QMAKE_TESTS_QMAKE_EXECUTABLE); buildDirGroup.sync(); QVERIFY(QFileInfo::exists(fileName)); } // opens project with kdevelop const QUrl projectUrl = QUrl::fromLocalFile( QStringLiteral("%1/%2/%3.kdev4").arg(QMAKE_TESTS_PROJECTS_DIR).arg(projectName).arg(projectName)); ICore::self()->projectController()->openProject(projectUrl); // wait for loading finished QSignalSpy spy(ICore::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*))); bool gotSignal = spy.wait(30000); QVERIFY2(gotSignal, "Timeout while waiting for opened signal"); IProject* project = ICore::self()->projectController()->findProjectByName(projectName); // adds expected directory to our base path Path expectedPath(Path(buildDir), expected); // path for files to build Path buildUrl(QStringLiteral("%1/%2/%3").arg(QMAKE_TESTS_PROJECTS_DIR).arg(projectName).arg(target)); QList buildItems = project->foldersForPath(IndexedString(buildUrl.pathOrUrl())); QCOMPARE(buildItems.size(), 1); IBuildSystemManager* buildManager = project->buildSystemManager(); const auto buildFolder = buildItems.first(); const Path actual = buildManager->buildDirectory(buildFolder); QCOMPARE(actual, expectedPath); auto buildJob = buildManager->builder()->configure(project); QVERIFY(buildJob->exec()); } diff --git a/plugins/qmljs/duchain/tests/test_qmljsdeclarations.cpp b/plugins/qmljs/duchain/tests/test_qmljsdeclarations.cpp index de813e6272..36b9e19f8f 100644 --- a/plugins/qmljs/duchain/tests/test_qmljsdeclarations.cpp +++ b/plugins/qmljs/duchain/tests/test_qmljsdeclarations.cpp @@ -1,289 +1,289 @@ /* * This file is part of KDevelop * Copyright 2013 Milian Wolff * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_qmljsdeclarations.h" #include "../helper.h" #include "../parsesession.h" #include "../declarationbuilder.h" #include "../cache.h" #include #include #include #include #include #include #include #include #include QTEST_GUILESS_MAIN(TestDeclarations); using namespace KDevelop; void TestDeclarations::initTestCase() { - AutoTestShell::init({"kdevqmljslanguagesupport"}); + AutoTestShell::init({"kdevqmljs"}); TestCore::initialize(Core::NoUi); QmlJS::registerDUChainItems(); } void TestDeclarations::cleanupTestCase() { TestCore::shutdown(); } void TestDeclarations::testJSProblems() { const IndexedString file(QUrl(QStringLiteral("file:///internal/jsproblems.js"))); ParseSession session(file, "function f(a) {}\n" "f(2);\n" "f(true);\n", 0); QVERIFY(session.ast()); DeclarationBuilder builder(&session); builder.build(file, session.ast()); auto problems = session.problems(); QCOMPARE(problems.count(), 1); QCOMPARE((KTextEditor::Range)problems.at(0)->finalLocation(), KTextEditor::Range(2, 2, 2, 6)); } void TestDeclarations::testFunction() { const IndexedString file(QUrl(QStringLiteral("file:///internal/functionArgs.js"))); // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/**\n * some comment\n */\n" "function foo(arg1, arg2, arg3) { var i = 0; }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::JavaScript); DeclarationBuilder builder(&session); ReferencedTopDUContext top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 3); // module, exports and foo FunctionDeclaration* fooDec = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(fooDec); QCOMPARE(fooDec->range(), RangeInRevision(3, 9, 3, 12)); QCOMPARE(QString::fromUtf8(fooDec->comment()), QString("some comment")); QVERIFY(fooDec->internalContext()); DUContext* argCtx = fooDec->internalContext(); QCOMPARE(argCtx->localDeclarations().size(), 3); Declaration* arg1 = argCtx->localDeclarations().at(0); QCOMPARE(arg1->identifier().toString(), QString("arg1")); QCOMPARE(arg1->range(), RangeInRevision(3, 13, 3, 17)); Declaration* arg2 = argCtx->localDeclarations().at(1); QCOMPARE(arg2->identifier().toString(), QString("arg2")); QCOMPARE(arg2->range(), RangeInRevision(3, 19, 3, 23)); Declaration* arg3 = argCtx->localDeclarations().at(2); QCOMPARE(arg3->identifier().toString(), QString("arg3")); QCOMPARE(arg3->range(), RangeInRevision(3, 25, 3, 29)); FunctionType::Ptr funType = fooDec->type(); QVERIFY(funType); QVERIFY(funType->returnType().cast()); QCOMPARE(funType->returnType().cast()->dataType(), (uint) IntegralType::TypeVoid); QCOMPARE(argCtx->childContexts().size(), 2); DUContext* bodyCtx = argCtx->childContexts().at(1); QVERIFY(bodyCtx); QVERIFY(bodyCtx->findDeclarations(arg1->identifier()).contains(arg1)); QVERIFY(bodyCtx->findDeclarations(arg2->identifier()).contains(arg2)); QVERIFY(bodyCtx->findDeclarations(arg3->identifier()).contains(arg3)); QCOMPARE(bodyCtx->localDeclarations().count(), 1); } void TestDeclarations::testQMLId() { const IndexedString file(QUrl(QStringLiteral("file:///internal/qmlId.qml"))); ReferencedTopDUContext top; DeclarationPointer oldDec; { // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/** file comment **/\n" "import QtQuick 1.0\n" "/**\n * some comment\n */\n" "Text { id: test; Text { id: child; } }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 5); // module, exports, Text, test and child are all in the global scope // First declaration, the anonymous class ClassDeclaration* classDecl = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(classDecl); QCOMPARE(classDecl->abstractType()->toString(), QString("qmlId")); QCOMPARE(classDecl->range(), RangeInRevision(5, 0, 5, 0)); QVERIFY(classDecl->internalContext()); QCOMPARE(classDecl->internalContext()->range(), RangeInRevision(5, 6, 5, 37)); // Second declaration: test Declaration* dec = top->localDeclarations().at(3); QVERIFY(dec); QCOMPARE(dec->identifier().toString(), QString("test")); QCOMPARE(dec->abstractType()->toString(), QString("qmlId")); oldDec = dec; } // test recompile { // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "/** file comment **/\n" "import QtQuick 1.0\n" "/**\n * some comment\n */\n" "Text { id: test; Text { id: child;}\n" " Text {id: foo;} }", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); ReferencedTopDUContext top2 = builder.build(file, session.ast(), top); QVERIFY(top2); QCOMPARE(top2.data(), top.data()); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 6); // module, exports, Text, test, child and foo // First declaration, the anonymous class ClassDeclaration* classDecl = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(classDecl); QCOMPARE(classDecl->abstractType()->toString(), QString("qmlId")); QCOMPARE(classDecl->range(), RangeInRevision(5, 0, 5, 0)); QVERIFY(classDecl->internalContext()); QCOMPARE(classDecl->internalContext()->range(), RangeInRevision(5, 6, 6, 17)); // Second declaration: test Declaration* dec = top->localDeclarations().at(3); QVERIFY(dec); QCOMPARE(dec->identifier().toString(), QString("test")); QCOMPARE(dec->abstractType()->toString(), QString("qmlId")); } } void TestDeclarations::testProperty() { const IndexedString file(QUrl(QStringLiteral("file:///internal/qmlProperty.qml"))); // 0 1 2 3 // 01234567890123456789012345678901234567890 ParseSession session(file, "Text {\n" " /// some comment\n" " property int foo;\n" "}", 0); QVERIFY(session.ast()); QVERIFY(session.problems().isEmpty()); QCOMPARE(session.language().dialect(), QmlJS::Dialect::Qml); DeclarationBuilder builder(&session); ReferencedTopDUContext top = builder.build(file, session.ast()); QVERIFY(top); DUChainReadLocker lock; QCOMPARE(top->localDeclarations().size(), 3); // module, exports, Text ClassDeclaration* text = dynamic_cast(top->localDeclarations().at(2)); QVERIFY(text); QVERIFY(text->internalContext()); QCOMPARE(text->internalContext()->type(), DUContext::Class); QCOMPARE(text->internalContext()->localDeclarations().size(), 1); ClassMemberDeclaration* foo = dynamic_cast(text->internalContext()->localDeclarations().first()); QVERIFY(foo); QCOMPARE(foo->identifier().toString(), QString("foo")); QVERIFY(foo->abstractType()); QCOMPARE(foo->abstractType()->toString(), QString("int")); QCOMPARE(QString::fromUtf8(foo->comment()), QString("some comment")); } /** * Test that all qmltypes files for built-in QtQuick modules are found on the system. * These files are also available on CI machines, since an installed Qt5 is assumed */ void TestDeclarations::testQMLtypesImportPaths() { KDevelop::IndexedString stubPath; QString path; // QtQuick QML modules path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick"), QStringLiteral("2.0")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtTest"), QStringLiteral("1.1")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.Layouts"), QStringLiteral("1.1")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.Controls"), QStringLiteral("1.1")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.Dialogs"), QStringLiteral("1.1")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.LocalStorage"), QStringLiteral("2.0")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.Particles"), QStringLiteral("2.0")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.Window"), QStringLiteral("2.2")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQuick.XmlListModel"), QStringLiteral("2.0")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); // QtQml QML modules path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtQml.Models"), QStringLiteral("2.3")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); // QtMultimedia QML modules path = QmlJS::Cache::instance().modulePath(stubPath, QStringLiteral("QtMultimedia"), QStringLiteral("5.6")); QVERIFY(QFileInfo::exists(path + "/plugins.qmltypes")); } diff --git a/plugins/qmljs/tests/test_files.cpp b/plugins/qmljs/tests/test_files.cpp index 72c4e30488..559d855376 100644 --- a/plugins/qmljs/tests/test_files.cpp +++ b/plugins/qmljs/tests/test_files.cpp @@ -1,141 +1,141 @@ /************************************************************************************* * Copyright (C) 2013 by Milian Wolff * * Copyright (C) 2013 Olivier de Gaalon * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "test_files.h" #include #include #include #include #include #include #include #include "testfilepaths.h" //Include all used json tests, otherwise "Test not found" #include #include #include #include #include // #include "cppjsontests.h" using namespace KDevelop; QTEST_MAIN(TestFiles) void TestFiles::initTestCase() { - AutoTestShell::init({"kdevqmljslanguagesupport"}); + AutoTestShell::init({"kdevqmljs"}); TestCore::initialize(KDevelop::Core::NoUi); DUChain::self()->disablePersistentStorage(); Core::self()->languageController()->backgroundParser()->setDelay(0); CodeRepresentation::setDiskChangesForbidden(true); } void TestFiles::cleanupTestCase() { TestCore::shutdown(); } void TestFiles::testQMLCustomComponent() { // First parse CustomComponent, so that it is visible and can be used // by CustomComponentUser. Then re-parse CustomComponent and assert that // it has been used. parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml", false); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponentUser.qml"); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml"); } void TestFiles::testQMLTypes() { parseAndCheck(TEST_FILES_DIR "/qmltypes/AnItem.qml", true); } void TestFiles::testTypeMismatchFalsePositives() { parseAndCheck(TEST_FILES_DIR "/type_mismatch_false_positives/code.js", true); } void TestFiles::testJSUsesBetweenFiles() { parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js", false); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_use.js"); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js"); } void TestFiles::testNodeJS() { parseAndCheck(TEST_FILES_DIR "/node_modules/module.js", false); // Ensure that module.js is in the DUChain parseAndCheck(TEST_FILES_DIR "/node.js/module2.js", false); parseAndCheck(TEST_FILES_DIR "/node.js/main.js"); } void TestFiles::testFiles_data() { QTest::addColumn("fileName"); const QString testDirPath = TEST_FILES_DIR; QStringList files = QDir(testDirPath).entryList(QStringList() << QStringLiteral("*.js") << QStringLiteral("*.qml"), QDir::Files); foreach (QString file, files) { QTest::newRow(file.toUtf8()) << QString(testDirPath + "/" + file); } } void TestFiles::testFiles() { QFETCH(QString, fileName); parseAndCheck(fileName); } void TestFiles::parseAndCheck(const QString& fileName, bool check) { const IndexedString indexedFileName(fileName); ReferencedTopDUContext top = DUChain::self()->waitForUpdate(indexedFileName, KDevelop::TopDUContext::AllDeclarationsContextsAndUses); while (!ICore::self()->languageController()->backgroundParser()->isIdle()) { QTest::qWait(500); } QVERIFY(top); if (check) { DUChainReadLocker lock; DeclarationValidator validator; top->visit(validator); QVERIFY(validator.testsPassed()); if (!top->problems().isEmpty()) { foreach(auto p, top->problems()) { qDebug() << p; } } if (!QTest::currentDataTag() || strcmp("failparse.js", QTest::currentDataTag()) != 0) { QEXPECT_FAIL("plugins.qml", "not working properly yet", Continue); QEXPECT_FAIL("qrc_import.qml", "just making sure it does not crash", Continue); QEXPECT_FAIL("dynamicObjectProperties.2.qml", "just making sure it does not crash", Continue); QVERIFY(top->problems().isEmpty()); } } } diff --git a/plugins/subversion/tests/test_svnimport.cpp b/plugins/subversion/tests/test_svnimport.cpp index 732a2569e2..d1a8b6906a 100644 --- a/plugins/subversion/tests/test_svnimport.cpp +++ b/plugins/subversion/tests/test_svnimport.cpp @@ -1,165 +1,165 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Fabian Wiesel * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "test_svnimport.h" #include #include #include #include #include #include #include #include #include #include #define VERBOSE #if defined(VERBOSE) #define TRACE(X) qDebug() << X #else #define TRACE(X) { line = line; } #endif using namespace KDevelop; void validatingExecJob(VcsJob* j, VcsJob::JobStatus status = VcsJob::JobSucceeded) { QVERIFY(j); if (!j->exec()) { qDebug() << j->errorString(); // On error, wait for key in order to allow manual state inspection } QCOMPARE(j->status(), status); } void setupLocalRepository( const QString& name, VcsLocation & reposLoc ) { KProcess cmd; cmd.setWorkingDirectory(name); cmd << QStringLiteral("svnadmin") << QStringLiteral("create") << name; QCOMPARE(cmd.execute(10000), 0); reposLoc.setRepositoryServer("file://" + name ); } void setupSampleProject( const QString& name, const QString& content ) { QFile sampleFile( name + "/sample.file" ); sampleFile.open( QIODevice::WriteOnly ); sampleFile.write( content.toUtf8() ); sampleFile.close(); } void TestSvnImport::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevplatform.plugins.svn.debug=true\n")); - AutoTestShell::init({QStringLiteral("kdevsubversion")}); + AutoTestShell::init({QStringLiteral("kdevsubversion"), QStringLiteral("KDevStandardOutputView")}); TestCore::initialize(); QList plugins = Core::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IBasicVersionControl")); foreach(IPlugin* p, plugins) { qDebug() << "checking plugin" << p; ICentralizedVersionControl* icentr = p->extension(); if (!icentr) continue; if (icentr->name() == QLatin1String("Subversion")) { vcs = icentr; break; } } qDebug() << "ok, got vcs" << vcs; QVERIFY(vcs); } void TestSvnImport::cleanupTestCase() { TestCore::shutdown(); } void TestSvnImport::testBasic() { QTemporaryDir reposDir; VcsLocation reposLoc; setupLocalRepository( reposDir.path(), reposLoc ); QTemporaryDir projectDir; QString origcontent = QStringLiteral("This is a Test"); setupSampleProject( projectDir.path(), origcontent ); VcsJob* job = vcs->import( QStringLiteral("import test"), QUrl::fromLocalFile( projectDir.path() ), reposLoc ); validatingExecJob(job); QTemporaryDir checkoutDir; validateImport( reposLoc.repositoryServer(), checkoutDir, origcontent ); } void TestSvnImport::testImportWithMissingDirs() { QTemporaryDir reposDir; VcsLocation reposLoc; setupLocalRepository( reposDir.path(), reposLoc ); QTemporaryDir projectDir; QString origcontent = QStringLiteral("This is a Test"); setupSampleProject( projectDir.path(), origcontent ); reposLoc.setRepositoryServer( reposLoc.repositoryServer() + "/foobar/" + QDir( projectDir.path() ).dirName() ); VcsJob* job = vcs->import( QStringLiteral("import test"), QUrl::fromLocalFile( projectDir.path() ), reposLoc ); validatingExecJob(job); QTemporaryDir checkoutDir; validateImport( reposLoc.repositoryServer(), checkoutDir, origcontent ); } void TestSvnImport::testImportIntoDir() { QTemporaryDir reposDir; VcsLocation reposLoc; setupLocalRepository( reposDir.path(), reposLoc ); QTemporaryDir projectDir; QString origcontent = QStringLiteral("This is a Test"); setupSampleProject( projectDir.path(), origcontent ); reposLoc.setRepositoryServer( reposLoc.repositoryServer() + '/' + QDir( projectDir.path() ).dirName() ); VcsJob* job = vcs->import( QStringLiteral("import test"), QUrl::fromLocalFile( projectDir.path() ), reposLoc ); validatingExecJob(job); QTemporaryDir checkoutDir; validateImport( reposLoc.repositoryServer(), checkoutDir, origcontent ); } void TestSvnImport::validateImport( const QString& repourl, QTemporaryDir& checkoutdir, const QString& origcontent ) { VcsLocation reposLoc; reposLoc.setRepositoryServer( repourl ); VcsJob* job = vcs->createWorkingCopy( reposLoc, QUrl::fromLocalFile(checkoutdir.path()) ); validatingExecJob(job); QFile newfile( checkoutdir.path() + "/sample.file" ); QVERIFY(newfile.exists()); QVERIFY(newfile.open(QIODevice::ReadOnly)); QCOMPARE(QString::fromUtf8( newfile.readAll() ), origcontent); } QTEST_MAIN(TestSvnImport) diff --git a/plugins/subversion/tests/test_svnrecursiveadd.cpp b/plugins/subversion/tests/test_svnrecursiveadd.cpp index 0b5a9da0ee..5269ba7053 100644 --- a/plugins/subversion/tests/test_svnrecursiveadd.cpp +++ b/plugins/subversion/tests/test_svnrecursiveadd.cpp @@ -1,160 +1,160 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2009 Fabian Wiesel * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "test_svnrecursiveadd.h" #include #include #include #include #include #include #include #include #include #define PATHETIC // A little motivator to make things work right :) #if defined(PATHETIC) inline QString vcsTestDir0() { return QStringLiteral("testdir0"); } inline QString vcsTestDir1() { return QStringLiteral("testdir1"); } inline QString vcsTest_FileName0() { return QStringLiteral("foo"); } inline QString vcsTest_FileName1() { return QStringLiteral("bar"); } inline QString keywordText() { return QStringLiteral("text"); } #else inline QString vcsTestDir0() { return QStringLiteral("dvcs\t testdir"); } // Directory containing whitespaces inline QString vcsTestDir1() { return QStringLiteral("--help"); } // Starting with hyphen for command-line tools inline QString vcsTest_FileName0() { return QStringLiteral("foo\t bar"); } inline QString vcsTest_FileName1() { return QStringLiteral("--help"); } inline QString keywordText() { return QStringLiteral("Author:\nDate:\nCommit:\n------------------------------------------------------------------------\nr999999 | ehrman | 1989-11-09 18:53:00 +0100 (Thu, 09 Nov 1989) | 1 lines\nthe line\n"); } // Text containing keywords of the various vcs-programs #endif inline QString simpleText() { return QStringLiteral("It's foo!\n"); } inline QString simpleAltText() { return QStringLiteral("No, foo()! It's bar()!\n"); } #define VERBOSE #if defined(VERBOSE) #define TRACE(X) qDebug() << X #else #define TRACE(X) { line = line; } #endif using namespace KDevelop; void validatingExecJob(VcsJob* j, VcsJob::JobStatus status = VcsJob::JobSucceeded) { QVERIFY(j); // Print the commands in full, for easier bug location #if 0 if (QLatin1String(j->metaObject()->className()) == "DVcsJob") { qDebug() << "Command: \"" << ((DVcsJob*)j)->getChildproc()->program() << ((DVcsJob*)j)->getChildproc()->workingDirectory(); qDebug() << "Output: \"" << ((DVcsJob*)j)->output(); } #endif if (!j->exec()) { qDebug() << "ooops, no exec"; qDebug() << j->errorString(); // On error, wait for key in order to allow manual state inspection #if 0 char c; std::cin.read(&c, 1); #endif } QCOMPARE(j->status(), status); } void verifiedWrite(QUrl const & url, QString const & contents) { QFile f(url.path()); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream filecontents(&f); filecontents << contents; filecontents.flush(); f.flush(); } void fillWorkingDirectory(QString const & dirname) { QDir dir(dirname); //we start it after repoInit, so we still have empty dvcs repo QVERIFY(dir.mkdir(vcsTestDir0())); QVERIFY(dir.cd(vcsTestDir0())); QUrl file0 = QUrl::fromLocalFile(dir.absoluteFilePath(vcsTest_FileName0())); QVERIFY(dir.mkdir(vcsTestDir1())); QVERIFY(dir.cd(vcsTestDir1())); QUrl file1 = QUrl::fromLocalFile(dir.absoluteFilePath(vcsTest_FileName1())); verifiedWrite(file0, simpleText()); verifiedWrite(file1, keywordText()); } void TestSvnRecursiveAdd::initTestCase() { - AutoTestShell::init({"kdevsubversion"}); + AutoTestShell::init({"kdevsubversion", "KDevStandardOutputView"}); TestCore::initialize(); } void TestSvnRecursiveAdd::cleanupTestCase() { TestCore::shutdown(); } void TestSvnRecursiveAdd::test() { QTemporaryDir reposDir; KProcess cmd; cmd.setWorkingDirectory(reposDir.path()); cmd << QStringLiteral("svnadmin") << QStringLiteral("create") << reposDir.path(); QCOMPARE(cmd.execute(10000), 0); QList plugins = Core::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IBasicVersionControl")); IBasicVersionControl* vcs = nullptr; foreach(IPlugin* p, plugins) { qDebug() << "checking plugin" << p; ICentralizedVersionControl* icentr = p->extension(); if (!icentr) continue; if (icentr->name() == QLatin1String("Subversion")) { vcs = icentr; break; } } qDebug() << "ok, got vcs" << vcs; QVERIFY(vcs); VcsLocation reposLoc; reposLoc.setRepositoryServer("file://" + reposDir.path()); QTemporaryDir checkoutDir; QUrl checkoutLoc = QUrl::fromLocalFile(checkoutDir.path()); qDebug() << "Checking out from " << reposLoc.repositoryServer() << " to " << checkoutLoc; qDebug() << "creating job"; VcsJob* job = vcs->createWorkingCopy( reposLoc, checkoutLoc ); validatingExecJob(job); qDebug() << "filling wc"; fillWorkingDirectory(checkoutDir.path()); QUrl addUrl = QUrl::fromLocalFile( checkoutDir.path() + '/' + vcsTestDir0() ); qDebug() << "Recursively adding files at " << addUrl; validatingExecJob(vcs->add({addUrl}, IBasicVersionControl::Recursive)); qDebug() << "Recursively reverting changes at " << addUrl; validatingExecJob(vcs->revert({addUrl}, IBasicVersionControl::Recursive)); } QTEST_MAIN(TestSvnRecursiveAdd)