1 /*
2     SPDX-FileCopyrightText: 2007 Nicolas Ternisien <nicolas.ternisien@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include <QList>
8 #include <QSignalSpy>
9 #include <QStringList>
10 #include <QTest>
11 #include <QThread>
12 
13 #include <KDirWatch>
14 
15 #include "testUtil.h"
16 
17 #include "analyzer.h"
18 #include "globals.h"
19 
20 #include "logFile.h"
21 #include "logLevel.h"
22 #include "logViewModel.h"
23 #include "logViewWidget.h"
24 
25 #include "ksystemlogConfig.h"
26 
27 #include "ksystemlog_debug.h"
28 
29 class SystemAnalyzerTest : public QObject
30 {
31     Q_OBJECT
32 
33 private Q_SLOTS:
34 
35     void initTestCase();
36 
37     void testMultipleLines();
38     void testOneLine();
39     void testTwoLines();
40     void testStrangeLines();
41 
42     void testDeleteProcessIdentifier();
43     void testMaxLines();
44     void testRemoveDuplicates();
45 
46 private:
47     void compareWithMinTime(const QList<LogLine *> &lines, const QDateTime &minTime);
48 
49 private:
50     TestUtil testUtil;
51 };
52 
initTestCase()53 void SystemAnalyzerTest::initTestCase()
54 {
55     testUtil.registerLogModeFactories();
56 }
57 
testOneLine()58 void SystemAnalyzerTest::testOneLine()
59 {
60     LogViewModel *model = nullptr;
61 
62     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
63     QVERIFY(systemAnalyzer);
64     QVERIFY(model);
65 
66     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/default/one-line.log"));
67 
68     systemAnalyzer->setLogFiles(logFiles);
69 
70     systemAnalyzer->watchLogFiles(true);
71 
72     QCOMPARE(model->itemCount(), 1);
73     QCOMPARE(model->isEmpty(), false);
74 
75     QList<LogLine *> logLines = model->logLines();
76 
77     QStringList items = QStringList() << QStringLiteral("localhost") << QStringLiteral("kernel")
78                                       << QStringLiteral("[11663.656000] eth1: no IPv6 routers present");
79 
80     const int year = QDate::currentDate().year();
81     testUtil.testLine(logLines.at(0),
82                       logFiles.at(0).url().toLocalFile(),
83                       logFiles.at(0).defaultLogLevel(),
84                       QDateTime(QDate(year, 8, 21), QTime(22, 52, 44)),
85                       items
86 
87     );
88 
89     testUtil.destroyReader(systemAnalyzer);
90 }
91 
testTwoLines()92 void SystemAnalyzerTest::testTwoLines()
93 {
94     LogViewModel *model = nullptr;
95 
96     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
97     QVERIFY(systemAnalyzer);
98     QVERIFY(model);
99 
100     // Specifical configuration
101     KSystemLogConfig::setMaxLines(1000);
102 
103     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/default/two-lines.log"));
104 
105     systemAnalyzer->setLogFiles(logFiles);
106 
107     systemAnalyzer->watchLogFiles(true);
108 
109     QCOMPARE(model->itemCount(), 2);
110     QCOMPARE(model->isEmpty(), false);
111 
112     testUtil.destroyReader(systemAnalyzer);
113 }
114 
testMultipleLines()115 void SystemAnalyzerTest::testMultipleLines()
116 {
117     LogViewModel *model = nullptr;
118 
119     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
120     QVERIFY(systemAnalyzer);
121     QVERIFY(model);
122 
123     // Specifical configuration
124     KSystemLogConfig::setMaxLines(1000);
125 
126     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/system/system.log"));
127     LogFile logFile = logFiles.at(0);
128 
129     systemAnalyzer->setLogFiles(logFiles);
130 
131     QSignalSpy stateSpy(systemAnalyzer, &Analyzer::logUpdated);
132     QList<QVariant> arguments;
133 
134     // Each watching relaunch a reading
135     systemAnalyzer->watchLogFiles(true);
136 
137     // Assert that the model has been updated
138     QCOMPARE(model->itemCount(), 24);
139 
140     // Assert that the logUpdated signal emits the right count
141     QCOMPARE(stateSpy.count(), 1);
142     arguments = stateSpy.takeFirst();
143     QCOMPARE(arguments.at(0).toInt(), 24);
144 
145     // Each watching relaunch a reading
146     systemAnalyzer->watchLogFiles(true);
147 
148     // Assert that the model has been updated
149     QCOMPARE(model->itemCount(), 48);
150 
151     // Assert that the logUpdated signal emits the right count
152     QCOMPARE(stateSpy.count(), 1);
153     arguments = stateSpy.takeFirst();
154     QCOMPARE(arguments.at(0).toInt(), 24);
155 
156     QStringList addedLines;
157     addedLines << QStringLiteral("Aug 18 17:04:28 localhost test: Test line 1");
158     addedLines << QStringLiteral("Aug 18 17:04:30 localhost test: Test line 2");
159 
160     testUtil.addLogLines(logFile.url().toLocalFile(), addedLines);
161 
162     // Assert that the model has been updated
163     QCOMPARE(model->itemCount(), 50);
164 
165     // Assert that the logUpdated signal emits the right count
166     QCOMPARE(stateSpy.count(), 1);
167     arguments = stateSpy.takeFirst();
168     QCOMPARE(arguments.at(0).toInt(), 2);
169 
170     testUtil.destroyReader(systemAnalyzer);
171 }
172 
testStrangeLines()173 void SystemAnalyzerTest::testStrangeLines()
174 {
175     LogViewModel *model = nullptr;
176 
177     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
178     QVERIFY(systemAnalyzer);
179     QVERIFY(model);
180 
181     // Specifical configuration
182     KSystemLogConfig::setMaxLines(1000);
183     KSystemLogConfig::setDeleteProcessIdentifier(false);
184 
185     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/system/strange-lines.log"));
186 
187     systemAnalyzer->setLogFiles(logFiles);
188 
189     systemAnalyzer->watchLogFiles(true);
190 
191     QCOMPARE(model->itemCount(), 8);
192 
193     // i18n("undefined")
194     QString undefined = QLatin1String("");
195 
196     QStringList items;
197 
198     QSKIP("This test/code is broken");
199     const int year = QDate::currentDate().year();
200 
201     // Classical log line
202     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("kernel") << QStringLiteral("Kernel panic");
203     testUtil.testLine(model->logLines().at(0),
204                       logFiles.at(0).url().toLocalFile(),
205                       logFiles.at(0).defaultLogLevel(),
206                       QDateTime(QDate(year, 8, 10), QTime(17, 04, 28)),
207                       items);
208 
209     //-- MARK -- log line
210     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("syslog") << QStringLiteral("-- MARK --");
211     testUtil.testLine(model->logLines().at(1),
212                       logFiles.at(0).url().toLocalFile(),
213                       logFiles.at(0).defaultLogLevel(),
214                       QDateTime(QDate(year, 8, 11), QTime(13, 49, 38)),
215                       items);
216 
217     // Last message repeated n time log line
218     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("syslog") << QStringLiteral("last message repeated 4 times");
219     testUtil.testLine(model->logLines().at(2),
220                       logFiles.at(0).url().toLocalFile(),
221                       logFiles.at(0).defaultLogLevel(),
222                       QDateTime(QDate(year, 8, 12), QTime(18, 10, 32)),
223                       items);
224 
225     //"Aug 13 17:04:28 testprocess: Say ouhou  " -> No host name
226     items = QStringList() << undefined << QStringLiteral("testprocess") << QStringLiteral("Say ouhou  ");
227     testUtil.testLine(model->logLines().at(3),
228                       logFiles.at(0).url().toLocalFile(),
229                       logFiles.at(0).defaultLogLevel(),
230                       QDateTime(QDate(year, 8, 13), QTime(17, 04, 28)),
231                       items);
232 
233     //"Aug 14 17:04:28 localhost kernel say ouhou" -> No process name and not a syslog message
234     items = QStringList() << QStringLiteral("localhost") << undefined << QStringLiteral("kernel say ouhou");
235     testUtil.testLine(model->logLines().at(4),
236                       logFiles.at(0).url().toLocalFile(),
237                       logFiles.at(0).defaultLogLevel(),
238                       QDateTime(QDate(year, 8, 14), QTime(17, 04, 28)),
239                       items);
240 
241     //"Aug 15 22:39:01 localhost /USR/SBIN/CRON[9433]: (root) CMD (  [ -d /var/lib/php5 ] && find
242     /// var/lib/php5/ -type f -cmin +$(/usr/lib/php5/maxlifetime) -print0 | xargs -r -0 rm)" -> Long log line
243     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("/USR/SBIN/CRON[9433]")
244                           << QStringLiteral(
245                                  "(root) CMD (  [ -d /var/lib/php5 ] && find /var/lib/php5/ -type f -cmin "
246                                  "+$(/usr/lib/php5/maxlifetime) -print0 | xargs -r -0 rm)");
247     testUtil.testLine(model->logLines().at(5),
248                       logFiles.at(0).url().toLocalFile(),
249                       logFiles.at(0).defaultLogLevel(),
250                       QDateTime(QDate(year, 8, 15), QTime(22, 39, 01)),
251                       items);
252 
253     //"blablalbla" -> Invalid line
254     items = QStringList() << undefined << undefined << QLatin1String("");
255     QCOMPARE(model->logLines().at(6)->logItems(), items);
256 
257     //"" -> Empty line
258     items = QStringList() << undefined << undefined << QStringLiteral("blablalbla");
259     QCOMPARE(model->logLines().at(7)->logItems(), items);
260 
261     testUtil.destroyReader(systemAnalyzer);
262 }
263 
testDeleteProcessIdentifier()264 void SystemAnalyzerTest::testDeleteProcessIdentifier()
265 {
266     LogViewModel *model = nullptr;
267 
268     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
269     QVERIFY(systemAnalyzer);
270     QVERIFY(model);
271 
272     // Specifical configuration
273     KSystemLogConfig::setMaxLines(1000);
274     KSystemLogConfig::setDeleteProcessIdentifier(true);
275 
276     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/system/delete-process-identifier.log"));
277 
278     systemAnalyzer->setLogFiles(logFiles);
279 
280     systemAnalyzer->watchLogFiles(true);
281 
282     QCOMPARE(model->itemCount(), 2);
283 
284     QStringList items;
285 
286     // Cron log line
287     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("/USR/SBIN/CRON") << QStringLiteral("Hello");
288     QCOMPARE(model->logLines().at(0)->logItems(), items);
289 
290     //"f" process
291     items = QStringList() << QStringLiteral("localhost") << QStringLiteral("f") << QStringLiteral("Ola");
292     QCOMPARE(model->logLines().at(1)->logItems(), items);
293 
294     testUtil.destroyReader(systemAnalyzer);
295 }
296 
testMaxLines()297 void SystemAnalyzerTest::testMaxLines()
298 {
299     LogViewModel *model = nullptr;
300 
301     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
302     QVERIFY(systemAnalyzer);
303     QVERIFY(model);
304 
305     // Specifical configuration
306     KSystemLogConfig::setMaxLines(5);
307 
308     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/system/max-lines.log"));
309     LogFile logFile = logFiles.at(0);
310 
311     systemAnalyzer->setLogFiles(logFiles);
312 
313     systemAnalyzer->watchLogFiles(true);
314 
315     QCOMPARE(model->itemCount(), 5);
316 
317     // Asserts that there is no line before the oldest one's time
318     compareWithMinTime(model->logLines(), QDateTime(QDate(2007, 8, 18), QTime(11, 0, 0)));
319 
320     QStringList addedLines;
321 
322     addedLines << QStringLiteral("Aug 18 10:00:00 localhost test: Line 8");
323     testUtil.addLogLines(logFile.url().toLocalFile(), addedLines);
324 
325     QCOMPARE(model->itemCount(), 5);
326     compareWithMinTime(model->logLines(), QDateTime(QDate(2007, 8, 18), QTime(11, 0, 0)));
327 
328     // Specifical configuration
329     KSystemLogConfig::setMaxLines(6);
330 
331     addedLines.clear();
332     addedLines << QStringLiteral("Aug 18 10:00:00 localhost test: Line 9");
333     addedLines << QStringLiteral("Aug 18 19:00:00 localhost test: Line 10");
334     testUtil.addLogLines(logFile.url().toLocalFile(), addedLines);
335 
336     QCOMPARE(model->itemCount(), 6);
337     compareWithMinTime(model->logLines(), QDateTime(QDate(2007, 8, 18), QTime(11, 0, 0)));
338 
339     addedLines.clear();
340     addedLines << QStringLiteral("Aug 18 20:00:00 localhost test: Line 11");
341     addedLines << QStringLiteral("Aug 18 21:00:00 localhost test: Line 12");
342     testUtil.addLogLines(logFile.url().toLocalFile(), addedLines);
343 
344     QCOMPARE(model->itemCount(), 6);
345     compareWithMinTime(model->logLines(), QDateTime(QDate(2007, 8, 18), QTime(13, 0, 0)));
346 
347     testUtil.destroyReader(systemAnalyzer);
348 }
349 
compareWithMinTime(const QList<LogLine * > & logLines,const QDateTime & minTime)350 void SystemAnalyzerTest::compareWithMinTime(const QList<LogLine *> &logLines, const QDateTime &minTime)
351 {
352     qCDebug(KSYSTEMLOG) << "Min time : " << minTime.toString();
353 
354     for (LogLine *logLine : logLines) {
355         if (logLine->time() < minTime) {
356             QFAIL(QString::fromLatin1("The line '%1' has a lesser time than the required min time (%2)")
357                       .arg(logLine->logItems().join(QLatin1Char(' ')), logLine->time().toString())
358                       .toUtf8()
359                       .constData());
360         }
361     }
362 }
363 
testRemoveDuplicates()364 void SystemAnalyzerTest::testRemoveDuplicates()
365 {
366     LogViewModel *model = nullptr;
367 
368     Analyzer *systemAnalyzer = testUtil.createAnalyzer(QStringLiteral("systemLogMode"), &model);
369     QVERIFY(systemAnalyzer);
370     QVERIFY(model);
371 
372     // Specifical configuration
373     KSystemLogConfig::setMaxLines(1000);
374     KSystemLogConfig::setDeleteDuplicatedLines(true);
375 
376     QVector<LogFile> logFiles = testUtil.createLogFiles(QStringLiteral(":/testFiles/system/duplicate-lines.log"));
377 
378     systemAnalyzer->setLogFiles(logFiles);
379 
380     systemAnalyzer->watchLogFiles(true);
381 
382     QCOMPARE(model->itemCount(), 5);
383 }
384 
385 QTEST_MAIN(SystemAnalyzerTest)
386 
387 #include "systemAnalyzerTest.moc"
388