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 | #include <QtTest/QtTest> |
32 | |
33 | #include <qtextstream.h> |
34 | #include <qdatastream.h> |
35 | #include <qelapsedtimer.h> |
36 | #include <QtNetwork/qlocalsocket.h> |
37 | #include <QtNetwork/qlocalserver.h> |
38 | |
39 | #ifdef Q_OS_UNIX |
40 | #include <sys/types.h> |
41 | #include <sys/socket.h> |
42 | #include <sys/un.h> |
43 | #include <unistd.h> // for unlink() |
44 | #endif |
45 | |
46 | #ifdef Q_OS_WIN |
47 | #include <QtCore/qt_windows.h> |
48 | #endif |
49 | |
50 | Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) |
51 | Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState) |
52 | Q_DECLARE_METATYPE(QLocalServer::SocketOption) |
53 | Q_DECLARE_METATYPE(QFile::Permissions) |
54 | |
55 | class tst_QLocalSocket : public QObject |
56 | { |
57 | Q_OBJECT |
58 | |
59 | public: |
60 | tst_QLocalSocket(); |
61 | |
62 | private slots: |
63 | // basics |
64 | void server_basic(); |
65 | void server_connectionsCount(); |
66 | void socket_basic(); |
67 | |
68 | void listen_data(); |
69 | void listen(); |
70 | |
71 | void listenAndConnect_data(); |
72 | void listenAndConnect(); |
73 | |
74 | void connectWithOpen(); |
75 | void connectWithOldOpen(); |
76 | |
77 | void sendData_data(); |
78 | void sendData(); |
79 | |
80 | void readBufferOverflow(); |
81 | |
82 | void simpleCommandProtocol1(); |
83 | void simpleCommandProtocol2(); |
84 | |
85 | void fullPath(); |
86 | |
87 | void hitMaximumConnections_data(); |
88 | void hitMaximumConnections(); |
89 | |
90 | void setSocketDescriptor(); |
91 | |
92 | void threadedConnection_data(); |
93 | void threadedConnection(); |
94 | |
95 | void processConnection_data(); |
96 | void processConnection(); |
97 | |
98 | void longPath(); |
99 | void waitForDisconnect(); |
100 | void waitForDisconnectByServer(); |
101 | |
102 | void removeServer(); |
103 | |
104 | void recycleServer(); |
105 | void recycleClientSocket(); |
106 | |
107 | void multiConnect(); |
108 | void writeOnlySocket(); |
109 | |
110 | void writeToClientAndDisconnect_data(); |
111 | void writeToClientAndDisconnect(); |
112 | |
113 | void debug(); |
114 | void bytesWrittenSignal(); |
115 | void syncDisconnectNotify(); |
116 | void asyncDisconnectNotify(); |
117 | |
118 | void verifySocketOptions(); |
119 | void verifySocketOptions_data(); |
120 | |
121 | void verifyListenWithDescriptor(); |
122 | void verifyListenWithDescriptor_data(); |
123 | |
124 | }; |
125 | |
126 | tst_QLocalSocket::tst_QLocalSocket() |
127 | { |
128 | qRegisterMetaType<QLocalSocket::LocalSocketState>(typeName: "QLocalSocket::LocalSocketState" ); |
129 | qRegisterMetaType<QLocalSocket::LocalSocketError>(typeName: "QLocalSocket::LocalSocketError" ); |
130 | qRegisterMetaType<QLocalServer::SocketOption>(typeName: "QLocalServer::SocketOption" ); |
131 | qRegisterMetaType<QFile::Permissions>(typeName: "QFile::Permissions" ); |
132 | } |
133 | |
134 | class LocalServer : public QLocalServer |
135 | { |
136 | Q_OBJECT |
137 | |
138 | public: |
139 | LocalServer() : QLocalServer() |
140 | { |
141 | connect(sender: this, SIGNAL(newConnection()), receiver: this, SLOT(slotNewConnection())); |
142 | } |
143 | |
144 | bool listen(const QString &name) |
145 | { |
146 | removeServer(name); |
147 | return QLocalServer::listen(name); |
148 | } |
149 | |
150 | QList<int> hits; |
151 | |
152 | protected: |
153 | void incomingConnection(quintptr socketDescriptor) |
154 | { |
155 | hits.append(t: socketDescriptor); |
156 | QLocalServer::incomingConnection(socketDescriptor); |
157 | } |
158 | |
159 | private slots: |
160 | void slotNewConnection() { |
161 | QVERIFY(!hits.isEmpty()); |
162 | QVERIFY(hasPendingConnections()); |
163 | } |
164 | }; |
165 | |
166 | class LocalSocket : public QLocalSocket |
167 | { |
168 | Q_OBJECT |
169 | |
170 | public: |
171 | LocalSocket(QObject *parent = 0) : QLocalSocket(parent) |
172 | { |
173 | connect(sender: this, SIGNAL(connected()), |
174 | receiver: this, SLOT(slotConnected())); |
175 | connect(sender: this, SIGNAL(disconnected()), |
176 | receiver: this, SLOT(slotDisconnected())); |
177 | connect(sender: this, SIGNAL(errorOccurred(QLocalSocket::LocalSocketError)), |
178 | receiver: this, SLOT(slotErrorOccurred(QLocalSocket::LocalSocketError))); |
179 | connect(sender: this, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), |
180 | receiver: this, SLOT(slotStateChanged(QLocalSocket::LocalSocketState))); |
181 | connect(sender: this, SIGNAL(readyRead()), |
182 | receiver: this, SLOT(slotReadyRead())); |
183 | } |
184 | |
185 | private slots: |
186 | void slotConnected() |
187 | { |
188 | QCOMPARE(state(), QLocalSocket::ConnectedState); |
189 | QVERIFY(isOpen()); |
190 | } |
191 | void slotDisconnected() |
192 | { |
193 | QCOMPARE(state(), QLocalSocket::UnconnectedState); |
194 | } |
195 | void slotErrorOccurred(QLocalSocket::LocalSocketError newError) |
196 | { |
197 | QVERIFY(errorString() != QLatin1String("Unknown error" )); |
198 | QCOMPARE(error(), newError); |
199 | } |
200 | void slotStateChanged(QLocalSocket::LocalSocketState newState) |
201 | { |
202 | QCOMPARE(state(), newState); |
203 | } |
204 | void slotReadyRead() |
205 | { |
206 | QVERIFY(bytesAvailable() > 0); |
207 | } |
208 | }; |
209 | |
210 | // basic test make sure no segfaults and check default values |
211 | void tst_QLocalSocket::server_basic() |
212 | { |
213 | LocalServer server; |
214 | QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); |
215 | server.close(); |
216 | QCOMPARE(server.errorString(), QString()); |
217 | QCOMPARE(server.hasPendingConnections(), false); |
218 | QCOMPARE(server.isListening(), false); |
219 | QCOMPARE(server.maxPendingConnections(), 30); |
220 | QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); |
221 | QCOMPARE(server.serverName(), QString()); |
222 | QCOMPARE(server.fullServerName(), QString()); |
223 | QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); |
224 | server.setMaxPendingConnections(20); |
225 | bool timedOut = true; |
226 | QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); |
227 | QVERIFY(!timedOut); |
228 | QCOMPARE(server.listen(QString()), false); |
229 | |
230 | QCOMPARE(server.hits.count(), 0); |
231 | QCOMPARE(spyNewConnection.count(), 0); |
232 | } |
233 | |
234 | void tst_QLocalSocket::server_connectionsCount() |
235 | { |
236 | LocalServer server; |
237 | server.setMaxPendingConnections(10); |
238 | QCOMPARE(server.maxPendingConnections(), 10); |
239 | } |
240 | |
241 | // basic test make sure no segfaults and check default values |
242 | void tst_QLocalSocket::socket_basic() |
243 | { |
244 | LocalSocket socket; |
245 | QSignalSpy spyConnected(&socket, SIGNAL(connected())); |
246 | QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); |
247 | QSignalSpy spyError(&socket, SIGNAL(errorOccurred(QLocalSocket::LocalSocketError))); |
248 | QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); |
249 | QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); |
250 | |
251 | QCOMPARE(socket.serverName(), QString()); |
252 | QCOMPARE(socket.fullServerName(), QString()); |
253 | socket.abort(); |
254 | QCOMPARE(socket.bytesAvailable(), 0); |
255 | QCOMPARE(socket.bytesToWrite(), 0); |
256 | QCOMPARE(socket.canReadLine(), false); |
257 | socket.close(); |
258 | socket.disconnectFromServer(); |
259 | QCOMPARE(QLocalSocket::UnknownSocketError, socket.error()); |
260 | QVERIFY(!socket.errorString().isEmpty()); |
261 | QCOMPARE(socket.flush(), false); |
262 | QCOMPARE(socket.isValid(), false); |
263 | QCOMPARE(socket.readBufferSize(), 0); |
264 | socket.setReadBufferSize(0); |
265 | //QCOMPARE(socket.socketDescriptor(), (qintptr)-1); |
266 | QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); |
267 | QCOMPARE(socket.waitForConnected(0), false); |
268 | QTest::ignoreMessage(type: QtWarningMsg, message: "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState" ); |
269 | QCOMPARE(socket.waitForDisconnected(0), false); |
270 | QCOMPARE(socket.waitForReadyRead(0), false); |
271 | |
272 | QCOMPARE(spyConnected.count(), 0); |
273 | QCOMPARE(spyDisconnected.count(), 0); |
274 | QCOMPARE(spyError.count(), 0); |
275 | QCOMPARE(spyStateChanged.count(), 0); |
276 | QCOMPARE(spyReadyRead.count(), 0); |
277 | } |
278 | |
279 | void tst_QLocalSocket::listen_data() |
280 | { |
281 | QTest::addColumn<QString>(name: "name" ); |
282 | QTest::addColumn<bool>(name: "canListen" ); |
283 | QTest::addColumn<bool>(name: "close" ); |
284 | QTest::newRow(dataTag: "null" ) << QString() << false << false; |
285 | QTest::newRow(dataTag: "tst_localsocket" ) << "tst_localsocket" << true << true; |
286 | QTest::newRow(dataTag: "tst_localsocket" ) << "tst_localsocket" << true << false; |
287 | } |
288 | |
289 | // start a server that listens, but don't connect a socket, make sure everything is in order |
290 | void tst_QLocalSocket::listen() |
291 | { |
292 | LocalServer server; |
293 | QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); |
294 | |
295 | QFETCH(QString, name); |
296 | QFETCH(bool, canListen); |
297 | QFETCH(bool, close); |
298 | QVERIFY2((server.listen(name) == canListen), server.errorString().toLatin1().constData()); |
299 | |
300 | // test listening |
301 | QCOMPARE(server.serverName(), name); |
302 | QVERIFY(server.fullServerName().contains(name)); |
303 | QCOMPARE(server.isListening(), canListen); |
304 | QCOMPARE(server.hasPendingConnections(), false); |
305 | QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); |
306 | QCOMPARE(server.hits.count(), 0); |
307 | QCOMPARE(spyNewConnection.count(), 0); |
308 | if (canListen) { |
309 | QVERIFY(server.errorString().isEmpty()); |
310 | QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); |
311 | // already isListening |
312 | QTest::ignoreMessage(type: QtWarningMsg, message: "QLocalServer::listen() called when already listening" ); |
313 | QVERIFY(!server.listen(name)); |
314 | QVERIFY(server.socketDescriptor() != -1); |
315 | } else { |
316 | QVERIFY(!server.errorString().isEmpty()); |
317 | QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); |
318 | QCOMPARE(server.socketDescriptor(), -1); |
319 | } |
320 | QCOMPARE(server.maxPendingConnections(), 30); |
321 | bool timedOut = false; |
322 | QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); |
323 | QCOMPARE(timedOut, canListen); |
324 | if (close) |
325 | server.close(); |
326 | } |
327 | |
328 | void tst_QLocalSocket::listenAndConnect_data() |
329 | { |
330 | QTest::addColumn<QString>(name: "name" ); |
331 | QTest::addColumn<bool>(name: "canListen" ); |
332 | QTest::addColumn<int>(name: "connections" ); |
333 | for (int i = 0; i < 3; ++i) { |
334 | int connections = i; |
335 | if (i == 2) |
336 | connections = 5; |
337 | const QByteArray iB = QByteArray::number(i); |
338 | QTest::newRow(dataTag: ("null " + iB).constData()) << QString() << false << connections; |
339 | QTest::newRow(dataTag: ("tst_localsocket " + iB).constData()) << "tst_localsocket" << true << connections; |
340 | } |
341 | } |
342 | |
343 | void tst_QLocalSocket::listenAndConnect() |
344 | { |
345 | LocalServer server; |
346 | QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); |
347 | |
348 | QFETCH(QString, name); |
349 | QFETCH(bool, canListen); |
350 | QCOMPARE(server.listen(name), canListen); |
351 | QTRY_COMPARE(server.serverError(), |
352 | canListen ? QAbstractSocket::UnknownSocketError : QAbstractSocket::HostNotFoundError); |
353 | |
354 | // test creating connection(s) |
355 | QFETCH(int, connections); |
356 | QList<QLocalSocket*> sockets; |
357 | for (int i = 0; i < connections; ++i) { |
358 | LocalSocket *socket = new LocalSocket; |
359 | |
360 | QSignalSpy spyConnected(socket, SIGNAL(connected())); |
361 | QSignalSpy spyDisconnected(socket, SIGNAL(disconnected())); |
362 | QSignalSpy spyError(socket, SIGNAL(errorOccurred(QLocalSocket::LocalSocketError))); |
363 | QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); |
364 | QSignalSpy spyReadyRead(socket, SIGNAL(readyRead())); |
365 | |
366 | socket->connectToServer(name); |
367 | #if defined(QT_LOCALSOCKET_TCP) |
368 | QTest::qWait(250); |
369 | #endif |
370 | |
371 | QCOMPARE(socket->serverName(), name); |
372 | QVERIFY(socket->fullServerName().contains(name)); |
373 | sockets.append(t: socket); |
374 | if (canListen) { |
375 | QVERIFY(socket->waitForConnected()); |
376 | QVERIFY(socket->isValid()); |
377 | QCOMPARE(socket->errorString(), QString("Unknown error" )); |
378 | QCOMPARE(socket->error(), QLocalSocket::UnknownSocketError); |
379 | QCOMPARE(socket->state(), QLocalSocket::ConnectedState); |
380 | //QVERIFY(socket->socketDescriptor() != -1); |
381 | QCOMPARE(spyError.count(), 0); |
382 | } else { |
383 | QVERIFY(!socket->errorString().isEmpty()); |
384 | QVERIFY(socket->error() != QLocalSocket::UnknownSocketError); |
385 | QCOMPARE(socket->state(), QLocalSocket::UnconnectedState); |
386 | //QCOMPARE(socket->socketDescriptor(), -1); |
387 | QCOMPARE(qvariant_cast<QLocalSocket::LocalSocketError>(spyError.first()[0]), |
388 | QLocalSocket::ServerNotFoundError); |
389 | } |
390 | |
391 | QCOMPARE(socket->bytesAvailable(), 0); |
392 | QCOMPARE(socket->bytesToWrite(), 0); |
393 | QCOMPARE(socket->canReadLine(), false); |
394 | QCOMPARE(socket->flush(), false); |
395 | QCOMPARE(socket->isValid(), canListen); |
396 | QCOMPARE(socket->readBufferSize(), (qint64)0); |
397 | QCOMPARE(socket->waitForConnected(0), canListen); |
398 | QCOMPARE(socket->waitForReadyRead(0), false); |
399 | |
400 | QTRY_COMPARE(spyConnected.count(), canListen ? 1 : 0); |
401 | QCOMPARE(spyDisconnected.count(), 0); |
402 | |
403 | // error signals |
404 | QVERIFY(spyError.count() >= 0); |
405 | if (canListen) { |
406 | if (spyError.count() > 0) |
407 | QCOMPARE(qvariant_cast<QLocalSocket::LocalSocketError>(spyError.first()[0]), |
408 | QLocalSocket::SocketTimeoutError); |
409 | } else { |
410 | QCOMPARE(qvariant_cast<QLocalSocket::LocalSocketError>(spyError.first()[0]), |
411 | QLocalSocket::ServerNotFoundError); |
412 | } |
413 | |
414 | // Check first and last state |
415 | QCOMPARE(qvariant_cast<QLocalSocket::LocalSocketState>(spyStateChanged.first()[0]), |
416 | QLocalSocket::ConnectingState); |
417 | |
418 | if (canListen) |
419 | QCOMPARE(qvariant_cast<QLocalSocket::LocalSocketState>(spyStateChanged.last()[0]), |
420 | QLocalSocket::ConnectedState); |
421 | QCOMPARE(spyStateChanged.count(), 2); |
422 | QCOMPARE(spyReadyRead.count(), 0); |
423 | |
424 | bool timedOut = true; |
425 | QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); |
426 | QVERIFY(!timedOut); |
427 | QCOMPARE(server.hasPendingConnections(), canListen); |
428 | QCOMPARE(server.isListening(), canListen); |
429 | // NOTE: socket disconnecting is not tested here |
430 | |
431 | // server checks post connection |
432 | if (canListen) { |
433 | QCOMPARE(server.serverName(), name); |
434 | QVERIFY(server.fullServerName().contains(name)); |
435 | QVERIFY(server.nextPendingConnection() != (QLocalSocket*)0); |
436 | QTRY_COMPARE(server.hits.count(), i + 1); |
437 | QCOMPARE(spyNewConnection.count(), i + 1); |
438 | QVERIFY(server.errorString().isEmpty()); |
439 | QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); |
440 | } else { |
441 | QVERIFY(server.serverName().isEmpty()); |
442 | QVERIFY(server.fullServerName().isEmpty()); |
443 | QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); |
444 | QCOMPARE(spyNewConnection.count(), 0); |
445 | QCOMPARE(server.hits.count(), 0); |
446 | QVERIFY(!server.errorString().isEmpty()); |
447 | QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); |
448 | } |
449 | } |
450 | qDeleteAll(begin: sockets.begin(), end: sockets.end()); |
451 | |
452 | server.close(); |
453 | |
454 | QCOMPARE(server.hits.count(), (canListen ? connections : 0)); |
455 | QCOMPARE(spyNewConnection.count(), (canListen ? connections : 0)); |
456 | } |
457 | |
458 | void tst_QLocalSocket::connectWithOpen() |
459 | { |
460 | LocalServer server; |
461 | QVERIFY(server.listen("tst_qlocalsocket" )); |
462 | |
463 | LocalSocket socket; |
464 | socket.setServerName("tst_qlocalsocket" ); |
465 | QVERIFY(socket.open()); |
466 | |
467 | bool timedOut = true; |
468 | QVERIFY(server.waitForNewConnection(3000, &timedOut)); |
469 | |
470 | #if defined(QT_LOCALSOCKET_TCP) |
471 | QTest::qWait(250); |
472 | #endif |
473 | QVERIFY(!timedOut); |
474 | |
475 | socket.close(); |
476 | server.close(); |
477 | } |
478 | |
479 | void tst_QLocalSocket::connectWithOldOpen() |
480 | { |
481 | class OverriddenOpen : public LocalSocket |
482 | { |
483 | public: |
484 | virtual bool open(OpenMode mode) override |
485 | { return QIODevice::open(mode); } |
486 | }; |
487 | |
488 | LocalServer server; |
489 | QCOMPARE(server.listen("tst_qlocalsocket" ), true); |
490 | |
491 | OverriddenOpen socket; |
492 | socket.connectToServer(name: "tst_qlocalsocket" ); |
493 | |
494 | bool timedOut = true; |
495 | QVERIFY(server.waitForNewConnection(3000, &timedOut)); |
496 | |
497 | #if defined(QT_LOCALSOCKET_TCP) |
498 | QTest::qWait(250); |
499 | #endif |
500 | QVERIFY(!timedOut); |
501 | |
502 | socket.close(); |
503 | server.close(); |
504 | } |
505 | |
506 | void tst_QLocalSocket::sendData_data() |
507 | { |
508 | QTest::addColumn<QString>(name: "name" ); |
509 | QTest::addColumn<bool>(name: "canListen" ); |
510 | |
511 | QTest::newRow(dataTag: "null" ) << QString() << false; |
512 | QTest::newRow(dataTag: "tst_localsocket" ) << "tst_localsocket" << true; |
513 | } |
514 | |
515 | void tst_QLocalSocket::sendData() |
516 | { |
517 | QFETCH(QString, name); |
518 | QFETCH(bool, canListen); |
519 | |
520 | LocalServer server; |
521 | QSignalSpy spy(&server, SIGNAL(newConnection())); |
522 | |
523 | QCOMPARE(server.listen(name), canListen); |
524 | |
525 | LocalSocket socket; |
526 | QSignalSpy spyConnected(&socket, SIGNAL(connected())); |
527 | QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); |
528 | QSignalSpy spyError(&socket, SIGNAL(errorOccurred(QLocalSocket::LocalSocketError))); |
529 | QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); |
530 | QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); |
531 | |
532 | // test creating a connection |
533 | socket.connectToServer(name); |
534 | bool timedOut = true; |
535 | int expectedReadyReadSignals = 0; |
536 | |
537 | QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); |
538 | |
539 | #if defined(QT_LOCALSOCKET_TCP) |
540 | QTest::qWait(250); |
541 | #endif |
542 | QVERIFY(!timedOut); |
543 | QCOMPARE(spyConnected.count(), canListen ? 1 : 0); |
544 | QCOMPARE(socket.state(), canListen ? QLocalSocket::ConnectedState : QLocalSocket::UnconnectedState); |
545 | |
546 | // test sending/receiving data |
547 | if (server.hasPendingConnections()) { |
548 | QString testLine = "test" ; |
549 | for (int i = 0; i < 50000; ++i) |
550 | testLine += QLatin1Char('a'); |
551 | QLocalSocket *serverSocket = server.nextPendingConnection(); |
552 | QVERIFY(serverSocket); |
553 | QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); |
554 | QTextStream out(serverSocket); |
555 | QTextStream in(&socket); |
556 | out << testLine << Qt::endl; |
557 | bool wrote = serverSocket->waitForBytesWritten(msecs: 3000); |
558 | |
559 | if (!socket.canReadLine()) { |
560 | expectedReadyReadSignals = 1; |
561 | QVERIFY(socket.waitForReadyRead()); |
562 | } |
563 | |
564 | QVERIFY(socket.bytesAvailable() >= 0); |
565 | QCOMPARE(socket.bytesToWrite(), (qint64)0); |
566 | QCOMPARE(socket.flush(), false); |
567 | QCOMPARE(socket.isValid(), canListen); |
568 | QCOMPARE(socket.readBufferSize(), (qint64)0); |
569 | QCOMPARE(spyReadyRead.count(), expectedReadyReadSignals); |
570 | |
571 | QVERIFY(testLine.startsWith(in.readLine())); |
572 | |
573 | QVERIFY(wrote || serverSocket->waitForBytesWritten(1000)); |
574 | |
575 | QCOMPARE(serverSocket->errorString(), QString("Unknown error" )); |
576 | QCOMPARE(socket.errorString(), QString("Unknown error" )); |
577 | } |
578 | |
579 | socket.disconnectFromServer(); |
580 | QCOMPARE(spyConnected.count(), canListen ? 1 : 0); |
581 | QCOMPARE(spyDisconnected.count(), canListen ? 1 : 0); |
582 | QCOMPARE(spyError.count(), canListen ? 0 : 1); |
583 | QCOMPARE(spyStateChanged.count(), canListen ? 4 : 2); |
584 | QCOMPARE(spyReadyRead.count(), canListen ? expectedReadyReadSignals : 0); |
585 | |
586 | server.close(); |
587 | |
588 | QCOMPARE(server.hits.count(), (canListen ? 1 : 0)); |
589 | QCOMPARE(spy.count(), (canListen ? 1 : 0)); |
590 | } |
591 | |
592 | void tst_QLocalSocket::readBufferOverflow() |
593 | { |
594 | const int readBufferSize = 128; |
595 | const int dataBufferSize = readBufferSize * 2; |
596 | const QString serverName = QLatin1String("myPreciousTestServer" ); |
597 | LocalServer server; |
598 | server.listen(name: serverName); |
599 | QVERIFY(server.isListening()); |
600 | |
601 | LocalSocket client; |
602 | client.setReadBufferSize(readBufferSize); |
603 | client.connectToServer(name: serverName); |
604 | |
605 | bool timedOut = true; |
606 | QVERIFY(server.waitForNewConnection(3000, &timedOut)); |
607 | QVERIFY(!timedOut); |
608 | |
609 | QCOMPARE(client.state(), QLocalSocket::ConnectedState); |
610 | QVERIFY(server.hasPendingConnections()); |
611 | |
612 | QLocalSocket* serverSocket = server.nextPendingConnection(); |
613 | char buffer[dataBufferSize]; |
614 | memset(s: buffer, c: 0, n: dataBufferSize); |
615 | serverSocket->write(data: buffer, len: dataBufferSize); |
616 | #ifndef Q_OS_WIN |
617 | // The data is not immediately sent, but buffered. |
618 | // On Windows, the flushing is done by an asynchronous write operation. |
619 | // However, this operation will never complete as long as the data is not |
620 | // read by the other end, so the call below always times out. |
621 | // On Unix, the flushing is synchronous and thus needs to be done before |
622 | // attempting to read the data in the same thread. Buffering by the OS |
623 | // prevents the deadlock seen on Windows. |
624 | serverSocket->waitForBytesWritten(); |
625 | #endif |
626 | |
627 | // wait until the first 128 bytes are ready to read |
628 | QVERIFY(client.waitForReadyRead()); |
629 | QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); |
630 | // wait until the second 128 bytes are ready to read |
631 | QVERIFY(client.waitForReadyRead()); |
632 | QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); |
633 | // no more bytes available |
634 | QCOMPARE(client.bytesAvailable(), 0); |
635 | |
636 | #ifdef Q_OS_WIN |
637 | serverSocket->write(buffer, readBufferSize); |
638 | QVERIFY(serverSocket->waitForBytesWritten()); |
639 | |
640 | // ensure the read completion routine is called |
641 | SleepEx(100, true); |
642 | QVERIFY(client.waitForReadyRead()); |
643 | QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); |
644 | |
645 | // Test overflow caused by an asynchronous pipe operation. |
646 | client.setReadBufferSize(1); |
647 | serverSocket->write(buffer, 2); |
648 | |
649 | QVERIFY(client.waitForReadyRead()); |
650 | // socket disconnects, if there any error on pipe |
651 | QCOMPARE(client.state(), QLocalSocket::ConnectedState); |
652 | QCOMPARE(client.bytesAvailable(), qint64(2)); |
653 | QCOMPARE(client.read(buffer, 2), qint64(2)); |
654 | #endif |
655 | } |
656 | |
657 | static qint64 writeCommand(const QVariant &command, QIODevice *device, int commandCounter) |
658 | { |
659 | QByteArray block; |
660 | QDataStream out(&block, QIODevice::WriteOnly); |
661 | out << qint64(0); |
662 | out << commandCounter; |
663 | out << command; |
664 | out.device()->seek(pos: 0); |
665 | out << qint64(block.size() - sizeof(qint64)); |
666 | return device->write(data: block); |
667 | } |
668 | |
669 | static QVariant readCommand(QIODevice *ioDevice, int *readCommandCounter, bool readSize = true) |
670 | { |
671 | QDataStream in(ioDevice); |
672 | qint64 blockSize; |
673 | int commandCounter; |
674 | if (readSize) |
675 | in >> blockSize; |
676 | in >> commandCounter; |
677 | *readCommandCounter = commandCounter; |
678 | |
679 | QVariant command; |
680 | in >> command; |
681 | |
682 | return command; |
683 | } |
684 | |
685 | void tst_QLocalSocket::simpleCommandProtocol1() |
686 | { |
687 | QLocalServer server; |
688 | server.listen(QStringLiteral("simpleProtocol" )); |
689 | |
690 | QLocalSocket localSocketWrite; |
691 | localSocketWrite.connectToServer(name: server.serverName()); |
692 | QVERIFY(server.waitForNewConnection()); |
693 | QLocalSocket *localSocketRead = server.nextPendingConnection(); |
694 | QVERIFY(localSocketRead); |
695 | |
696 | int readCounter = 0; |
697 | for (int i = 0; i < 2000; ++i) { |
698 | const QVariant command(QRect(readCounter, i, 10, 10)); |
699 | const qint64 blockSize = writeCommand(command, device: &localSocketWrite, commandCounter: i); |
700 | while (localSocketWrite.bytesToWrite()) |
701 | QVERIFY(localSocketWrite.waitForBytesWritten()); |
702 | while (localSocketRead->bytesAvailable() < blockSize) { |
703 | QVERIFY(localSocketRead->waitForReadyRead(1000)); |
704 | } |
705 | const QVariant variant = readCommand(ioDevice: localSocketRead, readCommandCounter: &readCounter); |
706 | QCOMPARE(readCounter, i); |
707 | QCOMPARE(variant, command); |
708 | } |
709 | } |
710 | |
711 | void tst_QLocalSocket::simpleCommandProtocol2() |
712 | { |
713 | QLocalServer server; |
714 | server.listen(QStringLiteral("simpleProtocol" )); |
715 | |
716 | QLocalSocket localSocketWrite; |
717 | localSocketWrite.connectToServer(name: server.serverName()); |
718 | QVERIFY(server.waitForNewConnection()); |
719 | QLocalSocket* localSocketRead = server.nextPendingConnection(); |
720 | QVERIFY(localSocketRead); |
721 | |
722 | int readCounter = 0; |
723 | qint64 writtenBlockSize = 0; |
724 | qint64 blockSize = 0; |
725 | |
726 | QObject::connect(sender: localSocketRead, signal: &QLocalSocket::readyRead, slot: [&] { |
727 | forever { |
728 | if (localSocketRead->bytesAvailable() < qint64(sizeof(qint64))) |
729 | return; |
730 | |
731 | if (blockSize == 0) { |
732 | QDataStream in(localSocketRead); |
733 | in >> blockSize; |
734 | } |
735 | |
736 | if (localSocketRead->bytesAvailable() < blockSize) |
737 | return; |
738 | |
739 | int commandNumber = 0; |
740 | const QVariant variant = readCommand(ioDevice: localSocketRead, readCommandCounter: &commandNumber, readSize: false); |
741 | QCOMPARE(writtenBlockSize, blockSize); |
742 | QCOMPARE(readCounter, commandNumber); |
743 | QCOMPARE(variant.userType(), (int)QMetaType::QRect); |
744 | |
745 | readCounter++; |
746 | blockSize = 0; |
747 | } |
748 | }); |
749 | |
750 | for (int i = 0; i < 500; ++i) { |
751 | const QVariant command(QRect(readCounter, i, 10, 10)); |
752 | writtenBlockSize = writeCommand(command, device: &localSocketWrite, commandCounter: i) - sizeof(qint64); |
753 | if (i % 10 == 0) |
754 | QTest::qWait(ms: 1); |
755 | } |
756 | |
757 | localSocketWrite.abort(); |
758 | QVERIFY(localSocketRead->waitForDisconnected(1000)); |
759 | } |
760 | |
761 | // QLocalSocket/Server can take a name or path, check that it works as expected |
762 | void tst_QLocalSocket::fullPath() |
763 | { |
764 | QLocalServer server; |
765 | QString name = "qlocalsocket_pathtest" ; |
766 | #if defined(QT_LOCALSOCKET_TCP) |
767 | QString path = "QLocalServer" ; |
768 | #elif defined(Q_OS_WIN) |
769 | QString path = "\\\\.\\pipe\\" ; |
770 | #else |
771 | QString path = "/tmp" ; |
772 | #endif |
773 | QString serverName = path + '/' + name; |
774 | QVERIFY2(server.listen(serverName), server.errorString().toLatin1().constData()); |
775 | QCOMPARE(server.serverName(), serverName); |
776 | QCOMPARE(server.fullServerName(), serverName); |
777 | |
778 | LocalSocket socket; |
779 | socket.connectToServer(name: serverName); |
780 | |
781 | QCOMPARE(socket.serverName(), serverName); |
782 | QCOMPARE(socket.fullServerName(), serverName); |
783 | socket.disconnectFromServer(); |
784 | #ifdef QT_LOCALSOCKET_TCP |
785 | QTest::qWait(250); |
786 | #endif |
787 | QCOMPARE(socket.serverName(), QString()); |
788 | QCOMPARE(socket.fullServerName(), QString()); |
789 | } |
790 | |
791 | void tst_QLocalSocket::hitMaximumConnections_data() |
792 | { |
793 | QTest::addColumn<int>(name: "max" ); |
794 | QTest::newRow(dataTag: "none" ) << 0; |
795 | QTest::newRow(dataTag: "1" ) << 1; |
796 | QTest::newRow(dataTag: "3" ) << 3; |
797 | } |
798 | |
799 | void tst_QLocalSocket::hitMaximumConnections() |
800 | { |
801 | QFETCH(int, max); |
802 | LocalServer server; |
803 | QString name = "tst_localsocket" ; |
804 | server.setMaxPendingConnections(max); |
805 | QVERIFY2(server.listen(name), server.errorString().toLatin1().constData()); |
806 | int connections = server.maxPendingConnections() + 1; |
807 | QList<QLocalSocket*> sockets; |
808 | for (int i = 0; i < connections; ++i) { |
809 | LocalSocket *socket = new LocalSocket; |
810 | sockets.append(t: socket); |
811 | socket->connectToServer(name); |
812 | } |
813 | bool timedOut = true; |
814 | QVERIFY(server.waitForNewConnection(3000, &timedOut)); |
815 | QVERIFY(!timedOut); |
816 | QVERIFY(server.hits.count() > 0); |
817 | qDeleteAll(begin: sockets.begin(), end: sockets.end()); |
818 | } |
819 | |
820 | // check that state and mode are kept |
821 | void tst_QLocalSocket::setSocketDescriptor() |
822 | { |
823 | LocalSocket socket; |
824 | qintptr minusOne = -1; |
825 | socket.setSocketDescriptor(socketDescriptor: minusOne, socketState: QLocalSocket::ConnectingState, openMode: QIODevice::Append); |
826 | QCOMPARE(socket.socketDescriptor(), minusOne); |
827 | QCOMPARE(socket.state(), QLocalSocket::ConnectingState); |
828 | QVERIFY((socket.openMode() & QIODevice::Append) != 0); |
829 | } |
830 | |
831 | class Client : public QThread |
832 | { |
833 | |
834 | public: |
835 | void run() |
836 | { |
837 | QString testLine = "test" ; |
838 | LocalSocket socket; |
839 | QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); |
840 | socket.connectToServer(name: "qlocalsocket_threadtest" ); |
841 | QVERIFY(socket.waitForConnected(1000)); |
842 | |
843 | // We should *not* have this signal yet! |
844 | QCOMPARE(spyReadyRead.count(), 0); |
845 | socket.waitForReadyRead(); |
846 | QCOMPARE(spyReadyRead.count(), 1); |
847 | QTextStream in(&socket); |
848 | QCOMPARE(in.readLine(), testLine); |
849 | socket.close(); |
850 | } |
851 | }; |
852 | |
853 | class Server : public QThread |
854 | { |
855 | |
856 | public: |
857 | int clients; |
858 | QMutex mutex; |
859 | QWaitCondition wc; |
860 | void run() |
861 | { |
862 | QString testLine = "test" ; |
863 | LocalServer server; |
864 | server.setMaxPendingConnections(10); |
865 | QVERIFY2(server.listen("qlocalsocket_threadtest" ), |
866 | server.errorString().toLatin1().constData()); |
867 | mutex.lock(); |
868 | wc.wakeAll(); |
869 | mutex.unlock(); |
870 | int done = clients; |
871 | while (done > 0) { |
872 | bool timedOut = true; |
873 | QVERIFY2(server.waitForNewConnection(7000, &timedOut), |
874 | (QByteArrayLiteral("done=" ) + QByteArray::number(done) |
875 | + QByteArrayLiteral(", timedOut=" ) |
876 | + (timedOut ? "true" : "false" )).constData()); |
877 | QVERIFY(!timedOut); |
878 | QLocalSocket *serverSocket = server.nextPendingConnection(); |
879 | QVERIFY(serverSocket); |
880 | QTextStream out(serverSocket); |
881 | out << testLine << Qt::endl; |
882 | QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); |
883 | QVERIFY2(serverSocket->waitForBytesWritten(), serverSocket->errorString().toLatin1().constData()); |
884 | QCOMPARE(serverSocket->errorString(), QString("Unknown error" )); |
885 | --done; |
886 | delete serverSocket; |
887 | } |
888 | QCOMPARE(server.hits.count(), clients); |
889 | } |
890 | }; |
891 | |
892 | void tst_QLocalSocket::threadedConnection_data() |
893 | { |
894 | QTest::addColumn<int>(name: "threads" ); |
895 | QTest::newRow(dataTag: "1 client" ) << 1; |
896 | QTest::newRow(dataTag: "2 clients" ) << 2; |
897 | QTest::newRow(dataTag: "5 clients" ) << 5; |
898 | QTest::newRow(dataTag: "10 clients" ) << 10; |
899 | QTest::newRow(dataTag: "20 clients" ) << 20; |
900 | } |
901 | |
902 | void tst_QLocalSocket::threadedConnection() |
903 | { |
904 | QFETCH(int, threads); |
905 | Server server; |
906 | server.clients = threads; |
907 | server.mutex.lock(); |
908 | server.start(); |
909 | server.wc.wait(lockedMutex: &server.mutex); |
910 | server.mutex.unlock(); |
911 | |
912 | QList<Client*> clients; |
913 | for (int i = 0; i < threads; ++i) { |
914 | clients.append(t: new Client()); |
915 | clients.last()->start(); |
916 | } |
917 | |
918 | server.wait(); |
919 | while (!clients.isEmpty()) { |
920 | QVERIFY(clients.first()->wait(3000)); |
921 | delete clients.takeFirst(); |
922 | } |
923 | } |
924 | |
925 | void tst_QLocalSocket::processConnection_data() |
926 | { |
927 | QTest::addColumn<int>(name: "processes" ); |
928 | QTest::newRow(dataTag: "1 client" ) << 1; |
929 | QTest::newRow(dataTag: "2 clients" ) << 2; |
930 | QTest::newRow(dataTag: "5 clients" ) << 5; |
931 | QTest::newRow(dataTag: "30 clients" ) << 30; |
932 | } |
933 | |
934 | #if QT_CONFIG(process) |
935 | class ProcessOutputDumper |
936 | { |
937 | public: |
938 | ProcessOutputDumper(QProcess *p = 0) |
939 | : process(p) |
940 | {} |
941 | |
942 | ~ProcessOutputDumper() |
943 | { |
944 | if (process) |
945 | fputs(s: process->readAll().data(), stdout); |
946 | } |
947 | |
948 | void clear() |
949 | { |
950 | process = 0; |
951 | } |
952 | |
953 | private: |
954 | QProcess *process; |
955 | }; |
956 | #endif |
957 | |
958 | /*! |
959 | Create external processes that produce and consume. |
960 | */ |
961 | void tst_QLocalSocket::processConnection() |
962 | { |
963 | #if !QT_CONFIG(process) |
964 | QSKIP("No qprocess support" , SkipAll); |
965 | #else |
966 | |
967 | #ifdef Q_OS_WIN |
968 | const QString exeSuffix = QStringLiteral(".exe" ); |
969 | #else |
970 | const QString exeSuffix; |
971 | #endif |
972 | |
973 | const QString socketProcess |
974 | = QFINDTESTDATA(QStringLiteral("socketprocess/socketprocess" ) + exeSuffix); |
975 | QVERIFY(QFile::exists(socketProcess)); |
976 | |
977 | QFETCH(int, processes); |
978 | QStringList serverArguments = QStringList() << "--server" << QString::number(processes); |
979 | QProcess producer; |
980 | ProcessOutputDumper producerOutputDumper(&producer); |
981 | QList<QProcess*> consumers; |
982 | producer.setProcessChannelMode(QProcess::MergedChannels); |
983 | producer.start(program: socketProcess, arguments: serverArguments); |
984 | QVERIFY2(producer.waitForStarted(-1), qPrintable(producer.errorString())); |
985 | for (int i = 0; i < processes; ++i) { |
986 | QStringList arguments = QStringList() << "--client" ; |
987 | QProcess *p = new QProcess; |
988 | consumers.append(t: p); |
989 | p->setProcessChannelMode(QProcess::MergedChannels); |
990 | p->start(program: socketProcess, arguments); |
991 | } |
992 | |
993 | while (!consumers.isEmpty()) { |
994 | QProcess *consumer = consumers.takeFirst(); |
995 | ProcessOutputDumper consumerOutputDumper(consumer); |
996 | consumer->waitForFinished(msecs: 20000); |
997 | QCOMPARE(consumer->exitStatus(), QProcess::NormalExit); |
998 | QCOMPARE(consumer->exitCode(), 0); |
999 | consumerOutputDumper.clear(); |
1000 | consumer->terminate(); |
1001 | delete consumer; |
1002 | } |
1003 | producer.waitForFinished(msecs: 15000); |
1004 | producerOutputDumper.clear(); |
1005 | #endif |
1006 | } |
1007 | |
1008 | void tst_QLocalSocket::longPath() |
1009 | { |
1010 | #ifndef Q_OS_WIN |
1011 | QString name; |
1012 | for (int i = 0; i < 256; ++i) |
1013 | name += 'a'; |
1014 | LocalServer server; |
1015 | QVERIFY(!server.listen(name)); |
1016 | |
1017 | LocalSocket socket; |
1018 | socket.connectToServer(name); |
1019 | QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); |
1020 | #endif |
1021 | } |
1022 | |
1023 | void tst_QLocalSocket::waitForDisconnect() |
1024 | { |
1025 | QString name = "tst_localsocket" ; |
1026 | LocalServer server; |
1027 | QVERIFY(server.listen(name)); |
1028 | LocalSocket socket; |
1029 | socket.connectToServer(name); |
1030 | QVERIFY(socket.waitForConnected(3000)); |
1031 | QVERIFY(server.waitForNewConnection(3000)); |
1032 | QLocalSocket *serverSocket = server.nextPendingConnection(); |
1033 | QVERIFY(serverSocket); |
1034 | socket.disconnectFromServer(); |
1035 | QElapsedTimer timer; |
1036 | timer.start(); |
1037 | QVERIFY(serverSocket->waitForDisconnected(3000)); |
1038 | QVERIFY(timer.elapsed() < 2000); |
1039 | } |
1040 | |
1041 | void tst_QLocalSocket::waitForDisconnectByServer() |
1042 | { |
1043 | QString name = "tst_localsocket" ; |
1044 | LocalServer server; |
1045 | QVERIFY(server.listen(name)); |
1046 | LocalSocket socket; |
1047 | QSignalSpy spy(&socket, SIGNAL(disconnected())); |
1048 | QVERIFY(spy.isValid()); |
1049 | socket.connectToServer(name); |
1050 | QVERIFY(socket.waitForConnected(3000)); |
1051 | QVERIFY(server.waitForNewConnection(3000)); |
1052 | QLocalSocket *serverSocket = server.nextPendingConnection(); |
1053 | QVERIFY(serverSocket); |
1054 | serverSocket->close(); |
1055 | QCOMPARE(serverSocket->state(), QLocalSocket::UnconnectedState); |
1056 | QVERIFY(socket.waitForDisconnected(3000)); |
1057 | QCOMPARE(spy.count(), 1); |
1058 | } |
1059 | |
1060 | void tst_QLocalSocket::removeServer() |
1061 | { |
1062 | // this is a hostile takeover, but recovering from a crash results in the same |
1063 | QLocalServer server, server2; |
1064 | QVERIFY(QLocalServer::removeServer("cleanuptest" )); |
1065 | QVERIFY(server.listen("cleanuptest" )); |
1066 | #ifndef Q_OS_WIN |
1067 | // on Windows, there can be several sockets listening on the same pipe |
1068 | // on Unix, there can only be one socket instance |
1069 | QVERIFY(! server2.listen("cleanuptest" )); |
1070 | #endif |
1071 | QVERIFY(QLocalServer::removeServer("cleanuptest" )); |
1072 | QVERIFY(server2.listen("cleanuptest" )); |
1073 | } |
1074 | |
1075 | void tst_QLocalSocket::recycleServer() |
1076 | { |
1077 | QLocalServer server; |
1078 | QLocalSocket client; |
1079 | |
1080 | QVERIFY(server.listen("recycletest1" )); |
1081 | client.connectToServer(name: "recycletest1" ); |
1082 | QVERIFY(client.waitForConnected(201)); |
1083 | QVERIFY(server.waitForNewConnection(201)); |
1084 | QVERIFY(server.nextPendingConnection() != 0); |
1085 | |
1086 | server.close(); |
1087 | client.disconnectFromServer(); |
1088 | qApp->processEvents(); |
1089 | |
1090 | QVERIFY(server.listen("recycletest2" )); |
1091 | client.connectToServer(name: "recycletest2" ); |
1092 | QVERIFY(client.waitForConnected(202)); |
1093 | QVERIFY(server.waitForNewConnection(202)); |
1094 | QVERIFY(server.nextPendingConnection() != 0); |
1095 | } |
1096 | |
1097 | void tst_QLocalSocket::recycleClientSocket() |
1098 | { |
1099 | const QByteArrayList lines = QByteArrayList() << "Have you heard of that new band" |
1100 | << "\"1023 Megabytes\"?" |
1101 | << "They haven't made it to a gig yet." ; |
1102 | QLocalServer server; |
1103 | const QString serverName = QStringLiteral("recycleClientSocket" ); |
1104 | QVERIFY(server.listen(serverName)); |
1105 | QLocalSocket client; |
1106 | QSignalSpy clientReadyReadSpy(&client, SIGNAL(readyRead())); |
1107 | QSignalSpy clientErrorSpy(&client, SIGNAL(errorOccurred(QLocalSocket::LocalSocketError))); |
1108 | for (int i = 0; i < lines.count(); ++i) { |
1109 | client.abort(); |
1110 | clientReadyReadSpy.clear(); |
1111 | client.connectToServer(name: serverName); |
1112 | QVERIFY(client.waitForConnected()); |
1113 | QVERIFY(server.waitForNewConnection()); |
1114 | QLocalSocket *serverSocket = server.nextPendingConnection(); |
1115 | QVERIFY(serverSocket); |
1116 | connect(sender: serverSocket, signal: &QLocalSocket::disconnected, slot: &QLocalSocket::deleteLater); |
1117 | serverSocket->write(data: lines.at(i)); |
1118 | serverSocket->flush(); |
1119 | QVERIFY(clientReadyReadSpy.wait()); |
1120 | QCOMPARE(client.readAll(), lines.at(i)); |
1121 | QVERIFY(clientErrorSpy.isEmpty()); |
1122 | } |
1123 | } |
1124 | |
1125 | void tst_QLocalSocket::multiConnect() |
1126 | { |
1127 | QLocalServer server; |
1128 | QLocalSocket client1; |
1129 | QLocalSocket client2; |
1130 | QLocalSocket client3; |
1131 | |
1132 | QVERIFY(server.listen("multiconnect" )); |
1133 | |
1134 | client1.connectToServer(name: "multiconnect" ); |
1135 | client2.connectToServer(name: "multiconnect" ); |
1136 | client3.connectToServer(name: "multiconnect" ); |
1137 | |
1138 | QVERIFY(client1.waitForConnected(201)); |
1139 | QVERIFY(client2.waitForConnected(202)); |
1140 | QVERIFY(client3.waitForConnected(203)); |
1141 | |
1142 | QVERIFY(server.waitForNewConnection(201)); |
1143 | QVERIFY(server.nextPendingConnection() != 0); |
1144 | QVERIFY(server.waitForNewConnection(202)); |
1145 | QVERIFY(server.nextPendingConnection() != 0); |
1146 | QVERIFY(server.waitForNewConnection(203)); |
1147 | QVERIFY(server.nextPendingConnection() != 0); |
1148 | } |
1149 | |
1150 | void tst_QLocalSocket::writeOnlySocket() |
1151 | { |
1152 | QLocalServer server; |
1153 | QVERIFY(server.listen("writeOnlySocket" )); |
1154 | |
1155 | QLocalSocket client; |
1156 | client.connectToServer(name: "writeOnlySocket" , openMode: QIODevice::WriteOnly); |
1157 | QVERIFY(client.waitForConnected()); |
1158 | QVERIFY(server.waitForNewConnection(200)); |
1159 | QLocalSocket* serverSocket = server.nextPendingConnection(); |
1160 | QVERIFY(serverSocket); |
1161 | |
1162 | QCOMPARE(client.bytesAvailable(), qint64(0)); |
1163 | QCOMPARE(client.state(), QLocalSocket::ConnectedState); |
1164 | } |
1165 | |
1166 | void tst_QLocalSocket::writeToClientAndDisconnect_data() |
1167 | { |
1168 | QTest::addColumn<int>(name: "chunks" ); |
1169 | QTest::newRow(dataTag: "one chunk" ) << 1; |
1170 | QTest::newRow(dataTag: "several chunks" ) << 20; |
1171 | } |
1172 | |
1173 | void tst_QLocalSocket::writeToClientAndDisconnect() |
1174 | { |
1175 | QFETCH(int, chunks); |
1176 | QLocalServer server; |
1177 | QLocalSocket client; |
1178 | QSignalSpy readChannelFinishedSpy(&client, SIGNAL(readChannelFinished())); |
1179 | |
1180 | QVERIFY(server.listen("writeAndDisconnectServer" )); |
1181 | client.connectToServer(name: "writeAndDisconnectServer" ); |
1182 | QVERIFY(client.waitForConnected(200)); |
1183 | QVERIFY(server.waitForNewConnection(200)); |
1184 | QLocalSocket* clientSocket = server.nextPendingConnection(); |
1185 | QVERIFY(clientSocket); |
1186 | |
1187 | char buffer[100]; |
1188 | memset(s: buffer, c: 0, n: sizeof(buffer)); |
1189 | for (int i = 0; i < chunks; ++i) |
1190 | QCOMPARE(clientSocket->write(buffer, sizeof(buffer)), qint64(sizeof(buffer))); |
1191 | while (clientSocket->bytesToWrite()) |
1192 | QVERIFY(clientSocket->waitForBytesWritten()); |
1193 | clientSocket->close(); |
1194 | server.close(); |
1195 | |
1196 | client.waitForDisconnected(); |
1197 | QCOMPARE(readChannelFinishedSpy.count(), 1); |
1198 | const QByteArray received = client.readAll(); |
1199 | QCOMPARE(received.size(), qint64(sizeof(buffer) * chunks)); |
1200 | QCOMPARE(client.state(), QLocalSocket::UnconnectedState); |
1201 | } |
1202 | |
1203 | void tst_QLocalSocket::debug() |
1204 | { |
1205 | // Make sure this compiles |
1206 | if (QLoggingCategory::defaultCategory()->isDebugEnabled()) |
1207 | QTest::ignoreMessage(type: QtDebugMsg, message: "QLocalSocket::ConnectionRefusedError QLocalSocket::UnconnectedState" ); |
1208 | qDebug() << QLocalSocket::ConnectionRefusedError << QLocalSocket::UnconnectedState; |
1209 | } |
1210 | |
1211 | class WriteThread : public QThread |
1212 | { |
1213 | Q_OBJECT |
1214 | public: |
1215 | void run() { |
1216 | QLocalSocket socket; |
1217 | socket.connectToServer(name: "qlocalsocket_readyread" ); |
1218 | |
1219 | if (!socket.waitForConnected(msecs: 3000)) |
1220 | exec(); |
1221 | connect(sender: &socket, SIGNAL(bytesWritten(qint64)), |
1222 | receiver: this, SLOT(bytesWritten(qint64)), Qt::QueuedConnection); |
1223 | socket.write(data: "testing\n" ); |
1224 | exec(); |
1225 | } |
1226 | signals: |
1227 | void bytesWrittenReceived(); |
1228 | public slots: |
1229 | void bytesWritten(qint64) { |
1230 | emit bytesWrittenReceived(); |
1231 | exit(); |
1232 | } |
1233 | }; |
1234 | |
1235 | /* |
1236 | Tests the emission of the bytesWritten(qint64) |
1237 | signal. |
1238 | |
1239 | Create a thread that will write to a socket. |
1240 | If the bytesWritten(qint64) signal is generated, |
1241 | the slot connected to it will exit the thread, |
1242 | indicating test success. |
1243 | |
1244 | */ |
1245 | void tst_QLocalSocket::bytesWrittenSignal() |
1246 | { |
1247 | QLocalServer server; |
1248 | QVERIFY(server.listen("qlocalsocket_readyread" )); |
1249 | WriteThread writeThread; |
1250 | QSignalSpy receivedSpy(&writeThread, &WriteThread::bytesWrittenReceived); |
1251 | writeThread.start(); |
1252 | bool timedOut = false; |
1253 | QVERIFY(server.waitForNewConnection(3000, &timedOut)); |
1254 | QVERIFY(!timedOut); |
1255 | QVERIFY(receivedSpy.wait(2000)); |
1256 | QVERIFY(writeThread.wait(2000)); |
1257 | } |
1258 | |
1259 | void tst_QLocalSocket::syncDisconnectNotify() |
1260 | { |
1261 | QLocalServer server; |
1262 | QVERIFY(server.listen("syncDisconnectNotify" )); |
1263 | QLocalSocket client; |
1264 | client.connectToServer(name: "syncDisconnectNotify" ); |
1265 | QVERIFY(server.waitForNewConnection()); |
1266 | QLocalSocket* serverSocket = server.nextPendingConnection(); |
1267 | QVERIFY(serverSocket); |
1268 | delete serverSocket; |
1269 | QCOMPARE(client.waitForReadyRead(), false); |
1270 | QVERIFY(!client.putChar(0)); |
1271 | } |
1272 | |
1273 | void tst_QLocalSocket::asyncDisconnectNotify() |
1274 | { |
1275 | QLocalServer server; |
1276 | QVERIFY(server.listen("asyncDisconnectNotify" )); |
1277 | QLocalSocket client; |
1278 | QSignalSpy disconnectedSpy(&client, SIGNAL(disconnected())); |
1279 | client.connectToServer(name: "asyncDisconnectNotify" ); |
1280 | QVERIFY(server.waitForNewConnection()); |
1281 | QLocalSocket* serverSocket = server.nextPendingConnection(); |
1282 | QVERIFY(serverSocket); |
1283 | delete serverSocket; |
1284 | QTRY_VERIFY(!disconnectedSpy.isEmpty()); |
1285 | } |
1286 | |
1287 | void tst_QLocalSocket::verifySocketOptions_data() |
1288 | { |
1289 | #ifdef Q_OS_LINUX |
1290 | QTest::addColumn<QString>(name: "service" ); |
1291 | QTest::addColumn<QLocalServer::SocketOption>(name: "opts" ); |
1292 | QTest::addColumn<QFile::Permissions>(name: "perms" ); |
1293 | |
1294 | QFile::Permissions p = QFile::ExeOwner|QFile::WriteOwner|QFile::ReadOwner | |
1295 | QFile::ExeUser|QFile::WriteUser|QFile::ReadUser; |
1296 | QTest::newRow(dataTag: "user" ) << "userPerms" << QLocalServer::UserAccessOption << p; |
1297 | |
1298 | p = QFile::ExeGroup|QFile::WriteGroup|QFile::ReadGroup; |
1299 | QTest::newRow(dataTag: "group" ) << "groupPerms" << QLocalServer::GroupAccessOption << p; |
1300 | |
1301 | p = QFile::ExeOther|QFile::WriteOther|QFile::ReadOther; |
1302 | QTest::newRow(dataTag: "other" ) << "otherPerms" << QLocalServer::OtherAccessOption << p; |
1303 | |
1304 | p = QFile::ExeOwner|QFile::WriteOwner|QFile::ReadOwner| |
1305 | QFile::ExeUser|QFile::WriteUser|QFile::ReadUser | |
1306 | QFile::ExeGroup|QFile::WriteGroup|QFile::ReadGroup| |
1307 | QFile::ExeOther|QFile::WriteOther|QFile::ReadOther; |
1308 | QTest::newRow(dataTag: "all" ) << "worldPerms" << QLocalServer::WorldAccessOption << p; |
1309 | #endif |
1310 | } |
1311 | |
1312 | void tst_QLocalSocket::verifySocketOptions() |
1313 | { |
1314 | // These are only guaranteed to be useful on linux at this time |
1315 | #ifdef Q_OS_LINUX |
1316 | QFETCH(QString, service); |
1317 | QFETCH(QLocalServer::SocketOption, opts); |
1318 | QFETCH(QFile::Permissions, perms); |
1319 | |
1320 | |
1321 | QLocalServer::removeServer(name: service); |
1322 | QLocalServer server; |
1323 | server.setSocketOptions(opts); |
1324 | QVERIFY2(server.listen(service), "service failed to start listening" ); |
1325 | |
1326 | // find the socket |
1327 | QString fullServerPath = QDir::cleanPath(path: QDir::tempPath()); |
1328 | fullServerPath += QLatin1Char('/') + service; |
1329 | |
1330 | QFile socketFile(fullServerPath); |
1331 | QVERIFY2(perms == socketFile.permissions(), "permissions on the socket don't match" ); |
1332 | #endif |
1333 | } |
1334 | |
1335 | void tst_QLocalSocket::verifyListenWithDescriptor() |
1336 | { |
1337 | #ifdef Q_OS_UNIX |
1338 | QFETCH(QString, path); |
1339 | QFETCH(bool, abstract); |
1340 | QFETCH(bool, bound); |
1341 | |
1342 | // qDebug() << "socket" << path << abstract; |
1343 | |
1344 | int listenSocket; |
1345 | |
1346 | if (bound) { |
1347 | // create the unix socket |
1348 | listenSocket = ::socket(PF_UNIX, SOCK_STREAM, protocol: 0); |
1349 | QVERIFY2(listenSocket != -1, "failed to create test socket" ); |
1350 | |
1351 | // Construct the unix address |
1352 | struct ::sockaddr_un addr; |
1353 | addr.sun_family = PF_UNIX; |
1354 | |
1355 | QVERIFY2(sizeof(addr.sun_path) > ((uint)path.size() + 1), "path to large to create socket" ); |
1356 | |
1357 | ::memset(s: addr.sun_path, c: 0, n: sizeof(addr.sun_path)); |
1358 | if (abstract) |
1359 | ::memcpy(dest: addr.sun_path+1, src: path.toLatin1().data(), n: path.toLatin1().size()); |
1360 | else |
1361 | ::memcpy(dest: addr.sun_path, src: path.toLatin1().data(), n: path.toLatin1().size()); |
1362 | |
1363 | if (path.startsWith(c: QLatin1Char('/'))) { |
1364 | ::unlink(name: path.toLatin1()); |
1365 | } |
1366 | |
1367 | QVERIFY2(-1 != ::bind(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un)), "failed to bind test socket to address" ); |
1368 | |
1369 | // listen for connections |
1370 | QVERIFY2(-1 != ::listen(listenSocket, 50), "failed to call listen on test socket" ); |
1371 | } else { |
1372 | int fds[2]; |
1373 | QVERIFY2(-1 != ::socketpair(PF_UNIX, SOCK_STREAM, 0, fds), "failed to create socket pair" ); |
1374 | |
1375 | listenSocket = fds[0]; |
1376 | close(fd: fds[1]); |
1377 | } |
1378 | |
1379 | QLocalServer server; |
1380 | QVERIFY2(server.listen(listenSocket), "failed to start create QLocalServer with local socket" ); |
1381 | |
1382 | #ifdef Q_OS_LINUX |
1383 | const QChar at(QLatin1Char('@')); |
1384 | if (!bound) { |
1385 | QCOMPARE(server.serverName().at(0), at); |
1386 | QCOMPARE(server.fullServerName().at(0), at); |
1387 | } else if (abstract) { |
1388 | QVERIFY2(server.fullServerName().at(0) == at, "abstract sockets should start with a '@'" ); |
1389 | } else { |
1390 | QCOMPARE(server.fullServerName(), path); |
1391 | if (path.contains(c: QLatin1Char('/'))) { |
1392 | QVERIFY2(server.serverName() == path.mid(path.lastIndexOf(QLatin1Char('/'))+1), "server name invalid short name" ); |
1393 | } else { |
1394 | QVERIFY2(server.serverName() == path, "servier name doesn't match the path provided" ); |
1395 | } |
1396 | } |
1397 | #else |
1398 | QVERIFY(server.serverName().isEmpty()); |
1399 | QVERIFY(server.fullServerName().isEmpty()); |
1400 | #endif |
1401 | |
1402 | |
1403 | #endif |
1404 | } |
1405 | |
1406 | void tst_QLocalSocket::verifyListenWithDescriptor_data() |
1407 | { |
1408 | #ifdef Q_OS_UNIX |
1409 | QTest::addColumn<QString>(name: "path" ); |
1410 | QTest::addColumn<bool>(name: "abstract" ); |
1411 | QTest::addColumn<bool>(name: "bound" ); |
1412 | |
1413 | QTest::newRow(dataTag: "normal" ) << QDir::tempPath() + QLatin1String("/testsocket" ) << false << true; |
1414 | #ifdef Q_OS_LINUX |
1415 | QTest::newRow(dataTag: "abstract" ) << QString::fromLatin1(str: "abstractsocketname" ) << true << true; |
1416 | QTest::newRow(dataTag: "abstractwithslash" ) << QString::fromLatin1(str: "abstractsocketwitha/inthename" ) << true << true; |
1417 | #endif |
1418 | QTest::newRow(dataTag: "no path" ) << QString::fromLatin1(str: "/invalid/no path name specified" ) << true << false; |
1419 | |
1420 | #endif |
1421 | |
1422 | } |
1423 | |
1424 | QTEST_MAIN(tst_QLocalSocket) |
1425 | #include "tst_qlocalsocket.moc" |
1426 | |
1427 | |