summaryrefslogtreecommitdiffstats
path: root/tests/auto/qlowenergycontroller-gattserver
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@theqtcompany.com>2015-12-08 11:36:58 +0100
committerAlex Blasche <alexander.blasche@theqtcompany.com>2015-12-15 15:13:34 +0000
commitc4f5a247cccda4bad46aeff530364f7e4da2df57 (patch)
treee2ee868fa1ce9bfae08bb4ecb146c48c66789c09 /tests/auto/qlowenergycontroller-gattserver
parent39781901b1183429cb62310a1cee4c3ffa49a0ec (diff)
Bluetooth LE: Implement GATT server write support.
Write Request, Write Command and Execute Write Request are fully implemented now. Signed Write support is still missing. Notifications and Indications are sent. The server side gets informed via the respective signals when a client writes a characteristic or descriptor. Change-Id: Icba6a0270f6e1c4c3ed2ba61b55c1a5fbb69752b Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'tests/auto/qlowenergycontroller-gattserver')
-rw-r--r--tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp72
-rw-r--r--tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp115
2 files changed, 176 insertions, 11 deletions
diff --git a/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp b/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
index b1fc7256..97adf9db 100644
--- a/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
+++ b/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
@@ -48,6 +48,9 @@ static QByteArray deviceName() { return "Qt GATT server"; }
static QScopedPointer<QLowEnergyController> leController;
typedef QSharedPointer<QLowEnergyService> ServicePtr;
static QHash<QBluetoothUuid, ServicePtr> services;
+static int descriptorWriteCount = 0;
+static int disconnectCount = 0;
+static QBluetoothAddress remoteDevice;
void addService(const QLowEnergyServiceData &serviceData)
{
@@ -64,7 +67,7 @@ void addRunningSpeedService()
QLowEnergyDescriptorData desc;
desc.setUuid(QBluetoothUuid::ClientCharacteristicConfiguration);
- desc.setValue(QByteArray(1, 0)); // Default: No indication, no notification.
+ desc.setValue(QByteArray(2, 0)); // Default: No indication, no notification.
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::RSCMeasurement);
charData.addDescriptor(desc);
@@ -111,7 +114,7 @@ void addGenericAccessService()
void addCustomService()
{
QLowEnergyServiceData serviceData;
- serviceData.setUuid(QBluetoothUuid(quint16(0x2000))); // Made up.
+ serviceData.setUuid(QBluetoothUuid(quint16(0x2000)));
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
QLowEnergyCharacteristicData charData;
@@ -120,12 +123,24 @@ void addCustomService()
charData.setValue(QByteArray(1024, 'x')); // Long value to test "Read Blob".
serviceData.addCharacteristic(charData);
- charData.setUuid(QBluetoothUuid(quint16(0x5001))); // Made up.
+ charData.setUuid(QBluetoothUuid(quint16(0x5001)));
charData.setProperties(QLowEnergyCharacteristic::Read);
charData.setReadConstraints(QBluetooth::AttAuthorizationRequired); // To test read failure.
serviceData.addCharacteristic(charData);
charData.setValue("something");
+ charData.setUuid(QBluetoothUuid(quint16(0x5002)));
+ charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Indicate);
+ charData.setReadConstraints(QBluetooth::AttAccessConstraints());
+ const QLowEnergyDescriptorData desc(QBluetoothUuid::ClientCharacteristicConfiguration,
+ QByteArray(2, 0));
+ charData.addDescriptor(desc);
+ serviceData.addCharacteristic(charData);
+
+ charData.setUuid(QBluetoothUuid(quint16(0x5003)));
+ charData.setProperties(QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify);
+ serviceData.addCharacteristic(charData);
+
addService(serviceData);
}
@@ -150,8 +165,55 @@ int main(int argc, char *argv[])
addCustomService();
startAdvertising();
- // TODO: Change characteristics, client checks that it gets indication/notification
- // TODO: Where to test that we get the characteristicChanged signal for characteristics that the client writes?
+ const ServicePtr customService = services.value(QBluetoothUuid(quint16(0x2000)));
+ Q_ASSERT(customService);
+
+ const auto stateChangedHandler = [customService]() {
+ switch (leController->state()) {
+ case QLowEnergyController::ConnectedState:
+ remoteDevice = leController->remoteAddress();
+ break;
+ case QLowEnergyController::UnconnectedState: {
+ if (++disconnectCount == 2) {
+ qApp->quit();
+ break;
+ }
+ Q_ASSERT(disconnectCount == 1);
+ const QLowEnergyCharacteristic indicatableChar
+ = customService->characteristic(QBluetoothUuid(quint16(0x5002)));
+ Q_ASSERT(indicatableChar.isValid());
+ customService->writeCharacteristic(indicatableChar, "indicated2");
+ Q_ASSERT(indicatableChar.value() == "indicated2");
+ const QLowEnergyCharacteristic notifiableChar
+ = customService->characteristic(QBluetoothUuid(quint16(0x5003)));
+ Q_ASSERT(notifiableChar.isValid());
+ customService->writeCharacteristic(notifiableChar, "notified2");
+ Q_ASSERT(notifiableChar.value() == "notified2");
+ startAdvertising();
+ break;
+ }
+ default:
+ break;
+ }
+ };
+
+ QObject::connect(leController.data(), &QLowEnergyController::stateChanged, stateChangedHandler);
+ const auto descriptorWriteHandler = [customService]() {
+ if (++descriptorWriteCount != 2)
+ return;
+ const QLowEnergyCharacteristic indicatableChar
+ = customService->characteristic(QBluetoothUuid(quint16(0x5002)));
+ Q_ASSERT(indicatableChar.isValid());
+ customService->writeCharacteristic(indicatableChar, "indicated");
+ Q_ASSERT(indicatableChar.value() == "indicated");
+ const QLowEnergyCharacteristic notifiableChar
+ = customService->characteristic(QBluetoothUuid(quint16(0x5003)));
+ Q_ASSERT(notifiableChar.isValid());
+ customService->writeCharacteristic(notifiableChar, "notified");
+ Q_ASSERT(notifiableChar.value() == "notified");
+ };
+ QObject::connect(customService.data(), &QLowEnergyService::descriptorWritten,
+ descriptorWriteHandler);
return app.exec();
}
diff --git a/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp b/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
index 25098273..c1701217 100644
--- a/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
+++ b/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
@@ -34,6 +34,7 @@
#include <QtBluetooth/qbluetoothaddress.h>
#include <QtBluetooth/qbluetoothdevicediscoveryagent.h>
#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/qbluetoothlocaldevice.h>
#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
#include <QtBluetooth/qlowenergycontroller.h>
@@ -63,7 +64,7 @@ private slots:
// Interaction with actual GATT server goes here. Order is relevant.
void advertisedData();
- void initialServices();
+ void serverCommunication();
private:
QBluetoothAddress m_serverAddress;
@@ -171,8 +172,15 @@ void TestQLowEnergyControllerGattServer::advertisedData()
QVERIFY(m_serverInfo.serviceUuids().contains(QBluetoothUuid(quint16(0x2000))));
}
-void TestQLowEnergyControllerGattServer::initialServices()
+// TODO: Why on earth is this not in the library???
+Q_DECLARE_METATYPE(QLowEnergyCharacteristic)
+Q_DECLARE_METATYPE(QLowEnergyDescriptor)
+
+void TestQLowEnergyControllerGattServer::serverCommunication()
{
+ qRegisterMetaType<QLowEnergyCharacteristic>();
+ qRegisterMetaType<QLowEnergyDescriptor>();
+
if (m_serverAddress.isNull())
QSKIP("No server address provided");
m_leController.reset(QLowEnergyController::createCentral(m_serverInfo));
@@ -235,7 +243,7 @@ void TestQLowEnergyControllerGattServer::initialServices()
const QLowEnergyDescriptor clientConfigDesc
= measurementChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
QVERIFY(clientConfigDesc.isValid());
- QCOMPARE(clientConfigDesc.value(), QByteArray(1, 0));
+ QCOMPARE(clientConfigDesc.value(), QByteArray(2, 0));
QCOMPARE(measurementChar.properties(), QLowEnergyCharacteristic::Notify);
QCOMPARE(measurementChar.value(), QByteArray()); // Empty because Read property not set
QLowEnergyCharacteristic featureChar
@@ -247,16 +255,16 @@ void TestQLowEnergyControllerGattServer::initialServices()
featureValue[0] = 1 << 2;
QCOMPARE(featureChar.value(), featureValue);
- const QScopedPointer<QLowEnergyService> customService(
+ QScopedPointer<QLowEnergyService> customService(
m_leController->createServiceObject(QBluetoothUuid(quint16(0x2000))));
QVERIFY(!customService.isNull());
customService->discoverDetails();
while (customService->state() != QLowEnergyService::ServiceDiscovered) {
spy.reset(new QSignalSpy(customService.data(), &QLowEnergyService::stateChanged));
- QVERIFY(spy->wait(3000));
+ QVERIFY(spy->wait(5000));
}
QCOMPARE(customService->includedServices().count(), 0);
- QCOMPARE(customService->characteristics().count(), 2);
+ QCOMPARE(customService->characteristics().count(), 4);
QLowEnergyCharacteristic customChar
= customService->characteristic(QBluetoothUuid(quint16(0x5000)));
QVERIFY(customChar.isValid());
@@ -269,11 +277,99 @@ void TestQLowEnergyControllerGattServer::initialServices()
QCOMPARE(customChar2.descriptors().count(), 0);
QCOMPARE(customChar2.value(), QByteArray()); // Was not readable due to authorization requirement.
+ QLowEnergyCharacteristic customChar3
+ = customService->characteristic(QBluetoothUuid(quint16(0x5002)));
+ QVERIFY(customChar3.isValid());
+ QCOMPARE(customChar3.properties(),
+ QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Indicate);
+ QCOMPARE(customChar3.descriptors().count(), 1);
+ QLowEnergyDescriptor cc3ClientConfig
+ = customChar3.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ QVERIFY(cc3ClientConfig.isValid());
+
+ QLowEnergyCharacteristic customChar4
+ = customService->characteristic(QBluetoothUuid(quint16(0x5003)));
+ QVERIFY(customChar4.isValid());
+ QCOMPARE(customChar4.properties(),
+ QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::Notify);
+ QCOMPARE(customChar4.descriptors().count(), 1);
+ QLowEnergyDescriptor cc4ClientConfig
+ = customChar4.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ QVERIFY(cc4ClientConfig.isValid());
+
customService->writeCharacteristic(customChar, "whatever");
spy.reset(new QSignalSpy(customService.data(), static_cast<void (QLowEnergyService::*)
(QLowEnergyService::ServiceError)>(&QLowEnergyService::error)));
QVERIFY(spy->wait(3000));
QCOMPARE(customService->error(), QLowEnergyService::CharacteristicWriteError);
+
+ QByteArray indicateValue(2, 0);
+ indicateValue[0] = 2;
+ customService->writeDescriptor(cc3ClientConfig, indicateValue);
+ spy.reset(new QSignalSpy(customService.data(), &QLowEnergyService::descriptorWritten));
+ QVERIFY(spy->wait(3000));
+
+ QByteArray notifyValue(2, 0);
+ notifyValue[0] = 1;
+ customService->writeDescriptor(cc4ClientConfig, notifyValue);
+ spy.reset(new QSignalSpy(customService.data(), &QLowEnergyService::descriptorWritten));
+ QVERIFY(spy->wait(3000));
+
+ // Server now changes the characteristic values.
+
+ spy.reset(new QSignalSpy(customService.data(), &QLowEnergyService::characteristicChanged));
+ QVERIFY(spy->wait(3000));
+ if (spy->count() == 1)
+ QVERIFY(spy->wait(3000));
+ QCOMPARE(customChar3.value().constData(), "indicated");
+ QCOMPARE(customChar4.value().constData(), "notified");
+
+ const bool isBonded = QBluetoothLocalDevice().pairingStatus(m_serverAddress)
+ != QBluetoothLocalDevice::Unpaired;
+ m_leController->disconnectFromDevice();
+
+ if (m_leController->state() != QLowEnergyController::UnconnectedState) {
+ spy.reset(new QSignalSpy(m_leController.data(), &QLowEnergyController::stateChanged));
+ QVERIFY(spy->wait(3000));
+ }
+ QCOMPARE(m_leController->state(), QLowEnergyController::UnconnectedState);
+
+ // Server now changes the characteristic values again while we're offline.
+ // Note: We cannot test indications and notifications for this case, as the client does
+ // not cache the old information and thus does not yet know the characteristics
+ // at the time the notification/indication is received.
+
+ QTest::qWait(3000);
+ m_leController->connectToDevice();
+ spy.reset(new QSignalSpy(m_leController.data(), &QLowEnergyController::connected));
+ QVERIFY(spy->wait(30000));
+ m_leController->discoverServices();
+ spy.reset(new QSignalSpy(m_leController.data(), &QLowEnergyController::discoveryFinished));
+ QVERIFY(spy->wait(30000));
+ customService.reset(m_leController->createServiceObject(QBluetoothUuid(quint16(0x2000))));
+ QVERIFY(!customService.isNull());
+ customService->discoverDetails();
+ while (customService->state() != QLowEnergyService::ServiceDiscovered) {
+ spy.reset(new QSignalSpy(customService.data(), &QLowEnergyService::stateChanged));
+ QVERIFY(spy->wait(5000));
+ }
+ customChar3 = customService->characteristic(QBluetoothUuid(quint16(0x5002)));
+ QVERIFY(customChar3.isValid());
+ QCOMPARE(customChar3.value().constData(), "indicated2");
+ customChar4 = customService->characteristic(QBluetoothUuid(quint16(0x5003)));
+ QVERIFY(customChar4.isValid());
+ QCOMPARE(customChar4.value().constData(), "notified2");
+ cc3ClientConfig = customChar3.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ QVERIFY(cc3ClientConfig.isValid());
+ cc4ClientConfig = customChar4.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ QVERIFY(cc4ClientConfig.isValid());
+ if (isBonded) {
+ QCOMPARE(cc3ClientConfig.value(), indicateValue);
+ QCOMPARE(cc4ClientConfig.value(), notifyValue);
+ } else {
+ QCOMPARE(cc3ClientConfig.value(), QByteArray(2, 0));
+ QCOMPARE(cc4ClientConfig.value(), QByteArray(2, 0));
+ }
}
void TestQLowEnergyControllerGattServer::controllerType()
@@ -320,6 +416,13 @@ void TestQLowEnergyControllerGattServer::serviceData()
charData.setValue("value");
QCOMPARE(charData.value().constData(), "value");
+ charData.setValueLength(4, 7);
+ QCOMPARE(charData.minimumValueLength(), 4);
+ QCOMPARE(charData.maximumValueLength(), 7);
+ charData.setValueLength(5, 2);
+ QCOMPARE(charData.minimumValueLength(), 5);
+ QCOMPARE(charData.maximumValueLength(), 5);
+
const QLowEnergyCharacteristic::PropertyTypes props
= QLowEnergyCharacteristic::Read | QLowEnergyCharacteristic::WriteSigned;
charData.setProperties(props);