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 QtBluetooth module 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
31#include <QDebug>
32
33#include <qbluetoothsocket.h>
34#include <qbluetoothdeviceinfo.h>
35#include <qbluetoothserviceinfo.h>
36#include <qbluetoothservicediscoveryagent.h>
37#include <qbluetoothlocaldevice.h>
38#if QT_CONFIG(bluez)
39#include <QtBluetooth/private/bluez5_helper_p.h>
40#endif
41
42QT_USE_NAMESPACE
43
44Q_DECLARE_METATYPE(QBluetoothServiceInfo::Protocol)
45
46//same uuid as tests/bttestui
47#define TEST_SERVICE_UUID "e8e10f95-1a70-4b27-9ccf-02010264e9c8"
48
49// Max time to wait for connection
50
51static const int MaxConnectTime = 60 * 1000; // 1 minute in ms
52static const int MaxReadWriteTime = 60 * 1000; // 1 minute in ms
53
54class tst_QBluetoothSocket : public QObject
55{
56 Q_OBJECT
57
58public:
59 enum ClientConnectionShutdown {
60 Error,
61 Disconnect,
62 Close,
63 Abort,
64 };
65
66 tst_QBluetoothSocket();
67 ~tst_QBluetoothSocket();
68
69private slots:
70 void initTestCase();
71
72 void tst_construction_data();
73 void tst_construction();
74
75 void tst_serviceConnection();
76
77 void tst_clientCommunication_data();
78 void tst_clientCommunication();
79
80 void tst_error();
81
82 void tst_preferredSecurityFlags();
83
84 void tst_unsupportedProtocolError();
85
86public slots:
87 void serviceDiscovered(const QBluetoothServiceInfo &info);
88 void finished();
89 void error(QBluetoothServiceDiscoveryAgent::Error error);
90private:
91 bool done_discovery;
92 bool localDeviceFound;
93 QBluetoothDeviceInfo remoteDevice;
94 QBluetoothHostInfo localDevice;
95 QBluetoothServiceInfo remoteServiceInfo;
96
97};
98
99Q_DECLARE_METATYPE(tst_QBluetoothSocket::ClientConnectionShutdown)
100
101tst_QBluetoothSocket::tst_QBluetoothSocket()
102{
103 qRegisterMetaType<QBluetoothSocket::SocketState>();
104 qRegisterMetaType<QBluetoothSocket::SocketError>();
105
106 localDeviceFound = false; // true if we have a local adapter
107 done_discovery = false; //true if we found remote device
108
109 //Enable logging to facilitate debugging in case of error
110 QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
111}
112
113tst_QBluetoothSocket::~tst_QBluetoothSocket()
114{
115}
116
117void tst_QBluetoothSocket::initTestCase()
118{
119 // setup Bluetooth env
120 const QList<QBluetoothHostInfo> foundDevices = QBluetoothLocalDevice::allDevices();
121 if (!foundDevices.count()) {
122 qWarning() << "Missing local device";
123 return;
124 } else {
125 localDevice = foundDevices.at(i: 0);
126 qDebug() << "Local device" << localDevice.name() << localDevice.address().toString();
127 }
128
129 localDeviceFound = true;
130
131 //start the device
132 QBluetoothLocalDevice device(localDevice.address());
133 device.powerOn();
134
135
136 //find the remote test server
137 //the remote test server is tests/bttestui
138
139 // Go find the server
140 QBluetoothServiceDiscoveryAgent *sda = new QBluetoothServiceDiscoveryAgent(this);
141 connect(sender: sda, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), receiver: this, SLOT(serviceDiscovered(QBluetoothServiceInfo)));
142 connect(sender: sda, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), receiver: this, SLOT(error(QBluetoothServiceDiscoveryAgent::Error)));
143 connect(sender: sda, SIGNAL(finished()), receiver: this, SLOT(finished()));
144
145 qDebug() << "Starting discovery";
146
147 sda->setUuidFilter(QBluetoothUuid(QString(TEST_SERVICE_UUID)));
148 sda->start(mode: QBluetoothServiceDiscoveryAgent::FullDiscovery);
149
150 for (int connectTime = MaxConnectTime; !done_discovery && connectTime > 0; connectTime -= 1000)
151 QTest::qWait(ms: 1000);
152
153 sda->stop();
154
155 if (!remoteDevice.isValid()) {
156 qWarning() << "#########################";
157 qWarning() << "Unable to find test service";
158 qWarning() << "Remote device may have to be changed into Discoverable mode";
159 qWarning() << "#########################";
160 }
161
162 delete sda;
163}
164
165void tst_QBluetoothSocket::error(QBluetoothServiceDiscoveryAgent::Error error)
166{
167 qDebug() << "Received error" << error;
168 done_discovery = true;
169}
170
171void tst_QBluetoothSocket::finished()
172{
173 qDebug() << "Finished";
174 done_discovery = true;
175}
176
177void tst_QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &info)
178{
179 qDebug() << "Found test service on:" << info.device().name() << info.device().address().toString();
180 remoteDevice = info.device();
181 remoteServiceInfo = info;
182 done_discovery = true;
183}
184
185void tst_QBluetoothSocket::tst_construction_data()
186{
187 QTest::addColumn<QBluetoothServiceInfo::Protocol>(name: "socketType");
188
189 QTest::newRow(dataTag: "unknown protocol") << QBluetoothServiceInfo::UnknownProtocol;
190 QTest::newRow(dataTag: "rfcomm socket") << QBluetoothServiceInfo::RfcommProtocol;
191 QTest::newRow(dataTag: "l2cap socket") << QBluetoothServiceInfo::L2capProtocol;
192}
193
194void tst_QBluetoothSocket::tst_construction()
195{
196 QFETCH(QBluetoothServiceInfo::Protocol, socketType);
197
198 {
199 QBluetoothSocket socket;
200
201 QCOMPARE(socket.socketType(), QBluetoothServiceInfo::UnknownProtocol);
202 QCOMPARE(socket.error(), QBluetoothSocket::NoSocketError);
203 QCOMPARE(socket.errorString(), QString());
204 QCOMPARE(socket.peerAddress(), QBluetoothAddress());
205 QCOMPARE(socket.peerName(), QString());
206 QCOMPARE(socket.peerPort(), quint16(0));
207 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
208 QCOMPARE(socket.socketDescriptor(), -1);
209 QCOMPARE(socket.bytesAvailable(), 0);
210 QCOMPARE(socket.bytesToWrite(), 0);
211 QCOMPARE(socket.canReadLine(), false);
212 QCOMPARE(socket.isSequential(), true);
213 QCOMPARE(socket.atEnd(), true);
214 QCOMPARE(socket.pos(), 0);
215 QCOMPARE(socket.size(), 0);
216 QCOMPARE(socket.isOpen(), false);
217 QCOMPARE(socket.isReadable(), false);
218 QCOMPARE(socket.isWritable(), false);
219 QCOMPARE(socket.openMode(), QIODevice::NotOpen);
220
221 QByteArray array = socket.readAll();
222 QVERIFY(array.isEmpty());
223
224 char buffer[10];
225 int returnValue = socket.read(data: (char*)&buffer, maxlen: 10);
226 QCOMPARE(returnValue, -1);
227 }
228
229 {
230 QBluetoothSocket socket(socketType);
231 QCOMPARE(socket.socketType(), socketType);
232 }
233}
234
235void tst_QBluetoothSocket::tst_serviceConnection()
236{
237 if (!remoteServiceInfo.isValid())
238 QSKIP("Remote service not found");
239
240 /* Construction */
241 QBluetoothSocket socket;
242
243 QSignalSpy stateSpy(&socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)));
244
245 QCOMPARE(socket.socketType(), QBluetoothServiceInfo::UnknownProtocol);
246 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
247
248 /* Connection */
249 QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
250 QSignalSpy errorSpy(&socket, SIGNAL(error(QBluetoothSocket::SocketError)));
251
252 QCOMPARE(socket.openMode(), QIODevice::NotOpen);
253 QCOMPARE(socket.isWritable(), false);
254 QCOMPARE(socket.isReadable(), false);
255 QCOMPARE(socket.isOpen(), false);
256
257 socket.connectToService(service: remoteServiceInfo);
258
259 QCOMPARE(stateSpy.count(), 1);
260 QCOMPARE(stateSpy.takeFirst().at(0).value<QBluetoothSocket::SocketState>(), QBluetoothSocket::ConnectingState);
261 QCOMPARE(socket.state(), QBluetoothSocket::ConnectingState);
262
263 stateSpy.clear();
264
265 int connectTime = MaxConnectTime;
266 while (connectedSpy.count() == 0 && errorSpy.count() == 0 && connectTime > 0) {
267 QTest::qWait(ms: 1000);
268 connectTime -= 1000;
269 }
270
271 if (errorSpy.count() != 0) {
272 qDebug() << errorSpy.takeFirst().at(i: 0).toInt();
273 QSKIP("Connection error");
274 }
275 QCOMPARE(connectedSpy.count(), 1);
276 QCOMPARE(stateSpy.count(), 1);
277 QCOMPARE(stateSpy.takeFirst().at(0).value<QBluetoothSocket::SocketState>(), QBluetoothSocket::ConnectedState);
278 QCOMPARE(socket.state(), QBluetoothSocket::ConnectedState);
279
280 QCOMPARE(socket.isWritable(), true);
281 QCOMPARE(socket.isReadable(), true);
282 QCOMPARE(socket.isOpen(), true);
283
284 stateSpy.clear();
285
286 //check the peer & local info
287 QCOMPARE(socket.localAddress(), localDevice.address());
288 QCOMPARE(socket.localName(), localDevice.name());
289 QCOMPARE(socket.peerName(), remoteDevice.name());
290 QCOMPARE(socket.peerAddress(), remoteDevice.address());
291
292 /* Disconnection */
293 QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
294
295 socket.abort(); // close() tested by other functions
296 QCOMPARE(socket.isWritable(), false);
297 QCOMPARE(socket.isReadable(), false);
298 QCOMPARE(socket.isOpen(), false);
299 QCOMPARE(socket.openMode(), QIODevice::NotOpen);
300
301 QVERIFY(stateSpy.count() >= 1);
302 QCOMPARE(stateSpy.takeFirst().at(0).value<QBluetoothSocket::SocketState>(), QBluetoothSocket::ClosingState);
303
304 int disconnectTime = MaxConnectTime;
305 while (disconnectedSpy.count() == 0 && disconnectTime > 0) {
306 QTest::qWait(ms: 1000);
307 disconnectTime -= 1000;
308 }
309
310 QCOMPARE(disconnectedSpy.count(), 1);
311 QCOMPARE(stateSpy.count(), 1);
312 QCOMPARE(stateSpy.takeFirst().at(0).value<QBluetoothSocket::SocketState>(), QBluetoothSocket::UnconnectedState);
313
314 // The remote service needs time to close the connection and resume listening
315 QTest::qSleep(ms: 100);
316}
317
318void tst_QBluetoothSocket::tst_clientCommunication_data()
319{
320 QTest::addColumn<QStringList>(name: "data");
321
322 {
323 QStringList data;
324 data << QStringLiteral("Echo: Test line one.\n");
325 data << QStringLiteral("Echo: Test line two, with longer data.\n");
326
327 QTest::newRow(dataTag: "two line test") << data;
328 }
329}
330
331void tst_QBluetoothSocket::tst_clientCommunication()
332{
333 if (!remoteServiceInfo.isValid())
334 QSKIP("Remote service not found");
335 QFETCH(QStringList, data);
336
337 /* Construction */
338 QBluetoothSocket socket(QBluetoothServiceInfo::RfcommProtocol);
339
340 QSignalSpy stateSpy(&socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)));
341
342 QCOMPARE(socket.socketType(), QBluetoothServiceInfo::RfcommProtocol);
343 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
344
345 /* Connection */
346 QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
347
348 QCOMPARE(socket.isWritable(), false);
349 QCOMPARE(socket.isReadable(), false);
350 QCOMPARE(socket.isOpen(), false);
351 QCOMPARE(socket.openMode(), QIODevice::NotOpen);
352 socket.connectToService(service: remoteServiceInfo);
353
354 QCOMPARE(stateSpy.count(), 1);
355 QCOMPARE(qvariant_cast<QBluetoothSocket::SocketState>(stateSpy.takeFirst().at(0)), QBluetoothSocket::ConnectingState);
356 QCOMPARE(socket.state(), QBluetoothSocket::ConnectingState);
357
358 stateSpy.clear();
359
360 int connectTime = MaxConnectTime;
361 while (connectedSpy.count() == 0 && connectTime > 0) {
362 QTest::qWait(ms: 1000);
363 connectTime -= 1000;
364 }
365
366 QCOMPARE(socket.isWritable(), true);
367 QCOMPARE(socket.isReadable(), true);
368 QCOMPARE(socket.isOpen(), true);
369
370 QCOMPARE(connectedSpy.count(), 1);
371 QCOMPARE(stateSpy.count(), 1);
372 QCOMPARE(qvariant_cast<QBluetoothSocket::SocketState>(stateSpy.takeFirst().at(0)), QBluetoothSocket::ConnectedState);
373 QCOMPARE(socket.state(), QBluetoothSocket::ConnectedState);
374
375 stateSpy.clear();
376
377 /* Read / Write */
378 QCOMPARE(socket.bytesToWrite(), qint64(0));
379 QCOMPARE(socket.bytesAvailable(), qint64(0));
380
381 {
382 /* Send line by line with event loop */
383 for (const QString &line : qAsConst(t&: data)) {
384 QSignalSpy readyReadSpy(&socket, SIGNAL(readyRead()));
385 QSignalSpy bytesWrittenSpy(&socket, SIGNAL(bytesWritten(qint64)));
386
387 qint64 dataWritten = socket.write(data: line.toUtf8());
388
389 if (socket.openMode() & QIODevice::Unbuffered)
390 QCOMPARE(socket.bytesToWrite(), qint64(0));
391 else
392 QCOMPARE(socket.bytesToWrite(), qint64(line.length()));
393
394 QCOMPARE(dataWritten, qint64(line.length()));
395
396 int readWriteTime = MaxReadWriteTime;
397 while ((bytesWrittenSpy.count() == 0 || readyReadSpy.count() == 0) && readWriteTime > 0) {
398 QTest::qWait(ms: 1000);
399 readWriteTime -= 1000;
400 }
401
402 QCOMPARE(bytesWrittenSpy.count(), 1);
403 QCOMPARE(bytesWrittenSpy.at(0).at(0).toLongLong(), qint64(line.length()));
404
405 readWriteTime = MaxReadWriteTime;
406 while ((readyReadSpy.count() == 0) && readWriteTime > 0) {
407 QTest::qWait(ms: 1000);
408 readWriteTime -= 1000;
409 }
410
411 QCOMPARE(readyReadSpy.count(), 1);
412
413 if (socket.openMode() & QIODevice::Unbuffered)
414 QVERIFY(socket.bytesAvailable() <= qint64(line.length()));
415 else
416 QCOMPARE(socket.bytesAvailable(), qint64(line.length()));
417
418 QVERIFY(socket.canReadLine());
419
420 QByteArray echoed = socket.readAll();
421
422 QCOMPARE(line.toUtf8(), echoed);
423 }
424 }
425
426 QCOMPARE(socket.isWritable(), true);
427 QCOMPARE(socket.isReadable(), true);
428 QCOMPARE(socket.isOpen(), true);
429
430 {
431 /* Send all at once */
432 QSignalSpy readyReadSpy(&socket, SIGNAL(readyRead()));
433 QSignalSpy bytesWrittenSpy(&socket, SIGNAL(bytesWritten(qint64)));
434
435 QString joined = data.join(sep: QString());
436 qint64 dataWritten = socket.write(data: joined.toUtf8());
437
438 if (socket.openMode() & QIODevice::Unbuffered)
439 QCOMPARE(socket.bytesToWrite(), qint64(0));
440 else
441 QCOMPARE(socket.bytesToWrite(), qint64(joined.length()));
442
443 QCOMPARE(dataWritten, qint64(joined.length()));
444
445 int readWriteTime = MaxReadWriteTime;
446 while ((bytesWrittenSpy.count() == 0 || readyReadSpy.count() == 0) && readWriteTime > 0) {
447 QTest::qWait(ms: 1000);
448 readWriteTime -= 1000;
449 }
450
451 QCOMPARE(bytesWrittenSpy.count(), 1);
452 QCOMPARE(bytesWrittenSpy.at(0).at(0).toLongLong(), qint64(joined.length()));
453 QVERIFY(readyReadSpy.count() > 0);
454
455 if (socket.openMode() & QIODevice::Unbuffered)
456 QVERIFY(socket.bytesAvailable() <= qint64(joined.length()));
457 else
458 QCOMPARE(socket.bytesAvailable(), qint64(joined.length()));
459
460 QVERIFY(socket.canReadLine());
461
462 QByteArray echoed = socket.readAll();
463
464 QCOMPARE(joined.toUtf8(), echoed);
465 }
466
467 /* Disconnection */
468 QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
469
470 socket.disconnectFromService();
471
472 QCOMPARE(socket.isWritable(), false);
473 QCOMPARE(socket.isReadable(), false);
474 QCOMPARE(socket.isOpen(), false);
475 QCOMPARE(socket.openMode(), QIODevice::NotOpen);
476
477 int disconnectTime = MaxConnectTime;
478 while (disconnectedSpy.count() == 0 && disconnectTime > 0) {
479 QTest::qWait(ms: 1000);
480 disconnectTime -= 1000;
481 }
482
483 QCOMPARE(disconnectedSpy.count(), 1);
484 QCOMPARE(stateSpy.count(), 2);
485 QCOMPARE(qvariant_cast<QBluetoothSocket::SocketState>(stateSpy.takeFirst().at(0)), QBluetoothSocket::ClosingState);
486 QCOMPARE(qvariant_cast<QBluetoothSocket::SocketState>(stateSpy.takeFirst().at(0)), QBluetoothSocket::UnconnectedState);
487
488 // The remote service needs time to close the connection and resume listening
489 QTest::qSleep(ms: 100);
490}
491
492void tst_QBluetoothSocket::tst_error()
493{
494 QBluetoothSocket socket;
495 QSignalSpy errorSpy(&socket, SIGNAL(error(QBluetoothSocket::SocketError)));
496 QCOMPARE(errorSpy.count(), 0);
497 const QBluetoothSocket::SocketError e = socket.error();
498
499 QVERIFY(e == QBluetoothSocket::NoSocketError);
500
501 QVERIFY(socket.errorString() == QString());
502}
503
504void tst_QBluetoothSocket::tst_preferredSecurityFlags()
505{
506 QBluetoothSocket socket;
507
508 //test default values
509#if defined(QT_ANDROID_BLUETOOTH) | defined(QT_OSX_BLUETOOTH)
510 QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Secure);
511#elif QT_CONFIG(bluez)
512 // The bluezdbus socket uses "NoSecurity" by default, whereas the non-dbus bluez
513 // socket uses "Authorization" by default
514 if (bluetoothdVersion() >= QVersionNumber(5, 42))
515 QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Security::NoSecurity);
516 else
517 QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Security::Authorization);
518#else
519 QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::NoSecurity);
520#endif
521
522 socket.setPreferredSecurityFlags(QBluetooth::Authentication|QBluetooth::Encryption);
523
524#if defined(QT_OSX_BLUETOOTH)
525 QCOMPARE(socket.preferredSecurityFlags(), QBluetooth::Secure);
526#else
527 QCOMPARE(socket.preferredSecurityFlags(),
528 QBluetooth::Encryption|QBluetooth::Authentication);
529#endif
530}
531
532void tst_QBluetoothSocket::tst_unsupportedProtocolError()
533{
534#if defined(QT_ANDROID_BLUETOOTH)
535 QSKIP("Android platform (re)sets RFCOMM socket type, nothing to test");
536#endif
537 // This socket has 'UnknownProtocol' socketType.
538 // Any attempt to connectToService should end in
539 // UnsupportedProtocolError.
540 QBluetoothSocket socket;
541 QCOMPARE(socket.socketType(), QBluetoothServiceInfo::UnknownProtocol);
542 QVERIFY(socket.error() == QBluetoothSocket::NoSocketError);
543 QVERIFY(socket.errorString() == QString());
544
545 QSignalSpy errorSpy(&socket, SIGNAL(error(QBluetoothSocket::SocketError)));
546
547 // 1. Stop early with 'UnsupportedProtocolError'.
548 QBluetoothServiceInfo dummyServiceInfo;
549 socket.connectToService(service: dummyServiceInfo, openMode: QIODevice::ReadWrite);
550 QTRY_COMPARE_WITH_TIMEOUT(errorSpy.size(), 1, 1000);
551 QCOMPARE(errorSpy.size(), 1);
552 QCOMPARE(errorSpy.takeFirst().at(0).toInt(), int(QBluetoothSocket::UnsupportedProtocolError));
553 QVERIFY(socket.errorString().size() != 0);
554 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
555
556 errorSpy.clear();
557
558 // 2. Stop early with UnsupportedProtocolError (before testing an invalid address/port).
559 socket.connectToService(address: QBluetoothAddress(), port: 1, openMode: QIODevice::ReadWrite);
560 QTRY_COMPARE_WITH_TIMEOUT(errorSpy.size(), 1, 1000);
561 QCOMPARE(errorSpy.size(), 1);
562 QCOMPARE(errorSpy.takeFirst().at(0).toInt(), int(QBluetoothSocket::UnsupportedProtocolError));
563 QVERIFY(socket.errorString().size() != 0);
564 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
565
566 errorSpy.clear();
567
568 // 3. Stop early (ignoring an invalid address/uuid).
569 socket.connectToService(address: QBluetoothAddress(), uuid: QBluetoothUuid(), openMode: QIODevice::ReadWrite);
570 QTRY_COMPARE_WITH_TIMEOUT(errorSpy.size(), 1, 1000);
571 QCOMPARE(errorSpy.size(), 1);
572 QCOMPARE(errorSpy.takeFirst().at(0).toInt(), int(QBluetoothSocket::UnsupportedProtocolError));
573 QVERIFY(socket.errorString().size() != 0);
574 QCOMPARE(socket.state(), QBluetoothSocket::UnconnectedState);
575}
576
577QTEST_MAIN(tst_QBluetoothSocket)
578
579#include "tst_qbluetoothsocket.moc"
580
581

source code of qtconnectivity/tests/auto/qbluetoothsocket/tst_qbluetoothsocket.cpp