diff --git a/shell/filteredproblemstore.cpp b/shell/filteredproblemstore.cpp index cf5cbde2f7..0daabb9d9a 100644 --- a/shell/filteredproblemstore.cpp +++ b/shell/filteredproblemstore.cpp @@ -1,335 +1,310 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 "filteredproblemstore.h" #include "problem.h" #include "watcheddocumentset.h" #include "problemstorenode.h" #include using namespace KDevelop; namespace { /// Adds diagnostics as sub-nodes void addDiagnostics(ProblemStoreNode *node, const QVector &diagnostics) { foreach (const IProblem::Ptr &ptr, diagnostics) { ProblemNode *child = new ProblemNode(node, ptr); node->addChild(child); addDiagnostics(child, ptr->diagnostics()); } } /** * @brief Base class for grouping strategy classes * * These classes build the problem tree based on the respective strategies */ class GroupingStrategy { public: GroupingStrategy( ProblemStoreNode *root ) : m_rootNode(root) , m_groupedRootNode(new ProblemStoreNode()) { } virtual ~GroupingStrategy(){ } /// Add a problem to the appropriate group virtual void addProblem(const IProblem::Ptr &problem) = 0; /// Find the specified noe const ProblemStoreNode* findNode(int row, ProblemStoreNode *parent = nullptr) const { if (parent == nullptr) return m_groupedRootNode->child(row); else return parent->child(row); } /// Returns the number of children nodes int count(ProblemStoreNode *parent = nullptr) { if (parent == nullptr) return m_groupedRootNode->count(); else return parent->count(); } /// Clears the problems virtual void clear() { m_groupedRootNode->clear(); } protected: ProblemStoreNode *m_rootNode; QScopedPointer m_groupedRootNode; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Implements no grouping strategy, that is just stores the problems without any grouping class NoGroupingStrategy final : public GroupingStrategy { public: NoGroupingStrategy(ProblemStoreNode *root) : GroupingStrategy(root) { } void addProblem(const IProblem::Ptr &problem) override { ProblemNode *node = new ProblemNode(m_groupedRootNode.data(), problem); addDiagnostics(node, problem->diagnostics()); m_groupedRootNode->addChild(node); } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Implements grouping based on path class PathGroupingStrategy final : public GroupingStrategy { public: PathGroupingStrategy(ProblemStoreNode *root) : GroupingStrategy(root) { } void addProblem(const IProblem::Ptr &problem) override { QString path = problem->finalLocation().document.str(); /// See if we already have this path ProblemStoreNode *parent = nullptr; foreach (ProblemStoreNode *node, m_groupedRootNode->children()) { if (node->label() == path) { parent = node; break; } } /// If not add it! if (parent == nullptr) { parent = new LabelNode(m_groupedRootNode.data(), path); m_groupedRootNode->addChild(parent); } ProblemNode *node = new ProblemNode(parent, problem); addDiagnostics(node, problem->diagnostics()); parent->addChild(node); } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Implements grouping based on severity class SeverityGroupingStrategy final : public GroupingStrategy { public: enum SeverityGroups { GroupError = 0, GroupWarning = 1, GroupHint = 2 }; SeverityGroupingStrategy(ProblemStoreNode *root) : GroupingStrategy(root) { /// Create the groups on construction, so there's no need to search for them on addition m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Error"))); m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Warning"))); m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Hint"))); } void addProblem(const IProblem::Ptr &problem) override { ProblemStoreNode *parent = nullptr; switch (problem->severity()) { case IProblem::Error: parent = m_groupedRootNode->child(GroupError); break; case IProblem::Warning: parent = m_groupedRootNode->child(GroupWarning); break; case IProblem::Hint: parent = m_groupedRootNode->child(GroupHint); break; } ProblemNode *node = new ProblemNode(m_groupedRootNode.data(), problem); addDiagnostics(node, problem->diagnostics()); parent->addChild(node); } void clear() override { m_groupedRootNode->child(GroupError)->clear(); m_groupedRootNode->child(GroupWarning)->clear(); m_groupedRootNode->child(GroupHint)->clear(); } }; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace KDevelop { struct FilteredProblemStorePrivate { FilteredProblemStorePrivate(FilteredProblemStore* q) : q(q) , m_strategy(new NoGroupingStrategy(q->rootNode())) , m_grouping(NoGrouping) - , m_bypassScopeFilter(false) { } /// Tells if the problem matches the filters bool match(const IProblem::Ptr &problem) const; FilteredProblemStore* q; QScopedPointer m_strategy; GroupingMethod m_grouping; - bool m_bypassScopeFilter; }; FilteredProblemStore::FilteredProblemStore(QObject *parent) : ProblemStore(parent) , d(new FilteredProblemStorePrivate(this)) { } FilteredProblemStore::~FilteredProblemStore() { } void FilteredProblemStore::addProblem(const IProblem::Ptr &problem) { ProblemStore::addProblem(problem); if (d->match(problem)) d->m_strategy->addProblem(problem); } const ProblemStoreNode* FilteredProblemStore::findNode(int row, ProblemStoreNode *parent) const { return d->m_strategy->findNode(row, parent); } int FilteredProblemStore::count(ProblemStoreNode *parent) const { return d->m_strategy->count(parent); } void FilteredProblemStore::clear() { d->m_strategy->clear(); ProblemStore::clear(); } void FilteredProblemStore::rebuild() { emit beginRebuild(); d->m_strategy->clear(); foreach (ProblemStoreNode *node, rootNode()->children()) { IProblem::Ptr problem = node->problem(); if (d->match(problem)) { d->m_strategy->addProblem(problem); } } emit endRebuild(); } void FilteredProblemStore::setGrouping(int grouping) { GroupingMethod g = GroupingMethod(grouping); if(g == d->m_grouping) return; d->m_grouping = g; switch (g) { case NoGrouping: d->m_strategy.reset(new NoGroupingStrategy(rootNode())); break; case PathGrouping: d->m_strategy.reset(new PathGroupingStrategy(rootNode())); break; case SeverityGrouping: d->m_strategy.reset(new SeverityGroupingStrategy(rootNode())); break; } rebuild(); emit changed(); } int FilteredProblemStore::grouping() const { return d->m_grouping; } -void FilteredProblemStore::setBypassScopeFilter(bool bypass) -{ - if (d->m_bypassScopeFilter != bypass) { - d->m_bypassScopeFilter = bypass; - rebuild(); - emit changed(); - } -} - -bool FilteredProblemStore::bypassScopeFilter() const -{ - return d->m_bypassScopeFilter; -} - - bool FilteredProblemStorePrivate::match(const IProblem::Ptr &problem) const { if(problem->severity()!=IProblem::NoSeverity) { /// If the problem severity isn't in the filter severities it's discarded if(!q->severities().testFlag(problem->severity())) return false; } else { if(!q->severities().testFlag(IProblem::Hint))//workaround for problems wothout correctly set severity return false; } - /// If we have bypass on, don't check the scope - if (!m_bypassScopeFilter) { - /// If the problem isn't in a file that's in the watched document set, it's discarded - const WatchedDocumentSet::DocumentSet &docs = q->documents()->get(); - if(!docs.contains(problem->finalLocation().document)) - return false; - } return true; } } - diff --git a/shell/filteredproblemstore.h b/shell/filteredproblemstore.h index b6faa38fe9..e839353ea5 100644 --- a/shell/filteredproblemstore.h +++ b/shell/filteredproblemstore.h @@ -1,118 +1,112 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 FILTEREDPROBLEMSTORE_H #define FILTEREDPROBLEMSTORE_H #include "problemstore.h" #include "problemconstants.h" namespace KDevelop { struct FilteredProblemStorePrivate; /** * @brief ProblemStore subclass that can group by severity, and path, and filter by scope, and severity. * * Internally grouping is implemented using a tree structure. * When grouping is on, the top level nodes are the groups, and their children are the nodes containing the problems that belong into that node. * If the problems have diagnostics, then the diagnostics are added as children nodes as well. This was implemented so they can be browsed in a model/view architecture. * When grouping is off, the top level nodes are the problem nodes. * * Grouping can be set and queried using the following methods * \li setGrouping(); * \li grouping(); * * Valid grouping settings: * \li NoGrouping * \li PathGrouping * \li SeverityGrouping * * Severity filter can be set and queried using the following methods * \li setSeverity() * \li severity() * * Valid severity setting: * \li IProblem::Error * \li IProblem::Warning * \li IProblem::Hint * * When changing the grouping or filtering method the following signals are emitted in order: * \li beginRebuild() * \li endRebuild() * \li changed() * * Usage example: * @code * IProblem::Ptr problem(new DetectedProblem); * problem->setSeverity(IProblem::Error); * ... * FilteredProblemStore *store = new FilteredProblemStore(); * store->setGrouping(SeverityGrouping); * store->addProblem(problem); * ProblemStoreNode *label = store->findNode(0); // Label node with the label "Error" * ProblemNode *problemNode = dynamic_cast(label->child(0)); // the node with the problem * problemNode->problem(); // The problem we added * @endcode * */ class KDEVPLATFORMSHELL_EXPORT FilteredProblemStore : public ProblemStore { Q_OBJECT public: explicit FilteredProblemStore(QObject *parent = nullptr); ~FilteredProblemStore() override; /// Adds a problem, which is then filtered and also added to the filtered problem list if it matches the filters void addProblem(const IProblem::Ptr &problem) override; /// Retrieves the specified node const ProblemStoreNode* findNode(int row, ProblemStoreNode *parent = nullptr) const override; /// Retrieves the number of filtered problems int count(ProblemStoreNode *parent = nullptr) const override; /// Clears the problems void clear() override; /// Rebuilds the filtered problem list void rebuild() override; /// Set the grouping method. See the GroupingMethod enum. void setGrouping(int grouping) override; /// Tells which grouping strategy is currently in use int grouping() const; - /// Sets whether we should bypass the scope filter - void setBypassScopeFilter(bool bypass) override; - - /// Tells whether the scope filter bypass is on - bool bypassScopeFilter() const; - private: friend struct FilteredProblemStorePrivate; QScopedPointer d; }; } #endif diff --git a/shell/problemstore.cpp b/shell/problemstore.cpp index cfd4911f76..6c18190cbb 100644 --- a/shell/problemstore.cpp +++ b/shell/problemstore.cpp @@ -1,238 +1,230 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 "problemstore.h" #include #include #include "problemstorenode.h" struct ProblemStorePrivate { ProblemStorePrivate() : m_documents(nullptr) , m_severities(KDevelop::IProblem::Error | KDevelop::IProblem::Warning | KDevelop::IProblem::Hint) , m_rootNode(new KDevelop::ProblemStoreNode()) { } /// Watched document set. Only problems that are in files in this set are stored. KDevelop::WatchedDocumentSet *m_documents; /// The severity filter setting KDevelop::IProblem::Severities m_severities; /// The problems list KDevelop::ProblemStoreNode *m_rootNode; /// Path of the currently open document KDevelop::IndexedString m_currentDocument; }; namespace KDevelop { ProblemStore::ProblemStore(QObject *parent) : QObject(parent), d(new ProblemStorePrivate) { } ProblemStore::~ProblemStore() { clear(); delete d->m_rootNode; } void ProblemStore::addProblem(const IProblem::Ptr &problem) { ProblemNode *node = new ProblemNode(d->m_rootNode); node->setProblem(problem); d->m_rootNode->addChild(node); } void ProblemStore::setProblems(const QVector &problems) { clear(); foreach (IProblem::Ptr problem, problems) { d->m_rootNode->addChild(new ProblemNode(d->m_rootNode, problem)); } rebuild(); } const ProblemStoreNode* ProblemStore::findNode(int row, ProblemStoreNode *parent) const { Q_UNUSED(parent); return d->m_rootNode->child(row); } int ProblemStore::count(ProblemStoreNode *parent) const { if(parent) return parent->count(); else return d->m_rootNode->count(); } void ProblemStore::clear() { d->m_rootNode->clear(); } void ProblemStore::rebuild() { } void ProblemStore::setSeverity(int severity) { switch (severity) { case KDevelop::IProblem::Error: setSeverities(KDevelop::IProblem::Error); break; case KDevelop::IProblem::Warning: setSeverities(KDevelop::IProblem::Error | KDevelop::IProblem::Warning); break; case KDevelop::IProblem::Hint: setSeverities(KDevelop::IProblem::Error | KDevelop::IProblem::Warning | KDevelop::IProblem::Hint); break; } } void ProblemStore::setSeverities(KDevelop::IProblem::Severities severities) { if(severities != d->m_severities) { d->m_severities = severities; rebuild(); emit changed(); } } int ProblemStore::severity() const { if (d->m_severities.testFlag(KDevelop::IProblem::Hint)) return KDevelop::IProblem::Hint; if (d->m_severities.testFlag(KDevelop::IProblem::Warning)) return KDevelop::IProblem::Warning; if (d->m_severities.testFlag(KDevelop::IProblem::Error)) return KDevelop::IProblem::Error; return 0; } KDevelop::IProblem::Severities ProblemStore::severities() const { return d->m_severities; } WatchedDocumentSet* ProblemStore::documents() const { return d->m_documents; } void ProblemStore::setScope(int scope) { ProblemScope cast_scope = static_cast(scope); if (cast_scope == BypassScopeFilter) { - setBypassScopeFilter(true); return; } - setBypassScopeFilter(false); - if (d->m_documents) { if(cast_scope == d->m_documents->getScope()) return; delete d->m_documents; } switch (cast_scope) { case CurrentDocument: d->m_documents = new CurrentDocumentSet(d->m_currentDocument, this); break; case OpenDocuments: d->m_documents = new OpenDocumentSet(this); break; case CurrentProject: d->m_documents = new CurrentProjectSet(d->m_currentDocument, this); break; case AllProjects: d->m_documents = new AllProjectSet(this); break; case BypassScopeFilter: // handled above break; } rebuild(); connect(d->m_documents, &WatchedDocumentSet::changed, this, &ProblemStore::onDocumentSetChanged); emit changed(); } int ProblemStore::scope() const { Q_ASSERT(d->m_documents != nullptr); return d->m_documents->getScope(); } void ProblemStore::setGrouping(int grouping) { Q_UNUSED(grouping); } -void ProblemStore::setBypassScopeFilter(bool bypass) -{ - Q_UNUSED(bypass); -} - void ProblemStore::setCurrentDocument(const IndexedString &doc) { d->m_currentDocument = doc; d->m_documents->setCurrentDocument(doc); } const KDevelop::IndexedString& ProblemStore::currentDocument() const { return d->m_currentDocument; } void ProblemStore::onDocumentSetChanged() { rebuild(); emit changed(); } ProblemStoreNode* ProblemStore::rootNode() { return d->m_rootNode; } } diff --git a/shell/problemstore.h b/shell/problemstore.h index 7ca137f38c..9fa8c4fda6 100644 --- a/shell/problemstore.h +++ b/shell/problemstore.h @@ -1,152 +1,149 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 PROBLEMSTORE_H #define PROBLEMSTORE_H #include #include #include #include #include struct ProblemStorePrivate; namespace KDevelop { class WatchedDocumentSet; class ProblemStoreNode; /** * @brief Stores and handles problems. Does no ordering or filtering, those should be done in subclasses. * * Used to store problems that are ordered, filtered somewhere else. For example: DUChain problems gathered by ProblemReporter. * Stores the problems in ProblemStoreNodes. * When implementing a subclass, first and foremost the rebuild method needs to be implemented, which is called every time there's a change in scope and severity filter. * If grouping is desired then also the setGrouping method must be implemented. * ProblemStore depending on settings uses CurrentDocumentSet, OpenDocumentSet, CurrentProjectSet, or AllProjectSet for scope support (NOTE: Filtering still has to be implemented in either a subclass, or somewhere else). * When the scope changes it emits the changed() signal. * * Scope set / query methods: * \li setScope() * \li scope() * * Valid scope settings: * \li CurrentDocument * \li OpenDocuments * \li CurrentProject * \li AllProjects * \li BypassScopeFilter * * Usage example: * @code * QVector problems; * // Add 4 problems * ... * ProblemStore *store = new ProblemStore(); * store->setProblems(problems); * store->count(); // Returns 4 * * ProblemStoreNode *node = store->findNode(0); // Returns the node with the first problem * @endcode * */ class KDEVPLATFORMSHELL_EXPORT ProblemStore : public QObject { Q_OBJECT public: explicit ProblemStore(QObject *parent = nullptr); ~ProblemStore() override; /// Adds a problem virtual void addProblem(const IProblem::Ptr &problem); /// Clears the current problems, and adds new ones from a list virtual void setProblems(const QVector &problems); /// Finds the specified node virtual const ProblemStoreNode* findNode(int row, ProblemStoreNode *parent = nullptr) const; /// Returns the number of problems virtual int count(ProblemStoreNode *parent = nullptr) const; /// Clears the problems virtual void clear(); /// Rebuild the problems list, if applicable. It does nothing in the base class. virtual void rebuild(); /// Specifies the severity filter virtual void setSeverity(int severity);///old-style severity access virtual void setSeverities(KDevelop::IProblem::Severities severities);///new-style severity access /// Retrives the severity filter settings int severity() const;///old-style severity access KDevelop::IProblem::Severities severities() const;//new-style severity access /// Retrieves the currently watched document set WatchedDocumentSet* documents() const; /// Sets the scope filter void setScope(int scope); /// Returns the current scope int scope() const; /// Sets the grouping method virtual void setGrouping(int grouping); - /// Sets whether we should bypass the scope filter - virtual void setBypassScopeFilter(bool bypass); - /// Sets the currently shown document (in the editor, it's triggered by the IDE) void setCurrentDocument(const IndexedString &doc); /// Retrives the path of the current document const KDevelop::IndexedString& currentDocument() const; signals: /// Emitted when the problems change void changed(); /// Emitted before the problemlist is rebuilt void beginRebuild(); /// Emitted once the problemlist has been rebuilt void endRebuild(); private slots: /// Triggered when the watched document set changes. E.g.:document closed, new one added, etc virtual void onDocumentSetChanged(); protected: ProblemStoreNode* rootNode(); private: QScopedPointer d; }; } #endif diff --git a/shell/tests/test_filteredproblemstore.cpp b/shell/tests/test_filteredproblemstore.cpp index f9b5dcbe7d..cde6673495 100644 --- a/shell/tests/test_filteredproblemstore.cpp +++ b/shell/tests/test_filteredproblemstore.cpp @@ -1,698 +1,681 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 #include #include #include #include #include #include #include #include namespace { const int ErrorCount = 1; const int WarningCount = 2; const int HintCount = 3; const int ProblemsCount = ErrorCount + WarningCount + HintCount; const int ErrorFilterProblemCount = ErrorCount; const int WarningFilterProblemCount = ErrorCount + WarningCount; const int HintFilterProblemCount = ErrorCount + WarningCount + HintCount; } using namespace KDevelop; class TestFilteredProblemStore : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); - void testBypass(); void testSeverity(); void testSeverities(); void testGrouping(); void testNoGrouping(); void testPathGrouping(); void testSeverityGrouping(); private: // Severity grouping testing bool checkCounts(int error, int warning, int hint); bool checkNodeLabels(); // --------------------------- void generateProblems(); QScopedPointer m_store; QVector m_problems; IProblem::Ptr m_diagnosticTestProblem; }; #define MYCOMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ return false #define MYVERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return false void TestFilteredProblemStore::initTestCase() { AutoTestShell::init(); TestCore::initialize(Core::NoUi); m_store.reset(new FilteredProblemStore()); generateProblems(); } void TestFilteredProblemStore::cleanupTestCase() { TestCore::shutdown(); } -void TestFilteredProblemStore::testBypass() -{ - QSignalSpy changedSpy(m_store.data(), &FilteredProblemStore::changed); - QSignalSpy beginRebuildSpy(m_store.data(), &FilteredProblemStore::beginRebuild); - QSignalSpy endRebuildSpy(m_store.data(), &FilteredProblemStore::endRebuild); - - QVERIFY(!m_store->bypassScopeFilter()); - - m_store->setBypassScopeFilter(true); - - QVERIFY(m_store->bypassScopeFilter()); - QCOMPARE(changedSpy.count(), 1); - QCOMPARE(beginRebuildSpy.count(), 1); - QCOMPARE(endRebuildSpy.count(), 1); -} - void TestFilteredProblemStore::testSeverity() { QVERIFY(m_store->severity() == IProblem::Hint); QSignalSpy changedSpy(m_store.data(), &FilteredProblemStore::changed); QSignalSpy beginRebuildSpy(m_store.data(), &FilteredProblemStore::beginRebuild); QSignalSpy endRebuildSpy(m_store.data(), &FilteredProblemStore::endRebuild); m_store->setSeverity(IProblem::Error); QVERIFY(m_store->severity() == IProblem::Error); QCOMPARE(changedSpy.count(), 1); QCOMPARE(beginRebuildSpy.count(), 1); QCOMPARE(endRebuildSpy.count(), 1); m_store->setSeverity(IProblem::Hint); } void TestFilteredProblemStore::testSeverities() { QVERIFY(m_store->severities() == (IProblem::Error | IProblem::Warning | IProblem::Hint)); QSignalSpy changedSpy(m_store.data(), &FilteredProblemStore::changed); QSignalSpy beginRebuildSpy(m_store.data(), &FilteredProblemStore::beginRebuild); QSignalSpy endRebuildSpy(m_store.data(), &FilteredProblemStore::endRebuild); m_store->setSeverities(IProblem::Error | IProblem::Hint); QVERIFY(m_store->severities() == (IProblem::Error | IProblem::Hint)); QCOMPARE(changedSpy.count(), 1); QCOMPARE(beginRebuildSpy.count(), 1); QCOMPARE(endRebuildSpy.count(), 1); m_store->setSeverities(IProblem::Error | IProblem::Warning | IProblem::Hint); } void TestFilteredProblemStore::testGrouping() { QVERIFY(m_store->grouping() == NoGrouping); QSignalSpy changedSpy(m_store.data(), &FilteredProblemStore::changed); QSignalSpy beginRebuildSpy(m_store.data(), &FilteredProblemStore::beginRebuild); QSignalSpy endRebuildSpy(m_store.data(), &FilteredProblemStore::endRebuild); m_store->setGrouping(PathGrouping); QVERIFY(m_store->grouping() == PathGrouping); QCOMPARE(changedSpy.count(), 1); QCOMPARE(beginRebuildSpy.count(), 1); QCOMPARE(endRebuildSpy.count(), 1); m_store->setGrouping(NoGrouping); } // Compares the node and it's children to a reference problem and it's diagnostics bool checkDiagnodes(const ProblemStoreNode *node, const IProblem::Ptr &reference) { const ProblemNode *problemNode = dynamic_cast(node); MYVERIFY(problemNode != nullptr); MYCOMPARE(problemNode->problem()->description(), reference->description()); MYCOMPARE(problemNode->problem()->finalLocation().document.str(), reference->finalLocation().document.str()); MYCOMPARE(problemNode->count(), 1); const IProblem::Ptr diag = reference->diagnostics().at(0); const ProblemNode *diagNode = dynamic_cast(problemNode->child(0)); MYVERIFY(diagNode != nullptr); MYCOMPARE(diagNode->problem()->description(), diag->description()); MYCOMPARE(diagNode->count(), 1); const IProblem::Ptr diagdiag = diag->diagnostics().at(0); const ProblemNode *diagdiagNode = dynamic_cast(diagNode->child(0)); MYVERIFY(diagdiagNode != nullptr); MYCOMPARE(diagdiagNode->problem()->description(), diagdiag->description()); MYCOMPARE(diagdiagNode->count(), 0); return true; } void TestFilteredProblemStore::testNoGrouping() { // Add problems int c = 0; foreach (const IProblem::Ptr &p, m_problems) { m_store->addProblem(p); c++; QCOMPARE(m_store->count(), c); } for (int i = 0; i < c; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Check if clear works m_store->clear(); QCOMPARE(m_store->count(), 0); // Set problems m_store->setProblems(m_problems); QCOMPARE(m_problems.count(), m_store->count()); for (int i = 0; i < c; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Check old style severity filtering // old-style setSeverity // Error filter m_store->setSeverity(IProblem::Error); QCOMPARE(m_store->count(), ErrorFilterProblemCount); for (int i = 0; i < ErrorFilterProblemCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(0)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Warning filter m_store->setSeverity(IProblem::Warning); QCOMPARE(m_store->count(), WarningFilterProblemCount); for (int i = 0; i < WarningFilterProblemCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Hint filter m_store->setSeverity(IProblem::Hint); QCOMPARE(m_store->count(), HintFilterProblemCount); for (int i = 0; i < HintFilterProblemCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Check new severity filtering // Error filter m_store->setSeverities(IProblem::Error); QCOMPARE(m_store->count(), ErrorCount); for (int i = 0; i < ErrorCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } // Warning filter m_store->setSeverities(IProblem::Warning); QCOMPARE(m_store->count(), WarningCount); for (int i = 0; i < WarningCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i+ErrorCount]->description()); } // Hint filter m_store->setSeverities(IProblem::Hint); QCOMPARE(m_store->count(), HintCount); for (int i = 0; i < HintCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i+ErrorCount+WarningCount]->description()); } //Error + Hint filter m_store->setSeverities(IProblem::Error | IProblem::Hint); QCOMPARE(m_store->count(), HintCount + ErrorCount); for (int i = 0; i < ErrorCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i]->description()); } for (int i = ErrorCount; i < ErrorCount+HintCount; i++) { const ProblemNode *node = dynamic_cast(m_store->findNode(i)); QVERIFY(node != nullptr); QCOMPARE(node->problem()->description(), m_problems[i+WarningCount]->description()); } m_store->setSeverities(IProblem::Error | IProblem::Warning | IProblem::Hint); m_store->clear(); // Check if diagnostics are added properly m_store->addProblem(m_diagnosticTestProblem); QCOMPARE(m_store->count(), 1); QVERIFY(checkDiagnodes(m_store->findNode(0), m_diagnosticTestProblem)); } bool checkNodeLabel(const ProblemStoreNode *node, const QString &label) { const LabelNode *parent = dynamic_cast(node); MYVERIFY(parent != nullptr); MYCOMPARE(parent->label(), label); return true; } bool checkNodeDescription(const ProblemStoreNode *node, const QString &descr) { const ProblemNode *n = dynamic_cast(node); MYVERIFY(n != nullptr); MYCOMPARE(n->problem()->description(), descr); return true; } void TestFilteredProblemStore::testPathGrouping() { m_store->clear(); // Rebuild the problem list with grouping m_store->setGrouping(PathGrouping); // Add problems foreach (const IProblem::Ptr &p, m_problems) { m_store->addProblem(p); } QCOMPARE(m_store->count(), ProblemsCount); for (int i = 0; i < 3; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } // Now add a new problem IProblem::Ptr p(new DetectedProblem()); p->setDescription(QStringLiteral("PROBLEM4")); p->setFinalLocation(m_problems[2]->finalLocation()); m_store->addProblem(p); QCOMPARE(m_store->count(), ProblemsCount); // Check the first 2 top-nodes for (int i = 0; i < 2; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } // check the last one, and check the added problem is at the right place { const ProblemStoreNode *node = m_store->findNode(2); checkNodeLabel(node, m_problems[2]->finalLocation().document.str()); QCOMPARE(node->count(), 2); checkNodeDescription(node->child(1), p->description()); } m_store->clear(); m_store->setProblems(m_problems); // Check filters // old-style setSeverity // Error filter m_store->setSeverity(IProblem::Error); QCOMPARE(m_store->count(), ErrorFilterProblemCount); { const ProblemStoreNode *node = m_store->findNode(0); checkNodeLabel(node, m_problems[0]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[0]->description()); } // Warning filter m_store->setSeverity(IProblem::Warning); QCOMPARE(m_store->count(), WarningFilterProblemCount); for (int i = 0; i < WarningFilterProblemCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } // Hint filter m_store->setSeverity(IProblem::Hint); QCOMPARE(m_store->count(), HintFilterProblemCount); for (int i = 0; i < HintFilterProblemCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } // Check new severity filtering // Error filter m_store->setSeverities(IProblem::Error); for (int i = 0; i < ErrorCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } // Warning filter m_store->setSeverities(IProblem::Warning); for (int i = 0; i < WarningCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i+ErrorCount]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i+ErrorCount]->description()); } // Hint filter m_store->setSeverities(IProblem::Hint); for (int i = 0; i < HintCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i+ErrorCount+WarningCount]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i+ErrorCount+WarningCount]->description()); } //Error + Hint filter m_store->setSeverities(IProblem::Error | IProblem::Hint); QCOMPARE(m_store->count(), HintCount + ErrorCount); for (int i = 0; i < ErrorCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i]->description()); } for (int i = ErrorCount; i < ErrorCount+HintCount; i++) { const ProblemStoreNode *node = m_store->findNode(i); checkNodeLabel(node, m_problems[i+WarningCount]->finalLocation().document.str()); QCOMPARE(node->count(), 1); checkNodeDescription(node->child(0), m_problems[i+WarningCount]->description()); } m_store->setSeverities(IProblem::Error | IProblem::Warning | IProblem::Hint); m_store->clear(); // Check if the diagnostics are added properly m_store->addProblem(m_diagnosticTestProblem); QCOMPARE(m_store->count(), 1); const LabelNode *node = dynamic_cast(m_store->findNode(0)); QVERIFY(node != nullptr); QCOMPARE(node->label(), m_diagnosticTestProblem->finalLocation().document.str()); QVERIFY(checkDiagnodes(node->child(0), m_diagnosticTestProblem)); } void TestFilteredProblemStore::testSeverityGrouping() { m_store->clear(); m_store->setGrouping(SeverityGrouping); QCOMPARE(m_store->count(), 3); const ProblemStoreNode *errorNode = m_store->findNode(0); const ProblemStoreNode *warningNode = m_store->findNode(1); const ProblemStoreNode *hintNode = m_store->findNode(2); // Add problems for (int i=0;iaddProblem(m_problems[i]); int severityType = 0; //error int addedCountOfCurrentSeverityType = i + 1; if (i>=ErrorCount) { severityType = 1; //warning addedCountOfCurrentSeverityType = i - ErrorCount + 1; } if (i>=ErrorCount+WarningCount) { severityType = 2; //hint addedCountOfCurrentSeverityType = i - (ErrorCount + WarningCount) + 1; } QCOMPARE(m_store->findNode(severityType)->count(), addedCountOfCurrentSeverityType); } QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, WarningCount, HintCount)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); checkNodeDescription(warningNode->child(0), m_problems[1]->description()); checkNodeDescription(hintNode->child(0), m_problems[3]->description()); // Clear m_store->clear(); QCOMPARE(m_store->count(), 3); QVERIFY(checkCounts(0,0,0)); // Set problems m_store->setProblems(m_problems); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, WarningCount, HintCount)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); checkNodeDescription(warningNode->child(0), m_problems[1]->description()); checkNodeDescription(hintNode->child(0), m_problems[3]->description()); // Check severity filter // old-style setSeverity // Error filter m_store->setSeverity(IProblem::Error); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, 0, 0)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); // Warning filter m_store->setSeverity(IProblem::Warning); QCOMPARE(m_store->count(), 3); checkNodeLabels(); QVERIFY(checkCounts(ErrorCount, WarningCount, 0)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); checkNodeDescription(warningNode->child(0), m_problems[1]->description()); // Hint filter m_store->setSeverity(IProblem::Hint); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, WarningCount, HintCount)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); checkNodeDescription(warningNode->child(0), m_problems[1]->description()); checkNodeDescription(hintNode->child(0), m_problems[3]->description()); // Check severity filter // Error filter m_store->setSeverities(IProblem::Error); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, 0, 0)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); // Warning filter m_store->setSeverities(IProblem::Warning); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(0, WarningCount, 0)); checkNodeDescription(warningNode->child(0), m_problems[1]->description()); // Hint filter m_store->setSeverities(IProblem::Hint); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(0, 0, HintCount)); checkNodeDescription(hintNode->child(0), m_problems[3]->description()); // Error + Hint filter m_store->setSeverities(IProblem::Error | IProblem::Hint); QCOMPARE(m_store->count(), 3); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(ErrorCount, 0, HintCount)); checkNodeDescription(errorNode->child(0), m_problems[0]->description()); checkNodeDescription(hintNode->child(0), m_problems[3]->description()); m_store->setSeverities(IProblem::Error | IProblem::Warning | IProblem::Hint); m_store->clear(); // Check if diagnostics are added properly m_store->addProblem(m_diagnosticTestProblem); QVERIFY(checkNodeLabels()); QVERIFY(checkCounts(1, 0, 0)); QVERIFY(checkDiagnodes(m_store->findNode(0)->child(0), m_diagnosticTestProblem)); } bool TestFilteredProblemStore::checkCounts(int error, int warning, int hint) { const ProblemStoreNode *errorNode = m_store->findNode(0); const ProblemStoreNode *warningNode = m_store->findNode(1); const ProblemStoreNode *hintNode = m_store->findNode(2); MYVERIFY(errorNode != nullptr); MYVERIFY(warningNode != nullptr); MYVERIFY(hintNode != nullptr); MYCOMPARE(errorNode->count(), error); MYCOMPARE(warningNode->count(), warning); MYCOMPARE(hintNode->count(), hint); return true; } bool TestFilteredProblemStore::checkNodeLabels() { const ProblemStoreNode *errorNode = m_store->findNode(0); const ProblemStoreNode *warningNode = m_store->findNode(1); const ProblemStoreNode *hintNode = m_store->findNode(2); MYCOMPARE(checkNodeLabel(errorNode, i18n("Error")), true); MYCOMPARE(checkNodeLabel(warningNode, i18n("Warning")), true); MYCOMPARE(checkNodeLabel(hintNode, i18n("Hint")), true); return true; } // Generate 3 problems, all with different paths, different severity // Also generates a problem with diagnostics void TestFilteredProblemStore::generateProblems() { IProblem::Ptr p1(new DetectedProblem()); IProblem::Ptr p2(new DetectedProblem()); IProblem::Ptr p3(new DetectedProblem()); IProblem::Ptr p4(new DetectedProblem()); IProblem::Ptr p5(new DetectedProblem()); IProblem::Ptr p6(new DetectedProblem()); DocumentRange r1; r1.document = IndexedString("/just/a/random/path"); p1->setDescription(QStringLiteral("PROBLEM1")); p1->setSeverity(IProblem::Error); p1->setFinalLocation(r1); DocumentRange r2; r2.document = IndexedString("/just/another/path"); p2->setDescription(QStringLiteral("PROBLEM2")); p2->setSeverity(IProblem::Warning); p2->setFinalLocation(r2); DocumentRange r3; r3.document = IndexedString("/just/another/pathy/patha"); p3->setDescription(QStringLiteral("PROBLEM3")); p3->setSeverity(IProblem::Warning); p3->setFinalLocation(r3); DocumentRange r4; r4.document = IndexedString("/yet/another/test/path"); p4->setDescription(QStringLiteral("PROBLEM4")); p4->setSeverity(IProblem::Hint); p4->setFinalLocation(r4); DocumentRange r5; r5.document = IndexedString("/yet/another/pathy/test/path"); p5->setDescription(QStringLiteral("PROBLEM5")); p5->setSeverity(IProblem::Hint); p5->setFinalLocation(r5); DocumentRange r6; r6.document = IndexedString("/yet/another/test/pathy/path"); p6->setDescription(QStringLiteral("PROBLEM6")); p6->setSeverity(IProblem::Hint); p6->setFinalLocation(r6); m_problems.push_back(p1); m_problems.push_back(p2); m_problems.push_back(p3); m_problems.push_back(p4); m_problems.push_back(p5); m_problems.push_back(p6); // Problem for diagnostic testing IProblem::Ptr p(new DetectedProblem()); DocumentRange r; r.document = IndexedString("DIAGTEST"); p->setFinalLocation(r); p->setDescription(QStringLiteral("PROBLEM")); p->setSeverity(IProblem::Error); IProblem::Ptr d(new DetectedProblem()); d->setDescription(QStringLiteral("DIAG")); IProblem::Ptr dd(new DetectedProblem()); dd->setDescription(QStringLiteral("DIAGDIAG")); d->addDiagnostic(dd); p->addDiagnostic(d); m_diagnosticTestProblem = p; } QTEST_MAIN(TestFilteredProblemStore) #include "test_filteredproblemstore.moc"