1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | #include <QLocalServer> |
31 | #include <QLocalSocket> |
32 | #include <QTimer> |
33 | #include "../../qbearertestcommon.h" |
34 | |
35 | #ifndef QT_NO_BEARERMANAGEMENT |
36 | #include <QtNetwork/qnetworkconfigmanager.h> |
37 | #include <QtNetwork/qnetworksession.h> |
38 | #include <private/qnetworksession_p.h> |
39 | #endif |
40 | |
41 | QT_USE_NAMESPACE |
42 | |
43 | // Can be used to configure tests that require manual attention (such as roaming) |
44 | //#define QNETWORKSESSION_MANUAL_TESTS 1 |
45 | |
46 | #ifndef QT_NO_BEARERMANAGEMENT |
47 | Q_DECLARE_METATYPE(QNetworkConfiguration::Type) |
48 | #endif |
49 | |
50 | class tst_QNetworkSession : public QObject |
51 | { |
52 | Q_OBJECT |
53 | |
54 | #ifndef QT_NO_BEARERMANAGEMENT |
55 | public slots: |
56 | void initTestCase(); |
57 | void cleanupTestCase(); |
58 | |
59 | private slots: |
60 | void robustnessBombing(); |
61 | |
62 | void sessionClosing_data(); |
63 | void sessionClosing(); |
64 | |
65 | void outOfProcessSession(); |
66 | void invalidSession(); |
67 | |
68 | void repeatedOpenClose_data(); |
69 | void repeatedOpenClose(); |
70 | |
71 | void sessionProperties_data(); |
72 | void sessionProperties(); |
73 | |
74 | void userChoiceSession_data(); |
75 | void userChoiceSession(); |
76 | |
77 | void sessionOpenCloseStop_data(); |
78 | void sessionOpenCloseStop(); |
79 | |
80 | void sessionAutoClose_data(); |
81 | void sessionAutoClose(); |
82 | |
83 | void usagePolicies(); |
84 | |
85 | private: |
86 | QNetworkConfigurationManager manager; |
87 | int inProcessSessionManagementCount; |
88 | QString lackeyDir; |
89 | #endif |
90 | }; |
91 | |
92 | #ifndef QT_NO_BEARERMANAGEMENT |
93 | // Helper functions |
94 | bool openSession(QNetworkSession *session); |
95 | bool closeSession(QNetworkSession *session, bool lastSessionOnConfiguration = true); |
96 | void updateConfigurations(); |
97 | void printConfigurations(); |
98 | QNetworkConfiguration suitableConfiguration(QString bearerType, QNetworkConfiguration::Type configType); |
99 | |
100 | void tst_QNetworkSession::initTestCase() |
101 | { |
102 | qRegisterMetaType<QNetworkConfiguration>(typeName: "QNetworkConfiguration" ); |
103 | qRegisterMetaType<QNetworkConfiguration::Type>(typeName: "QNetworkConfiguration::Type" ); |
104 | |
105 | inProcessSessionManagementCount = -1; |
106 | |
107 | QSignalSpy spy(&manager, SIGNAL(updateCompleted())); |
108 | manager.updateConfigurations(); |
109 | QTRY_VERIFY_WITH_TIMEOUT(spy.count() >= 1, TestTimeOut); |
110 | |
111 | lackeyDir = QFINDTESTDATA("lackey" ); |
112 | QVERIFY2(!lackeyDir.isEmpty(), qPrintable( |
113 | QString::fromLatin1("Couldn't find lackey dir starting from %1." ).arg(QDir::currentPath()))); |
114 | } |
115 | |
116 | void tst_QNetworkSession::cleanupTestCase() |
117 | { |
118 | if (!(manager.capabilities() & QNetworkConfigurationManager::SystemSessionSupport) && |
119 | (manager.capabilities() & QNetworkConfigurationManager::CanStartAndStopInterfaces) && |
120 | inProcessSessionManagementCount == 0) { |
121 | qWarning(msg: "No usable configurations found to complete all possible tests in " |
122 | "inProcessSessionManagement()" ); |
123 | } |
124 | } |
125 | |
126 | // Robustness test for calling interfaces in nonsense order / with nonsense parameters |
127 | void tst_QNetworkSession::robustnessBombing() |
128 | { |
129 | QNetworkConfigurationManager mgr; |
130 | QNetworkSession testSession(mgr.defaultConfiguration()); |
131 | // Should not reset even session is not opened |
132 | testSession.migrate(); |
133 | testSession.accept(); |
134 | testSession.ignore(); |
135 | testSession.reject(); |
136 | } |
137 | |
138 | void tst_QNetworkSession::sessionClosing_data() { |
139 | QTest::addColumn<QString>(name: "bearerType" ); |
140 | QTest::addColumn<QNetworkConfiguration::Type>(name: "configurationType" ); |
141 | |
142 | QTest::newRow(dataTag: "WLAN_IAP" ) << "WLAN" << QNetworkConfiguration::InternetAccessPoint; |
143 | QTest::newRow(dataTag: "Cellular_IAP" ) << "cellular" << QNetworkConfiguration::InternetAccessPoint; |
144 | QTest::newRow(dataTag: "SNAP" ) << "bearer_type_not_relevant_with_SNAPs" << QNetworkConfiguration::ServiceNetwork; |
145 | } |
146 | |
147 | // Testcase for closing the session at unexpected times |
148 | void tst_QNetworkSession::sessionClosing() |
149 | { |
150 | QFETCH(QString, bearerType); |
151 | QFETCH(QNetworkConfiguration::Type, configurationType); |
152 | |
153 | // Update configurations so that WLANs are discovered too. |
154 | updateConfigurations(); |
155 | |
156 | // First check that opening once succeeds and determine if test is doable |
157 | QNetworkConfiguration config = suitableConfiguration(bearerType, configType: configurationType); |
158 | if (!config.isValid()) |
159 | QSKIP("No suitable configurations, skipping this round of repeated open-close test." ); |
160 | qDebug() << "Using following configuration to bomb with close(): " << config.name(); |
161 | QNetworkSession session(config); |
162 | if (!openSession(session: &session) || !closeSession(session: &session)) |
163 | QSKIP("Unable to open/close session, skipping this round of close() bombing." ); |
164 | |
165 | qDebug() << "Closing without issuing open()" ; |
166 | session.close(); |
167 | |
168 | for (int i = 0; i < 25; i++) { |
169 | qDebug() << "Opening and then waiting: " << i * 100 << " ms before closing." ; |
170 | session.open(); |
171 | QTest::qWait(ms: i*100); |
172 | session.close(); |
173 | // Sooner or later session must end in Disconnected state, |
174 | // no matter what the phase was. |
175 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Disconnected, TestTimeOut); |
176 | QTest::qWait(ms: 200); // Give platform a breathe, otherwise we'll be catching other errors |
177 | } |
178 | } |
179 | |
180 | void tst_QNetworkSession::invalidSession() |
181 | { |
182 | // 1. Verify that session created with invalid configuration remains in invalid state |
183 | QNetworkSession session(QNetworkConfiguration(), 0); |
184 | QVERIFY(!session.isOpen()); |
185 | QCOMPARE(session.state(), QNetworkSession::Invalid); |
186 | QCOMPARE(session.error(), QNetworkSession::InvalidConfigurationError); |
187 | |
188 | // 2. Verify that opening session with invalid configuration both 1) emits invalidconfigurationerror and 2) sets session's state as invalid. |
189 | QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); |
190 | session.open(); |
191 | session.waitForOpened(msecs: 1000); // Should bail out right away |
192 | QCOMPARE(errorSpy.count(), 1); |
193 | QNetworkSession::SessionError error = |
194 | qvariant_cast<QNetworkSession::SessionError> (v: errorSpy.first().at(i: 0)); |
195 | QCOMPARE(error, QNetworkSession::InvalidConfigurationError); |
196 | QCOMPARE(session.error(), QNetworkSession::InvalidConfigurationError); |
197 | QCOMPARE(session.state(), QNetworkSession::Invalid); |
198 | |
199 | #ifdef QNETWORKSESSION_MANUAL_TESTS |
200 | |
201 | QNetworkConfiguration invalidatedConfig = suitableConfiguration("WLAN" ,QNetworkConfiguration::InternetAccessPoint); |
202 | if (invalidatedConfig.isValid()) { |
203 | // 3. Verify that invalidating a session after its successfully configured works |
204 | QNetworkSession invalidatedSession(invalidatedConfig); |
205 | qDebug() << "Delete the WLAN IAP from phone now (waiting 60 seconds): " << invalidatedConfig.name(); |
206 | QTest::qWait(60000); |
207 | QVERIFY(!invalidatedConfig.isValid()); |
208 | QCOMPARE(invalidatedSession.state(), QNetworkSession::Invalid); |
209 | qDebug() << "Add the WLAN IAP back (waiting 60 seconds): " << invalidatedConfig.name(); |
210 | QTest::qWait(60000); |
211 | } |
212 | |
213 | QNetworkConfiguration definedConfig = suitableConfiguration("WLAN" ,QNetworkConfiguration::InternetAccessPoint); |
214 | if (definedConfig.isValid()) { |
215 | // 4. Verify that opening a session with defined configuration emits error and enters notavailable-state |
216 | // TODO these timer waits should be changed to waiting appropriate signals, now these wait excessively |
217 | qDebug() << "Shutdown WLAN IAP (waiting 60 seconds): " << definedConfig.name(); |
218 | QTest::qWait(60000); |
219 | // Shutting down WLAN should bring back to defined -state. |
220 | QVERIFY((definedConfig.state() & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined); |
221 | QNetworkSession definedSession(definedConfig); |
222 | QSignalSpy errorSpy(&definedSession, SIGNAL(error(QNetworkSession::SessionError))); |
223 | QNetworkSession::SessionError sessionError; |
224 | updateConfigurations(); |
225 | |
226 | definedSession.open(); |
227 | updateConfigurations(); |
228 | |
229 | QVERIFY(definedConfig.isValid()); // Session remains valid |
230 | QVERIFY(definedSession.state() == QNetworkSession::NotAvailable); // State is not available because WLAN is not in coverage |
231 | QVERIFY(!errorSpy.isEmpty()); // Session tells with error about invalidated configuration |
232 | sessionError = qvariant_cast<QNetworkSession::SessionError> (errorSpy.first().at(0)); |
233 | QCOMPARE(sessionError, QNetworkSession::InvalidConfigurationError); |
234 | qDebug() << "Turn the WLAN IAP back on (waiting 60 seconds): " << definedConfig.name(); |
235 | QTest::qWait(60000); |
236 | updateConfigurations(); |
237 | QCOMPARE(definedConfig.state(), QNetworkConfiguration::Discovered); |
238 | } |
239 | #endif |
240 | } |
241 | |
242 | void tst_QNetworkSession::sessionProperties_data() |
243 | { |
244 | QTest::addColumn<QNetworkConfiguration>(name: "configuration" ); |
245 | |
246 | QTest::newRow(dataTag: "invalid configuration" ) << QNetworkConfiguration(); |
247 | |
248 | foreach (const QNetworkConfiguration &config, manager.allConfigurations()) { |
249 | const QString name = config.name().isEmpty() ? QString("<Hidden>" ) : config.name(); |
250 | QTest::newRow(dataTag: name.toLocal8Bit().constData()) << config; |
251 | } |
252 | } |
253 | |
254 | void tst_QNetworkSession::sessionProperties() |
255 | { |
256 | QFETCH(QNetworkConfiguration, configuration); |
257 | QNetworkSession session(configuration); |
258 | QCOMPARE(session.configuration(), configuration); |
259 | QStringList validBearerNames = QStringList() << QLatin1String("Unknown" ) |
260 | << QLatin1String("Ethernet" ) |
261 | << QLatin1String("WLAN" ) |
262 | << QLatin1String("2G" ) |
263 | << QLatin1String("CDMA2000" ) |
264 | << QLatin1String("WCDMA" ) |
265 | << QLatin1String("HSPA" ) |
266 | << QLatin1String("Bluetooth" ) |
267 | << QLatin1String("WiMAX" ) |
268 | << QLatin1String("BearerEVDO" ) |
269 | << QLatin1String("BearerLTE" ) |
270 | << QLatin1String("Bearer3G" ) |
271 | << QLatin1String("Bearer4G" ); |
272 | |
273 | if (!configuration.isValid()) { |
274 | QVERIFY(configuration.bearerTypeName().isEmpty()); |
275 | } else { |
276 | switch (configuration.type()) |
277 | { |
278 | case QNetworkConfiguration::ServiceNetwork: |
279 | case QNetworkConfiguration::UserChoice: |
280 | default: |
281 | QVERIFY(configuration.bearerTypeName().isEmpty()); |
282 | break; |
283 | case QNetworkConfiguration::InternetAccessPoint: |
284 | QVERIFY(validBearerNames.contains(configuration.bearerTypeName())); |
285 | break; |
286 | } |
287 | } |
288 | |
289 | // QNetworkSession::interface() should return an invalid interface unless |
290 | // session is in the connected state. |
291 | #ifndef QT_NO_NETWORKINTERFACE |
292 | QCOMPARE(session.state() == QNetworkSession::Connected, session.interface().isValid()); |
293 | #endif |
294 | |
295 | if (!configuration.isValid()) { |
296 | QVERIFY(configuration.state() == QNetworkConfiguration::Undefined && |
297 | session.state() == QNetworkSession::Invalid); |
298 | } else { |
299 | switch (configuration.state()) { |
300 | case QNetworkConfiguration::Undefined: |
301 | QCOMPARE(session.state(), QNetworkSession::NotAvailable); |
302 | break; |
303 | case QNetworkConfiguration::Defined: |
304 | QCOMPARE(session.state(), QNetworkSession::NotAvailable); |
305 | break; |
306 | case QNetworkConfiguration::Discovered: |
307 | QVERIFY(session.state() == QNetworkSession::Connecting || |
308 | session.state() == QNetworkSession::Disconnected); |
309 | break; |
310 | case QNetworkConfiguration::Active: |
311 | QVERIFY(session.state() == QNetworkSession::Connected || |
312 | session.state() == QNetworkSession::Closing || |
313 | session.state() == QNetworkSession::Roaming); |
314 | break; |
315 | default: |
316 | QFAIL("Invalid configuration state" ); |
317 | }; |
318 | } |
319 | } |
320 | |
321 | void tst_QNetworkSession::repeatedOpenClose_data() { |
322 | QTest::addColumn<QString>(name: "bearerType" ); |
323 | QTest::addColumn<QNetworkConfiguration::Type>(name: "configurationType" ); |
324 | QTest::addColumn<int>(name: "repeatTimes" ); |
325 | |
326 | QTest::newRow(dataTag: "WLAN_IAP" ) << "WLAN" << QNetworkConfiguration::InternetAccessPoint << 3; |
327 | // QTest::newRow("Cellular_IAP") << "cellular" << QNetworkConfiguration::InternetAccessPoint << 3; |
328 | // QTest::newRow("SNAP") << "bearer_type_not_relevant_with_SNAPs" << QNetworkConfiguration::ServiceNetwork << 3; |
329 | } |
330 | |
331 | // Tests repeated-open close. |
332 | void tst_QNetworkSession::repeatedOpenClose() |
333 | { |
334 | QFETCH(QString, bearerType); |
335 | QFETCH(QNetworkConfiguration::Type, configurationType); |
336 | QFETCH(int, repeatTimes); |
337 | |
338 | // First check that opening once succeeds and determine if repeatable testing is doable |
339 | QNetworkConfiguration config = suitableConfiguration(bearerType, configType: configurationType); |
340 | if (!config.isValid()) |
341 | QSKIP("No suitable configurations, skipping this round of repeated open-close test." ); |
342 | qDebug() << "Using following configuratio to repeatedly open and close: " << config.name(); |
343 | QNetworkSession permanentSession(config); |
344 | if (!openSession(session: &permanentSession) || !closeSession(session: &permanentSession)) |
345 | QSKIP("Unable to open/close session, skipping this round of repeated open-close test." ); |
346 | for (int i = 0; i < repeatTimes; i++) { |
347 | qDebug() << "Opening, loop number " << i; |
348 | QVERIFY(openSession(&permanentSession)); |
349 | qDebug() << "Closing, loop number, then waiting 5 seconds: " << i; |
350 | QVERIFY(closeSession(&permanentSession)); |
351 | QTest::qWait(ms: 5000); |
352 | } |
353 | } |
354 | |
355 | void tst_QNetworkSession::userChoiceSession_data() |
356 | { |
357 | QTest::addColumn<QNetworkConfiguration>(name: "configuration" ); |
358 | |
359 | QNetworkConfiguration config = manager.defaultConfiguration(); |
360 | if (config.type() == QNetworkConfiguration::UserChoice) |
361 | QTest::newRow(dataTag: "UserChoice" ) << config; |
362 | else |
363 | QSKIP("Default configuration is not a UserChoice configuration." ); |
364 | } |
365 | |
366 | void tst_QNetworkSession::userChoiceSession() |
367 | { |
368 | QFETCH(QNetworkConfiguration, configuration); |
369 | |
370 | QCOMPARE(configuration.type(), QNetworkConfiguration::UserChoice); |
371 | |
372 | QNetworkSession session(configuration); |
373 | |
374 | // Check that configuration was really set |
375 | QCOMPARE(session.configuration(), configuration); |
376 | |
377 | QVERIFY(!session.isOpen()); |
378 | |
379 | // Check that session is not active |
380 | QVERIFY(session.sessionProperty("ActiveConfiguration" ).toString().isEmpty()); |
381 | |
382 | // The remaining tests require the session to be not NotAvailable. |
383 | if (session.state() == QNetworkSession::NotAvailable) |
384 | QSKIP("Network is not available." ); |
385 | |
386 | QSignalSpy sessionOpenedSpy(&session, SIGNAL(opened())); |
387 | QSignalSpy sessionClosedSpy(&session, SIGNAL(closed())); |
388 | QSignalSpy stateChangedSpy(&session, SIGNAL(stateChanged(QNetworkSession::State))); |
389 | QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); |
390 | |
391 | // Test opening the session. |
392 | { |
393 | bool expectStateChange = session.state() != QNetworkSession::Connected; |
394 | |
395 | session.open(); |
396 | session.waitForOpened(); |
397 | |
398 | if (session.isOpen()) |
399 | QVERIFY(!sessionOpenedSpy.isEmpty() || !errorSpy.isEmpty()); |
400 | if (!errorSpy.isEmpty()) { |
401 | QNetworkSession::SessionError error = |
402 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy.first().at(i: 0)); |
403 | if (error == QNetworkSession::OperationNotSupportedError) { |
404 | // The session needed to bring up the interface, |
405 | // but the operation is not supported. |
406 | QSKIP("Configuration does not support open()." ); |
407 | } else if (error == QNetworkSession::InvalidConfigurationError) { |
408 | // The session needed to bring up the interface, but it is not possible for the |
409 | // specified configuration. |
410 | if ((session.configuration().state() & QNetworkConfiguration::Discovered) == |
411 | QNetworkConfiguration::Discovered) { |
412 | QFAIL("Failed to open session for Discovered configuration." ); |
413 | } else { |
414 | QSKIP("Cannot test session for non-Discovered configuration." ); |
415 | } |
416 | } else if (error == QNetworkSession::UnknownSessionError) { |
417 | QSKIP("Unknown session error." ); |
418 | } else { |
419 | QFAIL("Error opening session." ); |
420 | } |
421 | } else if (!sessionOpenedSpy.isEmpty()) { |
422 | QCOMPARE(sessionOpenedSpy.count(), 1); |
423 | QVERIFY(sessionClosedSpy.isEmpty()); |
424 | QVERIFY(errorSpy.isEmpty()); |
425 | |
426 | if (expectStateChange) |
427 | QTRY_VERIFY_WITH_TIMEOUT(!stateChangedSpy.isEmpty(), TestTimeOut); |
428 | |
429 | QCOMPARE(session.state(), QNetworkSession::Connected); |
430 | #ifndef QT_NO_NETWORKINTERFACE |
431 | QVERIFY(session.interface().isValid()); |
432 | #endif |
433 | const QString userChoiceIdentifier = |
434 | session.sessionProperty(key: "UserChoiceConfiguration" ).toString(); |
435 | |
436 | QVERIFY(!userChoiceIdentifier.isEmpty()); |
437 | QVERIFY(userChoiceIdentifier != configuration.identifier()); |
438 | |
439 | QNetworkConfiguration userChoiceConfiguration = |
440 | manager.configurationFromIdentifier(identifier: userChoiceIdentifier); |
441 | |
442 | QVERIFY(userChoiceConfiguration.isValid()); |
443 | QVERIFY(userChoiceConfiguration.type() != QNetworkConfiguration::UserChoice); |
444 | |
445 | const QString testIdentifier("abc" ); |
446 | //resetting UserChoiceConfiguration is ignored (read only property) |
447 | session.setSessionProperty(key: "UserChoiceConfiguration" , value: testIdentifier); |
448 | QVERIFY(session.sessionProperty("UserChoiceConfiguration" ).toString() != testIdentifier); |
449 | |
450 | const QString activeIdentifier = |
451 | session.sessionProperty(key: "ActiveConfiguration" ).toString(); |
452 | |
453 | QVERIFY(!activeIdentifier.isEmpty()); |
454 | QVERIFY(activeIdentifier != configuration.identifier()); |
455 | |
456 | QNetworkConfiguration activeConfiguration = |
457 | manager.configurationFromIdentifier(identifier: activeIdentifier); |
458 | |
459 | QVERIFY(activeConfiguration.isValid()); |
460 | QCOMPARE(activeConfiguration.type(), QNetworkConfiguration::InternetAccessPoint); |
461 | |
462 | //resetting ActiveConfiguration is ignored (read only property) |
463 | session.setSessionProperty(key: "ActiveConfiguration" , value: testIdentifier); |
464 | QVERIFY(session.sessionProperty("ActiveConfiguration" ).toString() != testIdentifier); |
465 | |
466 | if (userChoiceConfiguration.type() == QNetworkConfiguration::InternetAccessPoint) { |
467 | QCOMPARE(userChoiceConfiguration, activeConfiguration); |
468 | } else { |
469 | QCOMPARE(userChoiceConfiguration.type(), QNetworkConfiguration::ServiceNetwork); |
470 | QVERIFY(userChoiceConfiguration.children().contains(activeConfiguration)); |
471 | } |
472 | } else { |
473 | QFAIL("Timeout waiting for session to open." ); |
474 | } |
475 | } |
476 | } |
477 | |
478 | void tst_QNetworkSession::sessionOpenCloseStop_data() |
479 | { |
480 | QTest::addColumn<QNetworkConfiguration>(name: "configuration" ); |
481 | QTest::addColumn<bool>(name: "forceSessionStop" ); |
482 | |
483 | foreach (const QNetworkConfiguration &config, manager.allConfigurations()) { |
484 | const QString name = config.name().isEmpty() ? QString("<Hidden>" ) : config.name(); |
485 | QTest::newRow(dataTag: (name + QLatin1String(" close" )).toLocal8Bit().constData()) |
486 | << config << false; |
487 | QTest::newRow(dataTag: (name + QLatin1String(" stop" )).toLocal8Bit().constData()) |
488 | << config << true; |
489 | } |
490 | |
491 | inProcessSessionManagementCount = 0; |
492 | } |
493 | |
494 | void tst_QNetworkSession::sessionOpenCloseStop() |
495 | { |
496 | QFETCH(QNetworkConfiguration, configuration); |
497 | QFETCH(bool, forceSessionStop); |
498 | #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) |
499 | QSKIP("Deadlocks on Linux due to QTBUG-45655" ); |
500 | #endif |
501 | |
502 | QNetworkSession session(configuration); |
503 | |
504 | // Test initial state of the session. |
505 | { |
506 | QCOMPARE(session.configuration(), configuration); |
507 | QVERIFY(!session.isOpen()); |
508 | // session may be invalid if configuration is removed between when |
509 | // sessionOpenCloseStop_data() is called and here. |
510 | QVERIFY((configuration.isValid() && (session.state() != QNetworkSession::Invalid)) || |
511 | (!configuration.isValid() && (session.state() == QNetworkSession::Invalid))); |
512 | QCOMPARE(session.error(), QNetworkSession::UnknownSessionError); |
513 | } |
514 | |
515 | // The remaining tests require the session to be not NotAvailable. |
516 | if (session.state() == QNetworkSession::NotAvailable) |
517 | QSKIP("Network is not available." ); |
518 | |
519 | QSignalSpy sessionOpenedSpy(&session, SIGNAL(opened())); |
520 | QSignalSpy sessionClosedSpy(&session, SIGNAL(closed())); |
521 | QSignalSpy stateChangedSpy(&session, SIGNAL(stateChanged(QNetworkSession::State))); |
522 | QSignalSpy errorSpy(&session, SIGNAL(error(QNetworkSession::SessionError))); |
523 | |
524 | // Test opening the session. |
525 | { |
526 | QNetworkSession::State previousState = session.state(); |
527 | bool expectStateChange = previousState != QNetworkSession::Connected; |
528 | |
529 | session.open(); |
530 | session.waitForOpened(); |
531 | |
532 | // Wait until the configuration is uptodate as well, it may be signaled 'connected' |
533 | // bit later than the session |
534 | QTRY_VERIFY_WITH_TIMEOUT(configuration.state() == QNetworkConfiguration::Active, TestTimeOut); |
535 | |
536 | if (session.isOpen()) |
537 | QVERIFY(!sessionOpenedSpy.isEmpty() || !errorSpy.isEmpty()); |
538 | if (!errorSpy.isEmpty()) { |
539 | QNetworkSession::SessionError error = |
540 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy.first().at(i: 0)); |
541 | |
542 | QCOMPARE(session.state(), previousState); |
543 | |
544 | if (error == QNetworkSession::OperationNotSupportedError) { |
545 | // The session needed to bring up the interface, |
546 | // but the operation is not supported. |
547 | QSKIP("Configuration does not support open()." ); |
548 | } else if (error == QNetworkSession::InvalidConfigurationError) { |
549 | // The session needed to bring up the interface, but it is not possible for the |
550 | // specified configuration. |
551 | if ((session.configuration().state() & QNetworkConfiguration::Discovered) == |
552 | QNetworkConfiguration::Discovered) { |
553 | QFAIL("Failed to open session for Discovered configuration." ); |
554 | } else { |
555 | QSKIP("Cannot test session for non-Discovered configuration." ); |
556 | } |
557 | } else if (error == QNetworkSession::UnknownSessionError) { |
558 | QSKIP("Unknown Session error." ); |
559 | } else { |
560 | QFAIL("Error opening session." ); |
561 | } |
562 | } else if (!sessionOpenedSpy.isEmpty()) { |
563 | QCOMPARE(sessionOpenedSpy.count(), 1); |
564 | QVERIFY(sessionClosedSpy.isEmpty()); |
565 | QVERIFY(errorSpy.isEmpty()); |
566 | |
567 | if (expectStateChange) { |
568 | QTRY_VERIFY_WITH_TIMEOUT(stateChangedSpy.count() >= 2, TestTimeOut); |
569 | |
570 | QNetworkSession::State state = |
571 | qvariant_cast<QNetworkSession::State>(v: stateChangedSpy.at(i: 0).at(i: 0)); |
572 | QCOMPARE(state, QNetworkSession::Connecting); |
573 | |
574 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy.at(i: 1).at(i: 0)); |
575 | QCOMPARE(state, QNetworkSession::Connected); |
576 | } |
577 | |
578 | QCOMPARE(session.state(), QNetworkSession::Connected); |
579 | #ifndef QT_NO_NETWORKINTERFACE |
580 | QVERIFY(session.interface().isValid()); |
581 | #endif |
582 | } else { |
583 | QFAIL("Timeout waiting for session to open." ); |
584 | } |
585 | } |
586 | |
587 | sessionOpenedSpy.clear(); |
588 | sessionClosedSpy.clear(); |
589 | stateChangedSpy.clear(); |
590 | errorSpy.clear(); |
591 | |
592 | QNetworkSession session2(configuration); |
593 | |
594 | QSignalSpy sessionOpenedSpy2(&session2, SIGNAL(opened())); |
595 | QSignalSpy sessionClosedSpy2(&session2, SIGNAL(closed())); |
596 | QSignalSpy stateChangedSpy2(&session2, SIGNAL(stateChanged(QNetworkSession::State))); |
597 | QSignalSpy errorSpy2(&session2, SIGNAL(error(QNetworkSession::SessionError))); |
598 | |
599 | // Test opening a second session. |
600 | { |
601 | QCOMPARE(session2.configuration(), configuration); |
602 | QVERIFY(!session2.isOpen()); |
603 | QCOMPARE(session2.state(), QNetworkSession::Connected); |
604 | QCOMPARE(session.error(), QNetworkSession::UnknownSessionError); |
605 | |
606 | session2.open(); |
607 | |
608 | QTRY_VERIFY_WITH_TIMEOUT(!sessionOpenedSpy2.isEmpty() || !errorSpy2.isEmpty(), TestTimeOut); |
609 | |
610 | if (errorSpy2.isEmpty()) { |
611 | QVERIFY(session2.isOpen()); |
612 | QCOMPARE(session2.state(), QNetworkSession::Connected); |
613 | } |
614 | QVERIFY(session.isOpen()); |
615 | QCOMPARE(session.state(), QNetworkSession::Connected); |
616 | #ifndef QT_NO_NETWORKINTERFACE |
617 | QVERIFY(session.interface().isValid()); |
618 | if (errorSpy2.isEmpty()) { |
619 | QCOMPARE(session.interface().hardwareAddress(), session2.interface().hardwareAddress()); |
620 | QCOMPARE(session.interface().index(), session2.interface().index()); |
621 | } |
622 | #endif |
623 | } |
624 | |
625 | sessionOpenedSpy2.clear(); |
626 | |
627 | if (forceSessionStop && session2.isOpen()) { |
628 | // Test forcing the second session to stop the interface. |
629 | QNetworkSession::State previousState = session.state(); |
630 | bool expectStateChange = previousState != QNetworkSession::Disconnected; |
631 | session2.stop(); |
632 | |
633 | // QNetworkSession::stop() must result either closed() signal |
634 | // or error() signal |
635 | QTRY_VERIFY_WITH_TIMEOUT(!sessionClosedSpy2.isEmpty() || !errorSpy2.isEmpty(), TestTimeOut); |
636 | QVERIFY(!session2.isOpen()); |
637 | |
638 | if (!errorSpy2.isEmpty()) { |
639 | // QNetworkSession::stop() resulted error() signal for session2 |
640 | // => also session should emit error() signal |
641 | QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), TestTimeOut); |
642 | |
643 | // check for SessionAbortedError |
644 | QNetworkSession::SessionError error = |
645 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy.first().at(i: 0)); |
646 | QNetworkSession::SessionError error2 = |
647 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy2.first().at(i: 0)); |
648 | |
649 | QCOMPARE(error, QNetworkSession::SessionAbortedError); |
650 | QCOMPARE(error2, QNetworkSession::SessionAbortedError); |
651 | |
652 | QCOMPARE(errorSpy.count(), 1); |
653 | QCOMPARE(errorSpy2.count(), 1); |
654 | |
655 | errorSpy.clear(); |
656 | errorSpy2.clear(); |
657 | } |
658 | |
659 | QVERIFY(errorSpy.isEmpty()); |
660 | QVERIFY(errorSpy2.isEmpty()); |
661 | |
662 | // Wait for Disconnected state |
663 | QTRY_NOOP(session2.state() == QNetworkSession::Disconnected); |
664 | |
665 | if (expectStateChange) |
666 | QTRY_VERIFY_WITH_TIMEOUT(stateChangedSpy2.count() >= 1 || !errorSpy2.isEmpty(), TestTimeOut); |
667 | |
668 | if (!errorSpy2.isEmpty()) { |
669 | QCOMPARE(session2.state(), previousState); |
670 | QCOMPARE(session.state(), previousState); |
671 | |
672 | QNetworkSession::SessionError error = |
673 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy2.first().at(i: 0)); |
674 | if (error == QNetworkSession::OperationNotSupportedError) { |
675 | // The session needed to bring down the interface, |
676 | // but the operation is not supported. |
677 | QSKIP("Configuration does not support stop()." ); |
678 | } else if (error == QNetworkSession::InvalidConfigurationError) { |
679 | // The session needed to bring down the interface, but it is not possible for the |
680 | // specified configuration. |
681 | if ((session.configuration().state() & QNetworkConfiguration::Discovered) == |
682 | QNetworkConfiguration::Discovered) { |
683 | QFAIL("Failed to stop session for Discovered configuration." ); |
684 | } else { |
685 | QSKIP("Cannot test session for non-Discovered configuration." ); |
686 | } |
687 | } else { |
688 | QFAIL("Error stopping session." ); |
689 | } |
690 | } else if (!sessionClosedSpy2.isEmpty()) { |
691 | if (expectStateChange) { |
692 | if (configuration.type() == QNetworkConfiguration::ServiceNetwork) { |
693 | bool roamedSuccessfully = false; |
694 | |
695 | QNetworkSession::State state; |
696 | if (stateChangedSpy2.count() == 4) { |
697 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 0).at(i: 0)); |
698 | QCOMPARE(state, QNetworkSession::Connecting); |
699 | |
700 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 1).at(i: 0)); |
701 | QCOMPARE(state, QNetworkSession::Connected); |
702 | |
703 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 2).at(i: 0)); |
704 | QCOMPARE(state, QNetworkSession::Closing); |
705 | |
706 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 3).at(i: 0)); |
707 | QCOMPARE(state, QNetworkSession::Disconnected); |
708 | } else if (stateChangedSpy2.count() == 2) { |
709 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 0).at(i: 0)); |
710 | QCOMPARE(state, QNetworkSession::Closing); |
711 | |
712 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 1).at(i: 0)); |
713 | QCOMPARE(state, QNetworkSession::Disconnected); |
714 | } else { |
715 | QFAIL("Unexpected amount of state changes when roaming." ); |
716 | } |
717 | |
718 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Roaming || |
719 | session.state() == QNetworkSession::Connected || |
720 | session.state() == QNetworkSession::Disconnected, TestTimeOut); |
721 | |
722 | QTRY_VERIFY_WITH_TIMEOUT(stateChangedSpy.count() > 0, TestTimeOut); |
723 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy.at(i: stateChangedSpy.count() - 1).at(i: 0)); |
724 | |
725 | for (int i = 0; i < stateChangedSpy.count(); i++) { |
726 | QNetworkSession::State state_temp = |
727 | qvariant_cast<QNetworkSession::State>(v: stateChangedSpy.at(i).at(i: 0)); |
728 | // Extra debug because a fragile point in testcase because statuses vary. |
729 | qDebug() << "------- Statechange spy at: " << i << " is " << state_temp; |
730 | } |
731 | |
732 | if (state == QNetworkSession::Roaming) { |
733 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Connected, TestTimeOut); |
734 | QTRY_VERIFY_WITH_TIMEOUT(session2.state() == QNetworkSession::Connected, TestTimeOut); |
735 | roamedSuccessfully = true; |
736 | } else if (state == QNetworkSession::Closing) { |
737 | QTRY_VERIFY_WITH_TIMEOUT(session2.state() == QNetworkSession::Disconnected, TestTimeOut); |
738 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Connected || |
739 | session.state() == QNetworkSession::Disconnected, TestTimeOut ); |
740 | roamedSuccessfully = false; |
741 | } else if (state == QNetworkSession::Disconnected) { |
742 | QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), TestTimeOut); |
743 | QTRY_VERIFY_WITH_TIMEOUT(session2.state() == QNetworkSession::Disconnected, TestTimeOut); |
744 | } else if (state == QNetworkSession::Connected) { |
745 | QTRY_VERIFY_WITH_TIMEOUT(errorSpy.isEmpty(),TestTimeOut); |
746 | |
747 | if (stateChangedSpy.count() > 1) { |
748 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy.at(i: stateChangedSpy.count() - 2).at(i: 0)); |
749 | QCOMPARE(state, QNetworkSession::Roaming); |
750 | } |
751 | roamedSuccessfully = true; |
752 | } |
753 | |
754 | if (roamedSuccessfully) { |
755 | // Verify that you can open session based on the disconnected configuration |
756 | QString configId = session.sessionProperty(key: "ActiveConfiguration" ).toString(); |
757 | QNetworkConfiguration config = manager.configurationFromIdentifier(identifier: configId); |
758 | QNetworkSession session3(config); |
759 | QSignalSpy errorSpy3(&session3, SIGNAL(error(QNetworkSession::SessionError))); |
760 | QSignalSpy sessionOpenedSpy3(&session3, SIGNAL(opened())); |
761 | session3.open(); |
762 | session3.waitForOpened(); |
763 | QTest::qWait(ms: 1000); // Wait awhile to get all signals from platform |
764 | if (session.isOpen()) |
765 | QVERIFY(!sessionOpenedSpy3.isEmpty() || !errorSpy3.isEmpty()); |
766 | session.stop(); |
767 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Disconnected, TestTimeOut); |
768 | } |
769 | if (!roamedSuccessfully) |
770 | QVERIFY(!errorSpy.isEmpty()); |
771 | } else { |
772 | QTest::qWait(ms: 2000); // Wait awhile to get all signals from platform |
773 | |
774 | if (stateChangedSpy2.count() == 2) { |
775 | QNetworkSession::State state = |
776 | qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 0).at(i: 0)); |
777 | QCOMPARE(state, QNetworkSession::Closing); |
778 | state = qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: 1).at(i: 0)); |
779 | QCOMPARE(state, QNetworkSession::Disconnected); |
780 | } else { |
781 | QVERIFY(stateChangedSpy2.count() >= 1); |
782 | |
783 | for (int i = 0; i < stateChangedSpy2.count(); i++) { |
784 | QNetworkSession::State state_temp = |
785 | qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i).at(i: 0)); |
786 | // Extra debug because a fragile point in testcase. |
787 | qDebug() << "+++++ Statechange spy at: " << i << " is " << state_temp; |
788 | } |
789 | |
790 | QNetworkSession::State state = |
791 | qvariant_cast<QNetworkSession::State>(v: stateChangedSpy2.at(i: stateChangedSpy2.count() - 1).at(i: 0)); |
792 | QCOMPARE(state, QNetworkSession::Disconnected); |
793 | } |
794 | } |
795 | |
796 | QTRY_VERIFY_WITH_TIMEOUT(!sessionClosedSpy.isEmpty(), TestTimeOut); |
797 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Disconnected, TestTimeOut); |
798 | } |
799 | |
800 | QVERIFY(errorSpy2.isEmpty()); |
801 | |
802 | ++inProcessSessionManagementCount; |
803 | } else { |
804 | QFAIL("Timeout waiting for session to stop." ); |
805 | } |
806 | |
807 | QVERIFY(!sessionClosedSpy.isEmpty()); |
808 | QVERIFY(!sessionClosedSpy2.isEmpty()); |
809 | |
810 | QVERIFY(!session.isOpen()); |
811 | QVERIFY(!session2.isOpen()); |
812 | } else if (session2.isOpen()) { |
813 | // Test closing the second session. |
814 | { |
815 | int stateChangedCountBeforeClose = stateChangedSpy2.count(); |
816 | session2.close(); |
817 | |
818 | QTRY_VERIFY_WITH_TIMEOUT(!sessionClosedSpy2.isEmpty(), TestTimeOut); |
819 | QCOMPARE(stateChangedSpy2.count(), stateChangedCountBeforeClose); |
820 | |
821 | QVERIFY(sessionClosedSpy.isEmpty()); |
822 | |
823 | QVERIFY(session.isOpen()); |
824 | QVERIFY(!session2.isOpen()); |
825 | QCOMPARE(session.state(), QNetworkSession::Connected); |
826 | QCOMPARE(session2.state(), QNetworkSession::Connected); |
827 | #ifndef QT_NO_NETWORKINTERFACE |
828 | QVERIFY(session.interface().isValid()); |
829 | QCOMPARE(session.interface().hardwareAddress(), session2.interface().hardwareAddress()); |
830 | QCOMPARE(session.interface().index(), session2.interface().index()); |
831 | #endif |
832 | } |
833 | |
834 | sessionClosedSpy2.clear(); |
835 | |
836 | // Test closing the first session. |
837 | { |
838 | bool expectStateChange = session.state() != QNetworkSession::Disconnected && |
839 | manager.capabilities() & QNetworkConfigurationManager::SystemSessionSupport; |
840 | |
841 | session.close(); |
842 | |
843 | QTRY_VERIFY_WITH_TIMEOUT(!sessionClosedSpy.isEmpty() || !errorSpy.isEmpty(), TestTimeOut); |
844 | |
845 | QVERIFY(!session.isOpen()); |
846 | |
847 | if (expectStateChange) |
848 | QTRY_VERIFY_WITH_TIMEOUT(!stateChangedSpy.isEmpty() || !errorSpy.isEmpty(), TestTimeOut); |
849 | |
850 | if (!errorSpy.isEmpty()) { |
851 | QNetworkSession::SessionError error = |
852 | qvariant_cast<QNetworkSession::SessionError>(v: errorSpy.first().at(i: 0)); |
853 | if (error == QNetworkSession::OperationNotSupportedError) { |
854 | // The session needed to bring down the interface, |
855 | // but the operation is not supported. |
856 | QSKIP("Configuration does not support close()." ); |
857 | } else if (error == QNetworkSession::InvalidConfigurationError) { |
858 | // The session needed to bring down the interface, but it is not possible for the |
859 | // specified configuration. |
860 | if ((session.configuration().state() & QNetworkConfiguration::Discovered) == |
861 | QNetworkConfiguration::Discovered) { |
862 | QFAIL("Failed to close session for Discovered configuration." ); |
863 | } else { |
864 | QSKIP("Cannot test session for non-Discovered configuration." ); |
865 | } |
866 | } else { |
867 | QFAIL("Error closing session." ); |
868 | } |
869 | } else if (!sessionClosedSpy.isEmpty()) { |
870 | QVERIFY(sessionOpenedSpy.isEmpty()); |
871 | QCOMPARE(sessionClosedSpy.count(), 1); |
872 | if (expectStateChange) |
873 | QVERIFY(!stateChangedSpy.isEmpty()); |
874 | QVERIFY(errorSpy.isEmpty()); |
875 | |
876 | if (expectStateChange) |
877 | QTRY_VERIFY_WITH_TIMEOUT(session.state() == QNetworkSession::Disconnected, TestTimeOut); |
878 | |
879 | ++inProcessSessionManagementCount; |
880 | } else { |
881 | QFAIL("Timeout waiting for session to close." ); |
882 | } |
883 | } |
884 | } |
885 | } |
886 | |
887 | QDebug operator<<(QDebug debug, const QList<QNetworkConfiguration> &list) |
888 | { |
889 | debug.nospace() << "( " ; |
890 | foreach (const QNetworkConfiguration &config, list) |
891 | debug.nospace() << config.identifier() << ", " ; |
892 | debug.nospace() << ")\n" ; |
893 | return debug; |
894 | } |
895 | |
896 | // Note: outOfProcessSession requires that at least one configuration is |
897 | // at Discovered -state. |
898 | void tst_QNetworkSession::outOfProcessSession() |
899 | { |
900 | #if !QT_CONFIG(process) |
901 | QSKIP("No qprocess support" , SkipAll); |
902 | #else |
903 | updateConfigurations(); |
904 | QTest::qWait(ms: 2000); |
905 | |
906 | QNetworkConfigurationManager manager; |
907 | // Create a QNetworkConfigurationManager to detect configuration changes made in Lackey. This |
908 | // is actually the essence of this testcase - to check that platform mediates/reflects changes |
909 | // regardless of process boundaries. The interprocess communication is more like a way to get |
910 | // this test-case act correctly and timely. |
911 | QList<QNetworkConfiguration> before = manager.allConfigurations(flags: QNetworkConfiguration::Active); |
912 | QSignalSpy spy(&manager, SIGNAL(configurationChanged(QNetworkConfiguration))); |
913 | |
914 | // Cannot read/write to processes on WinCE. |
915 | // Easiest alternative is to use sockets for IPC. |
916 | QLocalServer oopServer; |
917 | // First remove possible earlier listening address which would cause listen to fail |
918 | // (e.g. previously abruptly ended unit test might cause this) |
919 | QLocalServer::removeServer(name: "tst_qnetworksession" ); |
920 | oopServer.listen(name: "tst_qnetworksession" ); |
921 | |
922 | QProcess lackey; |
923 | QString lackeyExe = lackeyDir + "/lackey" ; |
924 | lackey.start(command: lackeyExe); |
925 | QVERIFY2(lackey.waitForStarted(), qPrintable( |
926 | QString::fromLatin1("Could not start %1: %2" ).arg(lackeyExe, lackey.errorString()))); |
927 | |
928 | QVERIFY(oopServer.waitForNewConnection(-1)); |
929 | QLocalSocket *oopSocket = oopServer.nextPendingConnection(); |
930 | |
931 | do { |
932 | QByteArray output; |
933 | |
934 | if (oopSocket->waitForReadyRead()) |
935 | output = oopSocket->readLine().trimmed(); |
936 | |
937 | if (output.startsWith(c: "Started session " )) { |
938 | QString identifier = QString::fromLocal8Bit(str: output.mid(index: 20).constData()); |
939 | QNetworkConfiguration changed; |
940 | |
941 | do { |
942 | QTRY_VERIFY_WITH_TIMEOUT(!spy.isEmpty(), TestTimeOut); |
943 | changed = qvariant_cast<QNetworkConfiguration>(v: spy.takeFirst().at(i: 0)); |
944 | } while (changed.identifier() != identifier); |
945 | |
946 | QVERIFY((changed.state() & QNetworkConfiguration::Active) == |
947 | QNetworkConfiguration::Active); |
948 | |
949 | QVERIFY(!before.contains(changed)); |
950 | |
951 | QList<QNetworkConfiguration> after = |
952 | manager.allConfigurations(flags: QNetworkConfiguration::Active); |
953 | |
954 | QVERIFY(after.contains(changed)); |
955 | |
956 | spy.clear(); |
957 | |
958 | oopSocket->write(data: "stop\n" ); |
959 | oopSocket->waitForBytesWritten(); |
960 | |
961 | do { |
962 | QTRY_VERIFY_WITH_TIMEOUT(!spy.isEmpty(), TestTimeOut); |
963 | |
964 | changed = qvariant_cast<QNetworkConfiguration>(v: spy.takeFirst().at(i: 0)); |
965 | } while (changed.identifier() != identifier); |
966 | |
967 | QVERIFY((changed.state() & QNetworkConfiguration::Active) != |
968 | QNetworkConfiguration::Active); |
969 | |
970 | QList<QNetworkConfiguration> afterStop = |
971 | manager.allConfigurations(flags: QNetworkConfiguration::Active); |
972 | |
973 | QVERIFY(!afterStop.contains(changed)); |
974 | |
975 | oopSocket->disconnectFromServer(); |
976 | oopSocket->waitForDisconnected(msecs: -1); |
977 | |
978 | lackey.waitForFinished(); |
979 | } |
980 | // This is effected by QTBUG-4903, process will always report as running |
981 | //} while (lackey.state() == QProcess::Running); |
982 | |
983 | // Workaround: the socket in the lackey will disconnect on exit |
984 | } while (oopSocket->state() == QLocalSocket::ConnectedState); |
985 | |
986 | switch (lackey.exitCode()) { |
987 | case 0: |
988 | qDebug(msg: "Lackey returned exit success (0)" ); |
989 | break; |
990 | case 1: |
991 | QSKIP("No discovered configurations found." ); |
992 | case 2: |
993 | QSKIP("Lackey could not start session." ); |
994 | default: |
995 | QSKIP("Lackey failed" ); |
996 | } |
997 | #endif |
998 | } |
999 | |
1000 | // A convenience / helper function for testcases. Return the first matching configuration. |
1001 | // Ignores configurations in other than 'discovered' -state. Returns invalid (QNetworkConfiguration()) |
1002 | // if none found. |
1003 | QNetworkConfiguration suitableConfiguration(QString bearerType, QNetworkConfiguration::Type configType) { |
1004 | |
1005 | // Refresh configurations and derive configurations matching given parameters. |
1006 | QNetworkConfigurationManager mgr; |
1007 | QSignalSpy updateSpy(&mgr, SIGNAL(updateCompleted())); |
1008 | |
1009 | mgr.updateConfigurations(); |
1010 | QTRY_NOOP(updateSpy.count() >= 1); |
1011 | if (updateSpy.count() != 1) { |
1012 | qDebug(msg: "tst_QNetworkSession::suitableConfiguration() failure: unable to update configurations" ); |
1013 | return QNetworkConfiguration(); |
1014 | } |
1015 | QList<QNetworkConfiguration> discoveredConfigs = mgr.allConfigurations(flags: QNetworkConfiguration::Discovered); |
1016 | foreach(QNetworkConfiguration config, discoveredConfigs) { |
1017 | if ((config.state() & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { |
1018 | discoveredConfigs.removeOne(t: config); |
1019 | } else if (config.type() != configType) { |
1020 | // qDebug() << "Dumping config because type (IAP/SNAP) mismatches: " << config.name(); |
1021 | discoveredConfigs.removeOne(t: config); |
1022 | } else if ((config.type() == QNetworkConfiguration::InternetAccessPoint) && |
1023 | bearerType == "cellular" ) { // 'cellular' bearertype is for convenience |
1024 | if (config.bearerTypeName() != "2G" && |
1025 | config.bearerTypeName() != "CDMA2000" && |
1026 | config.bearerTypeName() != "WCDMA" && |
1027 | config.bearerTypeName() != "HSPA" && |
1028 | config.bearerTypeName() != "EVDO" && |
1029 | config.bearerTypeName() != "LTE" && |
1030 | config.bearerTypeName() != "3G" && |
1031 | config.bearerTypeName() != "4G" ) { |
1032 | // qDebug() << "Dumping config because bearer mismatches (cellular): " << config.name(); |
1033 | discoveredConfigs.removeOne(t: config); |
1034 | } |
1035 | } else if ((config.type() == QNetworkConfiguration::InternetAccessPoint) && |
1036 | bearerType != config.bearerTypeName()) { |
1037 | // qDebug() << "Dumping config because bearer mismatches (WLAN): " << config.name(); |
1038 | discoveredConfigs.removeOne(t: config); |
1039 | } |
1040 | } |
1041 | if (discoveredConfigs.isEmpty()) { |
1042 | qDebug(msg: "tst_QNetworkSession::suitableConfiguration() failure: no suitable configurations present." ); |
1043 | return QNetworkConfiguration(); |
1044 | } else { |
1045 | return discoveredConfigs.first(); |
1046 | } |
1047 | } |
1048 | |
1049 | // A convenience-function: updates configurations and waits that they are updated. |
1050 | void updateConfigurations() |
1051 | { |
1052 | QNetworkConfigurationManager mgr; |
1053 | QSignalSpy updateSpy(&mgr, SIGNAL(updateCompleted())); |
1054 | mgr.updateConfigurations(); |
1055 | QTRY_NOOP(updateSpy.count() >= 1); |
1056 | } |
1057 | |
1058 | // A convenience-function: updates and prints all available confiurations and their states |
1059 | void printConfigurations() |
1060 | { |
1061 | QNetworkConfigurationManager manager; |
1062 | QList<QNetworkConfiguration> allConfigs = |
1063 | manager.allConfigurations(); |
1064 | qDebug(msg: "tst_QNetworkSession::printConfigurations QNetworkConfigurationManager gives following configurations: " ); |
1065 | foreach(QNetworkConfiguration config, allConfigs) { |
1066 | qDebug() << "Name of the configuration: " << config.name(); |
1067 | qDebug() << "State of the configuration: " << config.state(); |
1068 | } |
1069 | } |
1070 | |
1071 | // A convenience function for test-cases: opens the given configuration and return |
1072 | // true if it was done gracefully. |
1073 | bool openSession(QNetworkSession *session) { |
1074 | bool result = true; |
1075 | QNetworkConfigurationManager mgr; |
1076 | QSignalSpy openedSpy(session, SIGNAL(opened())); |
1077 | QSignalSpy stateChangeSpy(session, SIGNAL(stateChanged(QNetworkSession::State))); |
1078 | QSignalSpy errorSpy(session, SIGNAL(error(QNetworkSession::SessionError))); |
1079 | QSignalSpy configChangeSpy(&mgr, SIGNAL(configurationChanged(QNetworkConfiguration))); |
1080 | // Store some initial statuses, because expected signals differ if the config is already |
1081 | // active by some other session |
1082 | QNetworkConfiguration::StateFlags configInitState = session->configuration().state(); |
1083 | QNetworkSession::State sessionInitState = session->state(); |
1084 | qDebug() << "tst_QNetworkSession::openSession() name of the configuration to be opened: " << session->configuration().name(); |
1085 | qDebug() << "tst_QNetworkSession::openSession() state of the configuration to be opened: " << session->configuration().state(); |
1086 | qDebug() << "tst_QNetworkSession::openSession() state of the session to be opened: " << session->state(); |
1087 | |
1088 | if (session->isOpen() || |
1089 | !session->sessionProperty(key: "ActiveConfiguration" ).toString().isEmpty()) { |
1090 | qDebug(msg: "tst_QNetworkSession::openSession() failure: session was already open / active." ); |
1091 | result = false; |
1092 | } else { |
1093 | session->open(); |
1094 | session->waitForOpened(msecs: 120000); // Bringing interfaces up and down may take time at platform |
1095 | } |
1096 | QTest::qWait(ms: 5000); // Wait a moment to ensure all signals are propagated |
1097 | // Check that connection opening went by the book. Add checks here if more strictness needed. |
1098 | if (!session->isOpen()) { |
1099 | qDebug(msg: "tst_QNetworkSession::openSession() failure: QNetworkSession::open() failed." ); |
1100 | result = false; |
1101 | } |
1102 | if (openedSpy.count() != 1) { |
1103 | qDebug(msg: "tst_QNetworkSession::openSession() failure: QNetworkSession::opened() - signal not received." ); |
1104 | result = false; |
1105 | } |
1106 | if (!errorSpy.isEmpty()) { |
1107 | qDebug(msg: "tst_QNetworkSession::openSession() failure: QNetworkSession::error() - signal was detected." ); |
1108 | result = false; |
1109 | } |
1110 | if (sessionInitState != QNetworkSession::Connected && |
1111 | stateChangeSpy.isEmpty()) { |
1112 | qDebug(msg: "tst_QNetworkSession::openSession() failure: QNetworkSession::stateChanged() - signals not detected." ); |
1113 | result = false; |
1114 | } |
1115 | if (configInitState != QNetworkConfiguration::Active && |
1116 | configChangeSpy.isEmpty()) { |
1117 | qDebug(msg: "tst_QNetworkSession::openSession() failure: QNetworkConfigurationManager::configurationChanged() - signals not detected." ); |
1118 | result = false; |
1119 | } |
1120 | if (session->configuration().state() != QNetworkConfiguration::Active) { |
1121 | qDebug(msg: "tst_QNetworkSession::openSession() failure: session's configuration is not in 'Active' -state." ); |
1122 | qDebug() << "tst_QNetworkSession::openSession() state is: " << session->configuration().state(); |
1123 | result = false; |
1124 | } |
1125 | if (result == false) { |
1126 | qDebug() << "tst_QNetworkSession::openSession() opening session failed." ; |
1127 | } else { |
1128 | qDebug() << "tst_QNetworkSession::openSession() opening session succeeded." ; |
1129 | } |
1130 | qDebug() << "tst_QNetworkSession::openSession() name of the configuration is: " << session->configuration().name(); |
1131 | qDebug() << "tst_QNetworkSession::openSession() configuration state is: " << session->configuration().state(); |
1132 | qDebug() << "tst_QNetworkSession::openSession() session state is: " << session->state(); |
1133 | |
1134 | return result; |
1135 | } |
1136 | |
1137 | // Helper function for closing opened session. Performs checks that |
1138 | // session is closed gradefully (e.g. signals). Function does not delete |
1139 | // the session. The lastSessionOnConfiguration (true by default) is used to |
1140 | // tell if there are more sessions open, basing on same configuration. This |
1141 | // impacts the checks made. |
1142 | bool closeSession(QNetworkSession *session, bool lastSessionOnConfiguration) { |
1143 | if (!session) { |
1144 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: NULL session given" ); |
1145 | return false; |
1146 | } |
1147 | |
1148 | qDebug() << "tst_QNetworkSession::closeSession() name of the configuration to be closed: " << session->configuration().name(); |
1149 | qDebug() << "tst_QNetworkSession::closeSession() state of the configuration to be closed: " << session->configuration().state(); |
1150 | qDebug() << "tst_QNetworkSession::closeSession() state of the session to be closed: " << session->state(); |
1151 | |
1152 | if (session->state() != QNetworkSession::Connected || |
1153 | !session->isOpen()) { |
1154 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: session is not opened." ); |
1155 | return false; |
1156 | } |
1157 | QNetworkConfigurationManager mgr; |
1158 | QSignalSpy sessionClosedSpy(session, SIGNAL(closed())); |
1159 | QSignalSpy sessionStateChangedSpy(session, SIGNAL(stateChanged(QNetworkSession::State))); |
1160 | QSignalSpy sessionErrorSpy(session, SIGNAL(error(QNetworkSession::SessionError))); |
1161 | QSignalSpy configChangeSpy(&mgr, SIGNAL(configurationChanged(QNetworkConfiguration))); |
1162 | |
1163 | bool result = true; |
1164 | session->close(); |
1165 | QTest::qWait(ms: 5000); // Wait a moment so that all signals are propagated |
1166 | |
1167 | if (!sessionErrorSpy.isEmpty()) { |
1168 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: QNetworkSession::error() received." ); |
1169 | result = false; |
1170 | } |
1171 | if (sessionClosedSpy.count() != 1) { |
1172 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: QNetworkSession::closed() signal not received." ); |
1173 | result = false; |
1174 | } |
1175 | if (lastSessionOnConfiguration && |
1176 | sessionStateChangedSpy.isEmpty()) { |
1177 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: QNetworkSession::stateChanged() signals not received." ); |
1178 | result = false; |
1179 | } |
1180 | if (lastSessionOnConfiguration && |
1181 | session->state() != QNetworkSession::Disconnected) { |
1182 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: QNetworkSession is not in Disconnected -state" ); |
1183 | result = false; |
1184 | } |
1185 | QTRY_NOOP(!configChangeSpy.isEmpty()); |
1186 | if (lastSessionOnConfiguration && |
1187 | configChangeSpy.isEmpty()) { |
1188 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: QNetworkConfigurationManager::configurationChanged() - signal not detected." ); |
1189 | result = false; |
1190 | } |
1191 | if (lastSessionOnConfiguration && |
1192 | session->configuration().state() == QNetworkConfiguration::Active) { |
1193 | qDebug(msg: "tst_QNetworkSession::closeSession() failure: session's configuration is still in active state." ); |
1194 | result = false; |
1195 | } |
1196 | if (result == false) { |
1197 | qDebug() << "tst_QNetworkSession::closeSession() closing session failed." ; |
1198 | } else { |
1199 | qDebug() << "tst_QNetworkSession::closeSession() closing session succeeded." ; |
1200 | } |
1201 | qDebug() << "tst_QNetworkSession::closeSession() name of the configuration is: " << session->configuration().name(); |
1202 | qDebug() << "tst_QNetworkSession::closeSession() configuration state is: " << session->configuration().state(); |
1203 | qDebug() << "tst_QNetworkSession::closeSession() session state is: " << session->state(); |
1204 | return result; |
1205 | } |
1206 | |
1207 | void tst_QNetworkSession::sessionAutoClose_data() |
1208 | { |
1209 | QTest::addColumn<QNetworkConfiguration>(name: "configuration" ); |
1210 | |
1211 | bool testData = false; |
1212 | foreach (const QNetworkConfiguration &config, |
1213 | manager.allConfigurations(QNetworkConfiguration::Discovered)) { |
1214 | QNetworkSession session(config); |
1215 | if (!session.sessionProperty(key: QLatin1String("AutoCloseSessionTimeout" )).isValid()) |
1216 | continue; |
1217 | |
1218 | testData = true; |
1219 | |
1220 | const QString name = config.name().isEmpty() ? QString("<Hidden>" ) : config.name(); |
1221 | QTest::newRow(dataTag: name.toLocal8Bit().constData()) << config; |
1222 | } |
1223 | |
1224 | if (!testData) |
1225 | QSKIP("No applicable configurations to test" ); |
1226 | } |
1227 | |
1228 | void tst_QNetworkSession::sessionAutoClose() |
1229 | { |
1230 | QFETCH(QNetworkConfiguration, configuration); |
1231 | |
1232 | QNetworkSession session(configuration); |
1233 | |
1234 | QCOMPARE(session.configuration(), configuration); |
1235 | |
1236 | QVariant autoCloseSession = session.sessionProperty(key: QLatin1String("AutoCloseSessionTimeout" )); |
1237 | |
1238 | QVERIFY(autoCloseSession.isValid()); |
1239 | |
1240 | // property defaults to false |
1241 | QCOMPARE(autoCloseSession.toInt(), -1); |
1242 | |
1243 | QSignalSpy closeSpy(&session, SIGNAL(closed())); |
1244 | |
1245 | session.open(); |
1246 | session.waitForOpened(); |
1247 | |
1248 | if (!session.isOpen()) |
1249 | QSKIP("Session not open" ); |
1250 | |
1251 | // set session to auto close at next polling interval. |
1252 | session.setSessionProperty(key: QLatin1String("AutoCloseSessionTimeout" ), value: 0); |
1253 | |
1254 | QTRY_VERIFY_WITH_TIMEOUT(!closeSpy.isEmpty(), TestTimeOut); |
1255 | |
1256 | QCOMPARE(session.state(), QNetworkSession::Connected); |
1257 | |
1258 | QVERIFY(!session.isOpen()); |
1259 | |
1260 | QCOMPARE(session.configuration(), configuration); |
1261 | |
1262 | autoCloseSession = session.sessionProperty(key: QLatin1String("AutoCloseSessionTimeout" )); |
1263 | |
1264 | QVERIFY(autoCloseSession.isValid()); |
1265 | |
1266 | QCOMPARE(autoCloseSession.toInt(), -1); |
1267 | } |
1268 | |
1269 | void tst_QNetworkSession::usagePolicies() |
1270 | { |
1271 | QNetworkSession session(manager.defaultConfiguration()); |
1272 | QNetworkSession::UsagePolicies initial; |
1273 | initial = session.usagePolicies(); |
1274 | if (initial != 0) |
1275 | QNetworkSessionPrivate::setUsagePolicies(session, { }); |
1276 | QSignalSpy spy(&session, SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies))); |
1277 | QNetworkSessionPrivate::setUsagePolicies(session, QNetworkSession::NoBackgroundTrafficPolicy); |
1278 | QCOMPARE(spy.count(), 1); |
1279 | QNetworkSession::UsagePolicies policies = qvariant_cast<QNetworkSession::UsagePolicies>(v: spy.at(i: 0).at(i: 0)); |
1280 | QCOMPARE(policies, QNetworkSession::NoBackgroundTrafficPolicy); |
1281 | QCOMPARE(session.usagePolicies(), QNetworkSession::NoBackgroundTrafficPolicy); |
1282 | QNetworkSessionPrivate::setUsagePolicies(session, initial); |
1283 | spy.clear(); |
1284 | |
1285 | session.open(); |
1286 | QVERIFY(session.waitForOpened()); |
1287 | |
1288 | //policies may be changed when session is opened, if so, signal should have been emitted |
1289 | if (session.usagePolicies() != initial) |
1290 | QCOMPARE(spy.count(), 1); |
1291 | else |
1292 | QCOMPARE(spy.count(), 0); |
1293 | } |
1294 | |
1295 | |
1296 | #endif |
1297 | |
1298 | QTEST_MAIN(tst_QNetworkSession) |
1299 | #include "tst_qnetworksession.moc" |
1300 | |