File indexing completed on 2024-04-14 14:19:40
0001 /* This file is part of the KDE libraries 0002 Copyright (c) 2009 David Faure <faure@kde.org> 0003 0004 This library is free software; you can redistribute it and/or modify 0005 it under the terms of the GNU Lesser General Public License as published by 0006 the Free Software Foundation; either version 2 of the License or ( at 0007 your option ) version 3 or, at the discretion of KDE e.V. ( which shall 0008 act as a proxy as in section 14 of the GPLv3 ), any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "kdebug_unittest.h" 0022 #include <qstandardpaths.h> 0023 #include <kconfig.h> 0024 #include <kconfiggroup.h> 0025 #include <QTest> 0026 #include <kdebug.h> 0027 #include <QProcess> 0028 0029 // Set up the env before the first qDebug/qWarning (e.g. from kcrash) 0030 void setEnvironmentVariables() 0031 { 0032 qputenv("KDE_DEBUG_TIMESTAMP", ""); 0033 qputenv("QT_MESSAGE_PATTERN", "%{appname}(%{pid})/%{category} %{function}: %{message}"); 0034 } 0035 Q_CONSTRUCTOR_FUNCTION(setEnvironmentVariables) 0036 0037 QTEST_MAIN(KDebugTest) 0038 0039 void KDebugTest::initTestCase() 0040 { 0041 // The source files (kdebugrc and kdebug.areas) are in the "global" config dir: 0042 qputenv("XDG_CONFIG_DIRS", QFile::encodeName(QFileInfo(QFINDTESTDATA("../src/kdebug.areas")).absolutePath())); 0043 0044 QStandardPaths::setTestModeEnabled(true); 0045 0046 QString kdebugrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + "kdebugrc"; 0047 if (!kdebugrc.isEmpty()) { 0048 QFile::remove(kdebugrc); 0049 } 0050 QFile::remove("kdebug.dbg"); 0051 QFile::remove("myarea.dbg"); 0052 0053 // Check that we can find kdebugrc and kdebug.areas 0054 QString filename(QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QLatin1String("kdebug.areas"))); 0055 QVERIFY2(QFile::exists(filename), filename.toLatin1() + " not found"); 0056 QVERIFY(QFile::exists(QFINDTESTDATA("../src/kdebugrc"))); 0057 0058 // Now set up logging to file 0059 KConfig config("kdebugrc"); 0060 config.group(QString()).writeEntry("DisableAll", false); // in case of a global kdebugrc with DisableAll=true 0061 config.group("180").writeEntry("InfoOutput", 0 /*FileOutput*/); 0062 config.group("myarea").writeEntry("InfoOutput", 0 /*FileOutput*/); 0063 config.group("myarea").writeEntry("InfoFilename", "myarea.dbg"); 0064 config.group("kdebug_unittest").writeEntry("InfoOutput", 0 /*FileOutput*/); 0065 config.group("kdebug_unittest").writeEntry("WarnOutput", 0 /*FileOutput*/); 0066 config.sync(); 0067 0068 //QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 0, false), false); 0069 0070 // Test for crash that used to happen when using an unknown area after only dynamic areas 0071 KDebug::registerArea("somearea"); // gets number 1 0072 KDebug::registerArea("someotherarea"); // gets number 2 0073 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 4242, false), false); // unknown area -> area 0 is being used 0074 0075 kClearDebugConfig(); 0076 } 0077 0078 void KDebugTest::cleanupTestCase() 0079 { 0080 QString kdebugrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + "kdebugrc"; 0081 if (!kdebugrc.isEmpty()) { 0082 QFile::remove(kdebugrc); 0083 } 0084 // TODO QFile::remove("kdebug.dbg"); 0085 QFile::remove("myarea.dbg"); 0086 } 0087 0088 static QList<QByteArray> readLines(const char *fileName = "kdebug.dbg") 0089 { 0090 const QString path = QFile::decodeName(fileName); 0091 Q_ASSERT(!path.isEmpty()); 0092 Q_ASSERT(QFile::exists(path)); 0093 QFile file(path); 0094 const bool opened = file.open(QIODevice::ReadOnly); 0095 Q_ASSERT(opened); 0096 Q_UNUSED(opened); 0097 QList<QByteArray> lines; 0098 QByteArray line; 0099 do { 0100 line = file.readLine(); 0101 if (!line.isEmpty()) { 0102 lines.append(line); 0103 } 0104 } while (!line.isEmpty()); 0105 return lines; 0106 } 0107 0108 void KDebugTest::compareLines(const QList<QByteArray> &expectedLines, const char *fileName) 0109 { 0110 QList<QByteArray> lines = readLines(fileName); 0111 //qDebug() << lines; 0112 QCOMPARE(lines.count(), expectedLines.count()); 0113 QVERIFY(lines[0].endsWith('\n')); 0114 for (int i = 0; i < lines.count(); ++i) { 0115 QByteArray line = lines[i]; 0116 if (expectedLines[i].contains("[...]")) { 0117 const int pos = line.indexOf('['); 0118 QVERIFY(pos >= 0); 0119 line.truncate(pos); 0120 line.append("[...]\n"); 0121 } 0122 //qDebug() << "line" << i << ":" << line << expectedLines[i]; 0123 QVERIFY2(line.endsWith(expectedLines[i]), "Got '" + line + "'\nexpected '" + expectedLines[i] + "'"); 0124 } 0125 } 0126 0127 // Test what happens when a operator<< calls a method that itself uses kDebug, 0128 // meaning that two kDebug instances will be active at the same time. 0129 // In this case it works, but technically, if area 180 was configured with 0130 // a different output file than area 0 then the output would currently go 0131 // into the wrong file (because the stream is static) (the "after the call" string 0132 // would go into the file for area 180) 0133 class TestClass 0134 { 0135 public: 0136 TestClass() {} 0137 QString getSomething() const 0138 { 0139 kDebug(180) << "Nested kDebug call"; 0140 return "TestClass"; 0141 } 0142 }; 0143 QDebug operator<<(QDebug s, const TestClass &me) 0144 { 0145 s << me.getSomething() << "after the call"; 0146 return s; 0147 } 0148 0149 void KDebugTest::testDebugToFile() 0150 { 0151 kDebug(180) << "TEST DEBUG 180"; 0152 kDebug(0) << "TEST DEBUG 0"; 0153 kWarning() << "TEST WARNING 0"; 0154 // The calls to kDebug(0) created a dynamic debug area named after the componentdata name 0155 KConfig config("kdebugrc"); 0156 QVERIFY(config.hasGroup("kdebug_unittest")); 0157 kDebug(0) << "TEST DEBUG with newline" << endl << "newline"; 0158 TestClass tc; 0159 kDebug(0) << "Re-entrance test" << tc << "[ok]"; 0160 { 0161 KDebug::Block block("block 1"); 0162 { 0163 KDebug::Block block("block 2"); 0164 } 0165 } 0166 QVERIFY(QFile::exists("kdebug.dbg")); 0167 QList<QByteArray> expected; 0168 expected << "/kdecore (kdelibs) KDebugTest::testDebugToFile: TEST DEBUG 180\n"; 0169 expected << "KDebugTest::testDebugToFile: TEST DEBUG 0\n"; 0170 expected << "KDebugTest::testDebugToFile: TEST WARNING 0\n"; 0171 expected << "KDebugTest::testDebugToFile: TEST DEBUG with newline\n"; 0172 expected << "newline\n"; 0173 expected << "/kdecore (kdelibs) TestClass::getSomething: Nested kDebug call\n"; 0174 expected << "Re-entrance test \"TestClass\" after the call [ok]\n"; 0175 expected << "BEGIN: block 1\n"; 0176 expected << "BEGIN: block 2\n"; 0177 expected << "END__: block 2 [...]\n"; 0178 expected << "END__: block 1 [...]\n"; 0179 compareLines(expected); 0180 } 0181 0182 void KDebugTest::testDisableArea() 0183 { 0184 QFile::remove("kdebug.dbg"); 0185 KConfig config("kdebugrc"); 0186 config.group("180").writeEntry("InfoOutput", 4 /*NoOutput*/); 0187 config.group("kdebug_unittest").writeEntry("InfoOutput", 4 /*NoOutput*/); 0188 config.sync(); 0189 kClearDebugConfig(); 0190 kDebug(180) << "TEST DEBUG 180 - SHOULD NOT APPEAR"; 0191 kDebug(0) << "TEST DEBUG 0 - SHOULD NOT APPEAR"; 0192 { 0193 KDebug::Block block("SHOULD NOT APPEAR"); 0194 kDebug(0) << "msg inside the block, should not appear"; 0195 } 0196 QVERIFY(!QFile::exists("kdebug.dbg")); 0197 0198 // Re-enable debug, for further tests 0199 config.group("180").writeEntry("InfoOutput", 0 /*FileOutput*/); 0200 config.group("kdebug_unittest").writeEntry("InfoOutput", 0 /*FileOutput*/); 0201 config.sync(); 0202 kClearDebugConfig(); 0203 } 0204 0205 void KDebugTest::testDynamicArea() 0206 { 0207 const int myArea = KDebug::registerArea("myarea"); // gets number 3 0208 QCOMPARE(myArea, 3); 0209 KConfig config("kdebugrc"); 0210 QVERIFY(!config.hasGroup(QString::number(myArea))); 0211 QVERIFY(config.hasGroup("myarea")); 0212 kDebug(myArea) << "TEST DEBUG using myArea" << myArea; 0213 QList<QByteArray> expected; 0214 expected << "/myarea KDebugTest::testDynamicArea: TEST DEBUG using myArea 3\n"; 0215 compareLines(expected, "myarea.dbg"); 0216 } 0217 0218 void KDebugTest::testDisabledDynamicArea() 0219 { 0220 const int verboseArea = KDebug::registerArea("verbosearea", false); 0221 QVERIFY(verboseArea > 0); 0222 kClearDebugConfig(); // force a sync() of KDebug's own kdebugrc so that it gets written out 0223 KConfig config("kdebugrc"); 0224 QVERIFY(config.hasGroup("verbosearea")); 0225 kDebug(verboseArea) << "TEST DEBUG using verboseArea" << verboseArea; 0226 } 0227 0228 static void disableAll(bool dis) 0229 { 0230 KConfig config("kdebugrc"); 0231 config.group(QString()).writeEntry("DisableAll", dis); 0232 config.sync(); 0233 kClearDebugConfig(); 0234 } 0235 0236 void KDebugTest::testDisableAll() 0237 { 0238 // Some people really don't like debug output :-) 0239 disableAll(true); 0240 QFile::remove("kdebug.dbg"); 0241 kDebug() << "Should not appear"; 0242 kDebug(123465) << "Unknown area, should not appear either"; 0243 QVERIFY(!QFile::exists("kdebug.dbg")); 0244 // Repair 0245 disableAll(false); 0246 } 0247 0248 void KDebugTest::testHasNullOutput() 0249 { 0250 // When compiling in debug mode: 0251 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 0, true), false); 0252 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 180, true), false); 0253 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 293, true), false); 0254 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 4242, true), false); 0255 0256 kClearDebugConfig(); // force dropping the cache 0257 0258 // When compiling in release mode: 0259 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 0, false), false); // controlled by "InfoOutput" key 0260 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 180, false), false); // controlled by "InfoOutput" key 0261 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 293, false), true); // no config -> the default is being used 0262 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 4242, false), false); // unknown area -> area 0 is being used 0263 0264 // And if we really have no config for area 0 (the app name) 0265 KConfig config("kdebugrc"); 0266 config.deleteGroup("kdebug_unittest"); 0267 config.sync(); 0268 kClearDebugConfig(); 0269 0270 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 0, false), true); 0271 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 293, false), true); 0272 QCOMPARE(KDebug::hasNullOutput(QtDebugMsg, true, 4242, false), true); 0273 0274 // Restore to normal for future tests 0275 config.group("kdebug_unittest").writeEntry("InfoOutput", 0 /*FileOutput*/); 0276 config.sync(); 0277 kClearDebugConfig(); 0278 } 0279 0280 void KDebugTest::testNoMainComponentData() 0281 { 0282 // change to the bin dir 0283 QDir::setCurrent(QCoreApplication::applicationDirPath()); 0284 // This test runs kdebug_qcoreapptest and checks its output 0285 QProcess proc; 0286 QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); 0287 // No process info, to make this easier 0288 environment.insert("QT_MESSAGE_PATTERN", "%{category} %{function}: %{message}"); 0289 proc.setProcessEnvironment(environment); 0290 proc.setProcessChannelMode(QProcess::SeparateChannels); 0291 #ifdef Q_OS_WIN 0292 proc.start("kdebug_qcoreapptest.exe"); 0293 #else 0294 if (QFile::exists("./kdebug_qcoreapptest.shell")) { 0295 proc.start("./kdebug_qcoreapptest.shell"); 0296 } else { 0297 QVERIFY(QFile::exists("./kdebug_qcoreapptest")); 0298 proc.start("./kdebug_qcoreapptest"); 0299 } 0300 #endif 0301 // kDebug() << proc.args(); 0302 const bool ok = proc.waitForFinished(); 0303 QVERIFY(ok); 0304 const QByteArray allOutput = proc.readAllStandardError(); 0305 const QList<QByteArray> receivedLines = allOutput.split('\n'); 0306 //qDebug() << receivedLines; 0307 QList<QByteArray> expectedLines; 0308 expectedLines << "qcoreapp_myarea main: Test debug using qcoreapp_myarea 1"; 0309 expectedLines << "kdebug_qcoreapptest main: Debug in area 100"; 0310 expectedLines << "kdebug_qcoreapptest main: Simple debug"; 0311 expectedLines << "kdebug_qcoreapptest main: This should appear, under the kdebug_qcoreapptest area"; 0312 expectedLines << "kdebug_qcoreapptest main: Debug in area 100"; 0313 expectedLines << ""; // artefact of split, I guess? 0314 for (int i = 0; i < qMin(expectedLines.count(), receivedLines.count()); ++i) { 0315 QCOMPARE(QString::fromLatin1(receivedLines[i]), QString::fromLatin1(expectedLines[i])); 0316 } 0317 QCOMPARE(receivedLines.count(), expectedLines.count()); 0318 QCOMPARE(receivedLines, expectedLines); 0319 } 0320 0321 #include <QThreadPool> 0322 #include <QFutureSynchronizer> 0323 #include <qtconcurrentrun.h> 0324 0325 class KDebugThreadTester 0326 { 0327 public: 0328 void doDebugs() 0329 { 0330 KDEBUG_BLOCK 0331 for (int i = 0; i < 10; ++i) { 0332 kDebug() << "A kdebug statement in a thread:" << i; 0333 } 0334 } 0335 }; 0336 0337 void KDebugTest::testMultipleThreads() 0338 { 0339 kDebug() << "kDebug works"; 0340 QVERIFY(QFile::exists("kdebug.dbg")); 0341 QFile::remove("kdebug.dbg"); 0342 0343 KDebugThreadTester tester; 0344 QThreadPool::globalInstance()->setMaxThreadCount(10); 0345 QFutureSynchronizer<void> sync; 0346 for (int threadNum = 0; threadNum < 10; ++threadNum) { 0347 sync.addFuture(QtConcurrent::run(&tester, &KDebugThreadTester::doDebugs)); 0348 } 0349 sync.waitForFinished(); 0350 0351 QVERIFY(QFile::exists("kdebug.dbg")); 0352 0353 //QFile f("kdebug.dbg"); f.open(QIODevice::ReadOnly); 0354 //qDebug() << QString::fromLatin1(f.readAll()); 0355 0356 // We have no guarantee that the debug lines are issued one after the other. 0357 // The \n comes from the destruction of the temp kDebug, and that's not mutexed, 0358 // so we can get msg1 + msg2 + \n + \n. 0359 // So this test is basically only good for running in helgrind. 0360 0361 #if 0 0362 // Check that the lines are whole 0363 QList<QByteArray> lines = readLines(); 0364 Q_FOREACH (const QByteArray &line, lines) { 0365 qDebug() << line; 0366 QCOMPARE(line.count("doDebugs"), 1); 0367 QCOMPARE(line.count('\n'), 1); 0368 QVERIFY(!line.startsWith(" ")); // more than 2 spaces? indentString messed up 0369 } 0370 #endif 0371 } 0372 0373 #include "moc_kdebug_unittest.cpp"