1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QBluetoothLocalDevice>
#include <QLowEnergyController>
#include <QLowEnergyServiceData>
#include <QLowEnergyCharacteristicData>
#include <QLowEnergyDescriptorData>
#if QT_CONFIG(permissions)
#include <QtTest/qtesteventloop.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qpermissions.h>
#include <QtCore/qnamespace.h>
#endif // permissions
using namespace Qt::Literals::StringLiterals;
static constexpr auto leServiceUuid{"10f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
static constexpr auto leCharUuid1{"11f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
static const qsizetype leCharacteristicSize = 4; // Set to 1...512 bytes
static QByteArray leCharacteristicValue = QByteArray{leCharacteristicSize, 1};
static QByteArray leDescriptorValue = "a descriptor value"_ba;
class tst_qlowenergycontroller_peripheral : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void init();
void cleanup();
void localCharacteristicReadAfterUpdate();
void localDescriptorReadAfterUpdate();
private:
QBluetoothHostInfo mDevice;
std::unique_ptr<QLowEnergyController> mController;
QList<QSharedPointer<QLowEnergyService>> mServices;
};
void tst_qlowenergycontroller_peripheral::initTestCase()
{
#ifndef Q_OS_IOS
auto devices = QBluetoothLocalDevice::allDevices();
if (devices.isEmpty())
QSKIP("Failed to find local adapter");
else
mDevice = devices.back();
#endif // Q_OS_IOS
#if QT_CONFIG(permissions)
Qt::PermissionStatus permissionStatus = qApp->checkPermission(QBluetoothPermission{});
// FIXME: Android will add more specific BT permissions, fix when appropriate
// change is in qtbase.
if (qApp->checkPermission(QBluetoothPermission{}) == Qt::PermissionStatus::Undetermined) {
QTestEventLoop loop;
qApp->requestPermission(QBluetoothPermission{}, [&permissionStatus, &loop](const QPermission &permission){
permissionStatus = permission.status();
loop.exitLoop();
});
if (permissionStatus == Qt::PermissionStatus::Undetermined)
loop.enterLoopMSecs(30000);
}
if (permissionStatus != Qt::PermissionStatus::Granted)
QSKIP("This manual test requires Blutooth permissions granted.");
#endif // permissions
}
void tst_qlowenergycontroller_peripheral::init()
{
QList<QLowEnergyServiceData> serviceDefinitions;
QLowEnergyServiceData sd;
sd.setType(QLowEnergyServiceData::ServiceTypePrimary);
sd.setUuid(QBluetoothUuid(leServiceUuid));
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid(leCharUuid1));
charData.setValue(leCharacteristicValue);
charData.setValueLength(leCharacteristicSize, leCharacteristicSize);
charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
| QLowEnergyCharacteristic::PropertyType::Write
| QLowEnergyCharacteristic::PropertyType::Notify
| QLowEnergyCharacteristic::ExtendedProperty);
const QLowEnergyDescriptorData clientConfig(
QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
QLowEnergyCharacteristic::CCCDDisable);
charData.addDescriptor(clientConfig);
const QLowEnergyDescriptorData userDescription(
QBluetoothUuid::DescriptorType::CharacteristicUserDescription,
leDescriptorValue);
charData.addDescriptor(userDescription);
const QLowEnergyDescriptorData extendedProperties(
QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties,
// From bluetooth specs: length 2 bytes
// bit 0: reliable write, bit 1: writable auxiliaries
QByteArray::fromHex("0300"));
charData.addDescriptor(extendedProperties);
sd.addCharacteristic(charData);
serviceDefinitions << sd;
#ifndef Q_OS_IOS
mController.reset(QLowEnergyController::createPeripheral(mDevice.address()));
#else
mController.reset(QLowEnergyController::createPeripheral());
#endif // Q_OS_IOS
QVERIFY(mController);
for (const auto &serviceData : serviceDefinitions) {
mServices.emplaceBack(mController->addService(serviceData));
}
}
void tst_qlowenergycontroller_peripheral::cleanup()
{
if (mController)
mController->stopAdvertising();
mController.reset();
mServices.clear();
}
void tst_qlowenergycontroller_peripheral::localCharacteristicReadAfterUpdate()
{
#ifdef Q_OS_WINDOWS
QSKIP("Windows does not support peripheral");
#endif
auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
QVERIFY(characteristic.isValid());
const auto initialValue = characteristic.value();
auto newValue = initialValue;
newValue[0] = newValue[0] + 1;
mServices[0]->writeCharacteristic(characteristic, newValue);
QTRY_COMPARE(characteristic.value(), newValue);
}
void tst_qlowenergycontroller_peripheral::localDescriptorReadAfterUpdate()
{
#ifdef Q_OS_WINDOWS
QSKIP("Windows does not support peripheral");
#elif defined Q_OS_DARWIN
QSKIP("Apple devices do not support descriptor value update");
#endif
auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
QVERIFY(characteristic.isValid());
auto descriptor = characteristic.descriptor(
QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
QVERIFY(descriptor.isValid());
const auto initialValue = descriptor.value();
auto newValue = initialValue;
newValue[0] = newValue[0] + 1;
mServices[0]->writeDescriptor(descriptor, newValue);
QCOMPARE(descriptor.value(), newValue);
}
QTEST_MAIN(tst_qlowenergycontroller_peripheral)
#include "tst_qlowenergycontroller_peripheral.moc"
|