File indexing completed on 2024-05-05 04:35:27

0001 /*
0002  * This file is part of KDevelop Krazy2 Plugin.
0003  *
0004  * Copyright 2012 Daniel Calviño Sánchez <danxuliu@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License
0008  * as published by the Free Software Foundation; either version 2
0009  * of the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program. If not, see <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include <QtTest>
0021 
0022 #include <KConfigGroup>
0023 #include <KSharedConfig>
0024 #include <KLocalizedString>
0025 
0026 #include <tests/autotestshell.h>
0027 #include <tests/testcore.h>
0028 
0029 #include "../analysisjob.h"
0030 #include "../analysisparameters.h"
0031 #include "../analysisprogressparser.h"
0032 #include "../analysisresults.h"
0033 #include "../checker.h"
0034 #include "../checkerlistjob.h"
0035 #include "../issue.h"
0036 
0037 //Needed for qRegisterMetaType
0038 Q_DECLARE_METATYPE(KJob*)
0039 
0040 /**
0041  * Modified version of private KSignalSpy found in
0042  * kdelibs/kdecore/util/qtest_kde.cpp.
0043  * Original KDESignalSpy, accessed through
0044  * QTest::kWaitForSignal(QObject*, const char*, int), can miss a signal if it is
0045  * emitted too quickly (that is, before the connect is reached). This modified
0046  * version, instead of starting the wait in the constructor, has a specific
0047  * method for it. So the object can be created before executing the call that
0048  * emits the signal, enabling it to register the signal before starting to wait
0049  * and thus ensuring that no signal will be missed.
0050  */
0051 class SignalSpy: public QObject {
0052 Q_OBJECT
0053 public:
0054     SignalSpy(QObject* object, const char* signal): QObject(nullptr),
0055         m_signalSpy(object, signal) {
0056         connect(object, signal, this, SLOT(signalEmitted()));
0057     }
0058 
0059     bool waitForSignal(int timeout = 0) {
0060         if (m_signalSpy.count() == 0) {
0061             if (timeout > 0) {
0062                 QObject::connect(&m_timer, SIGNAL(timeout()), &m_loop, SLOT(quit()));
0063                 m_timer.setSingleShot(true);
0064                 m_timer.start(timeout);
0065             }
0066             m_loop.exec(); //krazy:exclude=crashy
0067         }
0068 
0069         if (m_signalSpy.count() >= 0) {
0070             return true;
0071         }
0072         return false;
0073     }
0074 private Q_SLOTS:
0075     void signalEmitted() {
0076         m_timer.stop();
0077         m_loop.quit();
0078     }
0079 private:
0080     QSignalSpy m_signalSpy;
0081     QEventLoop m_loop;
0082     QTimer m_timer;
0083 };
0084 
0085 class AnalysisJobTest: public QObject {
0086 Q_OBJECT
0087 private slots:
0088 
0089     void initTestCase();
0090     void init();
0091     void cleanupTestCase();
0092 
0093     void testConstructor();
0094 
0095     void testRunCheckers();
0096     void testRunExtraCheckers();
0097     void testRunExtraCheckersAndSubsetOfCheckers();
0098     void testRunCheckerWithDuplicatedNamesAndSpecificFileTypes();
0099     void testRunCheckersWithNoFiles();
0100     void testRunSeveralAnalysisParameters();
0101     void testRunSeveralAnalysisParametersSomeOfThemWithoutFiles();
0102     void testRunWithInvalidKrazy2ExecutablePath();
0103 
0104     void testKill();
0105 
0106 private:
0107 
0108     QString m_examplesPath;
0109 
0110     bool examplesInSubdirectory() const;
0111     bool krazy2InPath() const;
0112 
0113     QList<const Checker*> getAvailableCheckers() const;
0114 
0115     const Issue* findIssue(const AnalysisResults* analysisResults,
0116                            const QString& checkerName,
0117                            const QString& exampleFileName, int line) const;
0118 
0119 };
0120 
0121 void AnalysisJobTest::initTestCase() {
0122     m_examplesPath = QStringLiteral(EXAMPLETESTDATA_PATH);
0123 
0124     //Needed for SignalSpy
0125     qRegisterMetaType<KJob*>();
0126     qRegisterMetaType<KDevelop::IStatus*>();
0127 
0128     KDevelop::AutoTestShell::init({"kdevkrazy2"});
0129     KDevelop::TestCore::initialize(KDevelop::Core::NoUi);
0130 }
0131 
0132 void AnalysisJobTest::init() {
0133     if (!examplesInSubdirectory()) {
0134         QString message = "The examples were not found in the 'examples' directory (" + m_examplesPath + ')';
0135         QSKIP(message.toLatin1(), SkipAll);
0136     }
0137 
0138     if (!krazy2InPath()) {
0139         QSKIP("krazy2 is not in the execution path", SkipAll);
0140     }
0141 
0142     KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2");
0143     krazy2Configuration.writeEntry("krazy2 Path", "krazy2");
0144 }
0145 
0146 void AnalysisJobTest::cleanupTestCase() {
0147     KDevelop::TestCore::shutdown();
0148 }
0149 
0150 void AnalysisJobTest::testConstructor() {
0151     QObject parent;
0152     auto  analysisJob = new AnalysisJob(&parent);
0153 
0154     QCOMPARE(analysisJob->parent(), &parent);
0155     QCOMPARE(analysisJob->capabilities(), KJob::Killable);
0156     QCOMPARE(analysisJob->objectName(),
0157              xi18nc("@action:inmenu", "<command>krazy2</command> analysis"));
0158 }
0159 
0160 void AnalysisJobTest::testRunCheckers() {
0161     AnalysisJob analysisJob;
0162     analysisJob.setAutoDelete(false);
0163 
0164     QList<const Checker*> availableCheckers;
0165 
0166     auto  doubleQuoteCharsChecker = new Checker();
0167     doubleQuoteCharsChecker->setFileType("c++");
0168     doubleQuoteCharsChecker->setName("doublequote_chars");
0169     doubleQuoteCharsChecker->setDescription("Check single-char QString operations for efficiency");
0170     doubleQuoteCharsChecker->setExplanation("Adding single characters to a QString "
0171                                             "is faster if the characters are QChars...");
0172     availableCheckers.append(doubleQuoteCharsChecker);
0173 
0174     auto  licenseChecker = new Checker();
0175     licenseChecker->setFileType("c++");
0176     licenseChecker->setName("license");
0177     licenseChecker->setDescription("Check for an acceptable license");
0178     licenseChecker->setExplanation("Each source file must contain a license "
0179                                    "or a reference to a license which states...");
0180     availableCheckers.append(licenseChecker);
0181 
0182     auto  spellingChecker = new Checker();
0183     spellingChecker->setFileType("c++");
0184     spellingChecker->setName("spelling");
0185     spellingChecker->setDescription("Check for spelling errors");
0186     spellingChecker->setExplanation("Spelling errors in comments and strings "
0187                                     "should be fixed as they may show up later...");
0188     availableCheckers.append(spellingChecker);
0189 
0190     auto  validateChecker = new Checker();
0191     validateChecker->setFileType("desktop");
0192     validateChecker->setName("validate");
0193     validateChecker->setDescription("Validates desktop files using 'desktop-file-validate'");
0194     validateChecker->setExplanation("Please make sure your .desktop files conform "
0195                                     "to the freedesktop.org standard. See the spec...");
0196     availableCheckers.append(validateChecker);
0197 
0198     auto  qmlLicenseChecker = new Checker();
0199     qmlLicenseChecker->setFileType("qml");
0200     qmlLicenseChecker->setName("license");
0201     qmlLicenseChecker->setDescription("Check for an acceptable license");
0202     qmlLicenseChecker->setExplanation("Each source file must contain a license "
0203                                       "or a reference to a license which states...");
0204     availableCheckers.append(qmlLicenseChecker);
0205 
0206     //Do not set spelling checker
0207     QList<const Checker*> checkersToRun;
0208     checkersToRun.append(doubleQuoteCharsChecker);
0209     checkersToRun.append(licenseChecker);
0210     checkersToRun.append(validateChecker);
0211     checkersToRun.append(qmlLicenseChecker);
0212 
0213     AnalysisParameters analysisParameters;
0214     analysisParameters.initializeCheckers(availableCheckers);
0215     analysisParameters.setCheckersToRun(checkersToRun);
0216     analysisParameters.setFilesAndDirectories(QStringList() << m_examplesPath);
0217     analysisJob.addAnalysisParameters(&analysisParameters);
0218 
0219     AnalysisResults analysisResults;
0220     analysisJob.setAnalysisResults(&analysisResults);
0221 
0222     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0223                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0224 
0225     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0226 
0227     analysisJob.start();
0228 
0229     resultSpy.waitForSignal();
0230 
0231     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0232     QCOMPARE(analysisResults.issues().count(), 10);
0233 
0234     //To prevent test failures due to the order of the issues, each issue is
0235     //searched in the results instead of using a specific index
0236     const Issue* issue = findIssue(&analysisResults, "doublequote_chars",
0237                                    "singleIssue.cpp", 8);
0238     QVERIFY(issue);
0239     QCOMPARE(issue->message(), QString(""));
0240     QCOMPARE(issue->checker()->description(),
0241              QString("Check single-char QString operations for efficiency"));
0242     QVERIFY(issue->checker()->explanation().startsWith(
0243                 "Adding single characters to a QString is faster"));
0244     QCOMPARE(issue->checker()->fileType(), QString("c++"));
0245     QVERIFY(!issue->checker()->isExtra());
0246 
0247     const Issue* issue2 = findIssue(&analysisResults, "doublequote_chars",
0248                                     "severalIssuesSingleChecker.cpp", 8);
0249     QVERIFY(issue2);
0250     QCOMPARE(issue2->message(), QString(""));
0251     QCOMPARE(issue2->checker(), issue->checker());
0252 
0253     const Issue* issue3 = findIssue(&analysisResults, "doublequote_chars",
0254                                     "severalIssuesSingleChecker.cpp", 10);
0255     QVERIFY(issue3);
0256     QCOMPARE(issue3->message(), QString(""));
0257     QCOMPARE(issue3->checker(), issue->checker());
0258 
0259     const Issue* issue4 = findIssue(&analysisResults, "doublequote_chars",
0260                                     "severalIssuesSeveralCheckers.cpp", 8);
0261     QVERIFY(issue4);
0262     QCOMPARE(issue4->message(), QString(""));
0263     QCOMPARE(issue4->checker(), issue->checker());
0264 
0265     const Issue* issue5 = findIssue(&analysisResults, "doublequote_chars",
0266                                     "severalIssuesSeveralCheckers.cpp", 12);
0267     QVERIFY(issue5);
0268     QCOMPARE(issue5->message(), QString(""));
0269     QCOMPARE(issue5->checker(), issue->checker());
0270 
0271     const Issue* issue6 = findIssue(&analysisResults, "license",
0272                                     "severalIssuesSeveralCheckers.cpp", -1);
0273     QVERIFY(issue6);
0274     QCOMPARE(issue6->message(), QString("missing license"));
0275     QCOMPARE(issue6->checker()->description(),
0276              QString("Check for an acceptable license"));
0277     QVERIFY(issue6->checker()->explanation().startsWith(
0278                 "Each source file must contain a license"));
0279     QCOMPARE(issue6->checker()->fileType(), QString("c++"));
0280     QVERIFY(!issue6->checker()->isExtra());
0281 
0282     const Issue* issue7 = findIssue(&analysisResults, "validate",
0283                                      "subdirectory/singleIssue.desktop", -1);
0284     QVERIFY(issue7);
0285     QCOMPARE(issue7->message(), QString("required key \"Type\" in group \"Desktop Entry\" is not present"));
0286     QCOMPARE(issue7->checker()->description(),
0287              QString("Validates desktop files using 'desktop-file-validate'"));
0288     QVERIFY(issue7->checker()->explanation().startsWith(
0289                 "Please make sure your .desktop files conform to the freedesktop.org"));
0290     QCOMPARE(issue7->checker()->fileType(), QString("desktop"));
0291     QVERIFY(!issue7->checker()->isExtra());
0292 
0293     const Issue* issue8 = findIssue(&analysisResults, "doublequote_chars",
0294                                      QString::fromUtf8("singleIssueNonAsciiFileNameḶḷambión.cpp"), 8);
0295     QVERIFY(issue8);
0296     QCOMPARE(issue8->message(), QString(""));
0297     QCOMPARE(issue8->checker(), issue->checker());
0298 
0299     const Issue* issue9 = findIssue(&analysisResults, "doublequote_chars",
0300                                      ".singleIssueHiddenUnixFileName.cpp", 8);
0301     QVERIFY(issue9);
0302     QCOMPARE(issue9->message(), QString(""));
0303     QCOMPARE(issue9->checker(), issue->checker());
0304 
0305     const Issue* issue10 = findIssue(&analysisResults, "license",
0306                                     "subdirectory/severalIssuesSeveralCheckers.qml", -1);
0307     QVERIFY(issue10);
0308     QCOMPARE(issue10->message(), QString("missing license"));
0309     QCOMPARE(issue10->checker()->description(),
0310              QString("Check for an acceptable license"));
0311     QVERIFY(issue10->checker()->explanation().startsWith(
0312                 "Each source file must contain a license"));
0313     QCOMPARE(issue10->checker()->fileType(), QString("qml"));
0314     QVERIFY(!issue10->checker()->isExtra());
0315 
0316     //Six signals should have been emitted: one for the start, one for the
0317     //finish, and one for each checker run.
0318     QCOMPARE(showProgressSpy.count(), 6);
0319 
0320     //First signal is the 0%
0321     //First parameter is the AnalysisProgressParser itself
0322     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
0323     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
0324     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
0325 
0326     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 25);
0327     QCOMPARE(showProgressSpy.at(2).at(3).toInt(), 50);
0328     QCOMPARE(showProgressSpy.at(3).at(3).toInt(), 75);
0329     QCOMPARE(showProgressSpy.at(4).at(3).toInt(), 99);
0330     QCOMPARE(showProgressSpy.at(5).at(3).toInt(), 100);
0331 }
0332 
0333 void AnalysisJobTest::testRunExtraCheckers() {
0334     AnalysisJob analysisJob;
0335     analysisJob.setAutoDelete(false);
0336 
0337     QList<const Checker*> availableCheckers = getAvailableCheckers();
0338 
0339     AnalysisParameters analysisParameters;
0340     analysisParameters.initializeCheckers(availableCheckers);
0341 
0342     QList<const Checker*> checkersToRun = analysisParameters.checkersToRun();
0343     foreach (const Checker* checker, availableCheckers) {
0344         if (checker->fileType() == "c++" && checker->name() == "style" && checker->isExtra()) {
0345             checkersToRun.append(checker);
0346         }
0347     }
0348 
0349     analysisParameters.setCheckersToRun(checkersToRun);
0350     analysisParameters.setFilesAndDirectories(QStringList() << m_examplesPath);
0351     analysisJob.addAnalysisParameters(&analysisParameters);
0352 
0353     AnalysisResults analysisResults;
0354     analysisJob.setAnalysisResults(&analysisResults);
0355 
0356     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0357                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0358 
0359     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0360 
0361     analysisJob.start();
0362 
0363     resultSpy.waitForSignal();
0364 
0365     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0366     QCOMPARE(analysisResults.issues().count(), 15);
0367 
0368     //To prevent test failures due to the order of the issues, each issue is
0369     //searched in the results instead of using a specific index
0370     const Issue* issue = findIssue(&analysisResults, "doublequote_chars",
0371                                    "singleIssue.cpp", 8);
0372     QVERIFY(issue);
0373     QCOMPARE(issue->message(), QString(""));
0374     QCOMPARE(issue->checker()->description(),
0375              QString("Check single-char QString operations for efficiency"));
0376     QVERIFY(issue->checker()->explanation().startsWith(
0377                 "Adding single characters to a QString is faster"));
0378     QCOMPARE(issue->checker()->fileType(), QString("c++"));
0379     QVERIFY(!issue->checker()->isExtra());
0380 
0381     const Issue* issue2 = findIssue(&analysisResults, "doublequote_chars",
0382                                     "severalIssuesSingleChecker.cpp", 8);
0383     QVERIFY(issue2);
0384     QCOMPARE(issue2->message(), QString(""));
0385     QCOMPARE(issue2->checker(), issue->checker());
0386 
0387     const Issue* issue3 = findIssue(&analysisResults, "doublequote_chars",
0388                                     "severalIssuesSingleChecker.cpp", 10);
0389     QVERIFY(issue3);
0390     QCOMPARE(issue3->message(), QString(""));
0391     QCOMPARE(issue3->checker(), issue->checker());
0392 
0393     const Issue* issue4 = findIssue(&analysisResults, "doublequote_chars",
0394                                     "severalIssuesSeveralCheckers.cpp", 8);
0395     QVERIFY(issue4);
0396     QCOMPARE(issue4->message(), QString(""));
0397     QCOMPARE(issue4->checker(), issue->checker());
0398 
0399     const Issue* issue5 = findIssue(&analysisResults, "doublequote_chars",
0400                                     "severalIssuesSeveralCheckers.cpp", 12);
0401     QVERIFY(issue5);
0402     QCOMPARE(issue5->message(), QString(""));
0403     QCOMPARE(issue5->checker(), issue->checker());
0404 
0405     const Issue* issue6 = findIssue(&analysisResults, "license",
0406                                     "severalIssuesSeveralCheckers.cpp", -1);
0407     QVERIFY(issue6);
0408     QCOMPARE(issue6->message(), QString("missing license"));
0409     QCOMPARE(issue6->checker()->description(),
0410              QString("Check for an acceptable license"));
0411     QVERIFY(issue6->checker()->explanation().startsWith(
0412                 "Each source file must contain a license"));
0413     QCOMPARE(issue6->checker()->fileType(), QString("c++"));
0414     QVERIFY(!issue6->checker()->isExtra());
0415 
0416     const Issue* issue7 = findIssue(&analysisResults, "spelling",
0417                                     "severalIssuesSeveralCheckers.cpp", 6);
0418     QVERIFY(issue7);
0419     QCOMPARE(issue7->message(), QString("begining")); //krazy:exclude=spelling
0420     QCOMPARE(issue7->checker()->description(),
0421              QString("Check for spelling errors"));
0422     QVERIFY(issue7->checker()->explanation().startsWith(
0423                 "Spelling errors in comments and strings should be fixed"));
0424     QCOMPARE(issue7->checker()->fileType(), QString("c++"));
0425     QVERIFY(!issue7->checker()->isExtra());
0426 
0427     const Issue* issue8 = findIssue(&analysisResults, "spelling",
0428                                     "severalIssuesSeveralCheckers.cpp", 10);
0429     QVERIFY(issue8);
0430     QCOMPARE(issue8->message(), QString("commiting")); //krazy:exclude=spelling
0431     QCOMPARE(issue8->checker(), issue7->checker());
0432 
0433     const Issue* issue9 = findIssue(&analysisResults, "spelling",
0434                                     "severalIssuesSeveralCheckers.cpp", 14);
0435     QVERIFY(issue9);
0436     QCOMPARE(issue9->message(), QString("labelling")); //krazy:exclude=spelling
0437     QCOMPARE(issue9->checker(), issue7->checker());
0438 
0439     const Issue* issue10 = findIssue(&analysisResults, "validate",
0440                                      "subdirectory/singleIssue.desktop", -1);
0441     QVERIFY(issue10);
0442     QCOMPARE(issue10->message(), QString("required key \"Type\" in group \"Desktop Entry\" is not present"));
0443     QCOMPARE(issue10->checker()->description(),
0444              QString("Validates desktop files using 'desktop-file-validate'"));
0445     QVERIFY(issue10->checker()->explanation().startsWith(
0446                 "Please make sure your .desktop files conform to the freedesktop.org"));
0447     QCOMPARE(issue10->checker()->fileType(), QString("desktop"));
0448     QVERIFY(!issue10->checker()->isExtra());
0449 
0450     const Issue* issue11 = findIssue(&analysisResults, "doublequote_chars",
0451                                      QString::fromUtf8("singleIssueNonAsciiFileNameḶḷambión.cpp"), 8);
0452     QVERIFY(issue11);
0453     QCOMPARE(issue11->message(), QString(""));
0454     QCOMPARE(issue11->checker(), issue->checker());
0455 
0456     const Issue* issue12 = findIssue(&analysisResults, "doublequote_chars",
0457                                      ".singleIssueHiddenUnixFileName.cpp", 8);
0458     QVERIFY(issue12);
0459     QCOMPARE(issue12->message(), QString(""));
0460     QCOMPARE(issue12->checker(), issue->checker());
0461 
0462     const Issue* issue13 = findIssue(&analysisResults, "style",
0463                                      "singleExtraIssue.cpp", 7);
0464     QVERIFY(issue13);
0465     QCOMPARE(issue13->message(), QString("Put 1 space before an asterisk or ampersand"));
0466     QCOMPARE(issue13->checker()->description(),
0467              QString("Check for adherence to a coding style"));
0468     QVERIFY(issue13->checker()->explanation().startsWith(
0469                 "Please follow the coding style guidelines"));
0470     QCOMPARE(issue13->checker()->fileType(), QString("c++"));
0471     QVERIFY(issue13->checker()->isExtra());
0472 
0473     const Issue* issue14 = findIssue(&analysisResults, "license",
0474                                     "subdirectory/severalIssuesSeveralCheckers.qml", -1);
0475     QVERIFY(issue14);
0476     QCOMPARE(issue14->message(), QString("missing license"));
0477     QCOMPARE(issue14->checker()->description(),
0478              QString("Check for an acceptable license"));
0479     QVERIFY(issue14->checker()->explanation().startsWith(
0480                 "Each source file must contain a license"));
0481     QCOMPARE(issue14->checker()->fileType(), QString("qml"));
0482     QVERIFY(!issue14->checker()->isExtra());
0483 
0484     const Issue* issue15 = findIssue(&analysisResults, "spelling",
0485                                     "subdirectory/severalIssuesSeveralCheckers.qml", 3);
0486     QVERIFY(issue15);
0487     QCOMPARE(issue15->message(), QString("occured")); //krazy:exclude=spelling
0488     QCOMPARE(issue15->checker()->description(),
0489              QString("Check for spelling errors"));
0490     QVERIFY(issue15->checker()->explanation().startsWith(
0491                 "Spelling errors in comments and strings should be fixed"));
0492     QCOMPARE(issue15->checker()->fileType(), QString("qml"));
0493     QVERIFY(!issue15->checker()->isExtra());
0494 
0495     //At least nine signals should have been emitted: one for the start, one for
0496     //the finish, and one for each checker with issues.
0497     QVERIFY(showProgressSpy.count() >= 9);
0498 
0499     //First signal is the 0%
0500     //First parameter is the AnalysisProgressParser itself
0501     QCOMPARE(showProgressSpy.first().at(1).toInt(), 0);
0502     QCOMPARE(showProgressSpy.first().at(2).toInt(), 100);
0503     QCOMPARE(showProgressSpy.first().at(3).toInt(), 0);
0504 
0505     //Last signal is the 100%
0506     QCOMPARE(showProgressSpy.last().at(1).toInt(), 0);
0507     QCOMPARE(showProgressSpy.last().at(2).toInt(), 100);
0508     QCOMPARE(showProgressSpy.last().at(3).toInt(), 100);
0509 
0510     //The second signal progress should be bigger than the start progress
0511     QVERIFY(showProgressSpy.at(1).at(3).toInt() > 0);
0512 
0513     //The second to last signal shows a 99% progress, as it is emitted once all
0514     //the checkers have run. It is not a 100% progress, though, because parsing
0515     //progress is limited to 99%; 100% is only emitted when krazy2 has finished.
0516     QCOMPARE(showProgressSpy.at(showProgressSpy.count()-2).at(3).toInt(), 99);
0517     QVERIFY(showProgressSpy.at(showProgressSpy.count()-3).at(3).toInt() <= 99);
0518 }
0519 
0520 void AnalysisJobTest::testRunExtraCheckersAndSubsetOfCheckers() {
0521     AnalysisJob analysisJob;
0522     analysisJob.setAutoDelete(false);
0523 
0524     QList<const Checker*> availableCheckers = getAvailableCheckers();
0525 
0526     QList<const Checker*> checkersToRun;
0527     foreach (const Checker* checker, availableCheckers) {
0528         if ((checker->fileType() == "c++" && checker->name() == "license" && !checker->isExtra()) ||
0529             (checker->fileType() == "qml" && checker->name() == "license" && !checker->isExtra()) ||
0530             (checker->fileType() == "c++" && checker->name() == "style" && checker->isExtra())) {
0531             checkersToRun.append(checker);
0532         }
0533     }
0534 
0535     AnalysisParameters analysisParameters;
0536     analysisParameters.initializeCheckers(availableCheckers);
0537     analysisParameters.setCheckersToRun(checkersToRun);
0538     analysisParameters.setFilesAndDirectories(QStringList() << m_examplesPath);
0539     analysisJob.addAnalysisParameters(&analysisParameters);
0540 
0541     AnalysisResults analysisResults;
0542     analysisJob.setAnalysisResults(&analysisResults);
0543 
0544     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0545                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0546 
0547     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0548 
0549     analysisJob.start();
0550 
0551     resultSpy.waitForSignal();
0552 
0553     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0554     QCOMPARE(analysisResults.issues().count(), 3);
0555 
0556     //To prevent test failures due to the order of the issues, each issue is
0557     //searched in the results instead of using a specific index
0558     const Issue* issue1 = findIssue(&analysisResults, "license",
0559                                     "severalIssuesSeveralCheckers.cpp", -1);
0560     QVERIFY(issue1);
0561     QCOMPARE(issue1->message(), QString("missing license"));
0562     QCOMPARE(issue1->checker()->description(),
0563              QString("Check for an acceptable license"));
0564     QVERIFY(issue1->checker()->explanation().startsWith(
0565                 "Each source file must contain a license"));
0566     QCOMPARE(issue1->checker()->fileType(), QString("c++"));
0567     QVERIFY(!issue1->checker()->isExtra());
0568 
0569     const Issue* issue2 = findIssue(&analysisResults, "style",
0570                                     "singleExtraIssue.cpp", 7);
0571     QVERIFY(issue2);
0572     QCOMPARE(issue2->message(), QString("Put 1 space before an asterisk or ampersand"));
0573     QCOMPARE(issue2->checker()->description(),
0574              QString("Check for adherence to a coding style"));
0575     QVERIFY(issue2->checker()->explanation().startsWith(
0576                 "Please follow the coding style guidelines"));
0577     QCOMPARE(issue2->checker()->fileType(), QString("c++"));
0578     QVERIFY(issue2->checker()->isExtra());
0579 
0580     const Issue* issue3 = findIssue(&analysisResults, "license",
0581                                     "subdirectory/severalIssuesSeveralCheckers.qml", -1);
0582     QVERIFY(issue3);
0583     QCOMPARE(issue3->message(), QString("missing license"));
0584     QCOMPARE(issue3->checker()->description(),
0585              QString("Check for an acceptable license"));
0586     QVERIFY(issue3->checker()->explanation().startsWith(
0587                 "Each source file must contain a license"));
0588     QCOMPARE(issue3->checker()->fileType(), QString("qml"));
0589     QVERIFY(!issue3->checker()->isExtra());
0590 
0591     //Five signals should have been emitted: one for the start, one for the
0592     //finish, and one for each checker run.
0593     QCOMPARE(showProgressSpy.count(), 5);
0594 
0595     //First signal is the 0%
0596     //First parameter is the AnalysisProgressParser itself
0597     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
0598     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
0599     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
0600 
0601     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 33);
0602     QCOMPARE(showProgressSpy.at(2).at(3).toInt(), 66);
0603     QCOMPARE(showProgressSpy.at(3).at(3).toInt(), 99);
0604     QCOMPARE(showProgressSpy.at(4).at(3).toInt(), 100);
0605 }
0606 
0607 void AnalysisJobTest::testRunCheckerWithDuplicatedNamesAndSpecificFileTypes() {
0608     AnalysisJob analysisJob;
0609     analysisJob.setAutoDelete(false);
0610 
0611     QList<const Checker*> availableCheckers;
0612 
0613     auto  cppLicenseChecker = new Checker();
0614     cppLicenseChecker->setFileType("c++");
0615     cppLicenseChecker->setName("license");
0616     cppLicenseChecker->setDescription("Check for an acceptable license");
0617     cppLicenseChecker->setExplanation("Each source file must contain a license "
0618                                       "or a reference to a license which states...");
0619     availableCheckers.append(cppLicenseChecker);
0620 
0621     auto  qmlSpellingChecker = new Checker();
0622     qmlSpellingChecker->setFileType("qml");
0623     qmlSpellingChecker->setName("spelling");
0624     qmlSpellingChecker->setDescription("Check for spelling errors");
0625     qmlSpellingChecker->setExplanation("Spelling errors in comments and strings "
0626                                        "should be fixed as they may show up later...");
0627     availableCheckers.append(qmlSpellingChecker);
0628 
0629     QList<const Checker*> checkersToRun;
0630     checkersToRun.append(cppLicenseChecker);
0631     checkersToRun.append(qmlSpellingChecker);
0632 
0633     //Both files have spelling and license issues, although different file types
0634     QStringList filesToAnalyze;
0635     filesToAnalyze << m_examplesPath + "severalIssuesSeveralCheckers.cpp";
0636     filesToAnalyze << m_examplesPath + "subdirectory/severalIssuesSeveralCheckers.qml";
0637 
0638     AnalysisParameters analysisParameters;
0639     analysisParameters.initializeCheckers(availableCheckers);
0640     analysisParameters.setCheckersToRun(checkersToRun);
0641     analysisParameters.setFilesAndDirectories(filesToAnalyze);
0642     analysisJob.addAnalysisParameters(&analysisParameters);
0643 
0644     AnalysisResults analysisResults;
0645     analysisJob.setAnalysisResults(&analysisResults);
0646 
0647     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0648                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0649 
0650     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0651 
0652     analysisJob.start();
0653 
0654     resultSpy.waitForSignal();
0655 
0656     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0657     QCOMPARE(analysisResults.issues().count(), 2);
0658 
0659     //To prevent test failures due to the order of the issues, each issue is
0660     //searched in the results instead of using a specific index
0661     const Issue* issue = findIssue(&analysisResults, "license",
0662                                     "severalIssuesSeveralCheckers.cpp", -1);
0663     QVERIFY(issue);
0664     QCOMPARE(issue->message(), QString("missing license"));
0665     QCOMPARE(issue->checker()->description(),
0666              QString("Check for an acceptable license"));
0667     QVERIFY(issue->checker()->explanation().startsWith(
0668                 "Each source file must contain a license"));
0669     QCOMPARE(issue->checker()->fileType(), QString("c++"));
0670     QVERIFY(!issue->checker()->isExtra());
0671 
0672     const Issue* issue2 = findIssue(&analysisResults, "spelling",
0673                                     "subdirectory/severalIssuesSeveralCheckers.qml", 3);
0674     QVERIFY(issue2);
0675     QCOMPARE(issue2->message(), QString("occured")); //krazy:exclude=spelling
0676     QCOMPARE(issue2->checker()->description(),
0677              QString("Check for spelling errors"));
0678     QVERIFY(issue2->checker()->explanation().startsWith(
0679                 "Spelling errors in comments and strings should be fixed"));
0680     QCOMPARE(issue2->checker()->fileType(), QString("qml"));
0681     QVERIFY(!issue2->checker()->isExtra());
0682 
0683     //Four signals should have been emitted: one for the start, one for the
0684     //finish, and one for each checker run.
0685     QCOMPARE(showProgressSpy.count(), 4);
0686 
0687     //First signal is the 0%
0688     //First parameter is the AnalysisProgressParser itself
0689     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
0690     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
0691     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
0692 
0693     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 50);
0694     QCOMPARE(showProgressSpy.at(2).at(3).toInt(), 99);
0695     QCOMPARE(showProgressSpy.at(3).at(3).toInt(), 100);
0696 }
0697 
0698 void AnalysisJobTest::testRunCheckersWithNoFiles() {
0699     AnalysisJob analysisJob;
0700     analysisJob.setAutoDelete(false);
0701 
0702     QList<const Checker*> availableCheckers;
0703 
0704     auto  doubleQuoteCharsChecker = new Checker();
0705     doubleQuoteCharsChecker->setFileType("c++");
0706     doubleQuoteCharsChecker->setName("doublequote_chars");
0707     doubleQuoteCharsChecker->setDescription("Check single-char QString operations for efficiency");
0708     doubleQuoteCharsChecker->setExplanation("Adding single characters to a QString "
0709                                             "is faster if the characters are QChars...");
0710     availableCheckers.append(doubleQuoteCharsChecker);
0711 
0712     auto  licenseChecker = new Checker();
0713     licenseChecker->setFileType("c++");
0714     licenseChecker->setName("license");
0715     licenseChecker->setDescription("Check for an acceptable license");
0716     licenseChecker->setExplanation("Each source file must contain a license "
0717                                    "or a reference to a license which states...");
0718     availableCheckers.append(licenseChecker);
0719 
0720     auto  spellingChecker = new Checker();
0721     spellingChecker->setFileType("c++");
0722     spellingChecker->setName("spelling");
0723     spellingChecker->setDescription("Check for spelling errors");
0724     spellingChecker->setExplanation("Spelling errors in comments and strings "
0725                                     "should be fixed as they may show up later...");
0726     availableCheckers.append(spellingChecker);
0727 
0728     auto  validateChecker = new Checker();
0729     validateChecker->setFileType("desktop");
0730     validateChecker->setName("validate");
0731     validateChecker->setDescription("Validates desktop files using 'desktop-file-validate'");
0732     validateChecker->setExplanation("Please make sure your .desktop files conform "
0733                                     "to the freedesktop.org standard. See the spec...");
0734     availableCheckers.append(validateChecker);
0735 
0736     auto  qmlLicenseChecker = new Checker();
0737     qmlLicenseChecker->setFileType("qml");
0738     qmlLicenseChecker->setName("license");
0739     qmlLicenseChecker->setDescription("Check for an acceptable license");
0740     qmlLicenseChecker->setExplanation("Each source file must contain a license "
0741                                       "or a reference to a license which states...");
0742     availableCheckers.append(qmlLicenseChecker);
0743 
0744     //Do not set spelling checker
0745     QList<const Checker*> checkersToRun;
0746     checkersToRun.append(doubleQuoteCharsChecker);
0747     checkersToRun.append(licenseChecker);
0748     checkersToRun.append(validateChecker);
0749     checkersToRun.append(qmlLicenseChecker);
0750 
0751     AnalysisParameters analysisParameters;
0752     analysisParameters.initializeCheckers(availableCheckers);
0753     analysisParameters.setCheckersToRun(checkersToRun);
0754     analysisJob.addAnalysisParameters(&analysisParameters);
0755 
0756     AnalysisResults analysisResults;
0757     analysisJob.setAnalysisResults(&analysisResults);
0758 
0759     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0760                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0761 
0762     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0763 
0764     analysisJob.start();
0765 
0766     resultSpy.waitForSignal();
0767 
0768     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0769     QCOMPARE(analysisResults.issues().count(), 0);
0770 
0771     //Two signals should have been emitted: one for the start and one for the
0772     //finish.
0773     QCOMPARE(showProgressSpy.count(), 2);
0774 
0775     //First signal is the 0%
0776     //First parameter is the AnalysisProgressParser itself
0777     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
0778     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
0779     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
0780 
0781     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 100);
0782 }
0783 
0784 void AnalysisJobTest::testRunSeveralAnalysisParameters() {
0785     AnalysisJob analysisJob;
0786     analysisJob.setAutoDelete(false);
0787 
0788     //First analysis parameters should give one issue for each checker (the
0789     //first and second checker are named like other checkers with issues,
0790     //although they should not be run)
0791     QList<const Checker*> availableCheckers = getAvailableCheckers();
0792 
0793     QList<const Checker*> checkersToRun;
0794     foreach (const Checker* checker, availableCheckers) {
0795         if ((checker->fileType() == "c++" && checker->name() == "license" && !checker->isExtra()) ||
0796             (checker->fileType() == "qml" && checker->name() == "spelling" && !checker->isExtra()) ||
0797             (checker->fileType() == "c++" && checker->name() == "style" && checker->isExtra())) {
0798             checkersToRun.append(checker);
0799         }
0800     }
0801 
0802     QStringList fileNames;
0803     fileNames << m_examplesPath;
0804 
0805     AnalysisParameters analysisParameters1;
0806     analysisParameters1.initializeCheckers(availableCheckers);
0807     analysisParameters1.setCheckersToRun(checkersToRun);
0808     analysisParameters1.setFilesAndDirectories(fileNames);
0809     analysisJob.addAnalysisParameters(&analysisParameters1);
0810 
0811     //Second analysis parameters should give one issue for the checker
0812     QMutableListIterator<const Checker*> it(availableCheckers);
0813     while (it.hasNext()) {
0814         it.setValue(new Checker(*it.next()));
0815     }
0816 
0817     checkersToRun.clear();
0818     foreach (const Checker* checker, availableCheckers) {
0819         if ((checker->fileType() == "c++" && checker->name() == "doublequote_chars" && !checker->isExtra())) {
0820             checkersToRun.append(checker);
0821         }
0822     }
0823 
0824     fileNames.clear();
0825     fileNames << m_examplesPath + "singleIssue.cpp";
0826 
0827     AnalysisParameters analysisParameters2;
0828     analysisParameters2.initializeCheckers(availableCheckers);
0829     analysisParameters2.setCheckersToRun(checkersToRun);
0830     analysisParameters2.setFilesAndDirectories(fileNames);
0831     analysisJob.addAnalysisParameters(&analysisParameters2);
0832 
0833     //Third analysis parameters should give three issues for the first checker
0834     //(although one is repeated), no issues for the second checker, and the
0835     //third one should not be run, as it is not compatible with any analyzed
0836     //file
0837     it = QMutableListIterator<const Checker*>(availableCheckers);
0838     while (it.hasNext()) {
0839         it.setValue(new Checker(*it.next()));
0840     }
0841 
0842     checkersToRun.clear();
0843     foreach (const Checker* checker, availableCheckers) {
0844         if ((checker->fileType() == "c++" && checker->name() == "doublequote_chars" && !checker->isExtra()) ||
0845             (checker->fileType() == "c++" && checker->name() == "license" && !checker->isExtra()) ||
0846             (checker->fileType() == "desktop" && checker->name() == "validate" && !checker->isExtra())) {
0847             checkersToRun.append(checker);
0848         }
0849     }
0850 
0851     fileNames.clear();
0852     fileNames << m_examplesPath + "singleIssue.cpp";
0853     fileNames << m_examplesPath + "severalIssuesSingleChecker.cpp";
0854     fileNames << m_examplesPath + "subdirectory/severalIssuesSeveralCheckers.qml";
0855 
0856     AnalysisParameters analysisParameters3;
0857     analysisParameters3.initializeCheckers(availableCheckers);
0858     analysisParameters3.setCheckersToRun(checkersToRun);
0859     analysisParameters3.setFilesAndDirectories(fileNames);
0860     analysisJob.addAnalysisParameters(&analysisParameters3);
0861 
0862     AnalysisResults analysisResults;
0863     analysisJob.setAnalysisResults(&analysisResults);
0864 
0865     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
0866                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
0867 
0868     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
0869 
0870     analysisJob.start();
0871 
0872     resultSpy.waitForSignal();
0873 
0874     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
0875     QCOMPARE(analysisResults.issues().count(), 6);
0876 
0877     //To prevent test failures due to the order of the issues, each issue is
0878     //searched in the results instead of using a specific index
0879     const Issue* issue1 = findIssue(&analysisResults, "license",
0880                                     "severalIssuesSeveralCheckers.cpp", -1);
0881     QVERIFY(issue1);
0882     QCOMPARE(issue1->message(), QString("missing license"));
0883     QCOMPARE(issue1->checker()->description(),
0884              QString("Check for an acceptable license"));
0885     QVERIFY(issue1->checker()->explanation().startsWith(
0886                 "Each source file must contain a license"));
0887     QCOMPARE(issue1->checker()->fileType(), QString("c++"));
0888     QVERIFY(!issue1->checker()->isExtra());
0889 
0890     const Issue* issue2 = findIssue(&analysisResults, "spelling",
0891                                     "subdirectory/severalIssuesSeveralCheckers.qml", 3);
0892     QVERIFY(issue2);
0893     QCOMPARE(issue2->message(), QString("occured")); //krazy:exclude=spelling
0894     QCOMPARE(issue2->checker()->description(),
0895              QString("Check for spelling errors"));
0896     QVERIFY(issue2->checker()->explanation().startsWith(
0897                 "Spelling errors in comments and strings should be fixed"));
0898     QCOMPARE(issue2->checker()->fileType(), QString("qml"));
0899     QVERIFY(!issue2->checker()->isExtra());
0900 
0901     const Issue* issue3 = findIssue(&analysisResults, "style",
0902                                     "singleExtraIssue.cpp", 7);
0903     QVERIFY(issue3);
0904     QCOMPARE(issue3->message(), QString("Put 1 space before an asterisk or ampersand"));
0905     QCOMPARE(issue3->checker()->description(),
0906              QString("Check for adherence to a coding style"));
0907     QVERIFY(issue3->checker()->explanation().startsWith(
0908                 "Please follow the coding style guidelines"));
0909     QCOMPARE(issue3->checker()->fileType(), QString("c++"));
0910     QVERIFY(issue3->checker()->isExtra());
0911 
0912     const Issue* issue4 = findIssue(&analysisResults, "doublequote_chars",
0913                                    "singleIssue.cpp", 8);
0914     QVERIFY(issue4);
0915     QCOMPARE(issue4->message(), QString(""));
0916     QCOMPARE(issue4->checker()->description(),
0917              QString("Check single-char QString operations for efficiency"));
0918     QVERIFY(issue4->checker()->explanation().startsWith(
0919                 "Adding single characters to a QString is faster"));
0920     QCOMPARE(issue4->checker()->fileType(), QString("c++"));
0921     QVERIFY(!issue4->checker()->isExtra());
0922 
0923     const Issue* issue5 = findIssue(&analysisResults, "doublequote_chars",
0924                                     "severalIssuesSingleChecker.cpp", 8);
0925     QVERIFY(issue5);
0926     QCOMPARE(issue5->message(), QString(""));
0927     QCOMPARE(issue5->checker(), issue4->checker());
0928 
0929     const Issue* issue6 = findIssue(&analysisResults, "doublequote_chars",
0930                                     "severalIssuesSingleChecker.cpp", 10);
0931     QVERIFY(issue6);
0932     QCOMPARE(issue6->message(), QString(""));
0933     QCOMPARE(issue6->checker(), issue4->checker());
0934 
0935     //Eight signals should have been emitted: one for the start, one for the
0936     //finish, and one for each checker run (desktop/validate was not run as it
0937     //was not compatible with the given files, and c++/doublequote_chars and
0938     //c++/license were both executed twice).
0939     QCOMPARE(showProgressSpy.count(), 8);
0940 
0941     //First signal is the 0%
0942     //First parameter is the AnalysisProgressParser itself
0943     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
0944     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
0945     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
0946 
0947     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 16);
0948     QCOMPARE(showProgressSpy.at(2).at(3).toInt(), 33);
0949     QCOMPARE(showProgressSpy.at(3).at(3).toInt(), 50);
0950     QCOMPARE(showProgressSpy.at(4).at(3).toInt(), 66);
0951     QCOMPARE(showProgressSpy.at(5).at(3).toInt(), 83);
0952     QCOMPARE(showProgressSpy.at(6).at(3).toInt(), 99);
0953     QCOMPARE(showProgressSpy.at(7).at(3).toInt(), 100);
0954 }
0955 
0956 void AnalysisJobTest::testRunSeveralAnalysisParametersSomeOfThemWithoutFiles() {
0957     AnalysisJob analysisJob;
0958     analysisJob.setAutoDelete(false);
0959 
0960     //First analysis parameters should give no issues (as there are no files)
0961     QList<const Checker*> availableCheckers = getAvailableCheckers();
0962 
0963     QList<const Checker*> checkersToRun;
0964     foreach (const Checker* checker, availableCheckers) {
0965         checkersToRun.append(checker);
0966     }
0967 
0968     AnalysisParameters analysisParameters1;
0969     analysisParameters1.initializeCheckers(availableCheckers);
0970     analysisParameters1.setCheckersToRun(checkersToRun);
0971     analysisJob.addAnalysisParameters(&analysisParameters1);
0972 
0973     //Second analysis parameters should give one issue for each checker (the
0974     //first and second checker are named like other checkers with issues,
0975     //although they should not be run)
0976     QMutableListIterator<const Checker*> it(availableCheckers);
0977     while (it.hasNext()) {
0978         it.setValue(new Checker(*it.next()));
0979     }
0980 
0981     checkersToRun.clear();
0982     foreach (const Checker* checker, availableCheckers) {
0983         if ((checker->fileType() == "c++" && checker->name() == "license" && !checker->isExtra()) ||
0984             (checker->fileType() == "qml" && checker->name() == "spelling" && !checker->isExtra()) ||
0985             (checker->fileType() == "c++" && checker->name() == "style" && checker->isExtra())) {
0986             checkersToRun.append(checker);
0987         }
0988     }
0989 
0990     QStringList fileNames;
0991     fileNames << m_examplesPath;
0992 
0993     AnalysisParameters analysisParameters2;
0994     analysisParameters2.initializeCheckers(availableCheckers);
0995     analysisParameters2.setCheckersToRun(checkersToRun);
0996     analysisParameters2.setFilesAndDirectories(fileNames);
0997     analysisJob.addAnalysisParameters(&analysisParameters2);
0998 
0999     //Third analysis parameters should give one issue for the checker
1000     it = QMutableListIterator<const Checker*>(availableCheckers);
1001     while (it.hasNext()) {
1002         it.setValue(new Checker(*it.next()));
1003     }
1004 
1005     checkersToRun.clear();
1006     foreach (const Checker* checker, availableCheckers) {
1007         if ((checker->fileType() == "c++" && checker->name() == "doublequote_chars" && !checker->isExtra())) {
1008             checkersToRun.append(checker);
1009         }
1010     }
1011 
1012     fileNames.clear();
1013     fileNames << m_examplesPath + "singleIssue.cpp";
1014 
1015     AnalysisParameters analysisParameters3;
1016     analysisParameters3.initializeCheckers(availableCheckers);
1017     analysisParameters3.setCheckersToRun(checkersToRun);
1018     analysisParameters3.setFilesAndDirectories(fileNames);
1019     analysisJob.addAnalysisParameters(&analysisParameters3);
1020 
1021     //Fourth analysis parameters should give no issues (as there are no files)
1022     it = QMutableListIterator<const Checker*>(availableCheckers);
1023     while (it.hasNext()) {
1024         it.setValue(new Checker(*it.next()));
1025     }
1026 
1027     checkersToRun.clear();
1028     foreach (const Checker* checker, availableCheckers) {
1029         checkersToRun.append(checker);
1030     }
1031 
1032     AnalysisParameters analysisParameters4;
1033     analysisParameters4.initializeCheckers(availableCheckers);
1034     analysisParameters4.setCheckersToRun(checkersToRun);
1035     analysisJob.addAnalysisParameters(&analysisParameters4);
1036 
1037     //Fifth analysis parameters should give no issues (as there are no files)
1038     it = QMutableListIterator<const Checker*>(availableCheckers);
1039     while (it.hasNext()) {
1040         it.setValue(new Checker(*it.next()));
1041     }
1042 
1043     checkersToRun.clear();
1044     foreach (const Checker* checker, availableCheckers) {
1045         checkersToRun.append(checker);
1046     }
1047 
1048     AnalysisParameters analysisParameters5;
1049     analysisParameters5.initializeCheckers(availableCheckers);
1050     analysisParameters5.setCheckersToRun(checkersToRun);
1051     analysisJob.addAnalysisParameters(&analysisParameters5);
1052 
1053     //Sixth analysis parameters should give three issues for the first checker
1054     //(although one is repeated), no issues for the second checker, and the
1055     //third one should not be run, as it is not compatible with any analyzed
1056     //file
1057     it = QMutableListIterator<const Checker*>(availableCheckers);
1058     while (it.hasNext()) {
1059         it.setValue(new Checker(*it.next()));
1060     }
1061 
1062     checkersToRun.clear();
1063     foreach (const Checker* checker, availableCheckers) {
1064         if ((checker->fileType() == "c++" && checker->name() == "doublequote_chars" && !checker->isExtra()) ||
1065             (checker->fileType() == "c++" && checker->name() == "license" && !checker->isExtra()) ||
1066             (checker->fileType() == "desktop" && checker->name() == "validate" && !checker->isExtra())) {
1067             checkersToRun.append(checker);
1068         }
1069     }
1070 
1071     fileNames.clear();
1072     fileNames << m_examplesPath + "singleIssue.cpp";
1073     fileNames << m_examplesPath + "severalIssuesSingleChecker.cpp";
1074     fileNames << m_examplesPath + "subdirectory/severalIssuesSeveralCheckers.qml";
1075 
1076     AnalysisParameters analysisParameters6;
1077     analysisParameters6.initializeCheckers(availableCheckers);
1078     analysisParameters6.setCheckersToRun(checkersToRun);
1079     analysisParameters6.setFilesAndDirectories(fileNames);
1080     analysisJob.addAnalysisParameters(&analysisParameters6);
1081 
1082     //Seventh analysis parameters should give no issues (as there are no files)
1083     it = QMutableListIterator<const Checker*>(availableCheckers);
1084     while (it.hasNext()) {
1085         it.setValue(new Checker(*it.next()));
1086     }
1087 
1088     checkersToRun.clear();
1089     foreach (const Checker* checker, availableCheckers) {
1090         checkersToRun.append(checker);
1091     }
1092 
1093     AnalysisParameters analysisParameters7;
1094     analysisParameters7.initializeCheckers(availableCheckers);
1095     analysisParameters7.setCheckersToRun(checkersToRun);
1096     analysisJob.addAnalysisParameters(&analysisParameters7);
1097 
1098     AnalysisResults analysisResults;
1099     analysisJob.setAnalysisResults(&analysisResults);
1100 
1101     QSignalSpy showProgressSpy(analysisJob.findChild<AnalysisProgressParser*>(),
1102                                SIGNAL(showProgress(KDevelop::IStatus*,int,int,int)));
1103 
1104     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
1105 
1106     analysisJob.start();
1107 
1108     resultSpy.waitForSignal();
1109 
1110     QCOMPARE(analysisJob.error(), (int)KJob::NoError);
1111     QCOMPARE(analysisResults.issues().count(), 6);
1112 
1113     //To prevent test failures due to the order of the issues, each issue is
1114     //searched in the results instead of using a specific index
1115     const Issue* issue1 = findIssue(&analysisResults, "license",
1116                                     "severalIssuesSeveralCheckers.cpp", -1);
1117     QVERIFY(issue1);
1118     QCOMPARE(issue1->message(), QString("missing license"));
1119     QCOMPARE(issue1->checker()->description(),
1120              QString("Check for an acceptable license"));
1121     QVERIFY(issue1->checker()->explanation().startsWith(
1122                 "Each source file must contain a license"));
1123     QCOMPARE(issue1->checker()->fileType(), QString("c++"));
1124     QVERIFY(!issue1->checker()->isExtra());
1125 
1126     const Issue* issue2 = findIssue(&analysisResults, "spelling",
1127                                     "subdirectory/severalIssuesSeveralCheckers.qml", 3);
1128     QVERIFY(issue2);
1129     QCOMPARE(issue2->message(), QString("occured")); //krazy:exclude=spelling
1130     QCOMPARE(issue2->checker()->description(),
1131              QString("Check for spelling errors"));
1132     QVERIFY(issue2->checker()->explanation().startsWith(
1133                 "Spelling errors in comments and strings should be fixed"));
1134     QCOMPARE(issue2->checker()->fileType(), QString("qml"));
1135     QVERIFY(!issue2->checker()->isExtra());
1136 
1137     const Issue* issue3 = findIssue(&analysisResults, "style",
1138                                     "singleExtraIssue.cpp", 7);
1139     QVERIFY(issue3);
1140     QCOMPARE(issue3->message(), QString("Put 1 space before an asterisk or ampersand"));
1141     QCOMPARE(issue3->checker()->description(),
1142              QString("Check for adherence to a coding style"));
1143     QVERIFY(issue3->checker()->explanation().startsWith(
1144                 "Please follow the coding style guidelines"));
1145     QCOMPARE(issue3->checker()->fileType(), QString("c++"));
1146     QVERIFY(issue3->checker()->isExtra());
1147 
1148     const Issue* issue4 = findIssue(&analysisResults, "doublequote_chars",
1149                                    "singleIssue.cpp", 8);
1150     QVERIFY(issue4);
1151     QCOMPARE(issue4->message(), QString(""));
1152     QCOMPARE(issue4->checker()->description(),
1153              QString("Check single-char QString operations for efficiency"));
1154     QVERIFY(issue4->checker()->explanation().startsWith(
1155                 "Adding single characters to a QString is faster"));
1156     QCOMPARE(issue4->checker()->fileType(), QString("c++"));
1157     QVERIFY(!issue4->checker()->isExtra());
1158 
1159     const Issue* issue5 = findIssue(&analysisResults, "doublequote_chars",
1160                                     "severalIssuesSingleChecker.cpp", 8);
1161     QVERIFY(issue5);
1162     QCOMPARE(issue5->message(), QString(""));
1163     QCOMPARE(issue5->checker(), issue4->checker());
1164 
1165     const Issue* issue6 = findIssue(&analysisResults, "doublequote_chars",
1166                                     "severalIssuesSingleChecker.cpp", 10);
1167     QVERIFY(issue6);
1168     QCOMPARE(issue6->message(), QString(""));
1169     QCOMPARE(issue6->checker(), issue4->checker());
1170 
1171     //Eight signals should have been emitted: one for the start, one for the
1172     //finish, and one for each checker run (desktop/validate was not run as it
1173     //was not compatible with the given files, and c++/doublequote_chars and
1174     //c++/license were both executed twice).
1175     QCOMPARE(showProgressSpy.count(), 8);
1176 
1177     //First signal is the 0%
1178     //First parameter is the AnalysisProgressParser itself
1179     QCOMPARE(showProgressSpy.at(0).at(1).toInt(), 0);
1180     QCOMPARE(showProgressSpy.at(0).at(2).toInt(), 100);
1181     QCOMPARE(showProgressSpy.at(0).at(3).toInt(), 0);
1182 
1183     QCOMPARE(showProgressSpy.at(1).at(3).toInt(), 16);
1184     QCOMPARE(showProgressSpy.at(2).at(3).toInt(), 33);
1185     QCOMPARE(showProgressSpy.at(3).at(3).toInt(), 50);
1186     QCOMPARE(showProgressSpy.at(4).at(3).toInt(), 66);
1187     QCOMPARE(showProgressSpy.at(5).at(3).toInt(), 83);
1188     QCOMPARE(showProgressSpy.at(6).at(3).toInt(), 99);
1189     QCOMPARE(showProgressSpy.at(7).at(3).toInt(), 100);
1190 }
1191 
1192 void AnalysisJobTest::testRunWithInvalidKrazy2ExecutablePath() {
1193     AnalysisJob analysisJob;
1194     analysisJob.setAutoDelete(false);
1195 
1196     QList<const Checker*> availableCheckers;
1197 
1198     auto  doubleQuoteCharsChecker = new Checker();
1199     doubleQuoteCharsChecker->setFileType("c++");
1200     doubleQuoteCharsChecker->setName("doublequote_chars");
1201     availableCheckers.append(doubleQuoteCharsChecker);
1202 
1203     AnalysisParameters analysisParameters;
1204     analysisParameters.initializeCheckers(availableCheckers);
1205     analysisParameters.setFilesAndDirectories(QStringList() << m_examplesPath);
1206     analysisJob.addAnalysisParameters(&analysisParameters);
1207 
1208     AnalysisResults analysisResults;
1209     analysisJob.setAnalysisResults(&analysisResults);
1210 
1211     KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2");
1212     krazy2Configuration.writeEntry("krazy2 Path", "invalid/krazy2/path");
1213 
1214     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
1215 
1216     analysisJob.start();
1217 
1218     resultSpy.waitForSignal();
1219 
1220     QCOMPARE(analysisJob.error(), (int)KJob::UserDefinedError);
1221     QCOMPARE(analysisJob.errorString(),
1222              xi18nc("@info", "<para><command>krazy2</command> failed to start "
1223                              "using the path"
1224                              "(<filename>%1</filename>).</para>", "invalid/krazy2/path"));
1225     QCOMPARE(analysisResults.issues().count(), 0);
1226 }
1227 
1228 void AnalysisJobTest::testKill() {
1229     AnalysisJob analysisJob;
1230     analysisJob.setAutoDelete(false);
1231 
1232     QList<const Checker*> availableCheckers = getAvailableCheckers();
1233 
1234     AnalysisParameters analysisParameters;
1235     analysisParameters.initializeCheckers(availableCheckers);
1236     analysisParameters.setFilesAndDirectories(QStringList() << m_examplesPath);
1237     analysisJob.addAnalysisParameters(&analysisParameters);
1238 
1239     AnalysisResults analysisResults;
1240     analysisJob.setAnalysisResults(&analysisResults);
1241 
1242     SignalSpy resultSpy(&analysisJob, SIGNAL(result(KJob*)));
1243 
1244     analysisJob.start();
1245 
1246     QTest::qWait(1000);
1247 
1248     analysisJob.kill(KJob::EmitResult);
1249 
1250     resultSpy.waitForSignal();
1251 
1252     QCOMPARE(analysisJob.error(), (int)KJob::KilledJobError);
1253     QCOMPARE(analysisResults.issues().count(), 0);
1254 }
1255 
1256 ///////////////////////////////// Helpers //////////////////////////////////////
1257 
1258 bool AnalysisJobTest::examplesInSubdirectory() const {
1259     if (QFile(m_examplesPath + "singleIssue.cpp").exists() &&
1260         QFile(m_examplesPath + "singleExtraIssue.cpp").exists() &&
1261         QFile(m_examplesPath + QString::fromUtf8("singleIssueNonAsciiFileNameḶḷambión.cpp")).exists() &&
1262         QFile(m_examplesPath + ".singleIssueHiddenUnixFileName.cpp").exists() &&
1263         QFile(m_examplesPath + "severalIssuesSingleChecker.cpp").exists() &&
1264         QFile(m_examplesPath + "severalIssuesSeveralCheckers.cpp").exists() &&
1265         QFile(m_examplesPath + "severalIssuesSeveralCheckersUnknownFileType.dqq").exists() &&
1266         QFile(m_examplesPath + "subdirectory/singleIssue.desktop").exists() &&
1267         QFile(m_examplesPath + "subdirectory/severalIssuesSeveralCheckers.qml").exists()) {
1268         return true;
1269     }
1270 
1271     return false;
1272 }
1273 
1274 bool AnalysisJobTest::krazy2InPath() const {
1275     //QProcess::exec is not used, as the static method uses ForwardedChannels
1276     QProcess process;
1277     process.start("krazy2 --version");
1278     process.waitForFinished();
1279 
1280     if (process.error() == QProcess::FailedToStart) {
1281         return false;
1282     }
1283 
1284     return true;
1285 }
1286 
1287 QList< const Checker* > AnalysisJobTest::getAvailableCheckers() const {
1288     KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2");
1289     krazy2Configuration.writeEntry("krazy2 Path", "krazy2");
1290 
1291     QList<const Checker*> availableCheckers;
1292 
1293     CheckerListJob checkerListJob;
1294     checkerListJob.setAutoDelete(false);
1295     checkerListJob.setCheckerList(&availableCheckers);
1296 
1297     SignalSpy checkerListResultSpy(&checkerListJob, SIGNAL(result(KJob*)));
1298     checkerListJob.start();
1299     checkerListResultSpy.waitForSignal();
1300 
1301     return availableCheckers;
1302 }
1303 
1304 const Issue* AnalysisJobTest::findIssue(const AnalysisResults* analysisResults,
1305                                         const QString& checkerName,
1306                                         const QString& exampleFileName, int line) const {
1307     QString fileName = m_examplesPath + exampleFileName;
1308     foreach (const Issue* issue, analysisResults->issues()) {
1309         if (issue->checker()->name() == checkerName &&
1310             issue->fileName() == fileName &&
1311             issue->line() == line) {
1312             return issue;
1313         }
1314     }
1315 
1316     return nullptr;
1317 }
1318 
1319 QTEST_GUILESS_MAIN(AnalysisJobTest)
1320 
1321 #include "analysisjobtest.moc"