File indexing completed on 2024-05-05 04:35:28
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 <QTest> 0021 0022 #include <QSignalSpy> 0023 #include <KLocalizedString> 0024 #include <KSharedConfig> 0025 #include <QTimer> 0026 0027 #include <KConfigGroup> 0028 0029 #include "../checkerlistjob.h" 0030 #include "../checker.h" 0031 0032 //Needed for qRegisterMetaType 0033 Q_DECLARE_METATYPE(KJob*) 0034 0035 /** 0036 * Modified version of private KSignalSpy found in 0037 * kdelibs/kdecore/util/qtest_kde.cpp. 0038 * Original KDESignalSpy, accessed through 0039 * QTest::kWaitForSignal(QObject*, const char*, int), can miss a signal if it is 0040 * emitted too quickly (that is, before the connect is reached). This modified 0041 * version, instead of starting the wait in the constructor, has a specific 0042 * method for it. So the object can be created before executing the call that 0043 * emits the signal, enabling it to register the signal before starting to wait 0044 * and thus ensuring that no signal will be missed. 0045 */ 0046 class SignalSpy: public QObject { 0047 Q_OBJECT 0048 public: 0049 SignalSpy(QObject* object, const char* signal): QObject(nullptr), 0050 m_signalSpy(object, signal) { 0051 connect(object, signal, this, SLOT(signalEmitted())); 0052 } 0053 0054 bool waitForSignal(int timeout = 0) { 0055 if (m_signalSpy.count() == 0) { 0056 if (timeout > 0) { 0057 QObject::connect(&m_timer, SIGNAL(timeout()), &m_loop, SLOT(quit())); 0058 m_timer.setSingleShot(true); 0059 m_timer.start(timeout); 0060 } 0061 m_loop.exec(); //krazy:exclude=crashy 0062 } 0063 0064 if (m_signalSpy.count() >= 0) { 0065 return true; 0066 } 0067 return false; 0068 } 0069 private Q_SLOTS: 0070 void signalEmitted() { 0071 m_timer.stop(); 0072 m_loop.quit(); 0073 } 0074 private: 0075 QSignalSpy m_signalSpy; 0076 QEventLoop m_loop; 0077 QTimer m_timer; 0078 }; 0079 0080 class CheckerListJobTest: public QObject { 0081 Q_OBJECT 0082 private slots: 0083 0084 void initTestCase(); 0085 void init(); 0086 void cleanup(); 0087 0088 void testConstructor(); 0089 0090 void testRun(); 0091 void testRunWithInvalidPaths(); 0092 0093 void testKill(); 0094 0095 private: 0096 0097 QList<const Checker*>* m_checkerList; 0098 0099 bool krazy2InPath() const; 0100 0101 const Checker* findChecker(const QList<const Checker*>* checkerList, 0102 const QString& checkerName, const QString& fileType) const; 0103 0104 }; 0105 0106 void CheckerListJobTest::initTestCase() { 0107 //Needed for SignalSpy 0108 qRegisterMetaType<KJob*>(); 0109 } 0110 0111 void CheckerListJobTest::init() { 0112 m_checkerList = new QList<const Checker*>(); 0113 } 0114 0115 void CheckerListJobTest::cleanup() { 0116 qDeleteAll(*m_checkerList); 0117 delete m_checkerList; 0118 } 0119 0120 void CheckerListJobTest::testConstructor() { 0121 QObject parent; 0122 auto checkerListJob = new CheckerListJob(&parent); 0123 0124 QCOMPARE(checkerListJob->parent(), &parent); 0125 QCOMPARE(checkerListJob->capabilities(), KJob::Killable); 0126 } 0127 0128 void CheckerListJobTest::testRun() { 0129 if (!krazy2InPath()) { 0130 QSKIP("krazy2 is not in the execution path", SkipAll); 0131 } 0132 0133 CheckerListJob checkerListJob; 0134 checkerListJob.setAutoDelete(false); 0135 checkerListJob.setCheckerList(m_checkerList); 0136 0137 KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2"); 0138 krazy2Configuration.writeEntry("krazy2 Path", "krazy2"); 0139 0140 SignalSpy resultSpy(&checkerListJob, SIGNAL(result(KJob*))); 0141 0142 checkerListJob.start(); 0143 0144 resultSpy.waitForSignal(); 0145 0146 QCOMPARE(checkerListJob.error(), (int)KJob::NoError); 0147 0148 //The full list of checkers may change between Krazy2 versions (maybe even 0149 //between the systems where it is installed), so just some checkers are 0150 //verified 0151 QVERIFY(m_checkerList->count() > 0); 0152 0153 const Checker* checker = findChecker(m_checkerList, "doublequote_chars", "c++"); 0154 QVERIFY(checker); 0155 QCOMPARE(checker->description(), 0156 QString("Check single-char QString operations for efficiency")); 0157 QVERIFY(checker->explanation().startsWith( 0158 "Adding single characters to a QString is faster")); 0159 QCOMPARE(checker->isExtra(), false); 0160 0161 const Checker* checker2 = findChecker(m_checkerList, "endswithnewline", "c++"); 0162 QVERIFY(checker2); 0163 QCOMPARE(checker2->description(), 0164 QString("Check that file ends with a newline")); 0165 QVERIFY(checker2->explanation().startsWith( 0166 "Files that do not end with a newline character")); 0167 QCOMPARE(checker2->isExtra(), false); 0168 0169 const Checker* checker3 = findChecker(m_checkerList, "endswithnewline", "designer"); 0170 QVERIFY(checker3); 0171 QCOMPARE(checker3->description(), 0172 QString("Check that file ends with a newline")); 0173 QVERIFY(checker3->explanation().startsWith( 0174 "Files that do not end with a newline character")); 0175 QCOMPARE(checker3->isExtra(), false); 0176 QVERIFY(checker3 != checker2); 0177 0178 const Checker* checker4 = findChecker(m_checkerList, "contractions", "c++"); 0179 QVERIFY(checker4); 0180 QCOMPARE(checker4->description(), 0181 QString("Check for contractions in strings")); 0182 QVERIFY(checker4->explanation().startsWith( 0183 "The KDE Style Guide recommends not using contractions")); 0184 QCOMPARE(checker4->isExtra(), true); 0185 0186 const Checker* checker5 = findChecker(m_checkerList, "contractions", "desktop"); 0187 QVERIFY(checker5); 0188 QCOMPARE(checker5->description(), 0189 QString("Check for contractions in strings")); 0190 QVERIFY(checker5->explanation().startsWith( 0191 "The KDE Style Guide recommends not using contractions")); 0192 QCOMPARE(checker5->isExtra(), true); 0193 QVERIFY(checker5 != checker4); 0194 } 0195 0196 void CheckerListJobTest::testRunWithInvalidPaths() { 0197 CheckerListJob checkerListJob; 0198 checkerListJob.setAutoDelete(false); 0199 checkerListJob.setCheckerList(m_checkerList); 0200 0201 KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2"); 0202 krazy2Configuration.writeEntry("krazy2 Path", "invalid/krazy2/path"); 0203 0204 SignalSpy resultSpy(&checkerListJob, SIGNAL(result(KJob*))); 0205 0206 checkerListJob.start(); 0207 0208 resultSpy.waitForSignal(); 0209 0210 QCOMPARE(checkerListJob.error(), (int)KJob::UserDefinedError); 0211 QCOMPARE(checkerListJob.errorString(), 0212 xi18nc("@info", "<para><command>krazy2</command> failed to start " 0213 "using the path " 0214 "(<filename>%1</filename>).</para>", "invalid/krazy2/path")); 0215 QCOMPARE(m_checkerList->count(), 0); 0216 } 0217 0218 void CheckerListJobTest::testKill() { 0219 if (!krazy2InPath()) { 0220 QSKIP("krazy2 is not in the execution path", SkipAll); 0221 } 0222 0223 CheckerListJob checkerListJob; 0224 checkerListJob.setAutoDelete(false); 0225 checkerListJob.setCheckerList(m_checkerList); 0226 0227 KConfigGroup krazy2Configuration = KSharedConfig::openConfig()->group("Krazy2"); 0228 krazy2Configuration.writeEntry("krazy2 Path", "krazy2"); 0229 0230 SignalSpy resultSpy(&checkerListJob, SIGNAL(result(KJob*))); 0231 0232 checkerListJob.start(); 0233 0234 QTest::qWait(500); 0235 0236 checkerListJob.kill(KJob::EmitResult); 0237 0238 resultSpy.waitForSignal(); 0239 0240 QCOMPARE(checkerListJob.error(), (int)KJob::KilledJobError); 0241 QCOMPARE(m_checkerList->count(), 0); 0242 } 0243 0244 ///////////////////////////////// Helpers ////////////////////////////////////// 0245 0246 bool CheckerListJobTest::krazy2InPath() const { 0247 //QProcess::exec is not used, as the static method uses ForwardedChannels 0248 QProcess process; 0249 process.start("krazy2 --version"); 0250 process.waitForFinished(); 0251 0252 if (process.error() == QProcess::FailedToStart) { 0253 return false; 0254 } 0255 0256 return true; 0257 } 0258 0259 const Checker* CheckerListJobTest::findChecker(const QList<const Checker*>* checkerList, 0260 const QString& checkerName, const QString& fileType) const { 0261 foreach (const Checker* checker, *checkerList) { 0262 if (checker->name() == checkerName && 0263 checker->fileType() == fileType) { 0264 return checker; 0265 } 0266 } 0267 0268 return nullptr; 0269 } 0270 0271 QTEST_GUILESS_MAIN(CheckerListJobTest) 0272 0273 #include "checkerlistjobtest.moc"