summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2022-08-23 18:02:15 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-08-26 11:33:43 +0000
commitf6e3885854e9415024763d9bb05322a954ed3a48 (patch)
tree381e362f184bc22520d298467f3d11b5baabb20e
parent0a1b965df26101b1e62b09e9df6061734a311132 (diff)
Add tst_qlowenergycontroller_device::testRepeatedCharacteristicsWrite()
... and its counterpart in bluetoothtestdevice. The client side generates multiple writes to the same characteristics. The server side, when receiving each new value, copies it to the notifying characteristic, which sends notification back to the clien. In this way the client can verify that all writes were completed successfully and in the right order. Fixes: QTBUG-105556 Change-Id: Id5ba5b00527a01903b3730733188065c1cc6a94e Reviewed-by: Juha Vuolle <juha.vuolle@insta.fi> (cherry picked from commit 5a5abc2d7388504510988df01e840d5dbe222ee0) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--tests/bluetoothtestdevice/bluetoothtestdevice.cpp74
-rw-r--r--tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp83
2 files changed, 157 insertions, 0 deletions
diff --git a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
index e6104952..cf7bc735 100644
--- a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
+++ b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
@@ -91,6 +91,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__;
@@ -278,6 +299,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()) {
@@ -316,6 +387,8 @@ int main(int argc, char *argv[])
services.emplaceBack(leController->addService(serviceData));
}
+ establishNotifyOnWriteConnection(services[5].get());
+
leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
advertisingData);
@@ -326,6 +399,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 e71eff4d..8b14b3b3 100644
--- a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
+++ b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
@@ -57,6 +57,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
@@ -104,6 +109,7 @@ private slots:
void readDuringServiceDiscovery();
void readNotificationAndIndicationProperty();
void testNotificationAndIndication();
+ void testRepeatedCharacteristicsWrite();
public:
void checkconnectionCounter(std::unique_ptr<QLowEnergyController> &control);
@@ -679,6 +685,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"