diff options
-rw-r--r-- | tests/bluetoothtestdevice/bluetoothtestdevice.cpp | 74 | ||||
-rw-r--r-- | tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp | 83 |
2 files changed, 157 insertions, 0 deletions
diff --git a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp index b2cefa4d..62daa942 100644 --- a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp +++ b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp @@ -44,6 +44,27 @@ static const QLatin1String connectionCountCharUuid("9414ec2d-792f-46a2-a19e-186d static const QLatin1String mtuServiceUuid("9a9483eb-cf4f-4c32-9a6b-794238d5b483"); static const QLatin1String mtuCharUuid("960d7e2a-a850-4a70-8064-cd74e9ccb6ff"); +static const QLatin1String repeatedWriteServiceUuid("72b12a31-98ea-406d-a89d-2c932d11ff67"); +static const QLatin1String repeatedWriteTargetCharUuid("2192ee43-6d17-4e78-b286-db2c3b696833"); +static const QLatin1String repeatedWriteNotifyCharUuid("b3f9d1a2-3d55-49c9-8b29-e09cec77ff86"); + +static void establishNotifyOnWriteConnection(QLowEnergyService *svc) +{ + // Make sure that the value from the repeatedWriteTargetCharUuid + // characteristic is writted to the repeatedWriteNotifyCharUuid + // characteristic + Q_ASSERT(svc->serviceUuid() == QBluetoothUuid(repeatedWriteServiceUuid)); + QObject::connect(svc, &QLowEnergyService::characteristicChanged, svc, + [svc](const QLowEnergyCharacteristic &characteristic, + const QByteArray &newValue) + { + if (characteristic.uuid() == QBluetoothUuid(repeatedWriteTargetCharUuid)) { + auto notifyChar = svc->characteristic(QBluetoothUuid(repeatedWriteNotifyCharUuid)); + svc->writeCharacteristic(notifyChar, newValue); + } + }); +} + int main(int argc, char *argv[]) { qDebug() << "build:" << __DATE__ << __TIME__; @@ -231,6 +252,56 @@ int main(int argc, char *argv[]) serviceDefinitions << serviceData; } + { + // repeated characteristic write service + // + // This service offers an 8 bytes large characteristic which can + // be read and written. Once the value is updated, it writes the + // same value to the other characteristic, which notifies the client + // about its change. This way we can make sure that all write were + // successful and happened in the right order. + // We can't use one characteristics for writing and notification, + // because on most backends when the characteristics was written + // by the client, there will be no notification about it. + QLowEnergyServiceData serviceData; + serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary); + serviceData.setUuid(QBluetoothUuid(repeatedWriteServiceUuid)); + + { + // The characteristics to be written by the client. + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid(repeatedWriteTargetCharUuid)); + QByteArray initialValue(8, 0); + charData.setValue(initialValue); + charData.setValueLength(8, 8); + charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read + | QLowEnergyCharacteristic::PropertyType::Write); + + serviceData.addCharacteristic(charData); + } + { + // The characteristics written by the server, + // it will send notifications to the client. + QLowEnergyCharacteristicData charData; + charData.setUuid(QBluetoothUuid(repeatedWriteNotifyCharUuid)); + QByteArray initialValue(8, 0); + charData.setValue(initialValue); + charData.setValueLength(8, 8); + charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read + | QLowEnergyCharacteristic::PropertyType::Write + | QLowEnergyCharacteristic::PropertyType::Notify); + + const QLowEnergyDescriptorData clientConfig( + QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration, + QLowEnergyCharacteristic::CCCDDisable); + charData.addDescriptor(clientConfig); + + serviceData.addCharacteristic(charData); + } + + serviceDefinitions << serviceData; + } + #ifndef Q_OS_IOS auto localAdapters = QBluetoothLocalDevice::allDevices(); if (localAdapters.isEmpty()) { @@ -281,6 +352,8 @@ int main(int argc, char *argv[]) services.emplaceBack(leController->addService(serviceData)); } + establishNotifyOnWriteConnection(services[5].get()); + leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData, advertisingData); @@ -291,6 +364,7 @@ int main(int argc, char *argv[]) services[i].reset(leController->addService(serviceDefinitions[i])); } + establishNotifyOnWriteConnection(services[5].get()); { // set connection counter diff --git a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp index 80dc1bef..5acf829d 100644 --- a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp +++ b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp @@ -32,6 +32,11 @@ static const QLatin1String static const QLatin1String connectionCountServiceUuid("78c61a07-a0f9-4b92-be2d-2570d8dbf010"); static const QLatin1String connectionCountCharUuid("9414ec2d-792f-46a2-a19e-186d0fb38a08"); +static const QLatin1String repeatedWriteServiceUuid("72b12a31-98ea-406d-a89d-2c932d11ff67"); +static const QLatin1String repeatedWriteTargetCharUuid("2192ee43-6d17-4e78-b286-db2c3b696833"); +static const QLatin1String repeatedWriteNotifyCharUuid("b3f9d1a2-3d55-49c9-8b29-e09cec77ff86"); + + #if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN) #define QT_BLUETOOTH_MTU_SUPPORTED @@ -79,6 +84,7 @@ private slots: void readDuringServiceDiscovery(); void readNotificationAndIndicationProperty(); void testNotificationAndIndication(); + void testRepeatedCharacteristicsWrite(); public: void checkconnectionCounter(std::unique_ptr<QLowEnergyController> &control); @@ -654,6 +660,83 @@ void tst_qlowenergycontroller_device::testNotificationAndIndication() } } +void tst_qlowenergycontroller_device::testRepeatedCharacteristicsWrite() +{ + // This test generates multiple consecutive writes to the same characteristic + // and waits for the notifications (on other characteristic) with the same + // values. After that it verifies that the received values are the same (and + // in the same order) as written values. The server writes each received + // value to a notifying characteristic, which allows us to perform the check. + + // Discover services + QVERIFY(mController->services().isEmpty()); + mController->discoverServices(); + QTRY_COMPARE(mController->state(), QLowEnergyController::DiscoveredState); + + checkconnectionCounter(mController); + + // Get service object. + QSharedPointer<QLowEnergyService> service(mController->createServiceObject( + QBluetoothUuid(repeatedWriteServiceUuid))); + QVERIFY(service != nullptr); + service->discoverDetails(QLowEnergyService::FullDiscovery); + QTRY_COMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovered); + + // Enable notification. + QLowEnergyCharacteristic notifyChar = + service->characteristic(QBluetoothUuid(repeatedWriteNotifyCharUuid)); + const auto notifyOrIndicate = QLowEnergyCharacteristic::PropertyType::Notify + | QLowEnergyCharacteristic::PropertyType::Indicate; + QCOMPARE(notifyChar.properties() & notifyOrIndicate, + QLowEnergyCharacteristic::PropertyType::Notify); + + QLowEnergyDescriptor cccd = notifyChar.clientCharacteristicConfiguration(); + QVERIFY(cccd.isValid()); + + QObject dummy; // for lifetime management + bool cccdWritten = false; + connect(service.get(), &QLowEnergyService::descriptorWritten, &dummy, + [&cccdWritten](const QLowEnergyDescriptor &info, const QByteArray &) { + if (info.uuid() + == QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration) { + cccdWritten = true; + } + }); + service->writeDescriptor(cccd, QLowEnergyCharacteristic::CCCDEnableNotification); + QTRY_VERIFY(cccdWritten); + + // Track the notifications of value changes. + QList<QByteArray> receivedValues; + connect(service.get(), &QLowEnergyService::characteristicChanged, &dummy, + [&receivedValues](const QLowEnergyCharacteristic &characteristic, + const QByteArray &value) + { + if (characteristic.uuid() == QBluetoothUuid(repeatedWriteNotifyCharUuid)) { + receivedValues.push_back(value); + } + }); + + // Write characteristics multiple times. This shouldn't crash, and all + // values should be written. We use the notifications to track it. + receivedValues.clear(); + QList<QByteArray> sentValues; + QLowEnergyCharacteristic writeChar = + service->characteristic(QBluetoothUuid(repeatedWriteTargetCharUuid)); + static const int totalWrites = 50; + QByteArray value(8, 0); + for (int i = 0; i < totalWrites; ++i) { + value[0] += 1; + value[7] += 1; + service->writeCharacteristic(writeChar, value); + sentValues.push_back(value); + } + + // We expect to get notifications about all writes. + // We set a large timeout to be on a safe side. + QTRY_COMPARE_WITH_TIMEOUT(receivedValues.size(), totalWrites, 60000); + QCOMPARE(receivedValues, sentValues); +} + QTEST_MAIN(tst_qlowenergycontroller_device) #include "tst_qlowenergycontroller_device.moc" |