| 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 <private/qtbluetoothglobal_p.h> | 
| 32 | #if QT_CONFIG(bluez) | 
| 33 | #include <QtBluetooth/private/bluez5_helper_p.h> | 
| 34 | #endif | 
| 35 | #include <QBluetoothAddress> | 
| 36 | #include <QBluetoothLocalDevice> | 
| 37 | #include <QBluetoothDeviceDiscoveryAgent> | 
| 38 | #include <QBluetoothUuid> | 
| 39 | #include <QLowEnergyController> | 
| 40 | #include <QLowEnergyCharacteristic> | 
| 41 |  | 
| 42 | #include <QDebug> | 
| 43 |  | 
| 44 | /*! | 
| 45 |   This test requires a TI sensor tag with Firmware version: 1.5 (Oct 23 2013). | 
| 46 |   Since revision updates change user strings and even shift handles around | 
| 47 |   other versions than the above are unlikely to succeed. Please update the | 
| 48 |   sensor tag before continuing. | 
| 49 |  | 
| 50 |   The TI sensor can be updated using the related iOS app. The Android version | 
| 51 |   doesn't seem to update at this point in time. | 
| 52 |   */ | 
| 53 |  | 
| 54 | QT_USE_NAMESPACE | 
| 55 |  | 
| 56 | // This define must be set if the platform provides access to GATT handles | 
| 57 | // otherwise it must not be defined. As of now the two supported platforms | 
| 58 | // (Android and Bluez/Linux) provide access or some notion of it. | 
| 59 | #ifndef Q_OS_MAC | 
| 60 | #define HANDLES_PROVIDED_BY_PLATFORM | 
| 61 | #endif | 
| 62 |  | 
| 63 | #ifdef HANDLES_PROVIDED_BY_PLATFORM | 
| 64 | #define HANDLE_COMPARE(actual,expected) \ | 
| 65 |     if (!isBluezDbusLE) {\ | 
| 66 |         QCOMPARE(actual, expected);\ | 
| 67 |     }; | 
| 68 | #else | 
| 69 | #define HANDLE_COMPARE(actual,expected) | 
| 70 | #endif | 
| 71 |  | 
| 72 | class tst_QLowEnergyController : public QObject | 
| 73 | { | 
| 74 |     Q_OBJECT | 
| 75 |  | 
| 76 | public: | 
| 77 |     tst_QLowEnergyController(); | 
| 78 |     ~tst_QLowEnergyController(); | 
| 79 |  | 
| 80 | private slots: | 
| 81 |     void initTestCase(); | 
| 82 |     void init(); | 
| 83 |     void cleanupTestCase(); | 
| 84 |     void tst_emptyCtor(); | 
| 85 |     void tst_connect(); | 
| 86 |     void tst_concurrentDiscovery(); | 
| 87 |     void tst_defaultBehavior(); | 
| 88 |     void tst_writeCharacteristic(); | 
| 89 |     void tst_writeCharacteristicNoResponse(); | 
| 90 |     void tst_readWriteDescriptor(); | 
| 91 |     void tst_customProgrammableDevice(); | 
| 92 |     void tst_errorCases(); | 
| 93 | private: | 
| 94 |     void verifyServiceProperties(const QLowEnergyService *info); | 
| 95 |     bool verifyClientCharacteristicValue(const QByteArray& value); | 
| 96 |  | 
| 97 |     QBluetoothDeviceDiscoveryAgent *devAgent; | 
| 98 |     QBluetoothAddress remoteDevice; | 
| 99 |     QBluetoothDeviceInfo remoteDeviceInfo; | 
| 100 |     QList<QBluetoothUuid> foundServices; | 
| 101 |     bool isBluezDbusLE = false; | 
| 102 | }; | 
| 103 |  | 
| 104 | tst_QLowEnergyController::tst_QLowEnergyController() | 
| 105 | { | 
| 106 |     //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); | 
| 107 | #ifndef Q_OS_MAC | 
| 108 |     // Core Bluetooth (OS X and iOS) does not work with addresses, | 
| 109 |     // making the code below useless. | 
| 110 |     const QString remote = qgetenv(varName: "BT_TEST_DEVICE" ); | 
| 111 |     if (!remote.isEmpty()) { | 
| 112 |         remoteDevice = QBluetoothAddress(remote); | 
| 113 |         qWarning() << "Using remote device "  << remote << " for testing. Ensure that the device is discoverable for pairing requests" ; | 
| 114 |     } else { | 
| 115 |         qWarning() << "Not using any remote device for testing. Set BT_TEST_DEVICE env to run manual tests involving a remote device" ; | 
| 116 |     } | 
| 117 | #endif | 
| 118 |  | 
| 119 | #if QT_CONFIG(bluez) | 
| 120 |     // This debug is needed to determine runtime configuration in the Qt CI. | 
| 121 |     isBluezDbusLE = (bluetoothdVersion() >= QVersionNumber(5, 42)); | 
| 122 |     qDebug() << "isDBusBluez:"  << isBluezDbusLE; | 
| 123 | #endif | 
| 124 | } | 
| 125 |  | 
| 126 | tst_QLowEnergyController::~tst_QLowEnergyController() | 
| 127 | { | 
| 128 |  | 
| 129 | } | 
| 130 |  | 
| 131 | void tst_QLowEnergyController::initTestCase() | 
| 132 | { | 
| 133 | #if !defined(Q_OS_MAC) | 
| 134 |     if (remoteDevice.isNull() | 
| 135 | #if !QT_CONFIG(winrt_bt) | 
| 136 |         || QBluetoothLocalDevice::allDevices().isEmpty()) { | 
| 137 | #else | 
| 138 |         ) { | 
| 139 | #endif | 
| 140 |         qWarning(msg: "No remote device or local adapter found." ); | 
| 141 |         return; | 
| 142 |     } | 
| 143 | #elif defined(Q_OS_OSX) | 
| 144 |     // allDevices is always empty on iOS: | 
| 145 |     if (QBluetoothLocalDevice::allDevices().isEmpty()) { | 
| 146 |         qWarning("No local adapter found." ); | 
| 147 |         return; | 
| 148 |     } | 
| 149 | #endif | 
| 150 |  | 
| 151 |     // QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); | 
| 152 |  | 
| 153 |     devAgent = new QBluetoothDeviceDiscoveryAgent(this); | 
| 154 |     devAgent->setLowEnergyDiscoveryTimeout(5000); | 
| 155 |  | 
| 156 |     QSignalSpy finishedSpy(devAgent, SIGNAL(finished())); | 
| 157 |     // there should be no changes yet | 
| 158 |     QVERIFY(finishedSpy.isValid()); | 
| 159 |     QVERIFY(finishedSpy.isEmpty()); | 
| 160 |  | 
| 161 |     bool deviceFound = false; | 
| 162 |     devAgent->start(method: QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); | 
| 163 |     QTRY_VERIFY_WITH_TIMEOUT(finishedSpy.count() > 0, 30000); | 
| 164 |     const QList<QBluetoothDeviceInfo> infos = devAgent->discoveredDevices(); | 
| 165 |     for (const QBluetoothDeviceInfo &info : infos) { | 
| 166 | #ifndef Q_OS_MAC | 
| 167 |         if (info.address() == remoteDevice) { | 
| 168 | #else | 
| 169 |         // On OS X/iOS the only way to find the device we are | 
| 170 |         // interested in - is to use device's name. | 
| 171 |         if (info.name().contains("Sensor" ) && info.name().contains("Tag" )) { | 
| 172 | #endif | 
| 173 |             remoteDeviceInfo = info; | 
| 174 |             deviceFound = true; | 
| 175 |             break; | 
| 176 |         } | 
| 177 |     } | 
| 178 |  | 
| 179 |     if (!deviceFound) | 
| 180 |         qWarning() << "Unable to find the TI sensor tag device, will skip most of the test" ; | 
| 181 |  | 
| 182 |     // These are the services exported by the TI SensorTag | 
| 183 | #ifndef Q_OS_MAC | 
| 184 |     // Core Bluetooth somehow ignores/hides/fails to discover these services. | 
| 185 |     if (!isBluezDbusLE) // Bluez LE Dbus intentionally hides 0x1800 service | 
| 186 |         foundServices << QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb" )); | 
| 187 |     foundServices << QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb" )); | 
| 188 | #endif | 
| 189 |     foundServices << QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb" )); | 
| 190 |     foundServices << QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb" )); | 
| 191 |     foundServices << QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000" )); | 
| 192 |     foundServices << QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000" )); | 
| 193 |     foundServices << QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000" )); | 
| 194 |     foundServices << QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000" )); | 
| 195 |     foundServices << QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000" )); | 
| 196 |     foundServices << QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000" )); | 
| 197 |     foundServices << QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000" )); | 
| 198 |     foundServices << QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000" )); | 
| 199 |     foundServices << QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000" )); | 
| 200 | } | 
| 201 |  | 
| 202 | /* | 
| 203 |  * Executed in between each test function call. | 
| 204 |  */ | 
| 205 | void tst_QLowEnergyController::init() | 
| 206 | { | 
| 207 | #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_TVOS) | 
| 208 |     /* | 
| 209 |      * Add a delay to give Android/iOS stack time to catch up in between | 
| 210 |      * the multiple connect/disconnects within each test function. | 
| 211 |      */ | 
| 212 |     QTest::qWait(2000); | 
| 213 | #endif | 
| 214 | } | 
| 215 |  | 
| 216 | void tst_QLowEnergyController::cleanupTestCase() | 
| 217 | { | 
| 218 |  | 
| 219 | } | 
| 220 |  | 
| 221 | void tst_QLowEnergyController::tst_emptyCtor() | 
| 222 | { | 
| 223 |     { | 
| 224 |         QBluetoothAddress remoteAddress; | 
| 225 |         QLowEnergyController control(remoteAddress); | 
| 226 |         QSignalSpy connectedSpy(&control, SIGNAL(connected())); | 
| 227 |         QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 228 |         QSignalSpy errorSpy(&control, SIGNAL(error(QLowEnergyController::Error))); | 
| 229 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 230 |         control.connectToDevice(); | 
| 231 |  | 
| 232 |         QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000); | 
| 233 |  | 
| 234 |         QVERIFY(connectedSpy.isEmpty()); | 
| 235 |         QVERIFY(stateSpy.isEmpty()); | 
| 236 |  | 
| 237 |         QLowEnergyController::Error lastError = errorSpy[0].at(i: 0).value<QLowEnergyController::Error>(); | 
| 238 |         QVERIFY(lastError == QLowEnergyController::UnknownRemoteDeviceError | 
| 239 |                 || lastError == QLowEnergyController::InvalidBluetoothAdapterError); | 
| 240 |     } | 
| 241 |  | 
| 242 |     { | 
| 243 |         QBluetoothDeviceInfo deviceInfo; | 
| 244 |         QLowEnergyController control(deviceInfo); | 
| 245 |         QSignalSpy connectedSpy(&control, SIGNAL(connected())); | 
| 246 |         QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 247 |         QSignalSpy errorSpy(&control, SIGNAL(error(QLowEnergyController::Error))); | 
| 248 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 249 |         control.connectToDevice(); | 
| 250 |  | 
| 251 |         QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000); | 
| 252 |  | 
| 253 |         QVERIFY(connectedSpy.isEmpty()); | 
| 254 |         QVERIFY(stateSpy.isEmpty()); | 
| 255 |  | 
| 256 |         QLowEnergyController::Error lastError = errorSpy[0].at(i: 0).value<QLowEnergyController::Error>(); | 
| 257 |         QVERIFY(lastError == QLowEnergyController::UnknownRemoteDeviceError  // if local device on platform found | 
| 258 |                 || lastError == QLowEnergyController::InvalidBluetoothAdapterError); // otherwise, e.g. fallback backend | 
| 259 |     } | 
| 260 |  | 
| 261 | } | 
| 262 |  | 
| 263 | void tst_QLowEnergyController::tst_connect() | 
| 264 | { | 
| 265 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 266 |  | 
| 267 | #if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || QT_CONFIG(winrt_bt) | 
| 268 |     if (!remoteDeviceInfo.isValid()) | 
| 269 | #else | 
| 270 |     if (localAdapters.isEmpty() || !remoteDeviceInfo.isValid()) | 
| 271 | #endif | 
| 272 |         QSKIP("No local Bluetooth or remote BTLE device found. Skipping test." ); | 
| 273 |  | 
| 274 |     QLowEnergyController control(remoteDeviceInfo); | 
| 275 |     QCOMPARE(remoteDeviceInfo.deviceUuid(), control.remoteDeviceUuid()); | 
| 276 |     QCOMPARE(control.role(), QLowEnergyController::CentralRole); | 
| 277 |     QSignalSpy connectedSpy(&control, SIGNAL(connected())); | 
| 278 |     QSignalSpy disconnectedSpy(&control, SIGNAL(disconnected())); | 
| 279 |     if (remoteDeviceInfo.name().isEmpty()) | 
| 280 |         QVERIFY(control.remoteName().isEmpty()); | 
| 281 |     else | 
| 282 |         QCOMPARE(control.remoteName(), remoteDeviceInfo.name()); | 
| 283 |  | 
| 284 | #if !defined(Q_OS_IOS) && !defined(Q_OS_TVOS) && !QT_CONFIG(winrt_bt) | 
| 285 |     const QBluetoothAddress localAdapter = localAdapters.at(i: 0).address(); | 
| 286 |     QCOMPARE(control.localAddress(), localAdapter); | 
| 287 |     QVERIFY(!control.localAddress().isNull()); | 
| 288 | #endif | 
| 289 | #ifndef Q_OS_MAC | 
| 290 |     QCOMPARE(control.remoteAddress(), remoteDevice); | 
| 291 | #endif | 
| 292 |     QCOMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 293 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 294 |     QVERIFY(control.errorString().isEmpty()); | 
| 295 |     QCOMPARE(disconnectedSpy.count(), 0); | 
| 296 |     QCOMPARE(connectedSpy.count(), 0); | 
| 297 |     QVERIFY(control.services().isEmpty()); | 
| 298 |  | 
| 299 |     bool wasError = false; | 
| 300 |     control.connectToDevice(); | 
| 301 |     QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 302 |               10000); | 
| 303 |  | 
| 304 |     QCOMPARE(disconnectedSpy.count(), 0); | 
| 305 |     if (control.error() != QLowEnergyController::NoError) { | 
| 306 |         //error during connect | 
| 307 |         QCOMPARE(connectedSpy.count(), 0); | 
| 308 |         QCOMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 309 |         wasError = true; | 
| 310 |     } else if (control.state() == QLowEnergyController::ConnectingState) { | 
| 311 |         //timeout | 
| 312 |         QCOMPARE(connectedSpy.count(), 0); | 
| 313 |         QVERIFY(control.errorString().isEmpty()); | 
| 314 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 315 |         QVERIFY(control.services().isEmpty()); | 
| 316 |         QSKIP("Connection to LE device cannot be established. Skipping test." ); | 
| 317 |         return; | 
| 318 |     } else { | 
| 319 |         QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 320 |         QCOMPARE(connectedSpy.count(), 1); | 
| 321 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 322 |         QVERIFY(control.errorString().isEmpty()); | 
| 323 |     } | 
| 324 |  | 
| 325 |     QVERIFY(control.services().isEmpty()); | 
| 326 |  | 
| 327 |     QList<QLowEnergyService *> savedReferences; | 
| 328 |  | 
| 329 |     if (!wasError) { | 
| 330 |         QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 331 |         QSignalSpy serviceFoundSpy(&control, SIGNAL(serviceDiscovered(QBluetoothUuid))); | 
| 332 |         QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 333 |         control.discoverServices(); | 
| 334 |         QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 335 |         QCOMPARE(stateSpy.count(), 2); | 
| 336 |         QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 337 |                  QLowEnergyController::DiscoveringState); | 
| 338 |         QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 339 |                  QLowEnergyController::DiscoveredState); | 
| 340 |  | 
| 341 |         QVERIFY(!serviceFoundSpy.isEmpty()); | 
| 342 |         QVERIFY(serviceFoundSpy.count() >= foundServices.count()); | 
| 343 |         QVERIFY(!serviceFoundSpy.isEmpty()); | 
| 344 |         QList<QBluetoothUuid> listing; | 
| 345 |         for (int i = 0; i < serviceFoundSpy.count(); i++) { | 
| 346 |             const QVariant v = serviceFoundSpy[i].at(i: 0); | 
| 347 |             listing.append(t: v.value<QBluetoothUuid>()); | 
| 348 |         } | 
| 349 |  | 
| 350 |         for (const QBluetoothUuid &uuid : qAsConst(t&: foundServices)) { | 
| 351 |             QVERIFY2(listing.contains(uuid), | 
| 352 |                      uuid.toString().toLatin1()); | 
| 353 |  | 
| 354 |             QLowEnergyService *service = control.createServiceObject(service: uuid); | 
| 355 |             QVERIFY2(service, uuid.toString().toLatin1()); | 
| 356 |             savedReferences.append(t: service); | 
| 357 |             QCOMPARE(service->type(), QLowEnergyService::PrimaryService); | 
| 358 |             QCOMPARE(service->state(), QLowEnergyService::DiscoveryRequired); | 
| 359 |         } | 
| 360 |  | 
| 361 |         // unrelated uuids don't return valid service object | 
| 362 |         // invalid service uuid | 
| 363 |         QVERIFY(!control.createServiceObject(QBluetoothUuid())); | 
| 364 |         // some random uuid | 
| 365 |         QVERIFY(!control.createServiceObject(QBluetoothUuid(QBluetoothUuid::DeviceName))); | 
| 366 |  | 
| 367 |         // initiate characteristic discovery | 
| 368 |         for (QLowEnergyService *service : qAsConst(t&: savedReferences)) { | 
| 369 |             qDebug() << "Discovering"  << service->serviceUuid(); | 
| 370 |             QSignalSpy stateSpy(service, | 
| 371 |                                 SIGNAL(stateChanged(QLowEnergyService::ServiceState))); | 
| 372 |             QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 373 |             service->discoverDetails(); | 
| 374 |  | 
| 375 |             QTRY_VERIFY_WITH_TIMEOUT( | 
| 376 |                         service->state() == QLowEnergyService::ServiceDiscovered, 10000); | 
| 377 |  | 
| 378 |             QCOMPARE(errorSpy.count(), 0); //no error | 
| 379 |             QCOMPARE(stateSpy.count(), 2); // | 
| 380 |  | 
| 381 |             verifyServiceProperties(info: service); | 
| 382 |         } | 
| 383 |  | 
| 384 |         // ensure that related service objects share same state | 
| 385 |         for (QLowEnergyService* originalService : qAsConst(t&: savedReferences)) { | 
| 386 |             QLowEnergyService *newService = control.createServiceObject( | 
| 387 |                         service: originalService->serviceUuid()); | 
| 388 |             QVERIFY(newService); | 
| 389 |             QCOMPARE(newService->state(), QLowEnergyService::ServiceDiscovered); | 
| 390 |             delete newService; | 
| 391 |         } | 
| 392 |     } | 
| 393 |  | 
| 394 |     // Finish off | 
| 395 |     control.disconnectFromDevice(); | 
| 396 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 397 |                 control.state() == QLowEnergyController::UnconnectedState, | 
| 398 |                 10000); | 
| 399 |  | 
| 400 |     if (wasError) { | 
| 401 |         QCOMPARE(disconnectedSpy.count(), 0); | 
| 402 |     } else { | 
| 403 |         QCOMPARE(disconnectedSpy.count(), 1); | 
| 404 |         // after disconnect all service references must be invalid | 
| 405 |         for (const QLowEnergyService *entry : qAsConst(t&: savedReferences)) { | 
| 406 |             const QBluetoothUuid &uuid = entry->serviceUuid(); | 
| 407 |             QVERIFY2(entry->state() == QLowEnergyService::InvalidService, | 
| 408 |                      uuid.toString().toLatin1()); | 
| 409 |  | 
| 410 |             //after disconnect all related characteristics and descriptors are invalid | 
| 411 |             QList<QLowEnergyCharacteristic> chars = entry->characteristics(); | 
| 412 |             for (int i = 0; i < chars.count(); i++) { | 
| 413 |                 QCOMPARE(chars.at(i).isValid(), false); | 
| 414 |                 QList<QLowEnergyDescriptor> descriptors = chars[i].descriptors(); | 
| 415 |                 for (int j = 0; j < descriptors.count(); j++) | 
| 416 |                     QCOMPARE(descriptors[j].isValid(), false); | 
| 417 |             } | 
| 418 |         } | 
| 419 |     } | 
| 420 |  | 
| 421 |     qDeleteAll(c: savedReferences); | 
| 422 |     savedReferences.clear(); | 
| 423 | } | 
| 424 |  | 
| 425 | void tst_QLowEnergyController::tst_concurrentDiscovery() | 
| 426 | { | 
| 427 | #if !defined(Q_OS_MACOS) && !QT_CONFIG(winrt_bt) | 
| 428 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 429 |     if (localAdapters.isEmpty()) | 
| 430 |         QSKIP("No local Bluetooth device found. Skipping test." ); | 
| 431 | #endif | 
| 432 |  | 
| 433 |     if (!remoteDeviceInfo.isValid()) | 
| 434 |         QSKIP("No remote BTLE device found. Skipping test." ); | 
| 435 |     QLowEnergyController control(remoteDeviceInfo); | 
| 436 |  | 
| 437 |  | 
| 438 |     QCOMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 439 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 440 |  | 
| 441 |     control.connectToDevice(); | 
| 442 |     { | 
| 443 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 444 |               30000); | 
| 445 |     } | 
| 446 |  | 
| 447 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 448 |             || control.error() != QLowEnergyController::NoError) { | 
| 449 |         // default BTLE backend forever hangs in ConnectingState | 
| 450 |         QSKIP("Cannot connect to remote device" ); | 
| 451 |     } | 
| 452 |  | 
| 453 |     QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 454 |  | 
| 455 |     // 2. new controller to same device fails | 
| 456 |     { | 
| 457 | #ifdef Q_OS_DARWIN | 
| 458 |         QLowEnergyController control2(remoteDeviceInfo); | 
| 459 | #else | 
| 460 |         QLowEnergyController control2(remoteDevice); | 
| 461 | #endif | 
| 462 |         control2.connectToDevice(); | 
| 463 |         { | 
| 464 |             QTRY_IMPL(control2.state() != QLowEnergyController::ConnectingState, | 
| 465 |                       30000); | 
| 466 |         } | 
| 467 |  | 
| 468 | #if defined(Q_OS_ANDROID) || defined(Q_OS_DARWIN) || QT_CONFIG(winrt_bt) | 
| 469 |         QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 470 |         QCOMPARE(control2.state(), QLowEnergyController::ConnectedState); | 
| 471 |         control2.disconnectFromDevice(); | 
| 472 |         QTest::qWait(3000); | 
| 473 |         QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 474 |         QCOMPARE(control2.state(), QLowEnergyController::UnconnectedState); | 
| 475 | #else | 
| 476 |         if (!isBluezDbusLE) { | 
| 477 |             // see QTBUG-42519 | 
| 478 |             // Linux non-DBus GATT cannot maintain two controller connections at the same time | 
| 479 |             QCOMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 480 |             QCOMPARE(control2.state(), QLowEnergyController::ConnectedState); | 
| 481 |             control2.disconnectFromDevice(); | 
| 482 |             QTRY_COMPARE(control2.state(), QLowEnergyController::UnconnectedState); | 
| 483 |             QTRY_COMPARE(control2.error(), QLowEnergyController::NoError); | 
| 484 |  | 
| 485 |             // reconnect control | 
| 486 |             control.connectToDevice(); | 
| 487 |             { | 
| 488 |                 QTRY_VERIFY_WITH_TIMEOUT(control.state() != QLowEnergyController::ConnectingState, | 
| 489 |                                          30000); | 
| 490 |             } | 
| 491 |             QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 492 |         } else { | 
| 493 |             QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 494 |             QCOMPARE(control2.state(), QLowEnergyController::ConnectedState); | 
| 495 |             QTRY_COMPARE(control2.error(), QLowEnergyController::NoError); | 
| 496 |             control2.disconnectFromDevice(); | 
| 497 |             QTRY_COMPARE(control2.state(), QLowEnergyController::UnconnectedState); | 
| 498 |             QTRY_COMPARE(control2.error(), QLowEnergyController::NoError); | 
| 499 |             QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 500 |             QTRY_COMPARE(control.error(), QLowEnergyController::NoError); | 
| 501 |  | 
| 502 |             // reconnect control | 
| 503 |             control.connectToDevice(); | 
| 504 |             { | 
| 505 |                 QTRY_VERIFY_WITH_TIMEOUT(control.state() != QLowEnergyController::ConnectingState, | 
| 506 |                                          30000); | 
| 507 |             } | 
| 508 |             QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 509 |         } | 
| 510 | #endif | 
| 511 |     } | 
| 512 |  | 
| 513 |     /* We are testing that we can run service discovery on the same device | 
| 514 |      * for multiple services at the same time. | 
| 515 |      * */ | 
| 516 |  | 
| 517 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 518 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 519 |     control.discoverServices(); | 
| 520 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 521 |     QCOMPARE(stateSpy.count(), 2); | 
| 522 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 523 |              QLowEnergyController::DiscoveringState); | 
| 524 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 525 |              QLowEnergyController::DiscoveredState); | 
| 526 |  | 
| 527 |     // pick MAX_SERVICES_SAME_TIME_ACCESS services | 
| 528 |     // and discover them at the same time | 
| 529 | #define MAX_SERVICES_SAME_TIME_ACCESS 3 | 
| 530 |     QLowEnergyService *services[MAX_SERVICES_SAME_TIME_ACCESS]; | 
| 531 |  | 
| 532 |     QVERIFY(control.services().count() >= MAX_SERVICES_SAME_TIME_ACCESS); | 
| 533 |  | 
| 534 |     QList<QBluetoothUuid> uuids = control.services(); | 
| 535 |  | 
| 536 |     // initialize services | 
| 537 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 538 |         services[i] = control.createServiceObject(service: uuids.at(i), parent: this); | 
| 539 |         QVERIFY(services[i]); | 
| 540 |     } | 
| 541 |  | 
| 542 |     // start complete discovery | 
| 543 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) | 
| 544 |         services[i]->discoverDetails(); | 
| 545 |  | 
| 546 |     // wait until discovery done | 
| 547 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 548 |         qWarning() << "Waiting for"  << i << services[i]->serviceUuid(); | 
| 549 |         QTRY_VERIFY_WITH_TIMEOUT( | 
| 550 |             services[i]->state() == QLowEnergyService::ServiceDiscovered, | 
| 551 |             30000); | 
| 552 |     } | 
| 553 |  | 
| 554 |     // verify discovered services | 
| 555 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 556 |         verifyServiceProperties(info: services[i]); | 
| 557 |  | 
| 558 |         QVERIFY(!services[i]->contains(QLowEnergyCharacteristic())); | 
| 559 |         QVERIFY(!services[i]->contains(QLowEnergyDescriptor())); | 
| 560 |     } | 
| 561 |  | 
| 562 |     control.disconnectFromDevice(); | 
| 563 |     QTRY_VERIFY_WITH_TIMEOUT(control.state() == QLowEnergyController::UnconnectedState, | 
| 564 |                              30000); | 
| 565 |     discoveryFinishedSpy.clear(); | 
| 566 |  | 
| 567 |     // redo the discovery with same controller | 
| 568 |     QLowEnergyService *services_second[MAX_SERVICES_SAME_TIME_ACCESS]; | 
| 569 |     control.connectToDevice(); | 
| 570 |     { | 
| 571 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 572 |               30000); | 
| 573 |     } | 
| 574 |  | 
| 575 |     QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 576 |     stateSpy.clear(); | 
| 577 |     control.discoverServices(); | 
| 578 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 579 |     QCOMPARE(stateSpy.count(), 2); | 
| 580 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 581 |              QLowEnergyController::DiscoveringState); | 
| 582 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 583 |              QLowEnergyController::DiscoveredState); | 
| 584 |  | 
| 585 |     // get all details | 
| 586 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 587 |         services_second[i] = control.createServiceObject(service: uuids.at(i), parent: this); | 
| 588 |         QVERIFY(services_second[i]->parent() == this); | 
| 589 |         QVERIFY(services[i]); | 
| 590 |         QVERIFY(services_second[i]->state() == QLowEnergyService::DiscoveryRequired); | 
| 591 |         services_second[i]->discoverDetails(); | 
| 592 |     } | 
| 593 |  | 
| 594 |     // wait until discovery done | 
| 595 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 596 |         qWarning() << "Waiting for"  << i << services_second[i]->serviceUuid(); | 
| 597 |         QTRY_VERIFY_WITH_TIMEOUT( | 
| 598 |             services_second[i]->state() == QLowEnergyService::ServiceDiscovered, | 
| 599 |             30000); | 
| 600 |         QCOMPARE(services_second[i]->serviceName(), services[i]->serviceName()); | 
| 601 |         QCOMPARE(services_second[i]->serviceUuid(), services[i]->serviceUuid()); | 
| 602 |     } | 
| 603 |  | 
| 604 |     // verify discovered services (1st and 2nd round) | 
| 605 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 606 |         verifyServiceProperties(info: services_second[i]); | 
| 607 |         //after disconnect all related characteristics and descriptors are invalid | 
| 608 |         const QList<QLowEnergyCharacteristic> chars = services[i]->characteristics(); | 
| 609 |         for (int j = 0; j < chars.count(); j++) { | 
| 610 |             QCOMPARE(chars.at(j).isValid(), false); | 
| 611 |             QVERIFY(services[i]->contains(chars[j])); | 
| 612 |             QVERIFY(!services_second[i]->contains(chars[j])); | 
| 613 |             const QList<QLowEnergyDescriptor> descriptors = chars[j].descriptors(); | 
| 614 |             for (int k = 0; k < descriptors.count(); k++) { | 
| 615 |                 QCOMPARE(descriptors[k].isValid(), false); | 
| 616 |                 services[i]->contains(descriptor: descriptors[k]); | 
| 617 |                 QVERIFY(!services_second[i]->contains(chars[j])); | 
| 618 |             } | 
| 619 |         } | 
| 620 |  | 
| 621 |         QCOMPARE(services[i]->serviceUuid(), services_second[i]->serviceUuid()); | 
| 622 |         QCOMPARE(services[i]->serviceName(), services_second[i]->serviceName()); | 
| 623 |         QCOMPARE(services[i]->type(), services_second[i]->type()); | 
| 624 |         QVERIFY(services[i]->state() == QLowEnergyService::InvalidService); | 
| 625 |         QVERIFY(services_second[i]->state() == QLowEnergyService::ServiceDiscovered); | 
| 626 |     } | 
| 627 |  | 
| 628 |     // cleanup | 
| 629 |     for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) { | 
| 630 |         delete services[i]; | 
| 631 |         delete services_second[i]; | 
| 632 |     } | 
| 633 |  | 
| 634 |     control.disconnectFromDevice(); | 
| 635 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 636 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 637 | } | 
| 638 |  | 
| 639 | void tst_QLowEnergyController::verifyServiceProperties( | 
| 640 |         const QLowEnergyService *info) | 
| 641 | { | 
| 642 |     if (info->serviceUuid() == | 
| 643 |             QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb" ))) { | 
| 644 |         qDebug() << "Verifying GAP Service" ; | 
| 645 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 646 |         QCOMPARE(chars.count(), 5); | 
| 647 |  | 
| 648 |         // Device Name | 
| 649 |         QString temp("00002a00-0000-1000-8000-00805f9b34fb" ); | 
| 650 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 651 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x3)); | 
| 652 |         QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read); | 
| 653 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("544920424c452053656e736f7220546167" )); | 
| 654 |         QVERIFY(chars[0].isValid()); | 
| 655 |         QCOMPARE(chars[0].descriptors().count(), 0); | 
| 656 |         QVERIFY(info->contains(chars[0])); | 
| 657 |  | 
| 658 |         // Appearance | 
| 659 |         temp = QString("00002a01-0000-1000-8000-00805f9b34fb" ); | 
| 660 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 661 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x5)); | 
| 662 |         QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read); | 
| 663 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("0000" )); | 
| 664 |         QVERIFY(chars[1].isValid()); | 
| 665 |         QCOMPARE(chars[1].descriptors().count(), 0); | 
| 666 |         QVERIFY(info->contains(chars[1])); | 
| 667 |  | 
| 668 |         // Peripheral Privacy Flag | 
| 669 |         temp = QString("00002a02-0000-1000-8000-00805f9b34fb" ); | 
| 670 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 671 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x7)); | 
| 672 |         QVERIFY(chars[2].properties() & QLowEnergyCharacteristic::Read); | 
| 673 |         QCOMPARE(chars[2].value(), QByteArray::fromHex("00" )); | 
| 674 |         QVERIFY(chars[2].isValid()); | 
| 675 |         QCOMPARE(chars[2].descriptors().count(), 0); | 
| 676 |         QVERIFY(info->contains(chars[2])); | 
| 677 |  | 
| 678 |         // Reconnection Address | 
| 679 |         temp = QString("00002a03-0000-1000-8000-00805f9b34fb" ); | 
| 680 |         QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp)); | 
| 681 |         HANDLE_COMPARE(chars[3].handle(), QLowEnergyHandle(0x9)); | 
| 682 |         //Early firmware version had this characteristic as Read|Write and may fail | 
| 683 |         QCOMPARE(chars[3].properties(), QLowEnergyCharacteristic::Write); | 
| 684 |         if (chars[3].properties() & QLowEnergyCharacteristic::Read) | 
| 685 |             QCOMPARE(chars[3].value(), QByteArray::fromHex("000000000000" )); | 
| 686 |         else | 
| 687 |             QCOMPARE(chars[3].value(), QByteArray()); | 
| 688 |         QVERIFY(chars[3].isValid()); | 
| 689 |         QCOMPARE(chars[3].descriptors().count(), 0); | 
| 690 |         QVERIFY(info->contains(chars[3])); | 
| 691 |  | 
| 692 |         // Peripheral Preferred Connection Parameters | 
| 693 |         temp = QString("00002a04-0000-1000-8000-00805f9b34fb" ); | 
| 694 |         QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp)); | 
| 695 |         HANDLE_COMPARE(chars[4].handle(), QLowEnergyHandle(0xb)); | 
| 696 |         QCOMPARE(chars[4].properties(), QLowEnergyCharacteristic::Read); | 
| 697 |         QCOMPARE(chars[4].value(), QByteArray::fromHex("5000a0000000e803" )); | 
| 698 |         QVERIFY(chars[4].isValid()); | 
| 699 |         QCOMPARE(chars[4].descriptors().count(), 0); | 
| 700 |         QVERIFY(info->contains(chars[4])); | 
| 701 |     } else if (info->serviceUuid() == | 
| 702 |                 QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb" ))) { | 
| 703 |         qDebug() << "Verifying GATT Service" ; | 
| 704 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 705 |         QCOMPARE(chars.count(), 1); | 
| 706 |  | 
| 707 |         // Service Changed | 
| 708 |         QString temp("00002a05-0000-1000-8000-00805f9b34fb" ); | 
| 709 |         //this should really be readable according to GATT Service spec | 
| 710 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 711 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0xe)); | 
| 712 |         QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Indicate); | 
| 713 |         QCOMPARE(chars[0].value(), QByteArray()); | 
| 714 |         QVERIFY(chars[0].isValid()); | 
| 715 |         QVERIFY(info->contains(chars[0])); | 
| 716 |  | 
| 717 |         QCOMPARE(chars[0].descriptors().count(), 1); | 
| 718 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 719 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0xf)); | 
| 720 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 721 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 722 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 723 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 724 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 725 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 726 |     } else if (info->serviceUuid() == | 
| 727 |                 QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb" ))) { | 
| 728 |         qDebug() << "Verifying Device Information" ; | 
| 729 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 730 |         QCOMPARE(chars.count(), 9); | 
| 731 |  | 
| 732 |         // System ID | 
| 733 |         QString temp("00002a23-0000-1000-8000-00805f9b34fb" ); | 
| 734 |         //this should really be readable according to GATT Service spec | 
| 735 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 736 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x12)); | 
| 737 |         QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read); | 
| 738 | //        Do not read the System ID as it is different for every device | 
| 739 | //        QEXPECT_FAIL("", "The value is different on different devices", Continue); | 
| 740 | //        QCOMPARE(chars[0].value(), QByteArray::fromHex("6e41ab0000296abc")); | 
| 741 |         QVERIFY(chars[0].isValid()); | 
| 742 |         QVERIFY(info->contains(chars[0])); | 
| 743 |         QCOMPARE(chars[0].descriptors().count(), 0); | 
| 744 |  | 
| 745 |         // Model Number | 
| 746 |         temp = QString("00002a24-0000-1000-8000-00805f9b34fb" ); | 
| 747 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 748 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x14)); | 
| 749 |         QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read); | 
| 750 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("4e2e412e00" )); | 
| 751 |         QVERIFY(chars[1].isValid()); | 
| 752 |         QVERIFY(info->contains(chars[1])); | 
| 753 |         QCOMPARE(chars[1].descriptors().count(), 0); | 
| 754 |  | 
| 755 |         // Serial Number | 
| 756 |         temp = QString("00002a25-0000-1000-8000-00805f9b34fb" ); | 
| 757 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 758 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x16)); | 
| 759 |         QCOMPARE(chars[2].properties(), | 
| 760 |                  (QLowEnergyCharacteristic::Read)); | 
| 761 |         QCOMPARE(chars[2].value(), QByteArray::fromHex("4e2e412e00" )); | 
| 762 |         QVERIFY(chars[2].isValid()); | 
| 763 |         QVERIFY(info->contains(chars[2])); | 
| 764 |         QCOMPARE(chars[2].descriptors().count(), 0); | 
| 765 |  | 
| 766 |         // Firmware Revision | 
| 767 |         temp = QString("00002a26-0000-1000-8000-00805f9b34fb" ); | 
| 768 |         QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp)); | 
| 769 |         HANDLE_COMPARE(chars[3].handle(), QLowEnergyHandle(0x18)); | 
| 770 |         QCOMPARE(chars[3].properties(), | 
| 771 |                  (QLowEnergyCharacteristic::Read)); | 
| 772 |         //FW rev. : 1.5 (Oct 23 2013) | 
| 773 |         // Other revisions will fail here | 
| 774 |         QCOMPARE(chars[3].value(), QByteArray::fromHex("312e3520284f637420323320323031332900" )); | 
| 775 |         QVERIFY(chars[3].isValid()); | 
| 776 |         QVERIFY(info->contains(chars[3])); | 
| 777 |         QCOMPARE(chars[3].descriptors().count(), 0); | 
| 778 |  | 
| 779 |         // Hardware Revision | 
| 780 |         temp = QString("00002a27-0000-1000-8000-00805f9b34fb" ); | 
| 781 |         QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp)); | 
| 782 |         HANDLE_COMPARE(chars[4].handle(), QLowEnergyHandle(0x1a)); | 
| 783 |         QCOMPARE(chars[4].properties(), | 
| 784 |                  (QLowEnergyCharacteristic::Read)); | 
| 785 |         QCOMPARE(chars[4].value(), QByteArray::fromHex("4e2e412e00" )); | 
| 786 |         QVERIFY(chars[4].isValid()); | 
| 787 |         QVERIFY(info->contains(chars[4])); | 
| 788 |         QCOMPARE(chars[4].descriptors().count(), 0); | 
| 789 |  | 
| 790 |         // Software Revision | 
| 791 |         temp = QString("00002a28-0000-1000-8000-00805f9b34fb" ); | 
| 792 |         QCOMPARE(chars[5].uuid(), QBluetoothUuid(temp)); | 
| 793 |         HANDLE_COMPARE(chars[5].handle(), QLowEnergyHandle(0x1c)); | 
| 794 |         QCOMPARE(chars[5].properties(), | 
| 795 |                  (QLowEnergyCharacteristic::Read)); | 
| 796 |         QCOMPARE(chars[5].value(), QByteArray::fromHex("4e2e412e00" )); | 
| 797 |         QVERIFY(chars[5].isValid()); | 
| 798 |         QVERIFY(info->contains(chars[5])); | 
| 799 |         QCOMPARE(chars[5].descriptors().count(), 0); | 
| 800 |  | 
| 801 |         // Manufacturer Name | 
| 802 |         temp = QString("00002a29-0000-1000-8000-00805f9b34fb" ); | 
| 803 |         QCOMPARE(chars[6].uuid(), QBluetoothUuid(temp)); | 
| 804 |         HANDLE_COMPARE(chars[6].handle(), QLowEnergyHandle(0x1e)); | 
| 805 |         QCOMPARE(chars[6].properties(), | 
| 806 |                  (QLowEnergyCharacteristic::Read)); | 
| 807 |         QCOMPARE(chars[6].value(), QByteArray::fromHex("546578617320496e737472756d656e747300" )); | 
| 808 |         QVERIFY(chars[6].isValid()); | 
| 809 |         QVERIFY(info->contains(chars[6])); | 
| 810 |         QCOMPARE(chars[6].descriptors().count(), 0); | 
| 811 |  | 
| 812 |         // IEEE | 
| 813 |         temp = QString("00002a2a-0000-1000-8000-00805f9b34fb" ); | 
| 814 |         QCOMPARE(chars[7].uuid(), QBluetoothUuid(temp)); | 
| 815 |         HANDLE_COMPARE(chars[7].handle(), QLowEnergyHandle(0x20)); | 
| 816 |         QCOMPARE(chars[7].properties(), | 
| 817 |                  (QLowEnergyCharacteristic::Read)); | 
| 818 |         QCOMPARE(chars[7].value(), QByteArray::fromHex("fe006578706572696d656e74616c" )); | 
| 819 |         QVERIFY(chars[7].isValid()); | 
| 820 |         QVERIFY(info->contains(chars[7])); | 
| 821 |         QCOMPARE(chars[7].descriptors().count(), 0); | 
| 822 |  | 
| 823 |         // PnP ID | 
| 824 |         temp = QString("00002a50-0000-1000-8000-00805f9b34fb" ); | 
| 825 |         QCOMPARE(chars[8].uuid(), QBluetoothUuid(temp)); | 
| 826 |         HANDLE_COMPARE(chars[8].handle(), QLowEnergyHandle(0x22)); | 
| 827 |         QCOMPARE(chars[8].properties(), | 
| 828 |                  (QLowEnergyCharacteristic::Read)); | 
| 829 |         QCOMPARE(chars[8].value(), QByteArray::fromHex("010d0000001001" )); | 
| 830 |         QVERIFY(chars[8].isValid()); | 
| 831 |         QVERIFY(info->contains(chars[8])); | 
| 832 |         QCOMPARE(chars[8].descriptors().count(), 0); | 
| 833 |     } else if (info->serviceUuid() == | 
| 834 |                QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000" ))) { | 
| 835 |         qDebug() << "Verifying Temperature" ; | 
| 836 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 837 |         QVERIFY(chars.count() >= 2); | 
| 838 |  | 
| 839 |         // Temp Data | 
| 840 |         QString temp("f000aa01-0451-4000-b000-000000000000" ); | 
| 841 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 842 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x25)); | 
| 843 |         QCOMPARE(chars[0].properties(), | 
| 844 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 845 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000" )); | 
| 846 |         QVERIFY(chars[0].isValid()); | 
| 847 |         QVERIFY(info->contains(chars[0])); | 
| 848 |  | 
| 849 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 850 |         //descriptor checks | 
| 851 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 852 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x26)); | 
| 853 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 854 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 855 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 856 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 857 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 858 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 859 |  | 
| 860 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 861 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x27)); | 
| 862 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 863 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 864 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 865 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 866 |         // value different in other revisions and test may fail | 
| 867 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 868 |                 QByteArray::fromHex("54656d702e2044617461" )); | 
| 869 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 870 |  | 
| 871 |         // Temp Config | 
| 872 |         temp = QString("f000aa02-0451-4000-b000-000000000000" ); | 
| 873 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 874 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x29)); | 
| 875 |         QCOMPARE(chars[1].properties(), | 
| 876 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 877 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 878 |         QVERIFY(chars[1].isValid()); | 
| 879 |         QVERIFY(info->contains(chars[1])); | 
| 880 |  | 
| 881 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 882 |         //descriptor checks | 
| 883 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 884 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x2a)); | 
| 885 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 886 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 887 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 888 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 889 |         // value different in other revisions and test may fail | 
| 890 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 891 |                 QByteArray::fromHex("54656d702e20436f6e662e" )); | 
| 892 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 893 |  | 
| 894 |  | 
| 895 |         //Temp Period (introduced by later firmware versions) | 
| 896 |         if (chars.count() > 2) { | 
| 897 |             temp = QString("f000aa03-0451-4000-b000-000000000000" ); | 
| 898 |             QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 899 |             HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x2c)); | 
| 900 |             QCOMPARE(chars[2].properties(), | 
| 901 |                      (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 902 |             QCOMPARE(chars[2].value(), QByteArray::fromHex("64" )); | 
| 903 |             QVERIFY(chars[2].isValid()); | 
| 904 |             QVERIFY(info->contains(chars[2])); | 
| 905 |  | 
| 906 |             QCOMPARE(chars[2].descriptors().count(), 1); | 
| 907 |             //descriptor checks | 
| 908 |             QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 909 |             HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x2d)); | 
| 910 |             QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 911 |                     QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 912 |             QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 913 |                     QBluetoothUuid::CharacteristicUserDescription); | 
| 914 |             QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 915 |                     QByteArray::fromHex("54656d702e20506572696f64" )); | 
| 916 |             QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 917 |         } | 
| 918 |     } else if (info->serviceUuid() == | 
| 919 |                QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb" ))) { | 
| 920 |         qDebug() << "Verifying Simple Keys" ; | 
| 921 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 922 |         QCOMPARE(chars.count(), 1); | 
| 923 |  | 
| 924 |         // Temp Data | 
| 925 |         QString temp("0000ffe1-0000-1000-8000-00805f9b34fb" ); | 
| 926 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 927 |         // value different in other revisions and test may fail | 
| 928 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x6b)); | 
| 929 |         QCOMPARE(chars[0].properties(), | 
| 930 |                 (QLowEnergyCharacteristic::Notify)); | 
| 931 |         QCOMPARE(chars[0].value(), QByteArray()); | 
| 932 |         QVERIFY(chars[0].isValid()); | 
| 933 |         QVERIFY(info->contains(chars[0])); | 
| 934 |  | 
| 935 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 936 |         //descriptor checks | 
| 937 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 938 |         // value different in other revisions and test may fail | 
| 939 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x6c)); | 
| 940 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 941 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 942 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 943 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 944 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 945 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 946 |  | 
| 947 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 948 |         // value different in other revisions and test may fail | 
| 949 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x6d)); | 
| 950 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 951 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 952 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 953 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 954 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 955 |                 QByteArray::fromHex("4b6579205072657373205374617465" )); | 
| 956 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 957 |  | 
| 958 |     } else if (info->serviceUuid() == | 
| 959 |                QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000" ))) { | 
| 960 |         qDebug() << "Verifying Accelerometer" ; | 
| 961 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 962 |         QCOMPARE(chars.count(), 3); | 
| 963 |  | 
| 964 |         // Accel Data | 
| 965 |         QString temp("f000aa11-0451-4000-b000-000000000000" ); | 
| 966 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 967 |         // value different in other revisions and test may fail | 
| 968 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x30)); | 
| 969 |         QCOMPARE(chars[0].properties(), | 
| 970 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 971 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("000000" )); | 
| 972 |         QVERIFY(chars[0].isValid()); | 
| 973 |         QVERIFY(info->contains(chars[0])); | 
| 974 |  | 
| 975 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 976 |  | 
| 977 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 978 |         // value different in other revisions and test may fail | 
| 979 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x31)); | 
| 980 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 981 |                  QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 982 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 983 |                  QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 984 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 985 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 986 |  | 
| 987 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 988 |         // value different in other revisions and test may fail | 
| 989 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x32)); | 
| 990 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 991 |                  QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 992 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 993 |                  QBluetoothUuid::CharacteristicUserDescription); | 
| 994 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 995 |                  QByteArray::fromHex("416363656c2e2044617461" )); | 
| 996 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 997 |  | 
| 998 |         // Accel Config | 
| 999 |         temp = QString("f000aa12-0451-4000-b000-000000000000" ); | 
| 1000 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1001 |         // value different in other revisions and test may fail | 
| 1002 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x34)); | 
| 1003 |         QCOMPARE(chars[1].properties(), | 
| 1004 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1005 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1006 |         QVERIFY(chars[1].isValid()); | 
| 1007 |         QVERIFY(info->contains(chars[1])); | 
| 1008 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1009 |  | 
| 1010 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1011 |         // value different in other revisions and test may fail | 
| 1012 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x35)); | 
| 1013 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1014 |                  QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1015 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1016 |                  QBluetoothUuid::CharacteristicUserDescription); | 
| 1017 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1018 |                  QByteArray::fromHex("416363656c2e20436f6e662e" )); | 
| 1019 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1020 |  | 
| 1021 |         // Accel Period | 
| 1022 |         temp = QString("f000aa13-0451-4000-b000-000000000000" ); | 
| 1023 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 1024 |         // value different in other revisions and test may fail | 
| 1025 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x37)); | 
| 1026 |         QCOMPARE(chars[2].properties(), | 
| 1027 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1028 |         QCOMPARE(chars[2].value(), QByteArray::fromHex("64" ));   // don't change it or set it to 0x64 | 
| 1029 |         QVERIFY(chars[2].isValid()); | 
| 1030 |         QVERIFY(info->contains(chars[2])); | 
| 1031 |  | 
| 1032 |         QCOMPARE(chars[2].descriptors().count(), 1); | 
| 1033 |         //descriptor checks | 
| 1034 |         QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 1035 |         // value different in other revisions and test may fail | 
| 1036 |         HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x38)); | 
| 1037 |         QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 1038 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1039 |         QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 1040 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1041 |         // value different in other revisions and test may fail | 
| 1042 |         QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 1043 |                 QByteArray::fromHex("416363656c2e20506572696f64" )); | 
| 1044 |         QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 1045 |     } else if (info->serviceUuid() == | 
| 1046 |                QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000" ))) { | 
| 1047 |         qDebug() << "Verifying Humidity" ; | 
| 1048 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1049 |         QVERIFY(chars.count() >= 2); //new firmware has more chars | 
| 1050 |  | 
| 1051 |         // Humidity Data | 
| 1052 |         QString temp("f000aa21-0451-4000-b000-000000000000" ); | 
| 1053 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1054 |         // value different in other revisions and test may fail | 
| 1055 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x3b)); | 
| 1056 |         QCOMPARE(chars[0].properties(), | 
| 1057 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 1058 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000" )); | 
| 1059 |         QVERIFY(chars[0].isValid()); | 
| 1060 |         QVERIFY(info->contains(chars[0])); | 
| 1061 |  | 
| 1062 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1063 |         //descriptor checks | 
| 1064 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1065 |         // value different in other revisions and test may fail | 
| 1066 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x3c)); | 
| 1067 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1068 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1069 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1070 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1071 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1072 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1073 |  | 
| 1074 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1075 |         // value different in other revisions and test may fail | 
| 1076 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x3d)); | 
| 1077 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1078 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1079 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1080 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1081 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1082 |                 QByteArray::fromHex("48756d69642e2044617461" )); | 
| 1083 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1084 |  | 
| 1085 |         // Humidity Config | 
| 1086 |         temp = QString("f000aa22-0451-4000-b000-000000000000" ); | 
| 1087 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1088 |         // value different in other revisions and test may fail | 
| 1089 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x3f)); | 
| 1090 |         QCOMPARE(chars[1].properties(), | 
| 1091 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1092 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1093 |         QVERIFY(chars[1].isValid()); | 
| 1094 |         QVERIFY(info->contains(chars[1])); | 
| 1095 |  | 
| 1096 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1097 |         //descriptor checks | 
| 1098 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1099 |         // value different in other revisions and test may fail | 
| 1100 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x40)); | 
| 1101 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1102 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1103 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1104 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1105 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1106 |                 QByteArray::fromHex("48756d69642e20436f6e662e" )); | 
| 1107 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1108 |  | 
| 1109 |         if (chars.count() >= 3) { | 
| 1110 |             // New firmware new characteristic | 
| 1111 |             // Humidity Period | 
| 1112 |             temp = QString("f000aa23-0451-4000-b000-000000000000" ); | 
| 1113 |             QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 1114 |             HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x42)); | 
| 1115 |             QCOMPARE(chars[2].properties(), | 
| 1116 |                      (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1117 |             QCOMPARE(chars[2].value(), QByteArray::fromHex("64" )); | 
| 1118 |             QVERIFY(chars[2].isValid()); | 
| 1119 |             QVERIFY(info->contains(chars[2])); | 
| 1120 |  | 
| 1121 |             QCOMPARE(chars[2].descriptors().count(), 1); | 
| 1122 |             //descriptor checks | 
| 1123 |             QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 1124 |             HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x43)); | 
| 1125 |             QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 1126 |                     QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1127 |             QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 1128 |                     QBluetoothUuid::CharacteristicUserDescription); | 
| 1129 |             QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 1130 |                     QByteArray::fromHex("48756d69642e20506572696f64" )); | 
| 1131 |             QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 1132 |         } | 
| 1133 |     } else if (info->serviceUuid() == | 
| 1134 |                QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000" ))) { | 
| 1135 |         qDebug() << "Verifying Magnetometer" ; | 
| 1136 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1137 |         QCOMPARE(chars.count(), 3); | 
| 1138 |  | 
| 1139 |         // Magnetometer Data | 
| 1140 |         QString temp("f000aa31-0451-4000-b000-000000000000" ); | 
| 1141 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1142 |         // value different in other revisions and test may fail | 
| 1143 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x46)); | 
| 1144 |         QCOMPARE(chars[0].properties(), | 
| 1145 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 1146 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000" )); | 
| 1147 |         QVERIFY(chars[0].isValid()); | 
| 1148 |         QVERIFY(info->contains(chars[0])); | 
| 1149 |  | 
| 1150 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1151 |  | 
| 1152 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1153 |         // value different in other revisions and test may fail | 
| 1154 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x47)); | 
| 1155 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1156 |                  QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1157 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1158 |                  QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1159 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1160 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1161 |  | 
| 1162 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1163 |         // value different in other revisions and test may fail | 
| 1164 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x48)); | 
| 1165 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1166 |                  QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1167 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1168 |                  QBluetoothUuid::CharacteristicUserDescription); | 
| 1169 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1170 |                  QByteArray::fromHex("4d61676e2e2044617461" )); | 
| 1171 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1172 |  | 
| 1173 |         // Magnetometer Config | 
| 1174 |         temp = QString("f000aa32-0451-4000-b000-000000000000" ); | 
| 1175 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1176 |         // value different in other revisions and test may fail | 
| 1177 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x4a)); | 
| 1178 |         QCOMPARE(chars[1].properties(), | 
| 1179 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1180 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1181 |         QVERIFY(chars[1].isValid()); | 
| 1182 |         QVERIFY(info->contains(chars[1])); | 
| 1183 |  | 
| 1184 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1185 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1186 |         // value different in other revisions and test may fail | 
| 1187 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x4b)); | 
| 1188 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1189 |                  QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1190 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1191 |                  QBluetoothUuid::CharacteristicUserDescription); | 
| 1192 |         // value different in other revisions and test may fail | 
| 1193 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1194 |                  QByteArray::fromHex("4d61676e2e20436f6e662e" )); | 
| 1195 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1196 |  | 
| 1197 |         // Magnetometer Period | 
| 1198 |         temp = QString("f000aa33-0451-4000-b000-000000000000" ); | 
| 1199 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 1200 |         // value different in other revisions and test may fail | 
| 1201 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x4d)); | 
| 1202 |         QCOMPARE(chars[2].properties(), | 
| 1203 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1204 |         QCOMPARE(chars[2].value(), QByteArray::fromHex("c8" ));   // don't change it or set it to 0xc8 | 
| 1205 |         QVERIFY(chars[2].isValid()); | 
| 1206 |         QVERIFY(info->contains(chars[2])); | 
| 1207 |  | 
| 1208 |         QCOMPARE(chars[2].descriptors().count(), 1); | 
| 1209 |         QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 1210 |         // value different in other revisions and test may fail | 
| 1211 |         HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x4e)); | 
| 1212 |         QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 1213 |                  QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1214 |         QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 1215 |                  QBluetoothUuid::CharacteristicUserDescription); | 
| 1216 |         // value different in other revisions and test may fail | 
| 1217 |         QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 1218 |                  QByteArray::fromHex("4d61676e2e20506572696f64" )); | 
| 1219 |         QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 1220 |     } else if (info->serviceUuid() == | 
| 1221 |                QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000" ))) { | 
| 1222 |         qDebug() << "Verifying Pressure" ; | 
| 1223 |         const QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1224 |         QVERIFY(chars.count() >= 3); | 
| 1225 |  | 
| 1226 |         // Pressure Data | 
| 1227 |         QString temp("f000aa41-0451-4000-b000-000000000000" ); | 
| 1228 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1229 |         // value different in other revisions and test may fail | 
| 1230 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x51)); | 
| 1231 |         QCOMPARE(chars[0].properties(), | 
| 1232 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 1233 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000" )); | 
| 1234 |         QVERIFY(chars[0].isValid()); | 
| 1235 |         QVERIFY(info->contains(chars[0])); | 
| 1236 |  | 
| 1237 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1238 |         //descriptor checks | 
| 1239 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1240 |         // value different in other revisions and test may fail | 
| 1241 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x52)); | 
| 1242 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1243 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1244 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1245 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1246 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1247 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1248 |  | 
| 1249 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1250 |         // value different in other revisions and test may fail | 
| 1251 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x53)); | 
| 1252 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1253 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1254 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1255 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1256 |         // value different in other revisions and test may fail | 
| 1257 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1258 |                 QByteArray::fromHex("4261726f6d2e2044617461" )); | 
| 1259 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1260 |  | 
| 1261 |         // Pressure Config | 
| 1262 |         temp = QString("f000aa42-0451-4000-b000-000000000000" ); | 
| 1263 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1264 |         // value different in other revisions and test may fail | 
| 1265 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x55)); | 
| 1266 |         QCOMPARE(chars[1].properties(), | 
| 1267 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1268 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1269 |         QVERIFY(chars[1].isValid()); | 
| 1270 |         QVERIFY(info->contains(chars[1])); | 
| 1271 |  | 
| 1272 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1273 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1274 |         // value different in other revisions and test may fail | 
| 1275 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x56)); | 
| 1276 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1277 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1278 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1279 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1280 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1281 |                 QByteArray::fromHex("4261726f6d2e20436f6e662e" )); | 
| 1282 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1283 |  | 
| 1284 |         //calibration and period characteristic are swapped, ensure we don't depend on their order | 
| 1285 |         QLowEnergyCharacteristic calibration, period; | 
| 1286 |         for (const QLowEnergyCharacteristic &ch : chars) { | 
| 1287 |             //find calibration characteristic | 
| 1288 |             if (ch.uuid() == QBluetoothUuid(QString("f000aa43-0451-4000-b000-000000000000" ))) | 
| 1289 |                 calibration = ch; | 
| 1290 |             else if (ch.uuid() == QBluetoothUuid(QString("f000aa44-0451-4000-b000-000000000000" ))) | 
| 1291 |                 period = ch; | 
| 1292 |         } | 
| 1293 |  | 
| 1294 |         if (calibration.isValid()) { | 
| 1295 |             // Pressure Calibration | 
| 1296 |             temp = QString("f000aa43-0451-4000-b000-000000000000" ); | 
| 1297 |             QCOMPARE(calibration.uuid(), QBluetoothUuid(temp)); | 
| 1298 |             // value different in other revisions and test may fail | 
| 1299 |             HANDLE_COMPARE(calibration.handle(), QLowEnergyHandle(0x5b)); | 
| 1300 |             QCOMPARE(calibration.properties(), | 
| 1301 |                      (QLowEnergyCharacteristic::Read)); | 
| 1302 |             QCOMPARE(calibration.value(), QByteArray::fromHex("00000000000000000000000000000000" ));   // don't change it | 
| 1303 |             QVERIFY(calibration.isValid()); | 
| 1304 |             QVERIFY(info->contains(calibration)); | 
| 1305 |  | 
| 1306 |             QCOMPARE(calibration.descriptors().count(), 2); | 
| 1307 |             //descriptor checks | 
| 1308 |             QCOMPARE(calibration.descriptors().at(0).isValid(), true); | 
| 1309 |             // value different in other revisions and test may fail | 
| 1310 |             HANDLE_COMPARE(calibration.descriptors().at(0).handle(), QLowEnergyHandle(0x5c)); | 
| 1311 |             QCOMPARE(calibration.descriptors().at(0).uuid(), | 
| 1312 |                     QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1313 |             QCOMPARE(calibration.descriptors().at(0).type(), | 
| 1314 |                     QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1315 |             QVERIFY(verifyClientCharacteristicValue(calibration.descriptors().at(0).value())); | 
| 1316 |             QVERIFY(info->contains(calibration.descriptors().at(0))); | 
| 1317 |  | 
| 1318 |             QCOMPARE(calibration.descriptors().at(1).isValid(), true); | 
| 1319 |             // value different in other revisions and test may fail | 
| 1320 |             HANDLE_COMPARE(calibration.descriptors().at(1).handle(), QLowEnergyHandle(0x5d)); | 
| 1321 |             QCOMPARE(calibration.descriptors().at(1).uuid(), | 
| 1322 |                     QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1323 |             QCOMPARE(calibration.descriptors().at(1).type(), | 
| 1324 |                     QBluetoothUuid::CharacteristicUserDescription); | 
| 1325 |             QCOMPARE(calibration.descriptors().at(1).value(), | 
| 1326 |                      QByteArray::fromHex("4261726f6d2e2043616c6962722e" )); | 
| 1327 |             QVERIFY(info->contains(calibration.descriptors().at(1))); | 
| 1328 |         } | 
| 1329 |  | 
| 1330 |         if (period.isValid()) { | 
| 1331 |             // Period Calibration | 
| 1332 |             temp = QString("f000aa44-0451-4000-b000-000000000000" ); | 
| 1333 |             QCOMPARE(period.uuid(), QBluetoothUuid(temp)); | 
| 1334 |             // value different in other revisions and test may fail | 
| 1335 |             HANDLE_COMPARE(period.handle(), QLowEnergyHandle(0x58)); | 
| 1336 |             QCOMPARE(period.properties(), | 
| 1337 |                      (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1338 |             QCOMPARE(period.value(), QByteArray::fromHex("64" )); | 
| 1339 |             QVERIFY(period.isValid()); | 
| 1340 |             QVERIFY(info->contains(period)); | 
| 1341 |  | 
| 1342 |             QCOMPARE(period.descriptors().count(), 1); | 
| 1343 |             //descriptor checks | 
| 1344 |             QCOMPARE(period.descriptors().at(0).isValid(), true); | 
| 1345 |             // value different in other revisions and test may fail | 
| 1346 |             HANDLE_COMPARE(period.descriptors().at(0).handle(), QLowEnergyHandle(0x59)); | 
| 1347 |             QCOMPARE(period.descriptors().at(0).uuid(), | 
| 1348 |                     QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1349 |             QCOMPARE(period.descriptors().at(0).type(), | 
| 1350 |                     QBluetoothUuid::CharacteristicUserDescription); | 
| 1351 |             QCOMPARE(period.descriptors().at(0).value(), | 
| 1352 |                      QByteArray::fromHex("4261726f6d2e20506572696f64" )); | 
| 1353 |             QVERIFY(info->contains(period.descriptors().at(0))); | 
| 1354 |         } | 
| 1355 |     } else if (info->serviceUuid() == | 
| 1356 |                QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000" ))) { | 
| 1357 |         qDebug() << "Verifying Gyroscope" ; | 
| 1358 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1359 |         QVERIFY(chars.count() >= 2); | 
| 1360 |  | 
| 1361 |         // Gyroscope Data | 
| 1362 |         QString temp("f000aa51-0451-4000-b000-000000000000" ); | 
| 1363 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1364 |         // value different in other revisions and test may fail | 
| 1365 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x60)); | 
| 1366 |         QCOMPARE(chars[0].properties(), | 
| 1367 |                 (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify)); | 
| 1368 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000" )); | 
| 1369 |         QVERIFY(chars[0].isValid()); | 
| 1370 |         QVERIFY(info->contains(chars[0])); | 
| 1371 |  | 
| 1372 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1373 |         //descriptor checks | 
| 1374 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1375 |         // value different in other revisions and test may fail | 
| 1376 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x61)); | 
| 1377 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1378 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1379 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1380 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1381 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1382 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1383 |  | 
| 1384 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1385 |         // value different in other revisions and test may fail | 
| 1386 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x62)); | 
| 1387 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1388 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1389 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1390 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1391 |         // value different in other revisions and test may fail | 
| 1392 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1393 |                 QByteArray::fromHex("4779726f2044617461" )); | 
| 1394 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1395 |  | 
| 1396 |         // Gyroscope Config | 
| 1397 |         temp = QString("f000aa52-0451-4000-b000-000000000000" ); | 
| 1398 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1399 |         // value different in other revisions and test may fail | 
| 1400 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x64)); | 
| 1401 |         QCOMPARE(chars[1].properties(), | 
| 1402 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1403 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1404 |         QVERIFY(chars[1].isValid()); | 
| 1405 |         QVERIFY(info->contains(chars[1])); | 
| 1406 |  | 
| 1407 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1408 |         //descriptor checks | 
| 1409 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1410 |         // value different in other revisions and test may fail | 
| 1411 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x65)); | 
| 1412 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1413 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1414 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1415 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1416 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1417 |                 QByteArray::fromHex("4779726f20436f6e662e" )); | 
| 1418 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1419 |  | 
| 1420 |         // Gyroscope Period | 
| 1421 |         temp = QString("f000aa53-0451-4000-b000-000000000000" ); | 
| 1422 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 1423 |         // value different in other revisions and test may fail | 
| 1424 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x67)); | 
| 1425 |         QCOMPARE(chars[2].properties(), | 
| 1426 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1427 |         QCOMPARE(chars[2].value(), QByteArray::fromHex("64" )); | 
| 1428 |         QVERIFY(chars[2].isValid()); | 
| 1429 |         QVERIFY(info->contains(chars[2])); | 
| 1430 |  | 
| 1431 |         QCOMPARE(chars[2].descriptors().count(), 1); | 
| 1432 |         //descriptor checks | 
| 1433 |         QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 1434 |         HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x68)); | 
| 1435 |         QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 1436 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1437 |         QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 1438 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1439 |         QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 1440 |                 QByteArray::fromHex("4779726f20506572696f64" )); | 
| 1441 |         QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 1442 |     } else if (info->serviceUuid() == | 
| 1443 |                QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000" ))) { | 
| 1444 |         qDebug() << "Verifying Test Service" ; | 
| 1445 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1446 |         QCOMPARE(chars.count(), 2); | 
| 1447 |  | 
| 1448 |         // Test Data | 
| 1449 |         QString temp("f000aa61-0451-4000-b000-000000000000" ); | 
| 1450 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1451 |         // value different in other revisions and test may fail | 
| 1452 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x70)); | 
| 1453 |         QCOMPARE(chars[0].properties(), | 
| 1454 |                 (QLowEnergyCharacteristic::Read)); | 
| 1455 |         QCOMPARE(chars[0].value(), QByteArray::fromHex("3f00" )); | 
| 1456 |         QVERIFY(chars[0].isValid()); | 
| 1457 |         QVERIFY(info->contains(chars[0])); | 
| 1458 |  | 
| 1459 |         QCOMPARE(chars[0].descriptors().count(), 1); | 
| 1460 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1461 |         // value different in other revisions and test may fail | 
| 1462 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x71)); | 
| 1463 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1464 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1465 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1466 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1467 |         QCOMPARE(chars[0].descriptors().at(0).value(), | 
| 1468 |                 QByteArray::fromHex("546573742044617461" )); | 
| 1469 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1470 |  | 
| 1471 |         // Test Config | 
| 1472 |         temp = QString("f000aa62-0451-4000-b000-000000000000" ); | 
| 1473 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1474 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x73)); | 
| 1475 |         QCOMPARE(chars[1].properties(), | 
| 1476 |                  (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write)); | 
| 1477 |         QCOMPARE(chars[1].value(), QByteArray::fromHex("00" )); | 
| 1478 |         QVERIFY(chars[1].isValid()); | 
| 1479 |         QVERIFY(info->contains(chars[1])); | 
| 1480 |  | 
| 1481 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1482 |         //descriptor checks | 
| 1483 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1484 |         // value different in other revisions and test may fail | 
| 1485 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x74)); | 
| 1486 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1487 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1488 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1489 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1490 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1491 |                 QByteArray::fromHex("5465737420436f6e666967" )); | 
| 1492 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1493 |     } else if (info->serviceUuid() == | 
| 1494 |                QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000" ))) { | 
| 1495 |         qDebug() << "Connection Control Service" ; | 
| 1496 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1497 |         QCOMPARE(chars.count(), 3); | 
| 1498 |  | 
| 1499 |         //first characteristic | 
| 1500 |         QString temp("f000ccc1-0451-4000-b000-000000000000" ); | 
| 1501 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1502 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x77)); | 
| 1503 |         QCOMPARE(chars[0].properties(), | 
| 1504 |                 (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Read)); | 
| 1505 |         // the connection control parameter change from platform to platform | 
| 1506 |         // better not test them here | 
| 1507 |         //QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000")); | 
| 1508 |         QVERIFY(chars[0].isValid()); | 
| 1509 |         QVERIFY(info->contains(chars[0])); | 
| 1510 |  | 
| 1511 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1512 |         //descriptor checks | 
| 1513 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1514 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x78)); | 
| 1515 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1516 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1517 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1518 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1519 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1520 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1521 |  | 
| 1522 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1523 |         // value different in other revisions and test may fail | 
| 1524 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x79)); | 
| 1525 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1526 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1527 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1528 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1529 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1530 |                 QByteArray::fromHex("436f6e6e2e20506172616d73" )); | 
| 1531 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1532 |  | 
| 1533 |         //second characteristic | 
| 1534 |         temp = QString("f000ccc2-0451-4000-b000-000000000000" ); | 
| 1535 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1536 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x7b)); | 
| 1537 |         QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Write); | 
| 1538 |         QCOMPARE(chars[1].value(), QByteArray()); | 
| 1539 |         QVERIFY(chars[1].isValid()); | 
| 1540 |         QVERIFY(info->contains(chars[1])); | 
| 1541 |  | 
| 1542 |         QCOMPARE(chars[1].descriptors().count(), 1); | 
| 1543 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1544 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x7c)); | 
| 1545 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1546 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1547 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1548 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1549 |         QCOMPARE(chars[1].descriptors().at(0).value(), | 
| 1550 |                 QByteArray::fromHex("436f6e6e2e20506172616d7320526571" )); | 
| 1551 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1552 |  | 
| 1553 |         //third characteristic | 
| 1554 |         temp = QString("f000ccc3-0451-4000-b000-000000000000" ); | 
| 1555 |         QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp)); | 
| 1556 |         HANDLE_COMPARE(chars[2].handle(), QLowEnergyHandle(0x7e)); | 
| 1557 |         QCOMPARE(chars[2].properties(), QLowEnergyCharacteristic::Write); | 
| 1558 |         QCOMPARE(chars[2].value(), QByteArray()); | 
| 1559 |         QVERIFY(chars[2].isValid()); | 
| 1560 |         QVERIFY(info->contains(chars[2])); | 
| 1561 |  | 
| 1562 |         QCOMPARE(chars[2].descriptors().count(), 1); | 
| 1563 |         QCOMPARE(chars[2].descriptors().at(0).isValid(), true); | 
| 1564 |         HANDLE_COMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x7f)); | 
| 1565 |         QCOMPARE(chars[2].descriptors().at(0).uuid(), | 
| 1566 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1567 |         QCOMPARE(chars[2].descriptors().at(0).type(), | 
| 1568 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1569 |         QCOMPARE(chars[2].descriptors().at(0).value(), | 
| 1570 |                 QByteArray::fromHex("446973636f6e6e65637420526571" )); | 
| 1571 |         QVERIFY(info->contains(chars[2].descriptors().at(0))); | 
| 1572 |     } else if (info->serviceUuid() == | 
| 1573 |                QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000" ))) { | 
| 1574 |         qDebug() << "Verifying OID Service" ; | 
| 1575 |         QList<QLowEnergyCharacteristic> chars = info->characteristics(); | 
| 1576 |         QCOMPARE(chars.count(), 2); | 
| 1577 |  | 
| 1578 |         // first characteristic | 
| 1579 |         QString temp("f000ffc1-0451-4000-b000-000000000000" ); | 
| 1580 |         QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp)); | 
| 1581 |         // value different in other revisions and test may fail | 
| 1582 |         HANDLE_COMPARE(chars[0].handle(), QLowEnergyHandle(0x82)); | 
| 1583 |         QCOMPARE(chars[0].properties(), | 
| 1584 |                 (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse)); | 
| 1585 |         QCOMPARE(chars[0].value(), QByteArray()); | 
| 1586 |         QVERIFY(chars[0].isValid()); | 
| 1587 |         QVERIFY(info->contains(chars[0])); | 
| 1588 |  | 
| 1589 |         QCOMPARE(chars[0].descriptors().count(), 2); | 
| 1590 |         //descriptor checks | 
| 1591 |         QCOMPARE(chars[0].descriptors().at(0).isValid(), true); | 
| 1592 |         HANDLE_COMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x83)); | 
| 1593 |         QCOMPARE(chars[0].descriptors().at(0).uuid(), | 
| 1594 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1595 |         QCOMPARE(chars[0].descriptors().at(0).type(), | 
| 1596 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1597 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1598 |         QVERIFY(info->contains(chars[0].descriptors().at(0))); | 
| 1599 |  | 
| 1600 |         QCOMPARE(chars[0].descriptors().at(1).isValid(), true); | 
| 1601 |         // value different in other revisions and test may fail | 
| 1602 |         HANDLE_COMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x84)); | 
| 1603 |         QCOMPARE(chars[0].descriptors().at(1).uuid(), | 
| 1604 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1605 |         QCOMPARE(chars[0].descriptors().at(1).type(), | 
| 1606 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1607 |         QCOMPARE(chars[0].descriptors().at(1).value(), | 
| 1608 |                 QByteArray::fromHex("496d67204964656e74696679" )); | 
| 1609 |         QVERIFY(info->contains(chars[0].descriptors().at(1))); | 
| 1610 |  | 
| 1611 |         // second characteristic | 
| 1612 |         temp = QString("f000ffc2-0451-4000-b000-000000000000" ); | 
| 1613 |         QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp)); | 
| 1614 |         // value different in other revisions and test may fail | 
| 1615 |         HANDLE_COMPARE(chars[1].handle(), QLowEnergyHandle(0x86)); | 
| 1616 |         QCOMPARE(chars[1].properties(), | 
| 1617 |                  (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse)); | 
| 1618 |         QCOMPARE(chars[1].value(), QByteArray()); | 
| 1619 |         QVERIFY(chars[1].isValid()); | 
| 1620 |         QVERIFY(info->contains(chars[1])); | 
| 1621 |  | 
| 1622 |         QCOMPARE(chars[1].descriptors().count(), 2); | 
| 1623 |         //descriptor checks | 
| 1624 |         QCOMPARE(chars[1].descriptors().at(0).isValid(), true); | 
| 1625 |         // value different in other revisions and test may fail | 
| 1626 |         HANDLE_COMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x87)); | 
| 1627 |         QCOMPARE(chars[1].descriptors().at(0).uuid(), | 
| 1628 |                 QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1629 |         QCOMPARE(chars[1].descriptors().at(0).type(), | 
| 1630 |                 QBluetoothUuid::ClientCharacteristicConfiguration); | 
| 1631 |         QVERIFY(verifyClientCharacteristicValue(chars[0].descriptors().at(0).value())); | 
| 1632 |         QVERIFY(info->contains(chars[1].descriptors().at(0))); | 
| 1633 |  | 
| 1634 |         QCOMPARE(chars[1].descriptors().at(1).isValid(), true); | 
| 1635 |         // value different in other revisions and test may fail | 
| 1636 |         HANDLE_COMPARE(chars[1].descriptors().at(1).handle(), QLowEnergyHandle(0x88)); | 
| 1637 |         QCOMPARE(chars[1].descriptors().at(1).uuid(), | 
| 1638 |                 QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription)); | 
| 1639 |         QCOMPARE(chars[1].descriptors().at(1).type(), | 
| 1640 |                 QBluetoothUuid::CharacteristicUserDescription); | 
| 1641 |         QCOMPARE(chars[1].descriptors().at(1).value(), | 
| 1642 |                 QByteArray::fromHex("496d6720426c6f636b" )); | 
| 1643 |         QVERIFY(info->contains(chars[1].descriptors().at(1))); | 
| 1644 |     } else { | 
| 1645 |         QFAIL(QString("Service not found"  + info->serviceUuid().toString()).toUtf8().constData()); | 
| 1646 |     } | 
| 1647 | } | 
| 1648 |  | 
| 1649 | /* | 
| 1650 |  * CCC descriptors can have one of three distinct values: | 
| 1651 |  *      0000 - notifications and indications are off | 
| 1652 |  *      0100 - notifications enabled | 
| 1653 |  *      0200 - indications enabled | 
| 1654 |  * | 
| 1655 |  * The exact value is managed by the BTLE peripheral for each central | 
| 1656 |  * that connects. The value of this field is session based and may be retained | 
| 1657 |  * during multiple connections. | 
| 1658 |  * | 
| 1659 |  * This function returns \c true if the CCC value has a valid range. | 
| 1660 |  * */ | 
| 1661 | bool tst_QLowEnergyController::verifyClientCharacteristicValue(const QByteArray &value) | 
| 1662 | { | 
| 1663 |     if (value == QByteArray::fromHex(hexEncoded: "0000" ) | 
| 1664 |             || value == QByteArray::fromHex(hexEncoded: "0100" ) | 
| 1665 |             || value == QByteArray::fromHex(hexEncoded: "0200" ) ) | 
| 1666 |         return true; | 
| 1667 |  | 
| 1668 |     qWarning() << "Found incorrect CC value"  << value.toHex(); | 
| 1669 |     return false; | 
| 1670 | } | 
| 1671 |  | 
| 1672 | void tst_QLowEnergyController::tst_defaultBehavior() | 
| 1673 | { | 
| 1674 |     QList<QBluetoothAddress> foundAddresses; | 
| 1675 |     const QList<QBluetoothHostInfo> infos = QBluetoothLocalDevice::allDevices(); | 
| 1676 |     for (const QBluetoothHostInfo &info : infos) | 
| 1677 |         foundAddresses.append(t: info.address()); | 
| 1678 |     const QBluetoothAddress randomAddress("11:22:33:44:55:66" ); | 
| 1679 |  | 
| 1680 |     // Test automatic detection of local adapter | 
| 1681 |     QLowEnergyController controlDefaultAdapter(randomAddress); | 
| 1682 |     QCOMPARE(controlDefaultAdapter.remoteAddress(), randomAddress); | 
| 1683 |     QCOMPARE(controlDefaultAdapter.state(), QLowEnergyController::UnconnectedState); | 
| 1684 |     if (foundAddresses.isEmpty()) { | 
| 1685 |         QVERIFY(controlDefaultAdapter.localAddress().isNull()); | 
| 1686 |     } else { | 
| 1687 |         QCOMPARE(controlDefaultAdapter.error(), QLowEnergyController::NoError); | 
| 1688 |         QVERIFY(controlDefaultAdapter.errorString().isEmpty()); | 
| 1689 |         QVERIFY(foundAddresses.contains(controlDefaultAdapter.localAddress())); | 
| 1690 |  | 
| 1691 |         // unrelated uuids don't return valid service object | 
| 1692 |         // invalid service uuid | 
| 1693 |         QVERIFY(!controlDefaultAdapter.createServiceObject( | 
| 1694 |                     QBluetoothUuid())); | 
| 1695 |         // some random uuid | 
| 1696 |         QVERIFY(!controlDefaultAdapter.createServiceObject( | 
| 1697 |                     QBluetoothUuid(QBluetoothUuid::DeviceName))); | 
| 1698 |     } | 
| 1699 |  | 
| 1700 |     QCOMPARE(controlDefaultAdapter.services().count(), 0); | 
| 1701 |  | 
| 1702 |     // Test explicit local adapter | 
| 1703 |     if (!foundAddresses.isEmpty()) { | 
| 1704 |         QLowEnergyController controlExplicitAdapter(randomAddress, | 
| 1705 |                                                        foundAddresses[0]); | 
| 1706 |         QCOMPARE(controlExplicitAdapter.remoteAddress(), randomAddress); | 
| 1707 |         QCOMPARE(controlExplicitAdapter.localAddress(), foundAddresses[0]); | 
| 1708 |         QCOMPARE(controlExplicitAdapter.state(), | 
| 1709 |                  QLowEnergyController::UnconnectedState); | 
| 1710 |         QCOMPARE(controlExplicitAdapter.services().count(), 0); | 
| 1711 |  | 
| 1712 |         // unrelated uuids don't return valid service object | 
| 1713 |         // invalid service uuid | 
| 1714 |         QVERIFY(!controlExplicitAdapter.createServiceObject( | 
| 1715 |                     QBluetoothUuid())); | 
| 1716 |         // some random uuid | 
| 1717 |         QVERIFY(!controlExplicitAdapter.createServiceObject( | 
| 1718 |                     QBluetoothUuid(QBluetoothUuid::DeviceName))); | 
| 1719 |     } | 
| 1720 | } | 
| 1721 |  | 
| 1722 | void tst_QLowEnergyController::tst_writeCharacteristic() | 
| 1723 | { | 
| 1724 | #if !defined(Q_OS_MACOS) && !QT_CONFIG(winrt_bt) | 
| 1725 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 1726 |     if (localAdapters.isEmpty()) | 
| 1727 |         QSKIP("No local Bluetooth device found. Skipping test." ); | 
| 1728 | #endif | 
| 1729 |  | 
| 1730 |     if (!remoteDeviceInfo.isValid()) | 
| 1731 |         QSKIP("No remote BTLE device found. Skipping test." ); | 
| 1732 |     QLowEnergyController control(remoteDeviceInfo); | 
| 1733 |  | 
| 1734 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 1735 |  | 
| 1736 |     control.connectToDevice(); | 
| 1737 |     { | 
| 1738 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 1739 |               30000); | 
| 1740 |     } | 
| 1741 |  | 
| 1742 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 1743 |             || control.error() != QLowEnergyController::NoError) { | 
| 1744 |         // default BTLE backend forever hangs in ConnectingState | 
| 1745 |         QSKIP("Cannot connect to remote device" ); | 
| 1746 |     } | 
| 1747 |  | 
| 1748 |     QTRY_VERIFY_WITH_TIMEOUT(control.state() == QLowEnergyController::ConnectedState, 20000); | 
| 1749 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 1750 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 1751 |     control.discoverServices(); | 
| 1752 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 1753 |     QCOMPARE(stateSpy.count(), 2); | 
| 1754 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 1755 |              QLowEnergyController::DiscoveringState); | 
| 1756 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 1757 |              QLowEnergyController::DiscoveredState); | 
| 1758 |  | 
| 1759 |     const QBluetoothUuid testService(QString("f000aa60-0451-4000-b000-000000000000" )); | 
| 1760 |     QList<QBluetoothUuid> uuids = control.services(); | 
| 1761 |     QVERIFY(uuids.contains(testService)); | 
| 1762 |  | 
| 1763 |     QLowEnergyService *service = control.createServiceObject(service: testService, parent: this); | 
| 1764 |     QVERIFY(service); | 
| 1765 |     service->discoverDetails(); | 
| 1766 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 1767 |         service->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 1768 |  | 
| 1769 |     // test service described by | 
| 1770 |     // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide | 
| 1771 |     const QList<QLowEnergyCharacteristic> chars = service->characteristics(); | 
| 1772 |  | 
| 1773 |     QLowEnergyCharacteristic dataChar; | 
| 1774 |     QLowEnergyCharacteristic configChar; | 
| 1775 |     for (int i = 0; i < chars.count(); i++) { | 
| 1776 |         if (chars[i].uuid() == QBluetoothUuid(QString("f000aa61-0451-4000-b000-000000000000" ))) | 
| 1777 |             dataChar = chars[i]; | 
| 1778 |         else if (chars[i].uuid() == QBluetoothUuid(QString("f000aa62-0451-4000-b000-000000000000" ))) | 
| 1779 |             configChar = chars[i]; | 
| 1780 |     } | 
| 1781 |  | 
| 1782 |     QVERIFY(dataChar.isValid()); | 
| 1783 |     QVERIFY(!(dataChar.properties() & ~QLowEnergyCharacteristic::Read)); // only a read char | 
| 1784 |     QVERIFY(service->contains(dataChar)); | 
| 1785 |     QVERIFY(configChar.isValid()); | 
| 1786 |     QVERIFY(configChar.properties() & QLowEnergyCharacteristic::Write); | 
| 1787 |     QVERIFY(configChar.properties() & QLowEnergyCharacteristic::Read); | 
| 1788 |     QVERIFY(service->contains(configChar)); | 
| 1789 |  | 
| 1790 |     QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00" )); | 
| 1791 |     QVERIFY(configChar.value() == QByteArray::fromHex("00" ) | 
| 1792 |             || configChar.value() == QByteArray::fromHex("81" )); | 
| 1793 |  | 
| 1794 |     QSignalSpy writeSpy(service, | 
| 1795 |                         SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray))); | 
| 1796 |     QSignalSpy readSpy(service, | 
| 1797 |                        SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); | 
| 1798 |  | 
| 1799 |     // ******************************************* | 
| 1800 |     // test writing of characteristic | 
| 1801 |     // enable Blinking LED if not already enabled | 
| 1802 |     if (configChar.value() != QByteArray::fromHex(hexEncoded: "81" )) { | 
| 1803 |         service->writeCharacteristic(characteristic: configChar, newValue: QByteArray::fromHex(hexEncoded: "81" )); //0x81 blink LED D1 | 
| 1804 |         QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000); | 
| 1805 |         QCOMPARE(configChar.value(), QByteArray::fromHex("81" )); | 
| 1806 |         QList<QVariant> firstSignalData = writeSpy.first(); | 
| 1807 |         QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>(); | 
| 1808 |         QByteArray signalValue = firstSignalData[1].toByteArray(); | 
| 1809 |  | 
| 1810 |         QCOMPARE(signalValue, QByteArray::fromHex("81" )); | 
| 1811 |         QVERIFY(signalChar == configChar); | 
| 1812 |  | 
| 1813 |         writeSpy.clear(); | 
| 1814 |  | 
| 1815 |     } | 
| 1816 |  | 
| 1817 |     // test direct read of configChar | 
| 1818 |     QVERIFY(readSpy.isEmpty()); | 
| 1819 |     service->readCharacteristic(characteristic: configChar); | 
| 1820 |     QTRY_VERIFY_WITH_TIMEOUT(!readSpy.isEmpty(), 10000); | 
| 1821 |     QCOMPARE(configChar.value(), QByteArray::fromHex("81" )); | 
| 1822 |     QCOMPARE(readSpy.count(), 1); //expect one characteristicRead signal | 
| 1823 |     { | 
| 1824 |         //verify the readCharacteristic() | 
| 1825 |         QList<QVariant> firstSignalData = readSpy.first(); | 
| 1826 |         QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>(); | 
| 1827 |         QByteArray signalValue = firstSignalData[1].toByteArray(); | 
| 1828 |  | 
| 1829 |         QCOMPARE(signalValue, QByteArray::fromHex("81" )); | 
| 1830 |         QCOMPARE(signalValue, configChar.value()); | 
| 1831 |         QVERIFY(signalChar == configChar); | 
| 1832 |     } | 
| 1833 |  | 
| 1834 |     service->writeCharacteristic(characteristic: configChar, newValue: QByteArray::fromHex(hexEncoded: "00" )); //turn LED D1 off | 
| 1835 |     QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000); | 
| 1836 |     QCOMPARE(configChar.value(), QByteArray::fromHex("00" )); | 
| 1837 |     QList<QVariant> firstSignalData = writeSpy.first(); | 
| 1838 |     QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>(); | 
| 1839 |     QByteArray signalValue = firstSignalData[1].toByteArray(); | 
| 1840 |  | 
| 1841 |     QCOMPARE(signalValue, QByteArray::fromHex("00" )); | 
| 1842 |     QVERIFY(signalChar == configChar); | 
| 1843 |  | 
| 1844 |     // ******************************************* | 
| 1845 |     // write wrong value -> error response required | 
| 1846 |     QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 1847 |     writeSpy.clear(); | 
| 1848 |     QCOMPARE(errorSpy.count(), 0); | 
| 1849 |     QCOMPARE(writeSpy.count(), 0); | 
| 1850 |  | 
| 1851 |     // write 2 byte value to 1 byte characteristic | 
| 1852 |     service->writeCharacteristic(characteristic: configChar, newValue: QByteArray::fromHex(hexEncoded: "1111" )); | 
| 1853 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000); | 
| 1854 |     QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 1855 |              QLowEnergyService::CharacteristicWriteError); | 
| 1856 |     QCOMPARE(service->error(), QLowEnergyService::CharacteristicWriteError); | 
| 1857 |     QCOMPARE(writeSpy.count(), 0); | 
| 1858 |     QCOMPARE(configChar.value(), QByteArray::fromHex("00" )); | 
| 1859 |  | 
| 1860 |     // ******************************************* | 
| 1861 |     // write to read-only characteristic -> error | 
| 1862 |     errorSpy.clear(); | 
| 1863 |     QCOMPARE(errorSpy.count(), 0); | 
| 1864 |     service->writeCharacteristic(characteristic: dataChar, newValue: QByteArray::fromHex(hexEncoded: "ffff" )); | 
| 1865 |  | 
| 1866 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000); | 
| 1867 |     QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 1868 |              QLowEnergyService::CharacteristicWriteError); | 
| 1869 |     QCOMPARE(service->error(), QLowEnergyService::CharacteristicWriteError); | 
| 1870 |     QCOMPARE(writeSpy.count(), 0); | 
| 1871 |     QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00" )); | 
| 1872 |  | 
| 1873 |  | 
| 1874 |     control.disconnectFromDevice(); | 
| 1875 |  | 
| 1876 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 1877 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 1878 |     // ******************************************* | 
| 1879 |     // write value while disconnected -> error | 
| 1880 |     errorSpy.clear(); | 
| 1881 |     QCOMPARE(errorSpy.count(), 0); | 
| 1882 |     service->writeCharacteristic(characteristic: configChar, newValue: QByteArray::fromHex(hexEncoded: "ffff" )); | 
| 1883 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000); | 
| 1884 |     QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 1885 |              QLowEnergyService::OperationError); | 
| 1886 |     QCOMPARE(service->error(), QLowEnergyService::OperationError); | 
| 1887 |     QCOMPARE(writeSpy.count(), 0); | 
| 1888 |     QCOMPARE(configChar.value(), QByteArray::fromHex("00" )); | 
| 1889 |  | 
| 1890 |     // invalid characteristics still belong to their respective service | 
| 1891 |     QVERIFY(service->contains(configChar)); | 
| 1892 |     QVERIFY(service->contains(dataChar)); | 
| 1893 |  | 
| 1894 |     QVERIFY(!service->contains(QLowEnergyCharacteristic())); | 
| 1895 |  | 
| 1896 |     delete service; | 
| 1897 | } | 
| 1898 |  | 
| 1899 | void tst_QLowEnergyController::tst_readWriteDescriptor() | 
| 1900 | { | 
| 1901 | #if !defined(Q_OS_MACOS) && !QT_CONFIG(winrt_bt) | 
| 1902 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 1903 |     if (localAdapters.isEmpty()) | 
| 1904 |         QSKIP("No local Bluetooth device found. Skipping test." ); | 
| 1905 | #endif | 
| 1906 |  | 
| 1907 |     if (!remoteDeviceInfo.isValid()) | 
| 1908 |         QSKIP("No remote BTLE device found. Skipping test." ); | 
| 1909 |     QLowEnergyController control(remoteDeviceInfo); | 
| 1910 |  | 
| 1911 |     // quick setup - more elaborate test is done by connect() | 
| 1912 |     control.connectToDevice(); | 
| 1913 |     { | 
| 1914 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 1915 |               30000); | 
| 1916 |     } | 
| 1917 |  | 
| 1918 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 1919 |             || control.error() != QLowEnergyController::NoError) { | 
| 1920 |         // default BTLE backend forever hangs in ConnectingState | 
| 1921 |         QSKIP("Cannot connect to remote device" ); | 
| 1922 |     } | 
| 1923 |  | 
| 1924 |     QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 1925 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 1926 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 1927 |     control.discoverServices(); | 
| 1928 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 1929 |     QCOMPARE(stateSpy.count(), 2); | 
| 1930 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 1931 |              QLowEnergyController::DiscoveringState); | 
| 1932 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 1933 |              QLowEnergyController::DiscoveredState); | 
| 1934 |  | 
| 1935 |     const QBluetoothUuid testService(QString("f000aa00-0451-4000-b000-000000000000" )); | 
| 1936 |     QList<QBluetoothUuid> uuids = control.services(); | 
| 1937 |     QVERIFY(uuids.contains(testService)); | 
| 1938 |  | 
| 1939 |     QLowEnergyService *service = control.createServiceObject(service: testService, parent: this); | 
| 1940 |     QVERIFY(service); | 
| 1941 |     service->discoverDetails(); | 
| 1942 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 1943 |         service->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 1944 |  | 
| 1945 |     // Temperature service described by | 
| 1946 |     // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide | 
| 1947 |  | 
| 1948 |     // 1. Find temperature data characteristic | 
| 1949 |     const QLowEnergyCharacteristic tempData = service->characteristic( | 
| 1950 |                 uuid: QBluetoothUuid(QStringLiteral("f000aa01-0451-4000-b000-000000000000" ))); | 
| 1951 |     const QLowEnergyCharacteristic tempConfig = service->characteristic( | 
| 1952 |                 uuid: QBluetoothUuid(QStringLiteral("f000aa02-0451-4000-b000-000000000000" ))); | 
| 1953 |  | 
| 1954 |     if (!tempData.isValid()) { | 
| 1955 |         delete service; | 
| 1956 |         control.disconnectFromDevice(); | 
| 1957 |         QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 1958 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 1959 |         QSKIP("Cannot find temperature data characteristic of TI Sensor" ); | 
| 1960 |     } | 
| 1961 |  | 
| 1962 |     // 2. Find temperature data notification descriptor | 
| 1963 |     const QLowEnergyDescriptor notification = tempData.descriptor( | 
| 1964 |                 uuid: QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 1965 |  | 
| 1966 |     if (!notification.isValid()) { | 
| 1967 |         delete service; | 
| 1968 |         control.disconnectFromDevice(); | 
| 1969 |         QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 1970 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 1971 |         QSKIP("Cannot find temperature data notification of TI Sensor" ); | 
| 1972 |     } | 
| 1973 |  | 
| 1974 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 1975 |     QVERIFY(service->contains(notification)); | 
| 1976 |     QVERIFY(service->contains(tempData)); | 
| 1977 |     if (tempConfig.isValid()) { | 
| 1978 |         QVERIFY(service->contains(tempConfig)); | 
| 1979 |         QCOMPARE(tempConfig.value(), QByteArray::fromHex("00" )); | 
| 1980 |     } | 
| 1981 |  | 
| 1982 |     // 3. Test reading and writing to descriptor -> activate notifications | 
| 1983 |     QSignalSpy descWrittenSpy(service, | 
| 1984 |                         SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray))); | 
| 1985 |     QSignalSpy descReadSpy(service, | 
| 1986 |                         SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray))); | 
| 1987 |     QSignalSpy charWrittenSpy(service, | 
| 1988 |                         SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray))); | 
| 1989 |     QSignalSpy charChangedSpy(service, | 
| 1990 |                         SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray))); | 
| 1991 |  | 
| 1992 |     QLowEnergyDescriptor signalDesc; | 
| 1993 |     QList<QVariant> firstSignalData; | 
| 1994 |     QByteArray signalValue; | 
| 1995 |     if (notification.value() != QByteArray::fromHex(hexEncoded: "0100" )) { | 
| 1996 |         // enable notifications if not already done | 
| 1997 |         service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 1998 |  | 
| 1999 |         QTRY_VERIFY_WITH_TIMEOUT(!descWrittenSpy.isEmpty(), 3000); | 
| 2000 |         QCOMPARE(notification.value(), QByteArray::fromHex("0100" )); | 
| 2001 |         firstSignalData = descWrittenSpy.first(); | 
| 2002 |         signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2003 |         signalValue = firstSignalData[1].toByteArray(); | 
| 2004 |         QCOMPARE(signalValue, QByteArray::fromHex("0100" )); | 
| 2005 |         QVERIFY(notification == signalDesc); | 
| 2006 |         descWrittenSpy.clear(); | 
| 2007 |     } | 
| 2008 |  | 
| 2009 |     // 4. Test reception of notifications | 
| 2010 |     // activate the temperature sensor if available | 
| 2011 |     if (tempConfig.isValid()) { | 
| 2012 |         service->writeCharacteristic(characteristic: tempConfig, newValue: QByteArray::fromHex(hexEncoded: "01" )); | 
| 2013 |  | 
| 2014 |         // first signal is confirmation of tempConfig write | 
| 2015 |         // subsequent signals are temp data updates | 
| 2016 |         QTRY_VERIFY_WITH_TIMEOUT(charWrittenSpy.count() == 1, 10000); | 
| 2017 |         QTRY_VERIFY_WITH_TIMEOUT(charChangedSpy.count() >= 4, 10000); | 
| 2018 |  | 
| 2019 |         QCOMPARE(charWrittenSpy.count(), 1); | 
| 2020 |         QLowEnergyCharacteristic writtenChar = charWrittenSpy[0].at(i: 0).value<QLowEnergyCharacteristic>(); | 
| 2021 |         QByteArray writtenValue = charWrittenSpy[0].at(i: 1).toByteArray(); | 
| 2022 |         QCOMPARE(tempConfig, writtenChar); | 
| 2023 |         QCOMPARE(tempConfig.value(), writtenValue); | 
| 2024 |         QCOMPARE(writtenChar.value(), writtenValue); | 
| 2025 |         QCOMPARE(writtenValue, QByteArray::fromHex("01" )); | 
| 2026 |  | 
| 2027 |         QList<QVariant> entry; | 
| 2028 |         for (int i = 0; i < charChangedSpy.count(); i++) { | 
| 2029 |             entry = charChangedSpy[i]; | 
| 2030 |             const QLowEnergyCharacteristic ch = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2031 |  | 
| 2032 |             QCOMPARE(tempData, ch); | 
| 2033 |  | 
| 2034 |             //check last characteristic changed value matches the characteristics current value | 
| 2035 |             if (i == (charChangedSpy.count() - 1)) { | 
| 2036 |                 writtenValue = entry[1].toByteArray(); | 
| 2037 |                 QCOMPARE(ch.value(), writtenValue); | 
| 2038 |                 QCOMPARE(tempData.value(), writtenValue); | 
| 2039 |             } | 
| 2040 |         } | 
| 2041 |  | 
| 2042 |         service->writeCharacteristic(characteristic: tempConfig, newValue: QByteArray::fromHex(hexEncoded: "00" )); | 
| 2043 |     } | 
| 2044 |  | 
| 2045 |     // 5. Test reading and writing of/to descriptor -> deactivate notifications | 
| 2046 |  | 
| 2047 |     service->readDescriptor(descriptor: notification); | 
| 2048 |     QTRY_VERIFY_WITH_TIMEOUT(!descReadSpy.isEmpty(), 3000); | 
| 2049 |     QCOMPARE(descReadSpy.count(), 1); | 
| 2050 |     firstSignalData = descReadSpy.first(); | 
| 2051 |     signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2052 |     signalValue = firstSignalData[1].toByteArray(); | 
| 2053 |     QCOMPARE(signalValue, notification.value()); | 
| 2054 |     QCOMPARE(notification.value(), QByteArray::fromHex("0100" )); | 
| 2055 |     descReadSpy.clear(); | 
| 2056 |  | 
| 2057 |  | 
| 2058 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0000" )); | 
| 2059 |     // verify | 
| 2060 |     QTRY_VERIFY_WITH_TIMEOUT(!descWrittenSpy.isEmpty(), 3000); | 
| 2061 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 2062 |     firstSignalData = descWrittenSpy.first(); | 
| 2063 |     signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2064 |     signalValue = firstSignalData[1].toByteArray(); | 
| 2065 |     QCOMPARE(signalValue, QByteArray::fromHex("0000" )); | 
| 2066 |     QVERIFY(notification == signalDesc); | 
| 2067 |     descWrittenSpy.clear(); | 
| 2068 |  | 
| 2069 |     // The series of wait calls below is required because toggling CCC via the notifying | 
| 2070 |     // property consistently crashes BlueZ 5.47. BlueZ 5.48 does not crash but | 
| 2071 |     // an error is thrown. For details see QTBUG-65729 | 
| 2072 |     if (isBluezDbusLE) | 
| 2073 |         QTest::qWait(ms: 1000); | 
| 2074 |  | 
| 2075 |     // test concurrent writeRequests | 
| 2076 |     // they need to be queued up | 
| 2077 |     service->writeDescriptor(descriptor: notification,newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 2078 |     if (isBluezDbusLE) | 
| 2079 |         QTest::qWait(ms: 1000); | 
| 2080 |  | 
| 2081 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0000" )); | 
| 2082 |     if (isBluezDbusLE) | 
| 2083 |         QTest::qWait(ms: 1000); | 
| 2084 |  | 
| 2085 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 2086 |     if (isBluezDbusLE) | 
| 2087 |         QTest::qWait(ms: 1000); | 
| 2088 |  | 
| 2089 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0000" )); | 
| 2090 |     if (isBluezDbusLE) | 
| 2091 |         QTest::qWait(ms: 1000); | 
| 2092 |  | 
| 2093 |     QTRY_VERIFY_WITH_TIMEOUT(descWrittenSpy.count() == 4, 10000); | 
| 2094 |  | 
| 2095 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 2096 |     for (int i = 0; i < descWrittenSpy.count(); i++) { | 
| 2097 |         firstSignalData = descWrittenSpy.at(i); | 
| 2098 |         signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2099 |         signalValue = firstSignalData[1].toByteArray(); | 
| 2100 |         if (i & 0x1) // odd | 
| 2101 |             QCOMPARE(signalValue, QByteArray::fromHex("0000" )); | 
| 2102 |         else // even | 
| 2103 |             QCOMPARE(signalValue, QByteArray::fromHex("0100" )); | 
| 2104 |         QVERIFY(notification == signalDesc); | 
| 2105 |  | 
| 2106 |     } | 
| 2107 |  | 
| 2108 |     // 5. Test reading and writing of/to descriptor -> deactivate notifications | 
| 2109 |  | 
| 2110 |     service->readDescriptor(descriptor: notification); | 
| 2111 |     QTRY_VERIFY_WITH_TIMEOUT(!descReadSpy.isEmpty(), 3000); | 
| 2112 |     QCOMPARE(descReadSpy.count(), 1); | 
| 2113 |     firstSignalData = descReadSpy.first(); | 
| 2114 |     signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2115 |     signalValue = firstSignalData[1].toByteArray(); | 
| 2116 |     QCOMPARE(signalValue, notification.value()); | 
| 2117 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 2118 |     descReadSpy.clear(); | 
| 2119 |  | 
| 2120 |     descWrittenSpy.clear(); | 
| 2121 |  | 
| 2122 |     // ******************************************* | 
| 2123 |     // write wrong value -> error response required | 
| 2124 |     QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2125 |     descWrittenSpy.clear(); | 
| 2126 |     QCOMPARE(errorSpy.count(), 0); | 
| 2127 |     QCOMPARE(descWrittenSpy.count(), 0); | 
| 2128 |  | 
| 2129 |     // write 4 byte value to 2 byte characteristic | 
| 2130 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "11112222" )); | 
| 2131 | #ifdef Q_OS_MAC | 
| 2132 |     // On OS X/iOS we have a special method to set notify value, | 
| 2133 |     // it accepts only false/true and not | 
| 2134 |     // writing descriptors, there is only one way to find this error - | 
| 2135 |     // immediately intercept in LE controller and set the error. | 
| 2136 |     QVERIFY(!errorSpy.isEmpty()); | 
| 2137 | #else | 
| 2138 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 30000); | 
| 2139 | #endif | 
| 2140 |     QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2141 |              QLowEnergyService::DescriptorWriteError); | 
| 2142 |     QCOMPARE(service->error(), QLowEnergyService::DescriptorWriteError); | 
| 2143 |     QCOMPARE(descWrittenSpy.count(), 0); | 
| 2144 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 2145 |  | 
| 2146 |     control.disconnectFromDevice(); | 
| 2147 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 2148 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2149 |  | 
| 2150 |     // ******************************************* | 
| 2151 |     // write value while disconnected -> error | 
| 2152 |     errorSpy.clear(); | 
| 2153 |     service->writeDescriptor(descriptor: notification, newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 2154 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000); | 
| 2155 |     QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2156 |              QLowEnergyService::OperationError); | 
| 2157 |     QCOMPARE(service->error(), QLowEnergyService::OperationError); | 
| 2158 |     QCOMPARE(descWrittenSpy.count(), 0); | 
| 2159 |     QCOMPARE(notification.value(), QByteArray::fromHex("0000" )); | 
| 2160 |  | 
| 2161 |     delete service; | 
| 2162 | } | 
| 2163 |  | 
| 2164 | /* | 
| 2165 |  * By default this test is skipped. | 
| 2166 |  * | 
| 2167 |  * Following tests are performed: | 
| 2168 |  * - encrypted read and discovery | 
| 2169 |  * - readCharacteristic() of values longer than MTU | 
| 2170 |  * - readCharacteristic() if values equal to MTU | 
| 2171 |  * | 
| 2172 |  * This test is semi manual as the test device environment is very specific. | 
| 2173 |  * A programmable BTLE device is required. Currently, the test requires | 
| 2174 |  * the CSR Dev Kit using the hr_sensor example. | 
| 2175 |  * | 
| 2176 |   * The following changes must be done to example to be able to fully | 
| 2177 |  * utilise the test: | 
| 2178 |  * 1.) gap_service_db.db -> UUID_DEVICE_NAME char - add FLAG_ENCR_R | 
| 2179 |  *      => tests encrypted read/discovery | 
| 2180 |  * 2.) dev_info_service_db.db -> UUID_DEVICE_INFO_MANUFACTURER_NAME | 
| 2181 |  *      =>  The default name "Cambridge Silicon Radio" must be changed | 
| 2182 |  *          to "Cambridge Silicon Radi" (new length 22) | 
| 2183 |  * 3.) revert change 1 above and redo test. This attempts to write a | 
| 2184 |  *     char that is readable w/o encryption but writeable with encryption | 
| 2185 |  *     => tests encryption code lines in writeCharacteristic() | 
| 2186 |  *     => otherwise the read encryption would have increased security level already | 
| 2187 |  *     => programmable CSR device must be reset before each run of this test | 
| 2188 |  *        (to undo the previous write) | 
| 2189 |  */ | 
| 2190 | void tst_QLowEnergyController::tst_customProgrammableDevice() | 
| 2191 | { | 
| 2192 |     QSKIP("Skipping encryption" ); | 
| 2193 |  | 
| 2194 |     //Adjust the uuids and device address as see fit to match | 
| 2195 |     //values that match the current test environment | 
| 2196 |     //The target characteristic must be readble and writable | 
| 2197 |     //under encryption to test dynamic switching of security level | 
| 2198 |     QBluetoothAddress encryptedDevice(QString("00:02:5B:00:15:10" )); | 
| 2199 |     QBluetoothUuid serviceUuid(QBluetoothUuid::GenericAccess); | 
| 2200 |     QBluetoothUuid characterristicUuid(QBluetoothUuid::DeviceName); | 
| 2201 |  | 
| 2202 |     QLowEnergyController control(encryptedDevice); | 
| 2203 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2204 |  | 
| 2205 |     control.connectToDevice(); | 
| 2206 |     { | 
| 2207 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 2208 |               30000); | 
| 2209 |     } | 
| 2210 |  | 
| 2211 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 2212 |             || control.error() != QLowEnergyController::NoError) { | 
| 2213 |         // default BTLE backend forever hangs in ConnectingState | 
| 2214 |         QSKIP("Cannot connect to remote device" ); | 
| 2215 |     } | 
| 2216 |  | 
| 2217 |     QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 2218 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 2219 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 2220 |     control.discoverServices(); | 
| 2221 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 2222 |     QCOMPARE(stateSpy.count(), 2); | 
| 2223 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2224 |              QLowEnergyController::DiscoveringState); | 
| 2225 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2226 |              QLowEnergyController::DiscoveredState); | 
| 2227 |  | 
| 2228 |     QList<QBluetoothUuid> uuids = control.services(); | 
| 2229 |     QVERIFY(uuids.contains(serviceUuid)); | 
| 2230 |  | 
| 2231 |     QLowEnergyService *service = control.createServiceObject(service: serviceUuid, parent: this); | 
| 2232 |     QVERIFY(service); | 
| 2233 |  | 
| 2234 |     // 1.) discovery triggers read of device name char which is encrypted | 
| 2235 |     service->discoverDetails(); | 
| 2236 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 2237 |         service->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 2238 |  | 
| 2239 |     QLowEnergyCharacteristic encryptedChar = service->characteristic( | 
| 2240 |                                                     uuid: characterristicUuid); | 
| 2241 |     const QByteArray encryptedReference("CSR HR Sensor" ); | 
| 2242 |     QVERIFY(encryptedChar.isValid()); | 
| 2243 |     QCOMPARE(encryptedChar.value(), encryptedReference); | 
| 2244 |  | 
| 2245 |     // 2.) read of encrypted characteristic | 
| 2246 |     //     => the discovery of the encrypted char above will have switched to | 
| 2247 |     //     encryption already. | 
| 2248 |     QSignalSpy encryptedReadSpy(service, | 
| 2249 |                                 SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); | 
| 2250 |     QSignalSpy encryptedErrorSpy(service, | 
| 2251 |                                  SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2252 |     service->readCharacteristic(characteristic: encryptedChar); | 
| 2253 |     QTRY_VERIFY_WITH_TIMEOUT(!encryptedReadSpy.isEmpty(), 10000); | 
| 2254 |     QVERIFY(encryptedErrorSpy.isEmpty()); | 
| 2255 |     QCOMPARE(encryptedReadSpy.count(), 1); | 
| 2256 |     QList<QVariant> entry = encryptedReadSpy[0]; | 
| 2257 |     QVERIFY(entry[0].value<QLowEnergyCharacteristic>() == encryptedChar); | 
| 2258 |     QCOMPARE(entry[1].toByteArray(), encryptedReference); | 
| 2259 |     QCOMPARE(encryptedChar.value(), encryptedReference); | 
| 2260 |  | 
| 2261 |     // 3.) write to encrypted characteristic | 
| 2262 |     QSignalSpy encryptedWriteSpy(service, | 
| 2263 |                                  SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray))); | 
| 2264 |     encryptedReadSpy.clear(); | 
| 2265 |     encryptedErrorSpy.clear(); | 
| 2266 |     const QByteArray newValue("ZZZ HR Sensor" ); | 
| 2267 |     service->writeCharacteristic(characteristic: encryptedChar, newValue); | 
| 2268 |     QTRY_VERIFY_WITH_TIMEOUT(!encryptedWriteSpy.isEmpty(), 10000); | 
| 2269 |     QVERIFY(encryptedErrorSpy.isEmpty()); | 
| 2270 |     QVERIFY(encryptedReadSpy.isEmpty()); | 
| 2271 |     QCOMPARE(encryptedWriteSpy.count(), 1); | 
| 2272 |     entry = encryptedWriteSpy[0]; | 
| 2273 |     QVERIFY(entry[0].value<QLowEnergyCharacteristic>() == encryptedChar); | 
| 2274 |     QCOMPARE(entry[1].toByteArray(), newValue); | 
| 2275 |     QCOMPARE(encryptedChar.value(), newValue); | 
| 2276 |  | 
| 2277 |     delete service; | 
| 2278 |  | 
| 2279 |     //change to Device Information service | 
| 2280 |     QVERIFY(uuids.contains(QBluetoothUuid::DeviceInformation)); | 
| 2281 |     service = control.createServiceObject(service: QBluetoothUuid::DeviceInformation); | 
| 2282 |     QVERIFY(service); | 
| 2283 |  | 
| 2284 |     service->discoverDetails(); | 
| 2285 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 2286 |         service->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 2287 |  | 
| 2288 |     // 4.) read of software revision string which is longer than mtu | 
| 2289 |     //     tests readCharacteristic() including blob reads | 
| 2290 |     QSignalSpy readSpy(service, | 
| 2291 |                        SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); | 
| 2292 |     QSignalSpy errorSpy(service, | 
| 2293 |                        SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2294 |  | 
| 2295 |     const QByteArray expectedSoftRev("Application version 2.3.0.0" ); | 
| 2296 |     QLowEnergyCharacteristic softwareRevChar | 
| 2297 |             = service->characteristic(uuid: QBluetoothUuid::SoftwareRevisionString); | 
| 2298 |     QVERIFY(softwareRevChar.isValid()); | 
| 2299 |     QCOMPARE(softwareRevChar.value(), expectedSoftRev); | 
| 2300 |  | 
| 2301 |     service->readCharacteristic(characteristic: softwareRevChar); | 
| 2302 |     QTRY_VERIFY_WITH_TIMEOUT(!readSpy.isEmpty(), 10000); | 
| 2303 |     QVERIFY(errorSpy.isEmpty()); | 
| 2304 |     QCOMPARE(readSpy.count(), 1); | 
| 2305 |     entry = readSpy[0]; | 
| 2306 |     QVERIFY(entry[0].value<QLowEnergyCharacteristic>() == softwareRevChar); | 
| 2307 |     QCOMPARE(entry[1].toByteArray(), expectedSoftRev); | 
| 2308 |     QCOMPARE(softwareRevChar.value(), expectedSoftRev); | 
| 2309 |  | 
| 2310 |  | 
| 2311 |     // 5.) read of manufacturer string which is exactly as long as single | 
| 2312 |     //     MTU size (assuming negotiated MTU is 23) | 
| 2313 |     //     => blob read test without blob being required | 
| 2314 |     //     => the read blob answer will have zero length | 
| 2315 |  | 
| 2316 |     readSpy.clear(); | 
| 2317 |  | 
| 2318 |     // This assumes the manufacturer string was mondified via CSR SDK | 
| 2319 |     // see function description above | 
| 2320 |     const QByteArray expectedManufacturer("Cambridge Silicon Radi" ); | 
| 2321 |     QLowEnergyCharacteristic manufacturerChar = service->characteristic( | 
| 2322 |                 uuid: QBluetoothUuid::ManufacturerNameString); | 
| 2323 |     QVERIFY(manufacturerChar.isValid()); | 
| 2324 |     QCOMPARE(manufacturerChar.value(), expectedManufacturer); | 
| 2325 |  | 
| 2326 |     service->readCharacteristic(characteristic: manufacturerChar); | 
| 2327 |     QTRY_VERIFY_WITH_TIMEOUT(!readSpy.isEmpty(), 10000); | 
| 2328 |     QVERIFY(errorSpy.isEmpty()); | 
| 2329 |     QCOMPARE(readSpy.count(), 1); | 
| 2330 |     entry = readSpy[0]; | 
| 2331 |     QVERIFY(entry[0].value<QLowEnergyCharacteristic>() == manufacturerChar); | 
| 2332 |     QCOMPARE(entry[1].toByteArray(), expectedManufacturer); | 
| 2333 |     QCOMPARE(manufacturerChar.value(), expectedManufacturer); | 
| 2334 |  | 
| 2335 |     delete service; | 
| 2336 |     control.disconnectFromDevice(); | 
| 2337 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 2338 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2339 | } | 
| 2340 |  | 
| 2341 |  | 
| 2342 | /*  1.) Test with undiscovered devices | 
| 2343 |         - read and write invalid char | 
| 2344 |     2.) Test with discovered devices | 
| 2345 |         - read non-readable char | 
| 2346 |         - write non-writable char | 
| 2347 |  */ | 
| 2348 | void tst_QLowEnergyController::tst_errorCases() | 
| 2349 | { | 
| 2350 | #if !defined(Q_OS_MACOS) && !QT_CONFIG(winrt_bt) | 
| 2351 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 2352 |     if (localAdapters.isEmpty()) | 
| 2353 |         QSKIP("No local Bluetooth device found. Skipping test." ); | 
| 2354 | #endif | 
| 2355 |  | 
| 2356 |     if (!remoteDeviceInfo.isValid()) | 
| 2357 |         QSKIP("No remote BTLE device found. Skipping test." ); | 
| 2358 |     QLowEnergyController control(remoteDeviceInfo); | 
| 2359 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2360 |  | 
| 2361 |     control.connectToDevice(); | 
| 2362 |     { | 
| 2363 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 2364 |               30000); | 
| 2365 |     } | 
| 2366 |  | 
| 2367 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 2368 |             || control.error() != QLowEnergyController::NoError) { | 
| 2369 |         // default BTLE backend forever hangs in ConnectingState | 
| 2370 |         QSKIP("Cannot connect to remote device" ); | 
| 2371 |     } | 
| 2372 |  | 
| 2373 |     QCOMPARE(control.state(), QLowEnergyController::ConnectedState); | 
| 2374 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 2375 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 2376 |     control.discoverServices(); | 
| 2377 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 2378 |     QCOMPARE(stateSpy.count(), 2); | 
| 2379 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2380 |              QLowEnergyController::DiscoveringState); | 
| 2381 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2382 |              QLowEnergyController::DiscoveredState); | 
| 2383 |  | 
| 2384 |  | 
| 2385 |     // Setup required uuids | 
| 2386 |     const QBluetoothUuid irTemperaturServiceUuid(QStringLiteral("f000aa00-0451-4000-b000-000000000000" )); | 
| 2387 |     const QBluetoothUuid irCharUuid(QString("f000aa01-0451-4000-b000-000000000000" )); | 
| 2388 |     const QBluetoothUuid oadServiceUuid(QStringLiteral("f000ffc0-0451-4000-b000-000000000000" )); | 
| 2389 |     const QBluetoothUuid oadCharUuid(QString("f000ffc1-0451-4000-b000-000000000000" )); | 
| 2390 |  | 
| 2391 |     QVERIFY(control.services().contains(irTemperaturServiceUuid)); | 
| 2392 |     QVERIFY(control.services().contains(oadServiceUuid)); | 
| 2393 |  | 
| 2394 |     // Create service objects and basic tests | 
| 2395 |     QLowEnergyService *irService = control.createServiceObject(service: irTemperaturServiceUuid); | 
| 2396 |     QVERIFY(irService); | 
| 2397 |     QCOMPARE(irService->state(), QLowEnergyService::DiscoveryRequired); | 
| 2398 |     QVERIFY(irService->characteristics().isEmpty()); | 
| 2399 |     QLowEnergyService *oadService = control.createServiceObject(service: oadServiceUuid); | 
| 2400 |     QVERIFY(oadService); | 
| 2401 |     QCOMPARE(oadService->state(), QLowEnergyService::DiscoveryRequired); | 
| 2402 |     QVERIFY(oadService->characteristics().isEmpty()); | 
| 2403 |  | 
| 2404 |     QLowEnergyCharacteristic invalidChar; | 
| 2405 |     QLowEnergyDescriptor invalidDesc; | 
| 2406 |  | 
| 2407 |     QVERIFY(!irService->contains(invalidChar)); | 
| 2408 |     QVERIFY(!irService->contains(invalidDesc)); | 
| 2409 |  | 
| 2410 |     QSignalSpy irErrorSpy(irService, SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2411 |     QSignalSpy oadErrorSpy(oadService, SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2412 |  | 
| 2413 |     QSignalSpy irReadSpy(irService, SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); | 
| 2414 |     QSignalSpy irWrittenSpy(irService, SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray))); | 
| 2415 |     QSignalSpy irDescReadSpy(irService, SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray))); | 
| 2416 |     QSignalSpy irDescWrittenSpy(irService, SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray))); | 
| 2417 |  | 
| 2418 |     QSignalSpy oadCharReadSpy(oadService, SIGNAL(descriptorRead(QLowEnergyDescriptor,QByteArray))); | 
| 2419 |  | 
| 2420 |     // ******************************************************** | 
| 2421 |     // Test read/write to discovered service | 
| 2422 |     // with invalid characteristic & descriptor | 
| 2423 |  | 
| 2424 |     // discover IR Service | 
| 2425 |     irService->discoverDetails(); | 
| 2426 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 2427 |         irService->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 2428 |     QVERIFY(!irService->contains(invalidChar)); | 
| 2429 |     QVERIFY(!irService->contains(invalidDesc)); | 
| 2430 |     irErrorSpy.clear(); | 
| 2431 |  | 
| 2432 |     // read invalid characteristic | 
| 2433 |     irService->readCharacteristic(characteristic: invalidChar); | 
| 2434 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2435 |     QCOMPARE(irErrorSpy.count(), 1); | 
| 2436 |     QVERIFY(irWrittenSpy.isEmpty()); | 
| 2437 |     QVERIFY(irReadSpy.isEmpty()); | 
| 2438 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2439 |              QLowEnergyService::OperationError); | 
| 2440 |     irErrorSpy.clear(); | 
| 2441 |  | 
| 2442 |     // read invalid descriptor | 
| 2443 |     irService->readDescriptor(descriptor: invalidDesc); | 
| 2444 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2445 |     QCOMPARE(irErrorSpy.count(), 1); | 
| 2446 |     QVERIFY(irDescWrittenSpy.isEmpty()); | 
| 2447 |     QVERIFY(irDescReadSpy.isEmpty()); | 
| 2448 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2449 |              QLowEnergyService::OperationError); | 
| 2450 |     irErrorSpy.clear(); | 
| 2451 |  | 
| 2452 |     // write invalid characteristic | 
| 2453 |     irService->writeCharacteristic(characteristic: invalidChar, newValue: QByteArray("foo" )); | 
| 2454 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2455 |     QCOMPARE(irErrorSpy.count(), 1); | 
| 2456 |     QVERIFY(irWrittenSpy.isEmpty()); | 
| 2457 |     QVERIFY(irReadSpy.isEmpty()); | 
| 2458 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2459 |              QLowEnergyService::OperationError); | 
| 2460 |     irErrorSpy.clear(); | 
| 2461 |  | 
| 2462 |     // write invalid descriptor | 
| 2463 |     irService->readDescriptor(descriptor: invalidDesc); | 
| 2464 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2465 |     QCOMPARE(irErrorSpy.count(), 1); | 
| 2466 |     QVERIFY(irDescWrittenSpy.isEmpty()); | 
| 2467 |     QVERIFY(irDescReadSpy.isEmpty()); | 
| 2468 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2469 |              QLowEnergyService::OperationError); | 
| 2470 |     irErrorSpy.clear(); | 
| 2471 |  | 
| 2472 |     // ******************************************************** | 
| 2473 |     // Test read/write to undiscovered service | 
| 2474 |     // with invalid characteristic & descriptor | 
| 2475 |  | 
| 2476 |     // read invalid characteristic | 
| 2477 |     oadService->readCharacteristic(characteristic: invalidChar); | 
| 2478 |     QTRY_VERIFY_WITH_TIMEOUT(!oadErrorSpy.isEmpty(), 5000); | 
| 2479 |     QCOMPARE(oadErrorSpy.count(), 1); | 
| 2480 |     QCOMPARE(oadErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2481 |              QLowEnergyService::OperationError); | 
| 2482 |     oadErrorSpy.clear(); | 
| 2483 |  | 
| 2484 |     // read invalid descriptor | 
| 2485 |     oadService->readDescriptor(descriptor: invalidDesc); | 
| 2486 |     QTRY_VERIFY_WITH_TIMEOUT(!oadErrorSpy.isEmpty(), 5000); | 
| 2487 |     QCOMPARE(oadErrorSpy.count(), 1); | 
| 2488 |     QCOMPARE(oadErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2489 |              QLowEnergyService::OperationError); | 
| 2490 |     oadErrorSpy.clear(); | 
| 2491 |  | 
| 2492 |     // write invalid characteristic | 
| 2493 |     oadService->writeCharacteristic(characteristic: invalidChar, newValue: QByteArray("foo" )); | 
| 2494 |     QTRY_VERIFY_WITH_TIMEOUT(!oadErrorSpy.isEmpty(), 5000); | 
| 2495 |     QCOMPARE(oadErrorSpy.count(), 1); | 
| 2496 |     QCOMPARE(oadErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2497 |              QLowEnergyService::OperationError); | 
| 2498 |     oadErrorSpy.clear(); | 
| 2499 |  | 
| 2500 |     // write invalid descriptor | 
| 2501 |     oadService->readDescriptor(descriptor: invalidDesc); | 
| 2502 |     QTRY_VERIFY_WITH_TIMEOUT(!oadErrorSpy.isEmpty(), 5000); | 
| 2503 |     QCOMPARE(oadErrorSpy.count(), 1); | 
| 2504 |     QCOMPARE(oadErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2505 |              QLowEnergyService::OperationError); | 
| 2506 |     oadErrorSpy.clear(); | 
| 2507 |  | 
| 2508 |     // ******************************************************** | 
| 2509 |     // Write to non-writable char | 
| 2510 |  | 
| 2511 |     QLowEnergyCharacteristic nonWritableChar = irService->characteristic(uuid: irCharUuid); | 
| 2512 |     QVERIFY(nonWritableChar.isValid()); | 
| 2513 |     // not writeable in any form | 
| 2514 |     QVERIFY(!(nonWritableChar.properties() | 
| 2515 |             & (QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse | 
| 2516 |                |QLowEnergyCharacteristic::WriteSigned))); | 
| 2517 |     irService->writeCharacteristic(characteristic: nonWritableChar, newValue: QByteArray("ABCD" )); | 
| 2518 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2519 |     QVERIFY(irWrittenSpy.isEmpty()); | 
| 2520 |     QVERIFY(irReadSpy.isEmpty()); | 
| 2521 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2522 |              QLowEnergyService::CharacteristicWriteError); | 
| 2523 |     irErrorSpy.clear(); | 
| 2524 |  | 
| 2525 |     // ******************************************************** | 
| 2526 |     // Write to non-writable desc | 
| 2527 |     // CharacteristicUserDescription is not writable | 
| 2528 |  | 
| 2529 |     QLowEnergyDescriptor nonWritableDesc = nonWritableChar.descriptor( | 
| 2530 |                 uuid: QBluetoothUuid::CharacteristicUserDescription); | 
| 2531 |     QVERIFY(nonWritableDesc.isValid()); | 
| 2532 |     irService->writeDescriptor(descriptor: nonWritableDesc, newValue: QByteArray("ABCD" )); | 
| 2533 |     QTRY_VERIFY_WITH_TIMEOUT(!irErrorSpy.isEmpty(), 5000); | 
| 2534 |     QVERIFY(irWrittenSpy.isEmpty()); | 
| 2535 |     QVERIFY(irReadSpy.isEmpty()); | 
| 2536 |     QCOMPARE(irErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2537 |              QLowEnergyService::DescriptorWriteError); | 
| 2538 |     irErrorSpy.clear(); | 
| 2539 |  | 
| 2540 |  | 
| 2541 |     // ******************************************************** | 
| 2542 |     // Read non-readable char | 
| 2543 |  | 
| 2544 |     // discover OAD Service | 
| 2545 |     oadService->discoverDetails(); | 
| 2546 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 2547 |         oadService->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 2548 |     oadErrorSpy.clear(); | 
| 2549 |  | 
| 2550 |     // Test reading | 
| 2551 |     QLowEnergyCharacteristic oadChar = oadService->characteristic(uuid: oadCharUuid); | 
| 2552 |     QVERIFY(oadChar.isValid()); | 
| 2553 |     oadService->readCharacteristic(characteristic: oadChar); | 
| 2554 |     QTRY_VERIFY_WITH_TIMEOUT(!oadErrorSpy.isEmpty(), 5000); | 
| 2555 |     QCOMPARE(oadErrorSpy.count(), 1); | 
| 2556 |     QVERIFY(oadCharReadSpy.isEmpty()); | 
| 2557 |     QCOMPARE(oadErrorSpy[0].at(0).value<QLowEnergyService::ServiceError>(), | 
| 2558 |              QLowEnergyService::CharacteristicReadError); | 
| 2559 |     oadErrorSpy.clear(); | 
| 2560 |  | 
| 2561 |     delete irService; | 
| 2562 |     delete oadService; | 
| 2563 |     control.disconnectFromDevice(); | 
| 2564 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 2565 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2566 | } | 
| 2567 |  | 
| 2568 | /* | 
| 2569 |     Tests write without responses. We utilize the Over-The-Air image update | 
| 2570 |     service of the SensorTag. | 
| 2571 |  */ | 
| 2572 | void tst_QLowEnergyController::tst_writeCharacteristicNoResponse() | 
| 2573 | { | 
| 2574 | #if !defined(Q_OS_MACOS) && !QT_CONFIG(winrt_bt) | 
| 2575 |     QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices(); | 
| 2576 |     if (localAdapters.isEmpty()) | 
| 2577 |         QSKIP("No local Bluetooth device found. Skipping test." ); | 
| 2578 | #endif | 
| 2579 |  | 
| 2580 |     if (!remoteDeviceInfo.isValid()) | 
| 2581 |         QSKIP("No remote BTLE device found. Skipping test." ); | 
| 2582 |     QLowEnergyController control(remoteDeviceInfo); | 
| 2583 |  | 
| 2584 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2585 |  | 
| 2586 |     control.connectToDevice(); | 
| 2587 |     { | 
| 2588 |         QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState, | 
| 2589 |               30000); | 
| 2590 |     } | 
| 2591 |  | 
| 2592 |     if (control.state() == QLowEnergyController::ConnectingState | 
| 2593 |             || control.error() != QLowEnergyController::NoError) { | 
| 2594 |         // default BTLE backend forever hangs in ConnectingState | 
| 2595 |         QSKIP("Cannot connect to remote device" ); | 
| 2596 |     } | 
| 2597 |  | 
| 2598 |     QTRY_VERIFY_WITH_TIMEOUT(control.state() == QLowEnergyController::ConnectedState, 20000); | 
| 2599 |     QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished())); | 
| 2600 |     QSignalSpy stateSpy(&control, SIGNAL(stateChanged(QLowEnergyController::ControllerState))); | 
| 2601 |     control.discoverServices(); | 
| 2602 |     QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 20000); | 
| 2603 |     QCOMPARE(stateSpy.count(), 2); | 
| 2604 |     QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2605 |              QLowEnergyController::DiscoveringState); | 
| 2606 |     QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(), | 
| 2607 |              QLowEnergyController::DiscoveredState); | 
| 2608 |  | 
| 2609 |     // The Over-The-Air update service uuid | 
| 2610 |     const QBluetoothUuid testService(QString("f000ffc0-0451-4000-b000-000000000000" )); | 
| 2611 |     QList<QBluetoothUuid> uuids = control.services(); | 
| 2612 |     QVERIFY(uuids.contains(testService)); | 
| 2613 |  | 
| 2614 |     QLowEnergyService *service = control.createServiceObject(service: testService, parent: this); | 
| 2615 |     QVERIFY(service); | 
| 2616 |     service->discoverDetails(); | 
| 2617 |     QTRY_VERIFY_WITH_TIMEOUT( | 
| 2618 |         service->state() == QLowEnergyService::ServiceDiscovered, 30000); | 
| 2619 |  | 
| 2620 |     // 1. Get "Image Identity" and "Image Block" characteristic | 
| 2621 |     const QLowEnergyCharacteristic imageIdentityChar = service->characteristic( | 
| 2622 |                 uuid: QBluetoothUuid(QString("f000ffc1-0451-4000-b000-000000000000" ))); | 
| 2623 |     const QLowEnergyCharacteristic imageBlockChar = service->characteristic( | 
| 2624 |                 uuid: QBluetoothUuid(QString("f000ffc2-0451-4000-b000-000000000000" ))); | 
| 2625 |     QVERIFY(imageIdentityChar.isValid()); | 
| 2626 |     QVERIFY(imageIdentityChar.properties() & QLowEnergyCharacteristic::Write); | 
| 2627 |     QVERIFY(imageIdentityChar.properties() & QLowEnergyCharacteristic::WriteNoResponse); | 
| 2628 |     QVERIFY(!(imageIdentityChar.properties() & QLowEnergyCharacteristic::Read)); //not readable | 
| 2629 |     QVERIFY(imageBlockChar.isValid()); | 
| 2630 |  | 
| 2631 |     // 2. Get "Image Identity" notification descriptor | 
| 2632 |     const QLowEnergyDescriptor identityNotification = imageIdentityChar.descriptor( | 
| 2633 |                 uuid: QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 2634 |     const QLowEnergyDescriptor blockNotification = imageBlockChar.descriptor( | 
| 2635 |                 uuid: QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)); | 
| 2636 |  | 
| 2637 |     if (!identityNotification.isValid() | 
| 2638 |             || !blockNotification.isValid() | 
| 2639 |             || !imageIdentityChar.isValid()) { | 
| 2640 |         delete service; | 
| 2641 |         control.disconnectFromDevice(); | 
| 2642 |         QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 2643 |         QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2644 |         QSKIP("Cannot find OAD char/notification" ); | 
| 2645 |     } | 
| 2646 |  | 
| 2647 |     // 3. Enable notifications | 
| 2648 |     QSignalSpy descWrittenSpy(service, | 
| 2649 |                         SIGNAL(descriptorWritten(QLowEnergyDescriptor,QByteArray))); | 
| 2650 |     QSignalSpy charChangedSpy(service, | 
| 2651 |                         SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray))); | 
| 2652 |     QSignalSpy charWrittenSpy(service, | 
| 2653 |                         SIGNAL(characteristicWritten(QLowEnergyCharacteristic,QByteArray))); | 
| 2654 |     QSignalSpy charReadSpy(service, | 
| 2655 |                         SIGNAL(characteristicRead(QLowEnergyCharacteristic,QByteArray))); | 
| 2656 |     QSignalSpy errorSpy(service, | 
| 2657 |                         SIGNAL(error(QLowEnergyService::ServiceError))); | 
| 2658 |  | 
| 2659 |     //enable notifications on both characteristics | 
| 2660 |     if (identityNotification.value() != QByteArray::fromHex(hexEncoded: "0100" )) { | 
| 2661 |         service->writeDescriptor(descriptor: identityNotification, newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 2662 |         QTRY_VERIFY_WITH_TIMEOUT(!descWrittenSpy.isEmpty(), 3000); | 
| 2663 |         QCOMPARE(identityNotification.value(), QByteArray::fromHex("0100" )); | 
| 2664 |         QList<QVariant> firstSignalData = descWrittenSpy.first(); | 
| 2665 |         QLowEnergyDescriptor signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2666 |         QByteArray signalValue = firstSignalData[1].toByteArray(); | 
| 2667 |         QCOMPARE(signalValue, QByteArray::fromHex("0100" )); | 
| 2668 |         QVERIFY(identityNotification == signalDesc); | 
| 2669 |         descWrittenSpy.clear(); | 
| 2670 |     } | 
| 2671 |  | 
| 2672 |     if (blockNotification.value() != QByteArray::fromHex(hexEncoded: "0100" )) { | 
| 2673 |         service->writeDescriptor(descriptor: blockNotification, newValue: QByteArray::fromHex(hexEncoded: "0100" )); | 
| 2674 |         QTRY_VERIFY_WITH_TIMEOUT(!descWrittenSpy.isEmpty(), 3000); | 
| 2675 |         QCOMPARE(blockNotification.value(), QByteArray::fromHex("0100" )); | 
| 2676 |         QList<QVariant> firstSignalData = descWrittenSpy.first(); | 
| 2677 |         QLowEnergyDescriptor signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>(); | 
| 2678 |         QByteArray signalValue = firstSignalData[1].toByteArray(); | 
| 2679 |         QCOMPARE(signalValue, QByteArray::fromHex("0100" )); | 
| 2680 |         QVERIFY(blockNotification == signalDesc); | 
| 2681 |         descWrittenSpy.clear(); | 
| 2682 |     } | 
| 2683 |  | 
| 2684 |     QList<QVariant> entry; | 
| 2685 |  | 
| 2686 |     // Test direct read of non-readable characteristic | 
| 2687 |     QVERIFY(errorSpy.isEmpty()); | 
| 2688 |     QVERIFY(charReadSpy.isEmpty()); | 
| 2689 |     service->readCharacteristic(characteristic: imageIdentityChar); | 
| 2690 |     QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000); | 
| 2691 |     QCOMPARE(errorSpy.count(), 1); // should throw CharacteristicReadError | 
| 2692 |     QVERIFY(charReadSpy.isEmpty()); | 
| 2693 |     entry = errorSpy[0]; | 
| 2694 |     QCOMPARE(entry[0].value<QLowEnergyService::ServiceError>(), | 
| 2695 |              QLowEnergyService::CharacteristicReadError); | 
| 2696 |  | 
| 2697 |     // 4. Trigger image identity announcement (using traditional write) | 
| 2698 |     bool foundOneImage = false; | 
| 2699 |  | 
| 2700 |     // Image A | 
| 2701 |     // Write triggers a notification and write confirmation | 
| 2702 |     service->writeCharacteristic(characteristic: imageIdentityChar, newValue: QByteArray::fromHex(hexEncoded: "0" )); | 
| 2703 |     QTest::qWait(ms: 1000); | 
| 2704 |     QTRY_COMPARE_WITH_TIMEOUT(charChangedSpy.count(), 1, 5000); | 
| 2705 |     QTRY_COMPARE_WITH_TIMEOUT(charWrittenSpy.count(), 1, 5000); | 
| 2706 |  | 
| 2707 |     // This is very SensorTag specific logic. | 
| 2708 |     // If the image block is empty the current firmware | 
| 2709 |     // does not even send a notification for imageIdentityChar | 
| 2710 |     // but for imageBlockChar | 
| 2711 |  | 
| 2712 |     entry = charChangedSpy[0]; | 
| 2713 |     QLowEnergyCharacteristic first = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2714 |     QByteArray val1 = entry[1].toByteArray(); | 
| 2715 |     if (val1.size() == 8) { | 
| 2716 |         QCOMPARE(imageIdentityChar, first); | 
| 2717 |         foundOneImage = true; | 
| 2718 |     } else { | 
| 2719 |         // we received a notification for imageBlockChar | 
| 2720 |         QCOMPARE(imageBlockChar, first); | 
| 2721 |         qWarning() << "Invalid image A ident info" ; | 
| 2722 |     } | 
| 2723 |  | 
| 2724 |     entry = charWrittenSpy[0]; | 
| 2725 |     QLowEnergyCharacteristic second = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2726 |     QByteArray val2 = entry[1].toByteArray(); | 
| 2727 |     QCOMPARE(imageIdentityChar, second); | 
| 2728 |     QVERIFY(val2 == QByteArray::fromHex("0" ) || val2 == val1); | 
| 2729 |  | 
| 2730 |     // notifications on non-readable characteristics do not update cache | 
| 2731 |     QVERIFY(imageIdentityChar.value().isEmpty()); | 
| 2732 |     QVERIFY(imageBlockChar.value().isEmpty()); | 
| 2733 |  | 
| 2734 |     charChangedSpy.clear(); | 
| 2735 |     charWrittenSpy.clear(); | 
| 2736 |  | 
| 2737 |     // Image B | 
| 2738 |     service->writeCharacteristic(characteristic: imageIdentityChar, newValue: QByteArray::fromHex(hexEncoded: "1" )); | 
| 2739 |     QTest::qWait(ms: 1000); | 
| 2740 |     QTRY_COMPARE_WITH_TIMEOUT(charChangedSpy.count(), 1, 5000); | 
| 2741 |     QTRY_COMPARE_WITH_TIMEOUT(charWrittenSpy.count(), 1, 5000);; | 
| 2742 |  | 
| 2743 |     entry = charChangedSpy[0]; | 
| 2744 |     first = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2745 |     val1 = entry[1].toByteArray(); | 
| 2746 |     if (val1.size() == 8) { | 
| 2747 |         QCOMPARE(imageIdentityChar, first); | 
| 2748 |         foundOneImage = true; | 
| 2749 |     } else { | 
| 2750 |         // we received a notification for imageBlockChar without explicitly | 
| 2751 |         // enabling them. This is caused by the device's default settings. | 
| 2752 |         QCOMPARE(imageBlockChar, first); | 
| 2753 |         qWarning() << "Invalid image B ident info" ; | 
| 2754 |     } | 
| 2755 |  | 
| 2756 |     entry = charWrittenSpy[0]; | 
| 2757 |     second = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2758 |     val2 = entry[1].toByteArray(); | 
| 2759 |     QCOMPARE(imageIdentityChar, second); | 
| 2760 |  | 
| 2761 |     // notifications on non-readable characteristics do not update cache | 
| 2762 |     QVERIFY(imageIdentityChar.value().isEmpty()); | 
| 2763 |     QVERIFY(imageBlockChar.value().isEmpty()); | 
| 2764 |  | 
| 2765 |     /* Bluez resends the last confirmed write value, other platforms | 
| 2766 |      * send the value received by the change notification value. | 
| 2767 |      */ | 
| 2768 |     qDebug() << "Image B(1):"  << val1.toHex() << val2.toHex(); | 
| 2769 |     QVERIFY(val2 == QByteArray::fromHex("1" ) || val2 == val1); | 
| 2770 |  | 
| 2771 |     QVERIFY2(foundOneImage, "The SensorTag doesn't have a valid image? (1)" ); | 
| 2772 |  | 
| 2773 |     // 5. Trigger image identity announcement (without response) | 
| 2774 |     charChangedSpy.clear(); | 
| 2775 |     charWrittenSpy.clear(); | 
| 2776 |     foundOneImage = false; | 
| 2777 |  | 
| 2778 |     // Image A | 
| 2779 |     service->writeCharacteristic(characteristic: imageIdentityChar, | 
| 2780 |                                  newValue: QByteArray::fromHex(hexEncoded: "0" ), | 
| 2781 |                                  mode: QLowEnergyService::WriteWithoutResponse); | 
| 2782 |  | 
| 2783 |     // we only expect one signal (the notification but not the write confirmation) | 
| 2784 |     // Wait at least a second for a potential second signals | 
| 2785 |     QTest::qWait(ms: 1000); | 
| 2786 |     QTRY_COMPARE_WITH_TIMEOUT(charChangedSpy.count(), 1, 10000); | 
| 2787 |     QTRY_COMPARE_WITH_TIMEOUT(charWrittenSpy.count(), 0, 10000); | 
| 2788 |  | 
| 2789 |     entry = charChangedSpy[0]; | 
| 2790 |     first = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2791 |     val1 = entry[1].toByteArray(); | 
| 2792 |  | 
| 2793 | #ifdef Q_OS_ANDROID | 
| 2794 |     QEXPECT_FAIL("" , "Android sends write confirmation when using WriteWithoutResponse" , | 
| 2795 |                  Continue); | 
| 2796 | #endif | 
| 2797 |     QVERIFY(charWrittenSpy.isEmpty()); | 
| 2798 |     if (val1.size() == 8) { | 
| 2799 |         QCOMPARE(first, imageIdentityChar); | 
| 2800 |         foundOneImage = true; | 
| 2801 |     } else { | 
| 2802 |         // we received a notification for imageBlockChar without explicitly | 
| 2803 |         // enabling them. This is caused by the device's default settings. | 
| 2804 |         QCOMPARE(imageBlockChar, first); | 
| 2805 |         qWarning() << "Image A not set?" ; | 
| 2806 |     } | 
| 2807 |  | 
| 2808 |     // notifications on non-readable characteristics do not update cache | 
| 2809 |     QVERIFY(imageIdentityChar.value().isEmpty()); | 
| 2810 |     QVERIFY(imageBlockChar.value().isEmpty()); | 
| 2811 |  | 
| 2812 |     charChangedSpy.clear(); | 
| 2813 |  | 
| 2814 |     // Image B | 
| 2815 |     service->writeCharacteristic(characteristic: imageIdentityChar, | 
| 2816 |                                  newValue: QByteArray::fromHex(hexEncoded: "1" ), | 
| 2817 |                                  mode: QLowEnergyService::WriteWithoutResponse); | 
| 2818 |  | 
| 2819 |     // we only expect one signal (the notification but not the write confirmation) | 
| 2820 |     // Wait at least a second for a potential second signals | 
| 2821 |     QTest::qWait(ms: 1000); | 
| 2822 |     QTRY_COMPARE_WITH_TIMEOUT(charWrittenSpy.count(), 0, 10000); | 
| 2823 |     QTRY_COMPARE_WITH_TIMEOUT(charChangedSpy.count(), 1, 10000); | 
| 2824 |  | 
| 2825 |     entry = charChangedSpy[0]; | 
| 2826 |     first = entry[0].value<QLowEnergyCharacteristic>(); | 
| 2827 |     val1 = entry[1].toByteArray(); | 
| 2828 |  | 
| 2829 | #ifdef Q_OS_ANDROID | 
| 2830 |     QEXPECT_FAIL("" , "Android sends write confirmation when using WriteWithoutResponse" , | 
| 2831 |                  Continue); | 
| 2832 | #endif | 
| 2833 |     QVERIFY(charWrittenSpy.isEmpty()); | 
| 2834 |     if (val1.size() == 8) { | 
| 2835 |         QCOMPARE(first, imageIdentityChar); | 
| 2836 |         foundOneImage = true; | 
| 2837 |     } else { | 
| 2838 |         // we received a notification for imageBlockChar without explicitly | 
| 2839 |         // enabling them. This is caused by the device's default settings. | 
| 2840 |         QCOMPARE(imageBlockChar, first); | 
| 2841 |         qWarning() << "Image B not set?" ; | 
| 2842 |     } | 
| 2843 |  | 
| 2844 |     // notifications on non-readable characteristics do not update cache | 
| 2845 |     QVERIFY(imageIdentityChar.value().isEmpty()); | 
| 2846 |     QVERIFY(imageBlockChar.value().isEmpty()); | 
| 2847 |  | 
| 2848 |  | 
| 2849 |     QVERIFY2(foundOneImage, "The SensorTag doesn't have a valid image? (2)" ); | 
| 2850 |  | 
| 2851 |     delete service; | 
| 2852 |     control.disconnectFromDevice(); | 
| 2853 |     QTRY_COMPARE(control.state(), QLowEnergyController::UnconnectedState); | 
| 2854 |     QCOMPARE(control.error(), QLowEnergyController::NoError); | 
| 2855 | } | 
| 2856 |  | 
| 2857 | QTEST_MAIN(tst_QLowEnergyController) | 
| 2858 |  | 
| 2859 | #include "tst_qlowenergycontroller.moc" | 
| 2860 |  |