1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | |
31 | // When using WinSock2 on Windows, it's the first thing that can be included |
32 | // (except qglobal.h), or else you'll get tons of compile errors |
33 | #include <qglobal.h> |
34 | |
35 | // To prevent windows system header files from re-defining min/max |
36 | #define NOMINMAX 1 |
37 | |
38 | #if defined(Q_OS_WIN) |
39 | # include <winsock2.h> |
40 | # include <ws2tcpip.h> |
41 | #endif |
42 | |
43 | #include <QtTest/QtTest> |
44 | #include <qcoreapplication.h> |
45 | #include <QDebug> |
46 | #include <QTcpSocket> |
47 | #include <private/qthread_p.h> |
48 | #include <QTcpServer> |
49 | |
50 | #ifndef QT_NO_BEARERMANAGEMENT |
51 | #include <QtNetwork/qnetworkconfigmanager.h> |
52 | #include <QtNetwork/qnetworkconfiguration.h> |
53 | #include <QtNetwork/qnetworksession.h> |
54 | #endif |
55 | |
56 | #include <time.h> |
57 | #if defined(Q_OS_WIN) |
58 | #include <windows.h> |
59 | #else |
60 | #include <unistd.h> |
61 | #include <signal.h> |
62 | #endif |
63 | |
64 | #include <qhostinfo.h> |
65 | #include "private/qhostinfo_p.h" |
66 | |
67 | #include <sys/types.h> |
68 | #if defined(Q_OS_UNIX) |
69 | # include <sys/socket.h> |
70 | # include <netdb.h> |
71 | #endif |
72 | |
73 | #include "../../../network-settings.h" |
74 | |
75 | #define TEST_DOMAIN ".test.qt-project.org" |
76 | |
77 | |
78 | class tst_QHostInfo : public QObject |
79 | { |
80 | Q_OBJECT |
81 | |
82 | private slots: |
83 | void init(); |
84 | void initTestCase(); |
85 | void swapFunction(); |
86 | void moveOperator(); |
87 | void getSetCheck(); |
88 | void staticInformation(); |
89 | void lookupIPv4_data(); |
90 | void lookupIPv4(); |
91 | void lookupIPv6_data(); |
92 | void lookupIPv6(); |
93 | void lookupConnectToFunctionPointer_data(); |
94 | void lookupConnectToFunctionPointer(); |
95 | void lookupConnectToFunctionPointerDeleted(); |
96 | void lookupConnectToLambda_data(); |
97 | void lookupConnectToLambda(); |
98 | void reverseLookup_data(); |
99 | void reverseLookup(); |
100 | |
101 | void blockingLookup_data(); |
102 | void blockingLookup(); |
103 | |
104 | void raceCondition(); |
105 | void threadSafety(); |
106 | void threadSafetyAsynchronousAPI(); |
107 | |
108 | void multipleSameLookups(); |
109 | void multipleDifferentLookups_data(); |
110 | void multipleDifferentLookups(); |
111 | |
112 | void cache(); |
113 | |
114 | void abortHostLookup(); |
115 | protected slots: |
116 | void resultsReady(const QHostInfo &); |
117 | |
118 | private: |
119 | bool ipv6LookupsAvailable; |
120 | bool ipv6Available; |
121 | bool lookupDone; |
122 | int lookupsDoneCounter; |
123 | QHostInfo lookupResults; |
124 | #ifndef QT_NO_BEARERMANAGEMENT |
125 | QNetworkConfigurationManager *netConfMan; |
126 | QNetworkConfiguration networkConfiguration; |
127 | QScopedPointer<QNetworkSession> networkSession; |
128 | #endif |
129 | }; |
130 | |
131 | void tst_QHostInfo::swapFunction() |
132 | { |
133 | QHostInfo obj1, obj2; |
134 | obj1.setError(QHostInfo::HostInfoError(0)); |
135 | obj2.setError(QHostInfo::HostInfoError(1)); |
136 | obj1.swap(other&: obj2); |
137 | QCOMPARE(QHostInfo::HostInfoError(0), obj2.error()); |
138 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
139 | } |
140 | |
141 | void tst_QHostInfo::moveOperator() |
142 | { |
143 | QHostInfo obj1, obj2, obj3(1); |
144 | obj1.setError(QHostInfo::HostInfoError(0)); |
145 | obj2.setError(QHostInfo::HostInfoError(1)); |
146 | obj1 = std::move(obj2); |
147 | obj2 = obj3; |
148 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
149 | QCOMPARE(obj3.lookupId(), obj2.lookupId()); |
150 | } |
151 | |
152 | |
153 | |
154 | // Testing get/set functions |
155 | void tst_QHostInfo::getSetCheck() |
156 | { |
157 | QHostInfo obj1; |
158 | // HostInfoError QHostInfo::error() |
159 | // void QHostInfo::setError(HostInfoError) |
160 | obj1.setError(QHostInfo::HostInfoError(0)); |
161 | QCOMPARE(QHostInfo::HostInfoError(0), obj1.error()); |
162 | obj1.setError(QHostInfo::HostInfoError(1)); |
163 | QCOMPARE(QHostInfo::HostInfoError(1), obj1.error()); |
164 | |
165 | // int QHostInfo::lookupId() |
166 | // void QHostInfo::setLookupId(int) |
167 | obj1.setLookupId(0); |
168 | QCOMPARE(0, obj1.lookupId()); |
169 | obj1.setLookupId(INT_MIN); |
170 | QCOMPARE(INT_MIN, obj1.lookupId()); |
171 | obj1.setLookupId(INT_MAX); |
172 | QCOMPARE(INT_MAX, obj1.lookupId()); |
173 | } |
174 | |
175 | void tst_QHostInfo::staticInformation() |
176 | { |
177 | qDebug() << "Hostname:" << QHostInfo::localHostName(); |
178 | qDebug() << "Domain name:" << QHostInfo::localDomainName(); |
179 | } |
180 | |
181 | void tst_QHostInfo::initTestCase() |
182 | { |
183 | #ifndef QT_NO_BEARERMANAGEMENT |
184 | //start the default network |
185 | netConfMan = new QNetworkConfigurationManager(this); |
186 | networkConfiguration = netConfMan->defaultConfiguration(); |
187 | networkSession.reset(other: new QNetworkSession(networkConfiguration)); |
188 | if (!networkSession->isOpen()) { |
189 | networkSession->open(); |
190 | networkSession->waitForOpened(msecs: 30000); |
191 | } |
192 | #endif |
193 | |
194 | ipv6Available = false; |
195 | ipv6LookupsAvailable = false; |
196 | |
197 | QTcpServer server; |
198 | if (server.listen(address: QHostAddress("::1" ))) { |
199 | // We have IPv6 support |
200 | ipv6Available = true; |
201 | } |
202 | |
203 | // check if the system getaddrinfo can do IPv6 lookups |
204 | struct addrinfo hint, *result = 0; |
205 | memset(s: &hint, c: 0, n: sizeof hint); |
206 | hint.ai_family = AF_UNSPEC; |
207 | #ifdef AI_ADDRCONFIG |
208 | hint.ai_flags = AI_ADDRCONFIG; |
209 | #endif |
210 | |
211 | int res = getaddrinfo(name: "::1" , service: "80" , req: &hint, pai: &result); |
212 | if (res == 0) { |
213 | // this test worked |
214 | freeaddrinfo(ai: result); |
215 | res = getaddrinfo(name: "aaaa-single" TEST_DOMAIN, service: "80" , req: &hint, pai: &result); |
216 | if (res == 0 && result != 0 && result->ai_family != AF_INET) { |
217 | freeaddrinfo(ai: result); |
218 | ipv6LookupsAvailable = true; |
219 | } |
220 | } |
221 | |
222 | // run each testcase with and without test enabled |
223 | QTest::addColumn<bool>(name: "cache" ); |
224 | QTest::newRow(dataTag: "WithCache" ) << true; |
225 | QTest::newRow(dataTag: "WithoutCache" ) << false; |
226 | } |
227 | |
228 | void tst_QHostInfo::init() |
229 | { |
230 | // delete the cache so inidividual testcase results are independent from each other |
231 | qt_qhostinfo_clear_cache(); |
232 | |
233 | QFETCH_GLOBAL(bool, cache); |
234 | qt_qhostinfo_enable_cache(e: cache); |
235 | } |
236 | |
237 | void tst_QHostInfo::lookupIPv4_data() |
238 | { |
239 | QTest::addColumn<QString>(name: "hostname" ); |
240 | QTest::addColumn<QString>(name: "addresses" ); |
241 | QTest::addColumn<int>(name: "err" ); |
242 | |
243 | QTest::newRow(dataTag: "empty" ) << "" << "" << int(QHostInfo::HostNotFound); |
244 | |
245 | QTest::newRow(dataTag: "single_ip4" ) << "a-single" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError); |
246 | QTest::newRow(dataTag: "multiple_ip4" ) << "a-multi" TEST_DOMAIN << "192.0.2.1 192.0.2.2 192.0.2.3" << int(QHostInfo::NoError); |
247 | QTest::newRow(dataTag: "literal_ip4" ) << "192.0.2.1" << "192.0.2.1" << int(QHostInfo::NoError); |
248 | |
249 | QTest::newRow(dataTag: "notfound" ) << "invalid" TEST_DOMAIN << "" << int(QHostInfo::HostNotFound); |
250 | |
251 | QTest::newRow(dataTag: "idn-ace" ) << "a-single.xn--alqualond-34a" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError); |
252 | QTest::newRow(dataTag: "idn-unicode" ) << QString::fromLatin1(str: "a-single.alqualond\353" TEST_DOMAIN) << "192.0.2.1" << int(QHostInfo::NoError); |
253 | } |
254 | |
255 | void tst_QHostInfo::lookupIPv4() |
256 | { |
257 | QFETCH(QString, hostname); |
258 | QFETCH(int, err); |
259 | QFETCH(QString, addresses); |
260 | |
261 | lookupDone = false; |
262 | QHostInfo::lookupHost(name: hostname, receiver: this, SLOT(resultsReady(QHostInfo))); |
263 | |
264 | QTestEventLoop::instance().enterLoop(secs: 10); |
265 | QVERIFY(!QTestEventLoop::instance().timeout()); |
266 | QVERIFY(lookupDone); |
267 | |
268 | if ((int)lookupResults.error() != (int)err) { |
269 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
270 | } |
271 | QCOMPARE((int)lookupResults.error(), (int)err); |
272 | |
273 | QStringList tmp; |
274 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
275 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
276 | tmp.sort(); |
277 | |
278 | QStringList expected = addresses.split(sep: ' '); |
279 | expected.sort(); |
280 | |
281 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
282 | } |
283 | |
284 | void tst_QHostInfo::lookupIPv6_data() |
285 | { |
286 | QTest::addColumn<QString>(name: "hostname" ); |
287 | QTest::addColumn<QString>(name: "addresses" ); |
288 | QTest::addColumn<int>(name: "err" ); |
289 | |
290 | QTest::newRow(dataTag: "aaaa-single" ) << "aaaa-single" TEST_DOMAIN << "2001:db8::1" << int(QHostInfo::NoError); |
291 | QTest::newRow(dataTag: "aaaa-multi" ) << "aaaa-multi" TEST_DOMAIN << "2001:db8::1 2001:db8::2 2001:db8::3" << int(QHostInfo::NoError); |
292 | QTest::newRow(dataTag: "a-plus-aaaa" ) << "a-plus-aaaa" TEST_DOMAIN << "198.51.100.1 2001:db8::1:1" << int(QHostInfo::NoError); |
293 | |
294 | // avoid using real IPv6 addresses here because this will do a DNS query |
295 | // real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff |
296 | QTest::newRow(dataTag: "literal_ip6" ) << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError); |
297 | QTest::newRow(dataTag: "literal_shortip6" ) << "f001:618:1401::4" << "f001:618:1401::4" << int(QHostInfo::NoError); |
298 | } |
299 | |
300 | void tst_QHostInfo::lookupIPv6() |
301 | { |
302 | QFETCH(QString, hostname); |
303 | QFETCH(int, err); |
304 | QFETCH(QString, addresses); |
305 | |
306 | if (!ipv6LookupsAvailable) |
307 | QSKIP("This platform does not support IPv6 lookups" ); |
308 | |
309 | lookupDone = false; |
310 | QHostInfo::lookupHost(name: hostname, receiver: this, SLOT(resultsReady(QHostInfo))); |
311 | |
312 | QTestEventLoop::instance().enterLoop(secs: 10); |
313 | QVERIFY(!QTestEventLoop::instance().timeout()); |
314 | QVERIFY(lookupDone); |
315 | |
316 | QCOMPARE((int)lookupResults.error(), (int)err); |
317 | |
318 | QStringList tmp; |
319 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
320 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
321 | tmp.sort(); |
322 | |
323 | QStringList expected = addresses.split(sep: ' '); |
324 | expected.sort(); |
325 | |
326 | QCOMPARE(tmp.join(' ').toLower(), expected.join(' ').toLower()); |
327 | } |
328 | |
329 | void tst_QHostInfo::lookupConnectToFunctionPointer_data() |
330 | { |
331 | lookupIPv4_data(); |
332 | } |
333 | |
334 | void tst_QHostInfo::lookupConnectToFunctionPointer() |
335 | { |
336 | QFETCH(QString, hostname); |
337 | QFETCH(int, err); |
338 | QFETCH(QString, addresses); |
339 | |
340 | lookupDone = false; |
341 | QHostInfo::lookupHost(name: hostname, receiver: this, slot: &tst_QHostInfo::resultsReady); |
342 | |
343 | QTestEventLoop::instance().enterLoop(secs: 10); |
344 | QVERIFY(!QTestEventLoop::instance().timeout()); |
345 | QVERIFY(lookupDone); |
346 | |
347 | if (int(lookupResults.error()) != int(err)) |
348 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
349 | QCOMPARE(int(lookupResults.error()), int(err)); |
350 | |
351 | QStringList tmp; |
352 | for (const auto &result : lookupResults.addresses()) |
353 | tmp.append(t: result.toString()); |
354 | tmp.sort(); |
355 | |
356 | QStringList expected = addresses.split(sep: ' '); |
357 | expected.sort(); |
358 | |
359 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
360 | } |
361 | |
362 | void tst_QHostInfo::lookupConnectToFunctionPointerDeleted() |
363 | { |
364 | { |
365 | QObject contextObject; |
366 | QHostInfo::lookupHost(name: "localhost" , context: &contextObject, slot: [](const QHostInfo){ |
367 | QFAIL("This should never be called!" ); |
368 | }); |
369 | } |
370 | QTestEventLoop::instance().enterLoop(secs: 3); |
371 | } |
372 | |
373 | void tst_QHostInfo::lookupConnectToLambda_data() |
374 | { |
375 | lookupIPv4_data(); |
376 | } |
377 | |
378 | void tst_QHostInfo::lookupConnectToLambda() |
379 | { |
380 | QFETCH(QString, hostname); |
381 | QFETCH(int, err); |
382 | QFETCH(QString, addresses); |
383 | |
384 | lookupDone = false; |
385 | QHostInfo::lookupHost(name: hostname, slot: [=](const QHostInfo &hostInfo) { |
386 | resultsReady(hostInfo); |
387 | }); |
388 | |
389 | QTestEventLoop::instance().enterLoop(secs: 10); |
390 | QVERIFY(!QTestEventLoop::instance().timeout()); |
391 | QVERIFY(lookupDone); |
392 | |
393 | if (int(lookupResults.error()) != int(err)) |
394 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
395 | QCOMPARE(int(lookupResults.error()), int(err)); |
396 | |
397 | QStringList tmp; |
398 | for (int i = 0; i < lookupResults.addresses().count(); ++i) |
399 | tmp.append(t: lookupResults.addresses().at(i).toString()); |
400 | tmp.sort(); |
401 | |
402 | QStringList expected = addresses.split(sep: ' '); |
403 | expected.sort(); |
404 | |
405 | QCOMPARE(tmp.join(' '), expected.join(' ')); |
406 | } |
407 | |
408 | static QStringList reverseLookupHelper(const QString &ip) |
409 | { |
410 | QStringList results; |
411 | |
412 | const QString pythonCode = |
413 | "import socket;" |
414 | "import sys;" |
415 | "print (socket.getnameinfo((sys.argv[1], 0), 0)[0]);" ; |
416 | |
417 | QList<QByteArray> lines; |
418 | QProcess python; |
419 | python.setProcessChannelMode(QProcess::ForwardedErrorChannel); |
420 | python.start(program: "python" , arguments: QStringList() << QString("-c" ) << pythonCode << ip); |
421 | if (python.waitForFinished()) { |
422 | if (python.exitStatus() == QProcess::NormalExit && python.exitCode() == 0) |
423 | lines = python.readAllStandardOutput().split(sep: '\n'); |
424 | for (QByteArray line : lines) { |
425 | if (!line.isEmpty()) |
426 | results << line.trimmed(); |
427 | } |
428 | if (!results.isEmpty()) |
429 | return results; |
430 | } |
431 | |
432 | qDebug() << "Python failed, falling back to nslookup" ; |
433 | QProcess lookup; |
434 | lookup.setProcessChannelMode(QProcess::ForwardedErrorChannel); |
435 | lookup.start(program: "nslookup" , arguments: QStringList(ip)); |
436 | if (!lookup.waitForFinished()) { |
437 | results << "nslookup failure" ; |
438 | qDebug() << "nslookup failure" ; |
439 | return results; |
440 | } |
441 | lines = lookup.readAllStandardOutput().split(sep: '\n'); |
442 | |
443 | QByteArray name; |
444 | |
445 | const QByteArray nameMarkerNix("name =" ); |
446 | const QByteArray nameMarkerWin("Name:" ); |
447 | const QByteArray addressMarkerWin("Address:" ); |
448 | |
449 | for (QByteArray line : lines) { |
450 | int index = -1; |
451 | if ((index = line.indexOf(a: nameMarkerNix)) != -1) { // Linux and macOS |
452 | name = line.mid(index: index + nameMarkerNix.length()).chopped(len: 1).trimmed(); |
453 | results << name; |
454 | } else if (line.startsWith(a: nameMarkerWin)) { // Windows formatting |
455 | name = line.mid(index: line.lastIndexOf(c: " " )).trimmed(); |
456 | } else if (line.startsWith(a: addressMarkerWin)) { |
457 | QByteArray address = line.mid(index: addressMarkerWin.length()).trimmed(); |
458 | if (address == ip.toUtf8()) { |
459 | results << name; |
460 | } |
461 | } |
462 | } |
463 | |
464 | if (results.isEmpty()) { |
465 | qDebug() << "Failure to parse nslookup output: " << lines; |
466 | } |
467 | return results; |
468 | } |
469 | |
470 | void tst_QHostInfo::reverseLookup_data() |
471 | { |
472 | QTest::addColumn<QString>(name: "address" ); |
473 | QTest::addColumn<QStringList>(name: "hostNames" ); |
474 | QTest::addColumn<int>(name: "err" ); |
475 | QTest::addColumn<bool>(name: "ipv6" ); |
476 | |
477 | QTest::newRow(dataTag: "dns.google" ) << QString("8.8.8.8" ) << reverseLookupHelper(ip: "8.8.8.8" ) << 0 << false; |
478 | QTest::newRow(dataTag: "one.one.one.one" ) << QString("1.1.1.1" ) << reverseLookupHelper(ip: "1.1.1.1" ) << 0 << false; |
479 | QTest::newRow(dataTag: "dns.google IPv6" ) << QString("2001:4860:4860::8888" ) << reverseLookupHelper(ip: "2001:4860:4860::8888" ) << 0 << true; |
480 | QTest::newRow(dataTag: "cloudflare IPv6" ) << QString("2606:4700:4700::1111" ) << reverseLookupHelper(ip: "2606:4700:4700::1111" ) << 0 << true; |
481 | QTest::newRow(dataTag: "bogus-name IPv6" ) << QString("1::2::3::4" ) << QStringList() << 1 << true; |
482 | } |
483 | |
484 | void tst_QHostInfo::reverseLookup() |
485 | { |
486 | QFETCH(QString, address); |
487 | QFETCH(QStringList, hostNames); |
488 | QFETCH(int, err); |
489 | QFETCH(bool, ipv6); |
490 | |
491 | if (ipv6 && !ipv6LookupsAvailable) { |
492 | QSKIP("IPv6 reverse lookups are not supported on this platform" ); |
493 | } |
494 | |
495 | QHostInfo info = QHostInfo::fromName(name: address); |
496 | |
497 | if (err == 0) { |
498 | if (!hostNames.contains(str: info.hostName())) |
499 | qDebug() << "Failure: expecting" << hostNames << ",got " << info.hostName(); |
500 | QVERIFY(hostNames.contains(info.hostName())); |
501 | QCOMPARE(info.addresses().first(), QHostAddress(address)); |
502 | } else { |
503 | QCOMPARE(info.hostName(), address); |
504 | QCOMPARE(info.error(), QHostInfo::HostNotFound); |
505 | } |
506 | |
507 | } |
508 | |
509 | void tst_QHostInfo::blockingLookup_data() |
510 | { |
511 | lookupIPv4_data(); |
512 | if (ipv6LookupsAvailable) |
513 | lookupIPv6_data(); |
514 | } |
515 | |
516 | void tst_QHostInfo::blockingLookup() |
517 | { |
518 | QFETCH(QString, hostname); |
519 | QFETCH(int, err); |
520 | QFETCH(QString, addresses); |
521 | |
522 | QHostInfo hostInfo = QHostInfo::fromName(name: hostname); |
523 | QStringList tmp; |
524 | for (int i = 0; i < hostInfo.addresses().count(); ++i) |
525 | tmp.append(t: hostInfo.addresses().at(i).toString()); |
526 | tmp.sort(); |
527 | |
528 | if ((int)hostInfo.error() != (int)err) { |
529 | qWarning() << hostname << "=>" << lookupResults.errorString(); |
530 | } |
531 | QCOMPARE((int)hostInfo.error(), (int)err); |
532 | |
533 | QStringList expected = addresses.split(sep: ' '); |
534 | expected.sort(); |
535 | |
536 | QCOMPARE(tmp.join(' ').toUpper(), expected.join(' ').toUpper()); |
537 | } |
538 | |
539 | void tst_QHostInfo::raceCondition() |
540 | { |
541 | for (int i = 0; i < 1000; ++i) { |
542 | QTcpSocket socket; |
543 | socket.connectToHost(hostName: "invalid" TEST_DOMAIN, port: 80); |
544 | } |
545 | } |
546 | |
547 | class LookupThread : public QThread |
548 | { |
549 | protected: |
550 | inline void run() |
551 | { |
552 | QHostInfo info = QHostInfo::fromName(name: "a-single" TEST_DOMAIN); |
553 | QCOMPARE(info.error(), QHostInfo::NoError); |
554 | QVERIFY(info.addresses().count() > 0); |
555 | QCOMPARE(info.addresses().at(0).toString(), QString("192.0.2.1" )); |
556 | } |
557 | }; |
558 | |
559 | void tst_QHostInfo::threadSafety() |
560 | { |
561 | const int nattempts = 5; |
562 | const int runs = 100; |
563 | LookupThread thr[nattempts]; |
564 | for (int j = 0; j < runs; ++j) { |
565 | for (int i = 0; i < nattempts; ++i) |
566 | thr[i].start(); |
567 | for (int k = nattempts - 1; k >= 0; --k) |
568 | thr[k].wait(); |
569 | } |
570 | } |
571 | |
572 | class LookupReceiver : public QObject |
573 | { |
574 | Q_OBJECT |
575 | public slots: |
576 | void start(); |
577 | void resultsReady(const QHostInfo&); |
578 | public: |
579 | QHostInfo result; |
580 | int numrequests; |
581 | }; |
582 | |
583 | void LookupReceiver::start() |
584 | { |
585 | for (int i=0;i<numrequests;i++) |
586 | QHostInfo::lookupHost(name: QString("a-single" TEST_DOMAIN), receiver: this, SLOT(resultsReady(QHostInfo))); |
587 | } |
588 | |
589 | void LookupReceiver::resultsReady(const QHostInfo &info) |
590 | { |
591 | result = info; |
592 | numrequests--; |
593 | if (numrequests == 0 || info.error() != QHostInfo::NoError) |
594 | QThread::currentThread()->quit(); |
595 | } |
596 | |
597 | void tst_QHostInfo::threadSafetyAsynchronousAPI() |
598 | { |
599 | const int nattempts = 10; |
600 | const int lookupsperthread = 10; |
601 | QList<QThread*> threads; |
602 | QList<LookupReceiver*> receivers; |
603 | for (int i = 0; i < nattempts; ++i) { |
604 | QThread* thread = new QThread; |
605 | LookupReceiver* receiver = new LookupReceiver; |
606 | receiver->numrequests = lookupsperthread; |
607 | receivers.append(t: receiver); |
608 | receiver->moveToThread(thread); |
609 | connect(sender: thread, SIGNAL(started()), receiver, SLOT(start())); |
610 | thread->start(); |
611 | threads.append(t: thread); |
612 | } |
613 | for (int k = threads.count() - 1; k >= 0; --k) |
614 | QVERIFY(threads.at(k)->wait(60000)); |
615 | foreach (LookupReceiver* receiver, receivers) { |
616 | QCOMPARE(receiver->result.error(), QHostInfo::NoError); |
617 | QCOMPARE(receiver->result.addresses().at(0).toString(), QString("192.0.2.1" )); |
618 | QCOMPARE(receiver->numrequests, 0); |
619 | } |
620 | } |
621 | |
622 | // this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, |
623 | // not about getting correct IPs |
624 | void tst_QHostInfo::multipleSameLookups() |
625 | { |
626 | const int COUNT = 10; |
627 | lookupsDoneCounter = 0; |
628 | |
629 | for (int i = 0; i < COUNT; i++) |
630 | QHostInfo::lookupHost(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo))); |
631 | |
632 | QElapsedTimer timer; |
633 | timer.start(); |
634 | while (timer.elapsed() < 10000 && lookupsDoneCounter < COUNT) { |
635 | QTestEventLoop::instance().enterLoop(secs: 2); |
636 | } |
637 | QCOMPARE(lookupsDoneCounter, COUNT); |
638 | } |
639 | |
640 | // this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all, |
641 | // not about getting correct IPs |
642 | void tst_QHostInfo::multipleDifferentLookups_data() |
643 | { |
644 | QTest::addColumn<int>(name: "repeats" ); |
645 | QTest::newRow(dataTag: "1" ) << 1; |
646 | QTest::newRow(dataTag: "2" ) << 2; |
647 | QTest::newRow(dataTag: "5" ) << 5; |
648 | QTest::newRow(dataTag: "10" ) << 10; |
649 | } |
650 | |
651 | void tst_QHostInfo::multipleDifferentLookups() |
652 | { |
653 | QStringList hostnameList; |
654 | hostnameList << "a-single" TEST_DOMAIN |
655 | << "a-multi" TEST_DOMAIN |
656 | << "aaaa-single" TEST_DOMAIN |
657 | << "aaaa-multi" TEST_DOMAIN |
658 | << "a-plus-aaaa" TEST_DOMAIN |
659 | << "multi" TEST_DOMAIN |
660 | << "localhost" TEST_DOMAIN |
661 | << "cname" TEST_DOMAIN |
662 | << "127.0.0.1" << "----" ; |
663 | |
664 | QFETCH(int, repeats); |
665 | const int COUNT = hostnameList.size(); |
666 | lookupsDoneCounter = 0; |
667 | |
668 | for (int i = 0; i < hostnameList.size(); i++) |
669 | for (int j = 0; j < repeats; ++j) |
670 | QHostInfo::lookupHost(name: hostnameList.at(i), receiver: this, SLOT(resultsReady(QHostInfo))); |
671 | |
672 | QElapsedTimer timer; |
673 | timer.start(); |
674 | while (timer.elapsed() < 60000 && lookupsDoneCounter < repeats*COUNT) { |
675 | QTestEventLoop::instance().enterLoop(secs: 2); |
676 | //qDebug() << "t:" << timer.elapsed(); |
677 | } |
678 | QCOMPARE(lookupsDoneCounter, repeats*COUNT); |
679 | } |
680 | |
681 | void tst_QHostInfo::cache() |
682 | { |
683 | QFETCH_GLOBAL(bool, cache); |
684 | if (!cache) |
685 | return; // test makes only sense when cache enabled |
686 | |
687 | // reset slot counter |
688 | lookupsDoneCounter = 0; |
689 | |
690 | // lookup once, wait in event loop, result should not come directly. |
691 | bool valid = true; |
692 | int id = -1; |
693 | QHostInfo result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
694 | QTestEventLoop::instance().enterLoop(secs: 5); |
695 | QVERIFY(!QTestEventLoop::instance().timeout()); |
696 | QVERIFY(!valid); |
697 | QVERIFY(result.addresses().isEmpty()); |
698 | |
699 | // loopkup second time, result should come directly |
700 | valid = false; |
701 | result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
702 | QVERIFY(valid); |
703 | QVERIFY(!result.addresses().isEmpty()); |
704 | |
705 | // clear the cache |
706 | qt_qhostinfo_clear_cache(); |
707 | |
708 | // lookup third time, result should not come directly. |
709 | valid = true; |
710 | result = qt_qhostinfo_lookup(name: "localhost" , receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
711 | QTestEventLoop::instance().enterLoop(secs: 5); |
712 | QVERIFY(!QTestEventLoop::instance().timeout()); |
713 | QVERIFY(!valid); |
714 | QVERIFY(result.addresses().isEmpty()); |
715 | |
716 | // the slot should have been called 2 times. |
717 | QCOMPARE(lookupsDoneCounter, 2); |
718 | } |
719 | |
720 | void tst_QHostInfo::resultsReady(const QHostInfo &hi) |
721 | { |
722 | QVERIFY(QThread::currentThread() == thread()); |
723 | lookupDone = true; |
724 | lookupResults = hi; |
725 | lookupsDoneCounter++; |
726 | QTestEventLoop::instance().exitLoop(); |
727 | } |
728 | |
729 | void tst_QHostInfo::abortHostLookup() |
730 | { |
731 | //reset counter |
732 | lookupsDoneCounter = 0; |
733 | bool valid = false; |
734 | int id = -1; |
735 | QHostInfo result = qt_qhostinfo_lookup(name: "a-single" TEST_DOMAIN, receiver: this, SLOT(resultsReady(QHostInfo)), valid: &valid, id: &id); |
736 | QVERIFY(!valid); |
737 | //it is assumed that the DNS request/response in the backend is slower than it takes to call abort |
738 | QHostInfo::abortHostLookup(lookupId: id); |
739 | QTestEventLoop::instance().enterLoop(secs: 5); |
740 | QCOMPARE(lookupsDoneCounter, 0); |
741 | } |
742 | |
743 | class LookupAborter : public QObject |
744 | { |
745 | Q_OBJECT |
746 | public slots: |
747 | void abort() |
748 | { |
749 | QHostInfo::abortHostLookup(lookupId: id); |
750 | QThread::currentThread()->quit(); |
751 | } |
752 | public: |
753 | int id; |
754 | }; |
755 | |
756 | QTEST_MAIN(tst_QHostInfo) |
757 | #include "tst_qhostinfo.moc" |
758 | |