1 #include "ksysguarddtest.h"
2 #include "processcore/processcore_debug.h"
3 
4 KSGRD::SensorAgent *agent;
5 
6 Q_DECLARE_METATYPE(KSGRD::SensorAgent *)
7 
8 using namespace KSGRD;
initTestCase()9 void TestKsysguardd::initTestCase()
10 {
11     qRegisterMetaType<KSGRD::SensorAgent *>();
12     QCOMPARE(manager.count(), 0);
13     hostConnectionLostSpy = new QSignalSpy(&manager, SIGNAL(hostConnectionLost(QString)));
14     updateSpy = new QSignalSpy(&manager, SIGNAL(update()));
15     hostAddedSpy = new QSignalSpy(&manager, SIGNAL(hostAdded(KSGRD::SensorAgent *, QString)));
16     bool success = manager.engage("", "", "../../ksysguardd/ksysguardd", -1);
17     QCOMPARE(hostAddedSpy->count(), 1);
18     QVERIFY(success);
19     QVERIFY(manager.isConnected(""));
20     QCOMPARE(hostConnectionLostSpy->count(), 0);
21     QCOMPARE(updateSpy->count(), 0);
22     client = new SensorClientTest;
23     nextId = 0;
24 }
init()25 void TestKsysguardd::init()
26 {
27 }
cleanup()28 void TestKsysguardd::cleanup()
29 {
30 }
31 
cleanupTestCase()32 void TestKsysguardd::cleanupTestCase()
33 {
34     QCOMPARE(hostAddedSpy->count(), 1);
35     QCOMPARE(manager.count(), 1);
36     manager.disengage("");
37     QCOMPARE(manager.count(), 0);
38     delete updateSpy;
39     delete hostConnectionLostSpy;
40     delete client;
41 }
42 
testSetup()43 void TestKsysguardd::testSetup()
44 {
45     QCOMPARE(manager.count(), 1);
46     QString shell;
47     QString command;
48     int port;
49     bool success = manager.hostInfo("", shell, command, port);
50     QCOMPARE(success, true);
51     QCOMPARE(shell, QString(""));
52     QCOMPARE(command, QString("../../ksysguardd/ksysguardd"));
53     QCOMPARE(port, -1);
54 
55     success = manager.hostInfo("nonexistent host", shell, command, port);
56     QCOMPARE(success, false);
57 }
58 
59 /** Test the result from ksysguardd for each monitor info and monitor name follows the correct syntax */
testFormatting_data()60 void TestKsysguardd::testFormatting_data()
61 {
62     QTest::addColumn<QByteArray>("monitorName");
63     QTest::addColumn<QByteArray>("monitorType");
64     QTest::addColumn<QByteArray>("monitorInfoName");
65 
66     int id = nextId++;
67     bool success = manager.sendRequest("", "monitors", client, id);
68     QVERIFY(success);
69 
70     QCOMPARE(hostConnectionLostSpy->count(), 0);
71     QCOMPARE(updateSpy->count(), 0);
72 
73     int timeout = 300; // Wait up to 30 seconds
74     while (!client->haveAnswer && !client->isSensorLost && timeout--)
75         QTest::qWait(100);
76     QVERIFY(client->haveAnswer);
77     QVERIFY(!client->isSensorLost);
78     QVERIFY(!client->answers[id].isSensorLost);
79     QCOMPARE(client->answers[id].id, id);
80 
81     QList<QByteArray> monitors = client->answers[id].answer;
82     QVERIFY(!monitors.isEmpty());
83 
84     // We now have a list of all the monitors
85     foreach (const QByteArray &monitor, monitors) {
86         QList<QByteArray> info = monitor.split('\t');
87         QCOMPARE(info.count(), 2);
88         QByteArray monitorName = info[0];
89         QByteArray monitorType = info[1];
90         QByteArray monitorInfoName = monitorName;
91         monitorInfoName.append('?');
92         client->haveAnswer = false;
93         QTest::newRow(monitorName.constData()) << monitorName << monitorType << monitorInfoName;
94     }
95 }
96 
97 // Test each monitor for a correctly formatted info and correctly formatted data
testFormatting()98 void TestKsysguardd::testFormatting()
99 {
100     QFETCH(QByteArray, monitorName);
101     QFETCH(QByteArray, monitorType);
102     QFETCH(QByteArray, monitorInfoName);
103 
104     // Query the monitor for its information
105     if (client->haveAnswer || client->isSensorLost)
106         return; // Skip rest of tests
107     int id = nextId++;
108     bool success = manager.sendRequest("", monitorInfoName, client, id);
109     QVERIFY(success);
110     int timeout = 50;
111     while (!client->haveAnswer && !client->isSensorLost && timeout--)
112         QTest::qWait(10);
113     QVERIFY(client->haveAnswer);
114     QVERIFY(!client->isSensorLost);
115     QVERIFY(!client->answers[id].isSensorLost);
116     QCOMPARE(client->answers[id].id, id);
117 
118     QCOMPARE(updateSpy->count(), 0);
119     QCOMPARE(hostConnectionLostSpy->count(), 0);
120 
121     QList<QByteArray> columnHeadings;
122     QList<QByteArray> columnTypes;
123 
124     // Now check the answer that we got for the monitor information
125     if (monitorType == "integer") {
126         QCOMPARE(client->answers[id].answer.count(), 1);
127         QList<QByteArray> answer = client->answers[id].answer[0].split('\t');
128         QCOMPARE(answer.count(), 4); // Name, minimum value, maximum value, unit
129         QVERIFY(!answer[0].isEmpty()); // Name can't be empty
130         QVERIFY(!answer[1].isEmpty()); // Minimum value cannot be empty
131         QVERIFY(!answer[2].isEmpty()); // Maximum value cannot be empty.  unit can be
132         // Make sure minimum and maximum values are numbers (doubles)
133         bool isNumber;
134         long min = answer[1].toLong(&isNumber); //(note that toLong is in C locale, which is what we want)
135         QVERIFY(isNumber);
136         long max = answer[2].toLong(&isNumber);
137         QVERIFY(isNumber);
138         QVERIFY(min <= max);
139     } else if (monitorType == "float") {
140         QCOMPARE(client->answers[id].answer.count(), 1);
141         QList<QByteArray> answer = client->answers[id].answer[0].split('\t');
142         QCOMPARE(answer.count(), 4); // Name, minimum value, maximum value, unit
143         QVERIFY(!answer[0].isEmpty()); // Name can't be empty
144         QVERIFY(!answer[1].isEmpty()); // Minimum value cannot be empty
145         QVERIFY(!answer[2].isEmpty()); // Maximum value cannot be empty.  unit can be
146         // Make sure minimum and maximum values are numbers (doubles)
147         bool isNumber;
148         double min = answer[1].toDouble(&isNumber); //(note that toDouble is in C locale, which is what we want)
149         QVERIFY(isNumber);
150         double max = answer[2].toDouble(&isNumber);
151         QVERIFY(isNumber);
152         QVERIFY(min <= max);
153     } else if (monitorType == "logfile") {
154         QCOMPARE(client->answers[id].answer.count(), 1);
155         QList<QByteArray> answer = client->answers[id].answer[0].split('\t');
156         QCOMPARE(answer.count(), 1);
157         QCOMPARE(answer[0], QByteArray("LogFile"));
158     } else if (monitorType == "listview" || monitorType == "table") {
159         // listview is two lines.  The first line is the column headings, the second line is the type of each column
160         QCOMPARE(client->answers[id].answer.count(), 2);
161         columnHeadings = client->answers[id].answer[0].split('\t');
162         columnTypes = client->answers[id].answer[1].split('\t');
163         QCOMPARE(columnHeadings.count(), columnTypes.count());
164         // column type is well defined
165         foreach (const QByteArray &columnType, columnTypes) {
166             switch (columnType[0]) {
167             case 's': // string
168             case 'S': // string to translate
169             case 'd': // integer
170             case 'D': // integer to display localized
171             case 'f': // floating point number
172             case 'M': // Disk stat - some special case
173             case '%': // Disk stat - some special case
174                 QCOMPARE(columnType.size(), 1);
175                 break;
176             case 'K': // Disk stat - some special case
177                 QCOMPARE(columnType.size(), 2);
178                 QCOMPARE(columnType, QByteArray("KB"));
179                 break;
180             default:
181                 QVERIFY(false);
182             }
183         }
184     } else if (monitorType == "string") {
185         // TODO Check for something in the answer?
186     } else {
187         QVERIFY(false);
188     }
189 
190     // Now read the actual data for the sensor, and check that it's valid
191     client->haveAnswer = false;
192     id = nextId++;
193     success = manager.sendRequest("", monitorName, client, id);
194     QVERIFY(success);
195     QTime timer;
196     timer.start();
197     timeout = 300; // Wait up to 30 seconds
198     while (!client->haveAnswer && !client->isSensorLost && timeout--)
199         QTest::qWait(100);
200     QVERIFY(client->haveAnswer);
201     QVERIFY(!client->isSensorLost);
202     QCOMPARE(client->answers[id].id, id);
203     if (timer.elapsed() > 200)
204         qCDebug(LIBKSYSGUARD) << monitorName << "took" << timer.elapsed() << "ms";
205 
206     if (monitorType == "integer") {
207         QList<QByteArray> answer = client->answers[id].answer[0].split('\t');
208         QCOMPARE(answer.count(), 1); // Just the number
209         QVERIFY(!answer[0].isEmpty()); // Value cannot be empty
210         // Make sure the value is valid
211         bool isNumber;
212         answer[0].toLong(&isNumber); //(note that toLong is in C locale, which is what we want)
213         QVERIFY(isNumber);
214     } else if (monitorType == "float") {
215         QList<QByteArray> answer = client->answers[id].answer[0].split('\t');
216         QCOMPARE(answer.count(), 1); // Just the number
217         QVERIFY(!answer[0].isEmpty()); // Value cannot be empty
218         // Make sure the value is valid
219         bool isNumber;
220         answer[0].toDouble(&isNumber); //(note that toDouble is in C locale, which is what we want)
221         QVERIFY(isNumber);
222     } else if (monitorType == "listview" || monitorType == "table") {
223         foreach (const QByteArray &row, client->answers[id].answer) {
224             QList<QByteArray> rowData = row.split('\t');
225             QCOMPARE(rowData.count(), columnHeadings.count());
226             for (int column = 0; column < columnHeadings.count(); column++) {
227                 switch (columnTypes[column][0]) {
228                 case 's': // string
229                 case 'S': // string to translate
230                     break;
231                 case 'd': // integer
232                 case 'D': { // integer to display localized
233                     bool isNumber;
234                     rowData[column].toLong(&isNumber);
235                     QVERIFY2(isNumber, (QString("Row data was ") + row).toLatin1().constData());
236                 }
237                 case 'f': { // floating point number
238                     bool isNumber;
239                     rowData[column].toDouble(&isNumber);
240                     QVERIFY(isNumber);
241                 }
242                 case 'M': // Disk stat - some special case
243                     break;
244                 }
245             }
246         }
247     }
248     client->haveAnswer = false;
249 }
250 
testQueueing()251 void TestKsysguardd::testQueueing()
252 {
253     // Send lots of commands to ksysguardd, and check that they all return the right answer, in order
254 
255     delete client; // Start with a new client
256     client = new SensorClientTest;
257 
258     const int N = 1000;
259     for (int i = 0; i < N; i++) {
260         bool success = manager.sendRequest("", "ps", client, i);
261         QVERIFY(success);
262     }
263 
264     int timeout = 300; // Wait up to 30 seconds for a single answer
265     int lastCount = 0;
266     while (client->answers.count() != N && !client->isSensorLost && timeout--) {
267         if (client->answers.count() != lastCount) {
268             lastCount = client->answers.count();
269             timeout = 300; // reset timeout as we get new answers
270         }
271         QTest::qWait(100);
272     }
273     QCOMPARE(client->answers.count(), N);
274 
275     for (int i = 0; i < N; i++) {
276         QCOMPARE(client->answers[i].id, i);
277     }
278 }
279 QTEST_MAIN(TestKsysguardd)
280