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