| 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 | #include <QVariant> | 
| 33 | #include <QList> | 
| 34 | #include <QLoggingCategory> | 
| 35 |  | 
| 36 | #include <private/qtbluetoothglobal_p.h> | 
| 37 | #include <qbluetoothaddress.h> | 
| 38 | #include <qbluetoothdevicediscoveryagent.h> | 
| 39 | #include <qbluetoothlocaldevice.h> | 
| 40 |  | 
| 41 | QT_USE_NAMESPACE | 
| 42 |  | 
| 43 | /* | 
| 44 |  * Some parts of this test require a remote and discoverable Bluetooth | 
| 45 |  * device. Setting the BT_TEST_DEVICE environment variable will | 
| 46 |  * set the test up to fail if it cannot find a remote device. | 
| 47 |  * BT_TEST_DEVICE should contain the address of the device the | 
| 48 |  * test expects to find. Ensure that the device is running | 
| 49 |  * in discovery mode. | 
| 50 |  **/ | 
| 51 |  | 
| 52 | // Maximum time to for bluetooth device scan | 
| 53 | const int MaxScanTime = 5 * 60 * 1000;  // 5 minutes in ms | 
| 54 |  | 
| 55 | //Bluez needs at least 10s for a device discovery to be cancelled | 
| 56 | const int MaxWaitForCancelTime = 15 * 1000;  // 15 seconds in ms | 
| 57 |  | 
| 58 | class tst_QBluetoothDeviceDiscoveryAgent : public QObject | 
| 59 | { | 
| 60 |     Q_OBJECT | 
| 61 |  | 
| 62 | public: | 
| 63 |     tst_QBluetoothDeviceDiscoveryAgent(); | 
| 64 |     ~tst_QBluetoothDeviceDiscoveryAgent(); | 
| 65 |  | 
| 66 | public slots: | 
| 67 |     void deviceDiscoveryDebug(const QBluetoothDeviceInfo &info); | 
| 68 |     void finished(); | 
| 69 |  | 
| 70 | private slots: | 
| 71 |     void initTestCase(); | 
| 72 |  | 
| 73 |     void tst_properties(); | 
| 74 |  | 
| 75 |     void tst_invalidBtAddress(); | 
| 76 |  | 
| 77 |     void tst_startStopDeviceDiscoveries(); | 
| 78 |  | 
| 79 |     void tst_deviceDiscovery_data(); | 
| 80 |     void tst_deviceDiscovery(); | 
| 81 |  | 
| 82 |     void tst_discoveryTimeout(); | 
| 83 |  | 
| 84 |     void tst_discoveryMethods(); | 
| 85 | private: | 
| 86 |     int noOfLocalDevices; | 
| 87 |     bool isBluez5Runtime = false; | 
| 88 | }; | 
| 89 |  | 
| 90 | tst_QBluetoothDeviceDiscoveryAgent::tst_QBluetoothDeviceDiscoveryAgent() | 
| 91 | { | 
| 92 |     QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true" )); | 
| 93 |     qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>(); | 
| 94 | } | 
| 95 |  | 
| 96 | tst_QBluetoothDeviceDiscoveryAgent::~tst_QBluetoothDeviceDiscoveryAgent() | 
| 97 | { | 
| 98 | } | 
| 99 |  | 
| 100 | #if QT_CONFIG(bluez) | 
| 101 | // This section was adopted from tst_qloggingcategory.cpp | 
| 102 | QString logMessage; | 
| 103 |  | 
| 104 | QByteArray qMyMessageFormatString(QtMsgType type, const QMessageLogContext &context, | 
| 105 |                                               const QString &str) | 
| 106 | { | 
| 107 |     QByteArray message; | 
| 108 |     message.append(context.category); | 
| 109 |     switch (type) { | 
| 110 |     case QtDebugMsg:   message.append(".debug" ); break; | 
| 111 |     case QtInfoMsg:    message.append(".info" ); break; | 
| 112 |     case QtWarningMsg: message.append(".warning" ); break; | 
| 113 |     case QtCriticalMsg:message.append(".critical" ); break; | 
| 114 |     case QtFatalMsg:   message.append(".fatal" ); break; | 
| 115 |     } | 
| 116 |     message.append(": " ); | 
| 117 |     message.append(qPrintable(str)); | 
| 118 |  | 
| 119 |     return message.simplified(); | 
| 120 | } | 
| 121 |  | 
| 122 | static void myCustomMessageHandler(QtMsgType type, | 
| 123 |                                    const QMessageLogContext &context, | 
| 124 |                                    const QString &msg) | 
| 125 | { | 
| 126 |     logMessage = qMyMessageFormatString(type, context, msg); | 
| 127 | } | 
| 128 | #endif | 
| 129 |  | 
| 130 |  | 
| 131 |  | 
| 132 | void tst_QBluetoothDeviceDiscoveryAgent::initTestCase() | 
| 133 | { | 
| 134 |     qRegisterMetaType<QBluetoothDeviceInfo>(); | 
| 135 |     qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::InquiryType>(); | 
| 136 |  | 
| 137 | #if QT_CONFIG(bluez) | 
| 138 |     // To distinguish Bluez 4 and 5 we peek into the debug output | 
| 139 |     // of first Bluetooth ctor. It executes a runtime test and prints the result | 
| 140 |     // as logging output. This avoids more complex runtime detection logic within this unit test. | 
| 141 |     QtMessageHandler oldMessageHandler; | 
| 142 |     oldMessageHandler = qInstallMessageHandler(myCustomMessageHandler); | 
| 143 |  | 
| 144 |     noOfLocalDevices = QBluetoothLocalDevice::allDevices().count(); | 
| 145 |     qInstallMessageHandler(oldMessageHandler); | 
| 146 |     isBluez5Runtime = logMessage.contains(QStringLiteral("Bluez 5" )); | 
| 147 |     if (isBluez5Runtime) | 
| 148 |         qDebug() << "BlueZ 5 runtime detected." ; | 
| 149 | #else | 
| 150 |     noOfLocalDevices = QBluetoothLocalDevice::allDevices().count(); | 
| 151 | #endif | 
| 152 |  | 
| 153 |     if (!noOfLocalDevices) | 
| 154 |         return; | 
| 155 |  | 
| 156 |     // turn on BT in case it is not on | 
| 157 |     QBluetoothLocalDevice *device = new QBluetoothLocalDevice(); | 
| 158 |     if (device->hostMode() == QBluetoothLocalDevice::HostPoweredOff) { | 
| 159 |         QSignalSpy hostModeSpy(device, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode))); | 
| 160 |         QVERIFY(hostModeSpy.isEmpty()); | 
| 161 |         device->powerOn(); | 
| 162 |         int connectTime = 5000;  // ms | 
| 163 |         while (hostModeSpy.count() < 1 && connectTime > 0) { | 
| 164 |             QTest::qWait(ms: 500); | 
| 165 |             connectTime -= 500; | 
| 166 |         } | 
| 167 |         QVERIFY(hostModeSpy.count() > 0); | 
| 168 |     } | 
| 169 |     QBluetoothLocalDevice::HostMode hostMode= device->hostMode(); | 
| 170 |     QVERIFY(hostMode == QBluetoothLocalDevice::HostConnectable | 
| 171 |          || hostMode == QBluetoothLocalDevice::HostDiscoverable | 
| 172 |          || hostMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry); | 
| 173 |     delete device; | 
| 174 | } | 
| 175 |  | 
| 176 | void tst_QBluetoothDeviceDiscoveryAgent::tst_properties() | 
| 177 | { | 
| 178 |     QBluetoothDeviceDiscoveryAgent discoveryAgent; | 
| 179 |  | 
| 180 |     QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); | 
| 181 |     discoveryAgent.setInquiryType(QBluetoothDeviceDiscoveryAgent::LimitedInquiry); | 
| 182 |     QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::LimitedInquiry); | 
| 183 |     discoveryAgent.setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); | 
| 184 |     QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); | 
| 185 | } | 
| 186 |  | 
| 187 | void tst_QBluetoothDeviceDiscoveryAgent::tst_invalidBtAddress() | 
| 188 | { | 
| 189 |     QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(QBluetoothAddress("11:11:11:11:11:11" )); | 
| 190 |  | 
| 191 |     QCOMPARE(discoveryAgent->error(), QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); | 
| 192 |     discoveryAgent->start(); | 
| 193 |     QCOMPARE(discoveryAgent->isActive(), false); | 
| 194 |     delete discoveryAgent; | 
| 195 |  | 
| 196 |     discoveryAgent = new QBluetoothDeviceDiscoveryAgent(QBluetoothAddress()); | 
| 197 |     QCOMPARE(discoveryAgent->error(), QBluetoothDeviceDiscoveryAgent::NoError); | 
| 198 |     if (QBluetoothLocalDevice::allDevices().count() > 0) { | 
| 199 |         discoveryAgent->start(); | 
| 200 |         QCOMPARE(discoveryAgent->isActive(), true); | 
| 201 |     } | 
| 202 |     delete discoveryAgent; | 
| 203 | } | 
| 204 |  | 
| 205 | void tst_QBluetoothDeviceDiscoveryAgent::deviceDiscoveryDebug(const QBluetoothDeviceInfo &info) | 
| 206 | { | 
| 207 |     qDebug() << "Discovered device:"  << info.address().toString() << info.name(); | 
| 208 | } | 
| 209 |  | 
| 210 | void tst_QBluetoothDeviceDiscoveryAgent::tst_startStopDeviceDiscoveries() | 
| 211 | { | 
| 212 |     QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType = QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; | 
| 213 |     QBluetoothDeviceDiscoveryAgent discoveryAgent; | 
| 214 |  | 
| 215 |     QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); | 
| 216 |     QVERIFY(discoveryAgent.errorString().isEmpty()); | 
| 217 |     QVERIFY(!discoveryAgent.isActive()); | 
| 218 |     QVERIFY(discoveryAgent.discoveredDevices().isEmpty()); | 
| 219 |  | 
| 220 |     QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); | 
| 221 |     QSignalSpy cancelSpy(&discoveryAgent, SIGNAL(canceled())); | 
| 222 |     QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); | 
| 223 |  | 
| 224 |     // Starting case 1: start-stop, expecting cancel signal | 
| 225 |     discoveryAgent.setInquiryType(inquiryType); | 
| 226 |     // we should have no errors at this point. | 
| 227 |     QVERIFY(errorSpy.isEmpty()); | 
| 228 |  | 
| 229 |     discoveryAgent.start(); | 
| 230 |  | 
| 231 |     if (errorSpy.isEmpty()) { | 
| 232 |         QVERIFY(discoveryAgent.isActive()); | 
| 233 |         QCOMPARE(discoveryAgent.errorString(), QString()); | 
| 234 |         QCOMPARE(discoveryAgent.error(), QBluetoothDeviceDiscoveryAgent::NoError); | 
| 235 |     } else { | 
| 236 |         QCOMPARE(noOfLocalDevices, 0); | 
| 237 |         QVERIFY(!discoveryAgent.isActive()); | 
| 238 |         QVERIFY(!discoveryAgent.errorString().isEmpty()); | 
| 239 |         QVERIFY(discoveryAgent.error() != QBluetoothDeviceDiscoveryAgent::NoError); | 
| 240 |         QSKIP("No local Bluetooth device available. Skipping remaining part of test." ); | 
| 241 |     } | 
| 242 |     // cancel current request. | 
| 243 |     discoveryAgent.stop(); | 
| 244 |  | 
| 245 |     // Wait for up to MaxWaitForCancelTime for the cancel to finish | 
| 246 |     int waitTime = MaxWaitForCancelTime; | 
| 247 |     while (cancelSpy.count() == 0 && waitTime > 0) { | 
| 248 |         QTest::qWait(ms: 100); | 
| 249 |         waitTime-=100; | 
| 250 |     } | 
| 251 |  | 
| 252 |     // we should not be active anymore | 
| 253 |     QVERIFY(!discoveryAgent.isActive()); | 
| 254 |     QVERIFY(errorSpy.isEmpty()); | 
| 255 |     QCOMPARE(cancelSpy.count(), 1); | 
| 256 |     cancelSpy.clear(); | 
| 257 |     // Starting case 2: start-start-stop, expecting cancel signal | 
| 258 |     discoveryAgent.start(); | 
| 259 |     // we should be active now | 
| 260 |     QVERIFY(discoveryAgent.isActive()); | 
| 261 |     QVERIFY(errorSpy.isEmpty()); | 
| 262 |     // start again. should this be error? | 
| 263 |     discoveryAgent.start(); | 
| 264 |     QVERIFY(discoveryAgent.isActive()); | 
| 265 |     QVERIFY(errorSpy.isEmpty()); | 
| 266 |     // stop | 
| 267 |     discoveryAgent.stop(); | 
| 268 |  | 
| 269 |     // Wait for up to MaxWaitForCancelTime for the cancel to finish | 
| 270 |     waitTime = MaxWaitForCancelTime; | 
| 271 |     while (cancelSpy.count() == 0 && waitTime > 0) { | 
| 272 |         QTest::qWait(ms: 100); | 
| 273 |         waitTime-=100; | 
| 274 |     } | 
| 275 |  | 
| 276 |     // we should not be active anymore | 
| 277 |     QVERIFY(!discoveryAgent.isActive()); | 
| 278 |     QVERIFY(errorSpy.isEmpty()); | 
| 279 |     QVERIFY(cancelSpy.count() == 1); | 
| 280 |     cancelSpy.clear(); | 
| 281 |  | 
| 282 |     //  Starting case 3: stop | 
| 283 |     discoveryAgent.stop(); | 
| 284 |     QVERIFY(!discoveryAgent.isActive()); | 
| 285 |     QVERIFY(errorSpy.isEmpty()); | 
| 286 |  | 
| 287 |     // Don't expect finished signal and no error | 
| 288 |     QVERIFY(finishedSpy.count() == 0); | 
| 289 |     QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); | 
| 290 |     QVERIFY(discoveryAgent.errorString().isEmpty()); | 
| 291 |  | 
| 292 |     /* | 
| 293 |         Starting case 4: start-stop-start-stop: | 
| 294 |         We are testing that two subsequent stop() calls reduce total number | 
| 295 |         of cancel() signals to 1 if the true cancellation requires | 
| 296 |         asynchronous function calls (signal consolidation); otherwise we | 
| 297 |         expect 2x cancel() signal. | 
| 298 |  | 
| 299 |         Examples are: | 
| 300 |             - Bluez4 (event loop needs to run for cancel) | 
| 301 |             - Bluez5 (no event loop required) | 
| 302 |     */ | 
| 303 |  | 
| 304 |     bool immediateSignal = false; | 
| 305 |     discoveryAgent.start(); | 
| 306 |     QVERIFY(discoveryAgent.isActive()); | 
| 307 |     QVERIFY(errorSpy.isEmpty()); | 
| 308 |     // cancel current request. | 
| 309 |     discoveryAgent.stop(); | 
| 310 |     //should only have triggered cancel() if stop didn't involve the event loop | 
| 311 |     if (cancelSpy.count() == 1) immediateSignal = true; | 
| 312 |  | 
| 313 |     // start a new one | 
| 314 |     discoveryAgent.start(); | 
| 315 |     // we should be active now | 
| 316 |     QVERIFY(discoveryAgent.isActive()); | 
| 317 |     QVERIFY(errorSpy.isEmpty()); | 
| 318 |     // stop | 
| 319 |     discoveryAgent.stop(); | 
| 320 |     if (immediateSignal) | 
| 321 |         QVERIFY(cancelSpy.count() == 2); | 
| 322 |  | 
| 323 |     // Wait for up to MaxWaitForCancelTime for the cancel to finish | 
| 324 |     waitTime = MaxWaitForCancelTime; | 
| 325 |     while (cancelSpy.count() == 0 && waitTime > 0) { | 
| 326 |         QTest::qWait(ms: 100); | 
| 327 |         waitTime-=100; | 
| 328 |     } | 
| 329 |     // we should not be active anymore | 
| 330 |     QVERIFY(!discoveryAgent.isActive()); | 
| 331 |     QVERIFY(errorSpy.isEmpty()); | 
| 332 |     // should only have 1 cancel | 
| 333 |  | 
| 334 |     if (immediateSignal) | 
| 335 |         QVERIFY(cancelSpy.count() == 2); | 
| 336 |     else | 
| 337 |         QVERIFY(cancelSpy.count() == 1); | 
| 338 |     cancelSpy.clear(); | 
| 339 |  | 
| 340 |     // Starting case 5: start-stop-start: expecting finished signal & no cancel | 
| 341 |     discoveryAgent.start(); | 
| 342 |     QVERIFY(discoveryAgent.isActive()); | 
| 343 |     QVERIFY(errorSpy.isEmpty()); | 
| 344 |     // cancel current request. | 
| 345 |     discoveryAgent.stop(); | 
| 346 |     // start a new one | 
| 347 |     discoveryAgent.start(); | 
| 348 |     // we should be active now | 
| 349 |     QVERIFY(discoveryAgent.isActive()); | 
| 350 |     QVERIFY(errorSpy.isEmpty()); | 
| 351 |  | 
| 352 |     // Wait for up to MaxScanTime for the cancel to finish | 
| 353 |     waitTime = MaxScanTime; | 
| 354 |     while (finishedSpy.count() == 0 && waitTime > 0) { | 
| 355 |         QTest::qWait(ms: 1000); | 
| 356 |         waitTime-=1000; | 
| 357 |     } | 
| 358 |  | 
| 359 |     // we should not be active anymore | 
| 360 |     QVERIFY(!discoveryAgent.isActive()); | 
| 361 |     QVERIFY(errorSpy.isEmpty()); | 
| 362 |     // should only have 1 cancel | 
| 363 |     QVERIFY(finishedSpy.count() == 1); | 
| 364 |  | 
| 365 |     // On OS X, stop is synchronous (signal will be emitted immediately). | 
| 366 |     if (!immediateSignal) | 
| 367 |         QVERIFY(cancelSpy.isEmpty()); | 
| 368 | } | 
| 369 |  | 
| 370 | void tst_QBluetoothDeviceDiscoveryAgent::finished() | 
| 371 | { | 
| 372 |     qDebug() << "Finished called" ; | 
| 373 | } | 
| 374 |  | 
| 375 | void tst_QBluetoothDeviceDiscoveryAgent::tst_deviceDiscovery_data() | 
| 376 | { | 
| 377 |     QTest::addColumn<QBluetoothDeviceDiscoveryAgent::InquiryType>(name: "inquiryType" ); | 
| 378 |  | 
| 379 |     QTest::newRow(dataTag: "general unlimited inquiry" ) << QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; | 
| 380 |     QTest::newRow(dataTag: "limited inquiry" ) << QBluetoothDeviceDiscoveryAgent::LimitedInquiry; | 
| 381 | } | 
| 382 |  | 
| 383 | void tst_QBluetoothDeviceDiscoveryAgent::tst_deviceDiscovery() | 
| 384 | { | 
| 385 |     { | 
| 386 |         QFETCH(QBluetoothDeviceDiscoveryAgent::InquiryType, inquiryType); | 
| 387 |  | 
| 388 |         //Run test in case of multiple Bluetooth adapters | 
| 389 |         QBluetoothLocalDevice localDevice; | 
| 390 |         //We will use default adapter if there is no other adapter | 
| 391 |         QBluetoothAddress address = localDevice.address(); | 
| 392 |         int numberOfAdapters = (localDevice.allDevices()).size(); | 
| 393 |         QList<QBluetoothAddress> addresses; | 
| 394 |         if (numberOfAdapters > 1) { | 
| 395 |  | 
| 396 |             for (int i=0; i < numberOfAdapters; i++) { | 
| 397 |                 addresses.append(t: ((QBluetoothHostInfo)localDevice.allDevices().at(i)).address()); | 
| 398 |             } | 
| 399 |             address = (QBluetoothAddress)addresses.at(i: 0); | 
| 400 |         } | 
| 401 |  | 
| 402 |         QBluetoothDeviceDiscoveryAgent discoveryAgent(address); | 
| 403 |         QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); | 
| 404 |         QVERIFY(discoveryAgent.errorString().isEmpty()); | 
| 405 |         QVERIFY(!discoveryAgent.isActive()); | 
| 406 |  | 
| 407 |         QVERIFY(discoveryAgent.discoveredDevices().isEmpty()); | 
| 408 |  | 
| 409 |         QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); | 
| 410 |         QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); | 
| 411 |         QSignalSpy discoveredSpy(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo))); | 
| 412 | //        connect(&discoveryAgent, SIGNAL(finished()), this, SLOT(finished())); | 
| 413 | //        connect(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), | 
| 414 | //                this, SLOT(deviceDiscoveryDebug(QBluetoothDeviceInfo))); | 
| 415 |  | 
| 416 |         discoveryAgent.setInquiryType(inquiryType); | 
| 417 |         discoveryAgent.start(); | 
| 418 |         if (!errorSpy.isEmpty()) { | 
| 419 |             QCOMPARE(noOfLocalDevices, 0); | 
| 420 |             QVERIFY(!discoveryAgent.isActive()); | 
| 421 |             QSKIP("No local Bluetooth device available. Skipping remaining part of test." ); | 
| 422 |         } | 
| 423 |  | 
| 424 |         QVERIFY(discoveryAgent.isActive()); | 
| 425 |  | 
| 426 |         // Wait for up to MaxScanTime for the scan to finish | 
| 427 |         int scanTime = MaxScanTime; | 
| 428 |         while (finishedSpy.count() == 0 && scanTime > 0) { | 
| 429 |             QTest::qWait(ms: 15000); | 
| 430 |             scanTime -= 15000; | 
| 431 |         } | 
| 432 |  | 
| 433 |         // verify that we are finished | 
| 434 |         QVERIFY(!discoveryAgent.isActive()); | 
| 435 |         // stop | 
| 436 |         discoveryAgent.stop(); | 
| 437 |         QVERIFY(!discoveryAgent.isActive()); | 
| 438 |         qDebug() << "Scan time left:"  << scanTime; | 
| 439 |         // Expect finished signal with no error | 
| 440 |         QVERIFY(finishedSpy.count() == 1); | 
| 441 |         QVERIFY(errorSpy.isEmpty()); | 
| 442 |         QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); | 
| 443 |         QVERIFY(discoveryAgent.errorString().isEmpty()); | 
| 444 |  | 
| 445 |         // verify that the list is as big as the signals received. | 
| 446 |         QVERIFY(discoveredSpy.count() == discoveryAgent.discoveredDevices().length()); | 
| 447 |         // verify that there really was some devices in the array | 
| 448 |  | 
| 449 |         const QString remote = qgetenv(varName: "BT_TEST_DEVICE" ); | 
| 450 |         QBluetoothAddress remoteDevice; | 
| 451 |         if (!remote.isEmpty()) { | 
| 452 |             remoteDevice = QBluetoothAddress(remote); | 
| 453 |             QVERIFY2(!remote.isNull(), "Expecting valid Bluetooth address to be passed via BT_TEST_DEVICE" ); | 
| 454 |         } else { | 
| 455 |             qWarning() << "Not using a remote device for testing. Set BT_TEST_DEVICE env to run extended tests involving a remote device" ; | 
| 456 |         } | 
| 457 |  | 
| 458 |         if (!remoteDevice.isNull()) | 
| 459 |             QVERIFY(discoveredSpy.count() > 0); | 
| 460 |         int counter = 0; | 
| 461 |         // All returned QBluetoothDeviceInfo should be valid. | 
| 462 |         while (!discoveredSpy.isEmpty()) { | 
| 463 |             const QBluetoothDeviceInfo info = | 
| 464 |                 qvariant_cast<QBluetoothDeviceInfo>(v: discoveredSpy.takeFirst().at(i: 0)); | 
| 465 |             QVERIFY(info.isValid()); | 
| 466 |             qDebug() << "Discovered device:"  << info.address().toString() << info.name(); | 
| 467 |  | 
| 468 |             if (numberOfAdapters > 1) { | 
| 469 |                 for (int i= 1; i < numberOfAdapters; i++) { | 
| 470 |                     if (info.address().toString() == addresses[i].toString()) | 
| 471 |                         counter++; | 
| 472 |                 } | 
| 473 |             } | 
| 474 |         } | 
| 475 | #if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || QT_CONFIG(winrt_bt) | 
| 476 |         //On iOS/WinRT, we do not have access to the local device/adapter, numberOfAdapters is 0, | 
| 477 |         //so we skip this test at all. | 
| 478 |         QSKIP("iOS/WinRT: no local Bluetooth device available. Skipping remaining part of test." ); | 
| 479 | #endif | 
| 480 |  | 
| 481 |         //For multiple Bluetooth adapter do the check only for GeneralUnlimitedInquiry. | 
| 482 |         if (!(inquiryType == QBluetoothDeviceDiscoveryAgent::LimitedInquiry)) | 
| 483 |             QVERIFY((numberOfAdapters-1) == counter); | 
| 484 |     } | 
| 485 | } | 
| 486 |  | 
| 487 |  | 
| 488 | void tst_QBluetoothDeviceDiscoveryAgent::tst_discoveryTimeout() | 
| 489 | { | 
| 490 |     QBluetoothDeviceDiscoveryAgent agent; | 
| 491 |  | 
| 492 |     // check default values | 
| 493 | #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || QT_CONFIG(winrt_bt) | 
| 494 |     QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); | 
| 495 |     agent.setLowEnergyDiscoveryTimeout(-1); // negative ignored | 
| 496 |     QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); | 
| 497 |     agent.setLowEnergyDiscoveryTimeout(20000); | 
| 498 |     QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); | 
| 499 | #elif QT_CONFIG(bluez) | 
| 500 |     if (isBluez5Runtime) { | 
| 501 |         QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); | 
| 502 |         agent.setLowEnergyDiscoveryTimeout(-1); // negative ignored | 
| 503 |         QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); | 
| 504 |         agent.setLowEnergyDiscoveryTimeout(25000); | 
| 505 |         QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); | 
| 506 |     } else { | 
| 507 |         QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); | 
| 508 |         agent.setLowEnergyDiscoveryTimeout(20000); // feature not supported -> ignored | 
| 509 |         QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); | 
| 510 |     } | 
| 511 | #else | 
| 512 |     QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); | 
| 513 |     agent.setLowEnergyDiscoveryTimeout(20000); // feature not supported -> ignored | 
| 514 |     QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); | 
| 515 | #endif | 
| 516 | } | 
| 517 |  | 
| 518 | void tst_QBluetoothDeviceDiscoveryAgent::tst_discoveryMethods() | 
| 519 | { | 
| 520 |     const QBluetoothLocalDevice localDevice; | 
| 521 |     if (localDevice.allDevices().size() != 1) { | 
| 522 |         // On iOS it returns 0 but we still have working BT. | 
| 523 | #ifndef Q_OS_IOS | 
| 524 |         QSKIP("This test expects exactly one local device working" ); | 
| 525 | #endif | 
| 526 |     } | 
| 527 |  | 
| 528 |     const QBluetoothDeviceDiscoveryAgent::DiscoveryMethods | 
| 529 |         supportedMethods = QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); | 
| 530 |  | 
| 531 |     QVERIFY(supportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod); | 
| 532 |  | 
| 533 |     QBluetoothDeviceDiscoveryAgent::DiscoveryMethod | 
| 534 |         unsupportedMethods = QBluetoothDeviceDiscoveryAgent::NoMethod; | 
| 535 |     QBluetoothDeviceInfo::CoreConfiguration | 
| 536 |         expectedConfiguration = QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration; | 
| 537 |  | 
| 538 |     if (supportedMethods == QBluetoothDeviceDiscoveryAgent::ClassicMethod) { | 
| 539 |         unsupportedMethods = QBluetoothDeviceDiscoveryAgent::LowEnergyMethod; | 
| 540 |         expectedConfiguration = QBluetoothDeviceInfo::BaseRateCoreConfiguration; | 
| 541 |     } else if (supportedMethods == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { | 
| 542 |         unsupportedMethods = QBluetoothDeviceDiscoveryAgent::ClassicMethod; | 
| 543 |         expectedConfiguration = QBluetoothDeviceInfo::LowEnergyCoreConfiguration; | 
| 544 |     } | 
| 545 |  | 
| 546 |     QBluetoothDeviceDiscoveryAgent agent; | 
| 547 |     QSignalSpy finishedSpy(&agent, SIGNAL(finished())); | 
| 548 |     QSignalSpy errorSpy(&agent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); | 
| 549 |     QSignalSpy discoveredSpy(&agent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo))); | 
| 550 |  | 
| 551 |     // NoMethod - should just immediately return: | 
| 552 |     agent.start(method: QBluetoothDeviceDiscoveryAgent::NoMethod); | 
| 553 |     QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); | 
| 554 |     QVERIFY(!agent.isActive()); | 
| 555 |     QCOMPARE(finishedSpy.size(), 0); | 
| 556 |     QCOMPARE(errorSpy.size(), 0); | 
| 557 |     QCOMPARE(discoveredSpy.size(), 0); | 
| 558 |  | 
| 559 |     if (unsupportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod) { | 
| 560 |         agent.start(method: unsupportedMethods); | 
| 561 |         QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); | 
| 562 |         QVERIFY(!agent.isActive()); | 
| 563 |         QVERIFY(finishedSpy.isEmpty()); | 
| 564 |         QCOMPARE(errorSpy.size(), 1); | 
| 565 |         errorSpy.clear(); | 
| 566 |         QVERIFY(discoveredSpy.isEmpty()); | 
| 567 |     } | 
| 568 |  | 
| 569 |     // Start discovery, probably both Classic and LE methods: | 
| 570 |     agent.start(method: supportedMethods); | 
| 571 |     QVERIFY(agent.isActive()); | 
| 572 |     QVERIFY(errorSpy.isEmpty()); | 
| 573 |  | 
| 574 |  | 
| 575 | #define RUN_DISCOVERY(maxTimeout, step, condition) \ | 
| 576 |     for (int scanTime = maxTimeout; (condition) && scanTime > 0; scanTime -= step) \ | 
| 577 |         QTest::qWait(step); | 
| 578 |  | 
| 579 |     // Wait for up to MaxScanTime for the scan to finish | 
| 580 |     const int timeStep = 15000; | 
| 581 |     RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty()) | 
| 582 |  | 
| 583 |     QVERIFY(!agent.isActive()); | 
| 584 |     QVERIFY(errorSpy.size() <= 1); | 
| 585 |  | 
| 586 |     if (errorSpy.size()) { | 
| 587 |         // For example, old iOS device could report it supports LE method, | 
| 588 |         // but it actually does not. | 
| 589 |         QVERIFY(supportedMethods == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); | 
| 590 |         QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); | 
| 591 |     } else { | 
| 592 |         QVERIFY(finishedSpy.count() == 1); | 
| 593 |         QVERIFY(agent.error() == QBluetoothDeviceDiscoveryAgent::NoError); | 
| 594 |         QVERIFY(agent.errorString().isEmpty()); | 
| 595 |  | 
| 596 |         while (!discoveredSpy.isEmpty()) { | 
| 597 |             const QBluetoothDeviceInfo info = | 
| 598 |                 qvariant_cast<QBluetoothDeviceInfo>(v: discoveredSpy.takeFirst().at(i: 0)); | 
| 599 |             QVERIFY(info.isValid()); | 
| 600 |             // on Android we do find devices with unknown configuration | 
| 601 |             if (info.coreConfigurations() != QBluetoothDeviceInfo::UnknownCoreConfiguration) | 
| 602 |                 QVERIFY(info.coreConfigurations() & expectedConfiguration); | 
| 603 |         } | 
| 604 |     } | 
| 605 |  | 
| 606 |     if (unsupportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod) | 
| 607 |         return; | 
| 608 |  | 
| 609 |     // Both methods were reported as supported. We already tested them | 
| 610 |     // above, now let's test first Classic then LE. | 
| 611 |     finishedSpy.clear(); | 
| 612 |     errorSpy.clear(); | 
| 613 |     discoveredSpy.clear(); | 
| 614 |  | 
| 615 |     agent.start(method: QBluetoothDeviceDiscoveryAgent::ClassicMethod); | 
| 616 |     QVERIFY(agent.isActive()); | 
| 617 |     QVERIFY(errorSpy.isEmpty()); | 
| 618 |     QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); | 
| 619 |  | 
| 620 |     RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty()) | 
| 621 |  | 
| 622 |     QVERIFY(!agent.isActive()); | 
| 623 |     QVERIFY(errorSpy.isEmpty()); | 
| 624 |     QCOMPARE(finishedSpy.size(), 1); | 
| 625 |  | 
| 626 |     finishedSpy.clear(); | 
| 627 |     discoveredSpy.clear(); | 
| 628 |  | 
| 629 |     agent.start(method: QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); | 
| 630 |     QVERIFY(agent.isActive()); | 
| 631 |     QVERIFY(errorSpy.isEmpty()); | 
| 632 |  | 
| 633 |     RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty() && errorSpy.isEmpty()) | 
| 634 |  | 
| 635 |     QVERIFY(!agent.isActive()); | 
| 636 |     QVERIFY(errorSpy.size() <= 1); | 
| 637 |  | 
| 638 |     if (errorSpy.size()) { | 
| 639 |         QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); | 
| 640 |         qDebug() << "QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods is inaccurate"  | 
| 641 |                     " on your platform/with your device, LowEnergyMethod is not supported" ; | 
| 642 |     } else { | 
| 643 |         QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); | 
| 644 |         QCOMPARE(finishedSpy.size(), 1); | 
| 645 |     } | 
| 646 | } | 
| 647 |  | 
| 648 | QTEST_MAIN(tst_QBluetoothDeviceDiscoveryAgent) | 
| 649 |  | 
| 650 | #include "tst_qbluetoothdevicediscoveryagent.moc" | 
| 651 |  |