diff --git a/outputview/outputfilteringstrategies.cpp b/outputview/outputfilteringstrategies.cpp index 91361f1bac..cccc0265d7 100644 --- a/outputview/outputfilteringstrategies.cpp +++ b/outputview/outputfilteringstrategies.cpp @@ -1,396 +1,397 @@ /* This file is part of KDevelop Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com 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 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 "outputfilteringstrategies.h" #include "outputformats.h" #include "filtereditem.h" #include #include #include #include namespace KDevelop { /// --- No filter strategy --- NoFilterStrategy::NoFilterStrategy() { } FilteredItem NoFilterStrategy::actionInLine(const QString& line) { return FilteredItem( line ); } FilteredItem NoFilterStrategy::errorInLine(const QString& line) { return FilteredItem( line ); } /// --- Compiler error filter strategy --- /// Impl. of CompilerFilterStrategy. struct CompilerFilterStrategyPrivate { CompilerFilterStrategyPrivate(const KUrl& buildDir); KUrl urlForFile( const QString& ) const; bool isMultiLineCase(ErrorFormat curErrFilter) const; QVector m_currentDirs; KUrl m_buildDir; typedef QMap PositionMap; PositionMap m_positionInCurrentDirs; }; // All the possible string that indicate an error if we via Regex have been able to // extract file and linenumber from a given outputline -static const QVector ERROR_INDICATORS = QVector() +typedef QPair Indicator; +static const QVector INDICATORS = QVector() // ld - << "undefined reference" - << "undefined symbol" - << "ld: cannot find" - << "No such file" - // Gcc - << "error"; + << Indicator("undefined reference", FilteredItem::ErrorItem) + << Indicator("undefined symbol", FilteredItem::ErrorItem) + << Indicator("ld: cannot find", FilteredItem::ErrorItem) + << Indicator("no such file", FilteredItem::ErrorItem) + // gcc + << Indicator("error", FilteredItem::ErrorItem) + // generic + << Indicator("warning", FilteredItem::WarningItem) + << Indicator("info", FilteredItem::InformationItem) + << Indicator("note", FilteredItem::InformationItem); // A list of filters for possible compiler, linker, and make errors const QList ERROR_FILTERS = QList() // GCC - another case, eg. for #include "pixmap.xpm" which does not exists << ErrorFormat( "^([^:\t]+):([0-9]+):([0-9]+):([^0-9]+)", 1, 2, 4, 3 ) // GCC << ErrorFormat( "^([^:\t]+):([0-9]+):([^0-9]+)", 1, 2, 3 ) // ICC << ErrorFormat( "^([^: \\t]+)\\(([0-9]+)\\):([^0-9]+)", 1, 2, 3, "intel" ) //libtool link << ErrorFormat( "^(libtool):( link):( warning): ", 0, 0, 0 ) // make << ErrorFormat( "No rule to make target", 0, 0, 0 ) // Fortran << ErrorFormat( "\"(.*)\", line ([0-9]+):(.*)", 1, 2, 3 ) // GFortran << ErrorFormat( "^(.*):([0-9]+)\\.([0-9]+):(.*)", 1, 2, 4, "gfortran", 3 ) // Jade << ErrorFormat( "^[a-zA-Z]+:([^: \t]+):([0-9]+):[0-9]+:[a-zA-Z]:(.*)", 1, 2, 3 ) // ifort << ErrorFormat( "^fortcom: (.*): (.*), line ([0-9]+):(.*)", 2, 3, 1, "intel" ) // PGI << ErrorFormat( "PGF9(.*)-(.*)-(.*)-(.*) \\((.*): ([0-9]+)\\)", 5, 6, 4, "pgi" ) // PGI (2) << ErrorFormat( "PGF9(.*)-(.*)-(.*)-Symbol, (.*) \\((.*)\\)", 5, 5, 4, "pgi" ); // A list of filters for possible compiler, linker, and make actions QList ACTION_FILTERS = QList() << ActionFormat( i18n("compiling"), 1, 2, "(?:^|[^=])\\b(gcc|CC|cc|distcc|c\\+\\+|" "g\\+\\+|icc|icpc)\\s+.*-c.*[/ '\\\\]+(\\w+\\.(?:cpp|CPP|c|C|cxx|CXX|cs|" "java|hpf|f|F|f90|F90|f95|F95))") //moc and uic << ActionFormat( i18n("generating"), 1, 2, "/(moc|uic)\\b.*\\s-o\\s([^\\s;]+)") //libtool linking << ActionFormat( i18nc("Linking object files into a library or executable", "linking"), "libtool", "/bin/sh\\s.*libtool.*--mode=link\\s.*\\s-o\\s([^\\s;]+)", 1 ) //unsermake << ActionFormat( i18n("compiling"), 1, 1, "^compiling (.*)" ) << ActionFormat( i18n("generating"), 1, 2, "^generating (.*)" ) << ActionFormat( i18nc("Linking object files into a library or executable", "linking"), 1, 2, "(gcc|cc|c\\+\\+|g\\+\\+|icc|icpc)\\S* (?:\\S* )*-o ([^\\s;]+)") << ActionFormat( i18nc("Linking object files into a library or executable", "linking"), 1, 2, "^linking (.*)" ) //cmake << ActionFormat( i18n("built"), -1, 1, "\\[.+%\\] Built target (.*)" ) << ActionFormat( i18n("compiling"), "cmake", "\\[.+%\\] Building .* object (.*)CMakeFiles/", 1 ) << ActionFormat( i18n("generating"), -1, 1, "\\[.+%\\] Generating (.*)" ) << ActionFormat( i18nc("Linking object files into a library or executable", "linking"), -1, 1, "^Linking (.*)" ) << ActionFormat( i18n("installing"), -1, 1, "-- Installing (.*)" ) //libtool install << ActionFormat( i18n("creating"), "", "/(?:bin/sh\\s.*mkinstalldirs).*\\s([^\\s;]+)", 1 ) << ActionFormat( i18n("installing"), "", "/(?:usr/bin/install|bin/sh\\s.*mkinstalldirs" "|bin/sh\\s.*libtool.*--mode=install).*\\s([^\\s;]+)", 1 ) //dcop << ActionFormat( i18n("generating"), "dcopidl", "dcopidl .* > ([^\\s;]+)", 1 ) << ActionFormat( i18n("compiling"), "dcopidl2cpp", "dcopidl2cpp (?:\\S* )*([^\\s;]+)", 1 ) // match against Entering directory to update current build dir << ActionFormat( "cd", "", "make\\[\\d+\\]: Entering directory (\\`|\\')(.+)'", 2); CompilerFilterStrategyPrivate::CompilerFilterStrategyPrivate(const KUrl& buildDir) : m_buildDir(buildDir) { } KUrl CompilerFilterStrategyPrivate::urlForFile(const QString& filename) const { QFileInfo fi( filename ); KUrl currentUrl; if( fi.isRelative() ) { if( m_currentDirs.isEmpty() ) { currentUrl = m_buildDir; currentUrl.addPath( filename ); return currentUrl; } QVector::const_iterator it = m_currentDirs.constEnd() - 1; do { currentUrl = KUrl( *it ); currentUrl.addPath( filename ); } while( (it-- != m_currentDirs.constBegin()) && !QFileInfo(currentUrl.toLocalFile()).exists() ); return currentUrl; } else { currentUrl = KUrl( filename ); } return currentUrl; } CompilerFilterStrategy::CompilerFilterStrategy(const KUrl& buildDir) : d(new CompilerFilterStrategyPrivate( buildDir )) { } CompilerFilterStrategy::~CompilerFilterStrategy() { delete d; } QVector< QString > CompilerFilterStrategy::getCurrentDirs() { return d->m_currentDirs; } FilteredItem CompilerFilterStrategy::actionInLine(const QString& line) { FilteredItem item(line); foreach( const ActionFormat& curActFilter, ACTION_FILTERS ) { QRegExp regEx = curActFilter.expression; if( regEx.indexIn( line ) != -1 ) { item.type = FilteredItem::ActionItem; if( curActFilter.fileGroup != -1 && curActFilter.toolGroup != -1 ) { item.shortenedText = QString( "%1 %2 (%3)").arg( curActFilter.action ).arg( regEx.cap( curActFilter.fileGroup ) ).arg( regEx.cap( curActFilter.toolGroup ) ); } if( curActFilter.action == "cd" ) { d->m_currentDirs.push_back( regEx.cap( curActFilter.fileGroup ) ); d->m_positionInCurrentDirs.insert( regEx.cap( curActFilter.fileGroup ) , d->m_currentDirs.size() - 1 ); } // Special case for cmake: we parse the "Compiling " expression // and use it to find out about the build paths encountered during a build. // They are later searched by urlForFile to find source files corresponding to // compiler errors. if ( curActFilter.action == i18n("compiling") && curActFilter.tool == "cmake") { KUrl url = d->m_buildDir; url.addPath(regEx.cap( curActFilter.fileGroup )); QString dirName = url.toLocalFile(); // Use map to check for duplicates, to avoid O(n^2) behaviour CompilerFilterStrategyPrivate::PositionMap::iterator it = d->m_positionInCurrentDirs.find(dirName); // Encountered new build directory? if (it == d->m_positionInCurrentDirs.end()) { d->m_currentDirs.push_back( dirName ); d->m_positionInCurrentDirs.insert( dirName, d->m_currentDirs.size() - 1 ); } else { // Build dir already in currentDirs, but move it to back of currentDirs list // (this gives us most-recently-used semantics in urlForFile) std::rotate(d->m_currentDirs.begin() + it.value(), d->m_currentDirs.begin() + it.value() + 1, d->m_currentDirs.end() ); it.value() = d->m_currentDirs.size() - 1; } } break; } } return item; } bool CompilerFilterStrategyPrivate::isMultiLineCase(KDevelop::ErrorFormat curErrFilter) const { if(curErrFilter.compiler == "gfortran") { return true; } return false; } FilteredItem CompilerFilterStrategy::errorInLine(const QString& line) { FilteredItem item(line); foreach( const ErrorFormat& curErrFilter, ERROR_FILTERS ) { QRegExp regEx = curErrFilter.expression; if( regEx.indexIn( line ) != -1 && !( line.contains( "Each undeclared identifier is reported only once" ) || line.contains( "for each function it appears in." ) ) ) { if(curErrFilter.fileGroup > 0) { item.url = d->urlForFile( regEx.cap( curErrFilter.fileGroup ) ); } item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; if(curErrFilter.columnGroup >= 0) { item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; } else { item.columnNo = 0; } QString txt = regEx.cap(curErrFilter.textGroup); - foreach( const QString curErrIndicator , ERROR_INDICATORS ) { - if(txt.contains(curErrIndicator, Qt::CaseInsensitive)) { - item.type = FilteredItem::ErrorItem; - break; - } - } - if(txt.contains("warning", Qt::CaseInsensitive)) { - item.type = FilteredItem::WarningItem; - } - - if(txt.contains("note", Qt::CaseInsensitive) || txt.contains("info", Qt::CaseInsensitive)) { - item.type = FilteredItem::InformationItem; + // Find the indicator which happens most early. + int earliestIndicatorIdx = txt.length(); + foreach( const Indicator& curIndicator, INDICATORS ) { + int curIndicatorIdx = txt.indexOf(curIndicator.first, 0, Qt::CaseInsensitive); + if((curIndicatorIdx >= 0) && (earliestIndicatorIdx > curIndicatorIdx)) { + earliestIndicatorIdx = curIndicatorIdx; + item.type = curIndicator.second; + } } // Make the item clickable if it comes with the necessary file & line number information if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) { item.isActivatable = true; if(item.type == FilteredItem::InvalidItem) { // If there are no error indicators in the line // maybe this is a multiline case if(d->isMultiLineCase(curErrFilter)) { item.type = FilteredItem::ErrorItem; } else { // Okay so we couldn't find anything to indicate an error, but we have file and lineGroup // Lets keep this item clickable and indicate this to the user. item.type = FilteredItem::InformationItem; } } } break; } } return item; } /// --- Script error filter strategy --- // A list of filters for possible Python and PHP errors const QList SCRIPT_ERROR_FILTERS = QList() //QRegExp python("^ File \"(.*)\", line (\\d*), in(.*)$"); << ErrorFormat( "^ File \"(.*)\", line ([0-9]+)(.*$|, in(.*)$)", 1, 2, -1 ) //QRegExp phpstacktrace("^.*(/.*):(\\d*).*$"); << ErrorFormat( "^.*(/.*):([0-9]+).*$", 1, 2, -1 ) //QRegExp phperror("^.* in (/.*) on line (\\d*).*$"); << ErrorFormat( "^.* in (/.*) on line ([0-9]+).*$", 1, 2, -1 ); ScriptErrorFilterStrategy::ScriptErrorFilterStrategy() { } FilteredItem ScriptErrorFilterStrategy::actionInLine(const QString& line) { return FilteredItem(line); } FilteredItem ScriptErrorFilterStrategy::errorInLine(const QString& line) { FilteredItem item(line); foreach( const ErrorFormat& curErrFilter, SCRIPT_ERROR_FILTERS ) { QRegExp regEx = curErrFilter.expression; if( regEx.indexIn( line ) != -1 ) { item.url = regEx.cap( curErrFilter.fileGroup ); item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; if(curErrFilter.columnGroup >= 0) { item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; } else { item.columnNo = 0; } QString txt = regEx.cap(curErrFilter.textGroup); item.type = FilteredItem::ErrorItem; // Make the item clickable if it comes with the necessary file & line number information if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) item.isActivatable = true; break; } } return item; } /// --- Static Analysis filter strategy --- // A list of filters for static analysis tools (krazy2, cppcheck) const QList STATIC_ANALYSIS_FILTERS = QList() // CppCheck << ErrorFormat( "^\\[(.*):([0-9]+)\\]:(.*)", 1, 2, 3 ) // krazy2 << ErrorFormat( "^\\t([^:]+).*line#([0-9]+).*", 1, 2, -1 ) // krazy2 without line info << ErrorFormat( "^\\t(.*): missing license", 1, -1, -1 ); StaticAnalysisFilterStrategy::StaticAnalysisFilterStrategy() { } FilteredItem StaticAnalysisFilterStrategy::actionInLine(const QString& line) { return FilteredItem(line); } FilteredItem StaticAnalysisFilterStrategy::errorInLine(const QString& line) { FilteredItem item(line); foreach( const ErrorFormat& curErrFilter, STATIC_ANALYSIS_FILTERS ) { QRegExp regEx = curErrFilter.expression; if( regEx.indexIn( line ) != -1 ) { item.url = regEx.cap( curErrFilter.fileGroup ); item.lineNo = regEx.cap( curErrFilter.lineGroup ).toInt() - 1; if(curErrFilter.columnGroup >= 0) { item.columnNo = regEx.cap( curErrFilter.columnGroup ).toInt() - 1; } else { item.columnNo = 0; } QString txt = regEx.cap(curErrFilter.textGroup); item.type = FilteredItem::ErrorItem; // Make the item clickable if it comes with the necessary file & line number information if (curErrFilter.fileGroup > 0 && curErrFilter.lineGroup > 0) { item.isActivatable = true; } break; } } return item; } } // namespace KDevelop diff --git a/outputview/tests/filteringstrategytest.cpp b/outputview/tests/filteringstrategytest.cpp index 8ff26b585c..bcd5b780f4 100644 --- a/outputview/tests/filteringstrategytest.cpp +++ b/outputview/tests/filteringstrategytest.cpp @@ -1,317 +1,351 @@ /* This file is part of KDevelop Copyright 2012 Milian Wolff Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com 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 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 "filteringstrategytest.h" #include "testlinebuilderfunctions.h" #include #include #include #include QTEST_KDEMAIN(KDevelop::FilteringStrategyTest, NoGUI) namespace KDevelop { void FilteringStrategyTest::testNoFilterstrategy_data() { QTest::addColumn("line"); QTest::addColumn("expected"); QTest::newRow("cppcheck-info-line") << buildCppCheckInformationLine() << FilteredItem::InvalidItem; QTest::newRow("cppcheck-error-line") << buildCppCheckErrorLine() << FilteredItem::InvalidItem; QTest::newRow("compiler-line") << buildCompilerLine() << FilteredItem::InvalidItem; QTest::newRow("compiler-error-line") << buildCompilerErrorLine() << FilteredItem::InvalidItem; QTest::newRow("compiler-action-line") << buildCompilerActionLine() << FilteredItem::InvalidItem; QTest::newRow("compiler-information-line") << buildCompilerInformationLine() << FilteredItem::InvalidItem; QTest::newRow("python-error-line") << buildPythonErrorLine() << FilteredItem::InvalidItem; } void FilteringStrategyTest::testNoFilterstrategy() { QFETCH(QString, line); QFETCH(FilteredItem::FilteredOutputItemType, expected); NoFilterStrategy testee; FilteredItem item1 = testee.errorInLine(line); QVERIFY(item1.type == expected); item1 = testee.actionInLine(line); QVERIFY(item1.type == expected); } void FilteringStrategyTest::testCompilerFilterstrategy_data() { QTest::addColumn("line"); QTest::addColumn("expectedError"); QTest::addColumn("expectedAction"); QTest::newRow("cppcheck-info-line") << buildCppCheckInformationLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("cppcheck-error-line") << buildCppCheckErrorLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("compiler-line") << buildCompilerLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("compiler-error-line") << buildCompilerErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("compiler-information-line") << buildCompilerInformationLine() << FilteredItem::InformationItem << FilteredItem::InvalidItem; QTest::newRow("linker-action-line") << "linking testCustombuild (g++)" << FilteredItem::InvalidItem << FilteredItem::ActionItem; QTest::newRow("linker-error-line") << buildLinkerErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("python-error-line") << buildPythonErrorLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; } void FilteringStrategyTest::testCompilerFilterstrategy() { QFETCH(QString, line); QFETCH(FilteredItem::FilteredOutputItemType, expectedError); QFETCH(FilteredItem::FilteredOutputItemType, expectedAction); KUrl projecturl( PROJECTS_SOURCE_DIR"/onefileproject/" ); CompilerFilterStrategy testee(projecturl); FilteredItem item1 = testee.errorInLine(line); QVERIFY(item1.type == expectedError); item1 = testee.actionInLine(line); QVERIFY(item1.type == expectedAction); } +void FilteringStrategyTest::testCompilerFilterstrategyMultipleKeywords_data() +{ + QTest::addColumn("line"); + QTest::addColumn("expectedError"); + QTest::addColumn("expectedAction"); + + QTest::newRow("warning-containing-error-word") + << "RingBuffer.cpp:64:6: warning: unused parameter ‘errorItem’ [-Wunused-parameter]" + << FilteredItem::WarningItem << FilteredItem::InvalidItem; + QTest::newRow("error-containing-info-word") + << "NodeSet.hpp:89:27: error: ‘Info’ was not declared in this scope" + << FilteredItem::ErrorItem << FilteredItem::InvalidItem; + QTest::newRow("warning-in-filename-containing-error-word") + << "ErrorHandling.cpp:100:56: warning: unused parameter ‘item’ [-Wunused-parameter]" + << FilteredItem::WarningItem << FilteredItem::InvalidItem; + QTest::newRow("error-in-filename-containing-warning-word") + << "WarningHandling.cpp:100:56: error: ‘Item’ was not declared in this scope" + << FilteredItem::ErrorItem << FilteredItem::InvalidItem; +} + +void FilteringStrategyTest::testCompilerFilterstrategyMultipleKeywords() +{ + QFETCH(QString, line); + QFETCH(FilteredItem::FilteredOutputItemType, expectedError); + QFETCH(FilteredItem::FilteredOutputItemType, expectedAction); + KUrl projecturl( PROJECTS_SOURCE_DIR"/onefileproject/" ); + CompilerFilterStrategy testee(projecturl); + FilteredItem item1 = testee.errorInLine(line); + QVERIFY(item1.type == expectedError); + item1 = testee.actionInLine(line); + QVERIFY(item1.type == expectedAction); +} + + void FilteringStrategyTest::testScriptErrorFilterstrategy_data() { QTest::addColumn("line"); QTest::addColumn("expectedError"); QTest::addColumn("expectedAction"); QTest::newRow("cppcheck-info-line") << buildCppCheckInformationLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("cppcheck-error-line") << buildCppCheckErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("compiler-line") << buildCompilerLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("compiler-error-line") << buildCompilerErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("compiler-action-line") << "linking testCustombuild (g++)" << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("python-error-line") << buildPythonErrorLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; } void FilteringStrategyTest::testScriptErrorFilterstrategy() { QFETCH(QString, line); QFETCH(FilteredItem::FilteredOutputItemType, expectedError); QFETCH(FilteredItem::FilteredOutputItemType, expectedAction); ScriptErrorFilterStrategy testee; FilteredItem item1 = testee.errorInLine(line); QVERIFY(item1.type == expectedError); item1 = testee.actionInLine(line); QVERIFY(item1.type == expectedAction); } void FilteringStrategyTest::testStaticAnalysisFilterStrategy_data() { QTest::addColumn("line"); QTest::addColumn("expectedError"); QTest::addColumn("expectedAction"); QTest::newRow("cppcheck-info-line") << buildCppCheckInformationLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("cppcheck-error-line") << buildCppCheckErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("krazy2-error-line") << buildKrazyErrorLine() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("krazy2-error-line-two-colons") << buildKrazyErrorLine2() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("krazy2-error-line-error-description") << buildKrazyErrorLine3() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("krazy2-error-line-wo-line-info") << buildKrazyErrorLineNoLineInfo() << FilteredItem::ErrorItem << FilteredItem::InvalidItem; QTest::newRow("compiler-line") << buildCompilerLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("compiler-error-line") << buildCompilerErrorLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("compiler-action-line") << "linking testCustombuild (g++)" << FilteredItem::InvalidItem << FilteredItem::InvalidItem; QTest::newRow("python-error-line") << buildPythonErrorLine() << FilteredItem::InvalidItem << FilteredItem::InvalidItem; } void FilteringStrategyTest::testStaticAnalysisFilterStrategy() { // Test that url's are extracted correctly as well QString referencePath( PROJECTS_SOURCE_DIR"/onefileproject/main.cpp" ); QFETCH(QString, line); QFETCH(FilteredItem::FilteredOutputItemType, expectedError); QFETCH(FilteredItem::FilteredOutputItemType, expectedAction); StaticAnalysisFilterStrategy testee; FilteredItem item1 = testee.errorInLine(line); QString extractedPath = item1.url.toLocalFile(); QVERIFY((item1.type != FilteredItem::ErrorItem) || ( extractedPath == referencePath)); QVERIFY(item1.type == expectedError); item1 = testee.actionInLine(line); QVERIFY(item1.type == expectedAction); } void FilteringStrategyTest::testCompilerFilterstrategyUrlFromAction_data() { QTest::addColumn("line"); QTest::addColumn("expectedLastDir"); QString basepath( PROJECTS_SOURCE_DIR"/onefileproject/" ); QTest::newRow("cmake-line1") << "[ 25%] Building CXX object /path/to/one/CMakeFiles/file.o" << QString( basepath + "path/to/one/" ); QTest::newRow("cmake-line2") << "[ 26%] Building CXX object /path/to/two/CMakeFiles/file.o" << QString( basepath + "path/to/two/"); QTest::newRow("cmake-line3") << "[ 26%] Building CXX object /path/to/three/CMakeFiles/file.o" << QString( basepath + "path/to/three/"); QTest::newRow("cmake-line4") << "[ 26%] Building CXX object /path/to/four/CMakeFiles/file.o" << QString( basepath + "path/to/four/"); QTest::newRow("cmake-line5") << "[ 26%] Building CXX object /path/to/two/CMakeFiles/file.o" << QString( basepath + "path/to/two/"); QTest::newRow("cd-line6") << QString("make[4]: Entering directory '" + basepath + "path/to/one/'") << QString( basepath + "path/to/one/"); QTest::newRow("cmake-line7") << QString("[ 50%] Building CXX object CMakeFiles/testdeque.dir/RingBuffer.cpp.o") << QString( basepath); } void FilteringStrategyTest::testCompilerFilterstrategyUrlFromAction() { QFETCH(QString, line); QFETCH(QString, expectedLastDir); KUrl projecturl( PROJECTS_SOURCE_DIR"/onefileproject/" ); static CompilerFilterStrategy testee(projecturl); FilteredItem item1 = testee.actionInLine(line); QCOMPARE(testee.getCurrentDirs().last(), expectedLastDir); } void FilteringStrategyTest::benchMarkCompilerFilterAction() { QString projecturl( PROJECTS_SOURCE_DIR"/onefileproject/" ); QStringList outputlines; const int numLines(10000); int j(0), k(0), l(0), m(0); do { ++j; ++k; ++l; QString tmp; if(m % 2 == 0) { tmp = QString( "[ 26%] Building CXX object /this/is/the/path/to/the/files/%1/%2/%3/CMakeFiles/file.o").arg( j ).arg( k ).arg( l ); } else { tmp = QString( "make[4]: Entering directory '" + projecturl + "/this/is/the/path/to/the/files/%1/%2/%3/").arg( j ).arg( k ).arg( l ); } outputlines << tmp; if(j % 6 == 0) { j = 0; ++m; } if(k % 9 == 0) { k = 0; ++m; } if(l % 13 == 0) { l = 0; ++m; } } while(outputlines.size() < numLines ); // gives us numLines (-ish) QElapsedTimer totalTime; totalTime.start(); static CompilerFilterStrategy testee(projecturl); FilteredItem item1("dummyline", FilteredItem::InvalidItem); QBENCHMARK { for(int i = 0; i < outputlines.size(); ++i) { item1 = testee.actionInLine(outputlines.at(i)); } } const qint64 elapsed = totalTime.elapsed(); qDebug() << "ms elapsed to add directories: " << elapsed; qDebug() << "total number of directories: " << outputlines.count(); const double avgDirectoryInsertion = double(elapsed) / outputlines.count(); qDebug() << "average ms spend pr. dir: " << avgDirectoryInsertion; QVERIFY(avgDirectoryInsertion < 2); } void FilteringStrategyTest::testExtractionOfLineAndCulmn_data() { QTest::addColumn("line"); QTest::addColumn("file"); QTest::addColumn("lineNr"); QTest::addColumn("column"); QTest::addColumn("itemtype"); QTest::newRow("gcc-with-col") << "/path/to/file.cpp:123:45: fatal error: ..." << "/path/to/file.cpp" << 122 << 44 << FilteredItem::ErrorItem; QTest::newRow("gcc-no-col") << "/path/to/file.cpp:123: error ..." << "/path/to/file.cpp" << 122 << 0 << FilteredItem::ErrorItem; QTest::newRow("fortcom") << "fortcom: Error: Ogive8.f90, line 123: ..." << "./Ogive8.f90" << 122 << 0 << FilteredItem::ErrorItem; QTest::newRow("fortcomError") << "fortcom: Error: ./Ogive8.f90, line 123: ..." << "././Ogive8.f90" << 122 << 0 << FilteredItem::ErrorItem; QTest::newRow("fortcomWarning") << "fortcom: Warning: /path/Ogive8.f90, line 123: ..." << "/path/Ogive8.f90" << 122 << 0 << FilteredItem::WarningItem; QTest::newRow("fortcomInfo") << "fortcom: Info: Ogive8.f90, line 123: ..." << "./Ogive8.f90" << 122 << 0 << FilteredItem::InformationItem; QTest::newRow("libtool") << "libtool: link: warning: ..." << "" << -1 << 0 << FilteredItem::WarningItem; QTest::newRow("gfortranError1") << "/path/to/file.f90:123.456:Error: ...." << "/path/to/file.f90" << 122 << 455 << FilteredItem::ErrorItem; QTest::newRow("gfortranError2") << "/path/flib.f90:3567.22:" << "/path/flib.f90" << 3566 << 21 << FilteredItem::ErrorItem; } void FilteringStrategyTest::testExtractionOfLineAndCulmn() { QFETCH(QString, line); QFETCH(QString, file); QFETCH(int, lineNr); QFETCH(int, column); QFETCH(FilteredItem::FilteredOutputItemType, itemtype); KUrl projecturl( "./" ); CompilerFilterStrategy testee(projecturl); FilteredItem item1 = testee.errorInLine(line); QCOMPARE(item1.type , itemtype); QCOMPARE(item1.url.path(), file); QCOMPARE(item1.lineNo , lineNr); QCOMPARE(item1.columnNo , column); } } diff --git a/outputview/tests/filteringstrategytest.h b/outputview/tests/filteringstrategytest.h index 0d9b9433df..9f04503ac0 100644 --- a/outputview/tests/filteringstrategytest.h +++ b/outputview/tests/filteringstrategytest.h @@ -1,48 +1,50 @@ /* This file is part of KDevelop Copyright (C) 2012 Morten Danielsen Volden mvolden2@gmail.com 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 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 . */ #ifndef FILTERINGSTRATEGYTEST_H #define FILTERINGSTRATEGYTEST_H #include namespace KDevelop { class FilteringStrategyTest : public QObject { Q_OBJECT private slots: void testNoFilterstrategy_data(); void testNoFilterstrategy(); void testCompilerFilterstrategy_data(); void testCompilerFilterstrategy(); + void testCompilerFilterstrategyMultipleKeywords_data(); + void testCompilerFilterstrategyMultipleKeywords(); void testCompilerFilterstrategyUrlFromAction_data(); void testCompilerFilterstrategyUrlFromAction(); void testScriptErrorFilterstrategy_data(); void testScriptErrorFilterstrategy(); void testStaticAnalysisFilterStrategy_data(); void testStaticAnalysisFilterStrategy(); void benchMarkCompilerFilterAction(); void testExtractionOfLineAndCulmn_data(); void testExtractionOfLineAndCulmn(); }; } #endif // FILTERINGSTRATEGYTEST_H