WinRT: Added socket engine implementation
[qt:qtbase.git] / tests / auto / corelib / kernel / qsocketnotifier / tst_qsocketnotifier.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QTest>
43 #include <QtTest/QSignalSpy>
44 #include <QtTest/QTestEventLoop>
45
46 #include <QtCore/QCoreApplication>
47 #include <QtCore/QTimer>
48 #include <QtCore/QSocketNotifier>
49 #include <QtNetwork/QTcpServer>
50 #include <QtNetwork/QTcpSocket>
51 #ifndef Q_OS_WINRT
52 #include <private/qnativesocketengine_p.h>
53 #else
54 #include <private/qnativesocketengine_winrt_p.h>
55 #endif
56 #define NATIVESOCKETENGINE QNativeSocketEngine
57 #ifdef Q_OS_UNIX
58 #include <private/qnet_unix_p.h>
59 #include <sys/select.h>
60 #endif
61 #include <limits>
62
63 #if defined (Q_CC_MSVC) && defined(max)
64 #  undef max
65 #  undef min
66 #endif // Q_CC_MSVC
67
68 class tst_QSocketNotifier : public QObject
69 {
70     Q_OBJECT
71 private slots:
72     void unexpectedDisconnection();
73     void mixingWithTimers();
74 #ifdef Q_OS_UNIX
75     void posixSockets();
76 #endif
77 };
78
79 class UnexpectedDisconnectTester : public QObject
80 {
81     Q_OBJECT
82 public:
83     NATIVESOCKETENGINE *readEnd1, *readEnd2;
84     int sequence;
85
86     UnexpectedDisconnectTester(NATIVESOCKETENGINE *s1, NATIVESOCKETENGINE *s2)
87         : readEnd1(s1), readEnd2(s2), sequence(0)
88     {
89         QSocketNotifier *notifier1 =
90             new QSocketNotifier(readEnd1->socketDescriptor(), QSocketNotifier::Read, this);
91         connect(notifier1, SIGNAL(activated(int)), SLOT(handleActivated()));
92         QSocketNotifier *notifier2 =
93             new QSocketNotifier(readEnd2->socketDescriptor(), QSocketNotifier::Read, this);
94         connect(notifier2, SIGNAL(activated(int)), SLOT(handleActivated()));
95     }
96
97 public slots:
98     void handleActivated()
99     {
100         char data1[1], data2[1];
101         ++sequence;
102         if (sequence == 1) {
103             // read from both ends
104             (void) readEnd1->read(data1, sizeof(data1));
105             (void) readEnd2->read(data2, sizeof(data2));
106             emit finished();
107         } else if (sequence == 2) {
108             // we should never get here
109             QCOMPARE(readEnd2->read(data2, sizeof(data2)), qint64(-2));
110             QVERIFY(readEnd2->isValid());
111         }
112     }
113
114 signals:
115     void finished();
116 };
117
118 void tst_QSocketNotifier::unexpectedDisconnection()
119 {
120     /*
121       Given two sockets and two QSocketNotifiers registered on each
122       their socket. If both sockets receive data, and the first slot
123       invoked by one of the socket notifiers empties both sockets, the
124       other notifier will also emit activated(). This results in
125       unexpected disconnection in QAbstractSocket.
126
127       The use case is that somebody calls one of the
128       waitFor... functions in a QSocketNotifier activated slot, and
129       the waitFor... functions do local selects that can empty both
130       stdin and stderr while waiting for fex bytes to be written.
131     */
132
133     QTcpServer server;
134     QVERIFY(server.listen(QHostAddress::LocalHost, 0));
135
136     NATIVESOCKETENGINE readEnd1;
137     readEnd1.initialize(QAbstractSocket::TcpSocket);
138     readEnd1.connectToHost(server.serverAddress(), server.serverPort());
139     QVERIFY(readEnd1.waitForWrite());
140     QVERIFY(readEnd1.state() == QAbstractSocket::ConnectedState);
141     QVERIFY(server.waitForNewConnection());
142     QTcpSocket *writeEnd1 = server.nextPendingConnection();
143     QVERIFY(writeEnd1 != 0);
144
145     NATIVESOCKETENGINE readEnd2;
146     readEnd2.initialize(QAbstractSocket::TcpSocket);
147     readEnd2.connectToHost(server.serverAddress(), server.serverPort());
148     QVERIFY(readEnd2.waitForWrite());
149     QVERIFY(readEnd2.state() == QAbstractSocket::ConnectedState);
150     QVERIFY(server.waitForNewConnection());
151     QTcpSocket *writeEnd2 = server.nextPendingConnection();
152     QVERIFY(writeEnd2 != 0);
153
154     writeEnd1->write("1", 1);
155     writeEnd2->write("2", 1);
156
157     writeEnd1->waitForBytesWritten();
158     writeEnd2->waitForBytesWritten();
159
160     writeEnd1->flush();
161     writeEnd2->flush();
162
163     UnexpectedDisconnectTester tester(&readEnd1, &readEnd2);
164
165     QTimer timer;
166     timer.setSingleShot(true);
167     timer.start(30000);
168     do {
169         // we have to wait until sequence value changes
170         // as any event can make us jump out processing
171         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
172         QVERIFY(timer.isActive()); //escape if test would hang
173     }  while(tester.sequence <= 0);
174
175     QVERIFY(readEnd1.state() == QAbstractSocket::ConnectedState);
176     QVERIFY(readEnd2.state() == QAbstractSocket::ConnectedState);
177
178     QCOMPARE(tester.sequence, 2);
179
180     readEnd1.close();
181     readEnd2.close();
182     writeEnd1->close();
183     writeEnd2->close();
184     server.close();
185 }
186
187 class MixingWithTimersHelper : public QObject
188 {
189     Q_OBJECT
190
191 public:
192     MixingWithTimersHelper(QTimer *timer, QTcpServer *server);
193
194     bool timerActivated;
195     bool socketActivated;
196
197 private slots:
198     void timerFired();
199     void socketFired();
200 };
201
202 MixingWithTimersHelper::MixingWithTimersHelper(QTimer *timer, QTcpServer *server)
203 {
204     timerActivated = false;
205     socketActivated = false;
206
207     connect(timer, SIGNAL(timeout()), SLOT(timerFired()));
208     connect(server, SIGNAL(newConnection()), SLOT(socketFired()));
209 }
210
211 void MixingWithTimersHelper::timerFired()
212 {
213     timerActivated = true;
214 }
215
216 void MixingWithTimersHelper::socketFired()
217 {
218     socketActivated = true;
219 }
220
221 void tst_QSocketNotifier::mixingWithTimers()
222 {
223     QTimer timer;
224     timer.setInterval(0);
225     timer.start();
226
227     QTcpServer server;
228     QVERIFY(server.listen(QHostAddress::LocalHost, 0));
229
230     MixingWithTimersHelper helper(&timer, &server);
231
232     QCoreApplication::processEvents();
233
234     QCOMPARE(helper.timerActivated, true);
235     QCOMPARE(helper.socketActivated, false);
236
237     helper.timerActivated = false;
238     helper.socketActivated = false;
239
240     QTcpSocket socket;
241     socket.connectToHost(server.serverAddress(), server.serverPort());
242
243     QCoreApplication::processEvents();
244
245     QCOMPARE(helper.timerActivated, true);
246     QTRY_COMPARE(helper.socketActivated, true);
247 }
248
249 #ifdef Q_OS_UNIX
250 // test only for posix
251 void tst_QSocketNotifier::posixSockets()
252 {
253     QTcpServer server;
254     QVERIFY(server.listen(QHostAddress::LocalHost, 0));
255
256     int posixSocket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
257     sockaddr_in addr;
258     addr.sin_addr.s_addr = htonl(0x7f000001);
259     addr.sin_family = AF_INET;
260     addr.sin_port = htons(server.serverPort());
261     qt_safe_connect(posixSocket, (const struct sockaddr*)&addr, sizeof(sockaddr_in));
262     QVERIFY(server.waitForNewConnection(5000));
263     QScopedPointer<QTcpSocket> passive(server.nextPendingConnection());
264
265     ::fcntl(posixSocket, F_SETFL, ::fcntl(posixSocket, F_GETFL) | O_NONBLOCK);
266
267     {
268         QSocketNotifier rn(posixSocket, QSocketNotifier::Read);
269         connect(&rn, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
270         QSignalSpy readSpy(&rn, SIGNAL(activated(int)));
271         QVERIFY(readSpy.isValid());
272         // No write notifier, some systems trigger write notification on socket creation, but not all
273         QSocketNotifier en(posixSocket, QSocketNotifier::Exception);
274         connect(&en, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
275         QSignalSpy errorSpy(&en, SIGNAL(activated(int)));
276         QVERIFY(errorSpy.isValid());
277
278         passive->write("hello",6);
279         passive->waitForBytesWritten(5000);
280
281         QTestEventLoop::instance().enterLoop(3);
282         QCOMPARE(readSpy.count(), 1);
283         QCOMPARE(errorSpy.count(), 0);
284
285         char buffer[100];
286         int r = qt_safe_read(posixSocket, buffer, 100);
287         QCOMPARE(r, 6);
288         QCOMPARE(buffer, "hello");
289
290         QSocketNotifier wn(posixSocket, QSocketNotifier::Write);
291         connect(&wn, SIGNAL(activated(int)), &QTestEventLoop::instance(), SLOT(exitLoop()));
292         QSignalSpy writeSpy(&wn, SIGNAL(activated(int)));
293         QVERIFY(writeSpy.isValid());
294         qt_safe_write(posixSocket, "goodbye", 8);
295
296         QTestEventLoop::instance().enterLoop(3);
297         QCOMPARE(readSpy.count(), 1);
298         QCOMPARE(writeSpy.count(), 1);
299         QCOMPARE(errorSpy.count(), 0);
300
301         // Write notifier may have fired before the read notifier inside
302         // QTcpSocket, give QTcpSocket a chance to see the incoming data
303         passive->waitForReadyRead(100);
304         QCOMPARE(passive->readAll(), QByteArray("goodbye",8));
305     }
306     qt_safe_close(posixSocket);
307 }
308 #endif
309
310 QTEST_MAIN(tst_QSocketNotifier)
311 #include <tst_qsocketnotifier.moc>