1/***************************************************************************
2**
3** Copyright (C) 2016 BlackBerry Limited all rights reserved
4** Copyright (C) 2016 The Qt Company Ltd.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtBluetooth module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtTest/QtTest>
31#include <QUuid>
32
33#include <QDebug>
34
35#include <QBluetoothDeviceDiscoveryAgent>
36#include <QLowEnergyDescriptor>
37#include <QLowEnergyController>
38#include <QBluetoothLocalDevice>
39
40QT_USE_NAMESPACE
41
42class tst_QLowEnergyDescriptor : public QObject
43{
44 Q_OBJECT
45
46public:
47 tst_QLowEnergyDescriptor();
48 ~tst_QLowEnergyDescriptor();
49
50protected slots:
51 void deviceDiscovered(const QBluetoothDeviceInfo &info);
52
53private slots:
54 void initTestCase();
55 void cleanupTestCase();
56 void tst_constructionDefault();
57 void tst_assignCompare();
58
59private:
60 QList<QBluetoothDeviceInfo> remoteLeDeviceInfos;
61 QLowEnergyController *globalControl;
62 QLowEnergyService *globalService;
63};
64
65tst_QLowEnergyDescriptor::tst_QLowEnergyDescriptor() :
66 globalControl(nullptr), globalService(nullptr)
67{
68 QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
69}
70
71tst_QLowEnergyDescriptor::~tst_QLowEnergyDescriptor()
72{
73}
74
75void tst_QLowEnergyDescriptor::initTestCase()
76{
77 if (QBluetoothLocalDevice::allDevices().isEmpty()) {
78 qWarning(msg: "No remote device discovered.");
79
80 return;
81 }
82
83 // start Bluetooth if not started
84 QBluetoothLocalDevice device;
85 device.powerOn();
86
87 // find an arbitrary low energy device in vincinity
88 // find an arbitrary service with descriptor
89
90 QBluetoothDeviceDiscoveryAgent *devAgent = new QBluetoothDeviceDiscoveryAgent(this);
91 connect(sender: devAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
92 receiver: this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
93
94 QSignalSpy errorSpy(devAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)));
95 QVERIFY(errorSpy.isValid());
96 QVERIFY(errorSpy.isEmpty());
97
98 QSignalSpy spy(devAgent, SIGNAL(finished()));
99 // there should be no changes yet
100 QVERIFY(spy.isValid());
101 QVERIFY(spy.isEmpty());
102
103 devAgent->start();
104 QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, 50000);
105
106 // find first service with descriptor
107 QLowEnergyController *controller = nullptr;
108 for (const QBluetoothDeviceInfo& remoteDeviceInfo : qAsConst(t&: remoteLeDeviceInfos)) {
109 controller = QLowEnergyController::createCentral(remoteDevice: remoteDeviceInfo, parent: this);
110 qDebug() << "Connecting to" << remoteDeviceInfo.address();
111 controller->connectToDevice();
112 QTRY_IMPL(controller->state() != QLowEnergyController::ConnectingState,
113 20000)
114 if (controller->state() != QLowEnergyController::ConnectedState) {
115 // any error and we skip
116 delete controller;
117 qDebug() << "Skipping device";
118 continue;
119 }
120
121 QSignalSpy discoveryFinishedSpy(controller, SIGNAL(discoveryFinished()));
122 QSignalSpy stateSpy(controller, SIGNAL(stateChanged(QLowEnergyController::ControllerState)));
123 controller->discoverServices();
124 QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
125 QCOMPARE(stateSpy.count(), 2);
126 QCOMPARE(stateSpy.at(0).at(0).value<QLowEnergyController::ControllerState>(),
127 QLowEnergyController::DiscoveringState);
128 QCOMPARE(stateSpy.at(1).at(0).value<QLowEnergyController::ControllerState>(),
129 QLowEnergyController::DiscoveredState);
130
131 const QList<QBluetoothUuid> leServiceUuids = controller->services();
132 for (const QBluetoothUuid &leServiceUuid : leServiceUuids) {
133 QLowEnergyService *leService = controller->createServiceObject(service: leServiceUuid, parent: this);
134 if (!leService)
135 continue;
136
137 leService->discoverDetails();
138 QTRY_VERIFY_WITH_TIMEOUT(
139 leService->state() == QLowEnergyService::ServiceDiscovered, 10000);
140
141 const QList<QLowEnergyCharacteristic> chars = leService->characteristics();
142 for (const QLowEnergyCharacteristic &ch : chars) {
143 const QList<QLowEnergyDescriptor> descriptors = ch.descriptors();
144 for (const QLowEnergyDescriptor &d : descriptors) {
145 if (!d.value().isEmpty()) {
146 globalService = leService;
147 globalControl = controller;
148 qWarning() << "Found service with descriptor" << remoteDeviceInfo.address()
149 << globalService->serviceName() << globalService->serviceUuid();
150 break;
151 }
152 }
153 if (globalControl)
154 break;
155 }
156
157 if (globalControl)
158 break;
159 else
160 delete leService;
161 }
162
163 if (globalControl)
164 break;
165
166 delete controller;
167 }
168
169 if (!globalControl) {
170 qWarning() << "Test limited due to missing remote QLowEnergyDescriptor."
171 << "Please ensure the Bluetooth Low Energy device is advertising its services.";
172 }
173}
174
175void tst_QLowEnergyDescriptor::cleanupTestCase()
176{
177 if (globalControl)
178 globalControl->disconnectFromDevice();
179}
180
181void tst_QLowEnergyDescriptor::deviceDiscovered(const QBluetoothDeviceInfo &info)
182{
183 if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
184 remoteLeDeviceInfos.append(t: info);
185}
186
187void tst_QLowEnergyDescriptor::tst_constructionDefault()
188{
189 QLowEnergyDescriptor descriptor;
190 QVERIFY(!descriptor.isValid());
191 QCOMPARE(descriptor.value(), QByteArray());
192 QVERIFY(descriptor.uuid().isNull());
193 QVERIFY(descriptor.handle() == 0);
194 QCOMPARE(descriptor.name(), QString());
195 QCOMPARE(descriptor.type(), QBluetoothUuid::UnknownDescriptorType);
196
197 QLowEnergyDescriptor copyConstructed(descriptor);
198 QVERIFY(!copyConstructed.isValid());
199 QCOMPARE(copyConstructed.value(), QByteArray());
200 QVERIFY(copyConstructed.uuid().isNull());
201 QVERIFY(copyConstructed.handle() == 0);
202 QCOMPARE(copyConstructed.name(), QString());
203 QCOMPARE(copyConstructed.type(), QBluetoothUuid::UnknownDescriptorType);
204
205 QVERIFY(copyConstructed == descriptor);
206 QVERIFY(descriptor == copyConstructed);
207 QVERIFY(!(copyConstructed != descriptor));
208 QVERIFY(!(descriptor != copyConstructed));
209
210 QLowEnergyDescriptor assigned;
211
212 QVERIFY(assigned == descriptor);
213 QVERIFY(descriptor == assigned);
214 QVERIFY(!(assigned != descriptor));
215 QVERIFY(!(descriptor != assigned));
216
217 assigned = descriptor;
218 QVERIFY(!assigned.isValid());
219 QCOMPARE(assigned.value(), QByteArray());
220 QVERIFY(assigned.uuid().isNull());
221 QVERIFY(assigned.handle() == 0);
222 QCOMPARE(assigned.name(), QString());
223 QCOMPARE(assigned.type(), QBluetoothUuid::UnknownDescriptorType);
224
225 QVERIFY(assigned == descriptor);
226 QVERIFY(descriptor == assigned);
227 QVERIFY(!(assigned != descriptor));
228 QVERIFY(!(descriptor != assigned));
229}
230
231
232void tst_QLowEnergyDescriptor::tst_assignCompare()
233{
234 //find the descriptor
235 if (!globalService)
236 QSKIP("No descriptor found.");
237
238 QLowEnergyDescriptor target;
239 QVERIFY(!target.isValid());
240 QCOMPARE(target.type(), QBluetoothUuid::UnknownDescriptorType);
241 QCOMPARE(target.name(), QString());
242 QCOMPARE(target.handle(), QLowEnergyHandle(0));
243 QCOMPARE(target.uuid(), QBluetoothUuid());
244 QCOMPARE(target.value(), QByteArray());
245
246 int index = -1;
247 QList<QLowEnergyDescriptor> targets;
248 const QList<QLowEnergyCharacteristic> chars = globalService->characteristics();
249 for (const QLowEnergyCharacteristic &ch : chars) {
250 if (!ch.descriptors().isEmpty()) {
251 targets = ch.descriptors();
252 for (int i = 0; i < targets.size(); ++i) {
253 // try to get a descriptor we can read
254 if (targets[i].type() == QBluetoothUuid::CharacteristicUserDescription) {
255 index = i;
256 break;
257 }
258 }
259 break;
260 }
261 }
262
263 if (targets.isEmpty())
264 QSKIP("No descriptor found despite prior indication.");
265
266 QVERIFY(index != -1);
267
268 // test assignment operator
269 target = targets[index];
270 QVERIFY(target.isValid());
271 QVERIFY(target.type() != QBluetoothUuid::UnknownDescriptorType);
272 QVERIFY(!target.name().isEmpty());
273 QVERIFY(target.handle() > 0);
274 QVERIFY(!target.uuid().isNull());
275 QVERIFY(!target.value().isEmpty());
276
277 QVERIFY(target == targets[index]);
278 QVERIFY(targets[index] == target);
279 QVERIFY(!(target != targets[index]));
280 QVERIFY(!(targets[index] != target));
281
282 QCOMPARE(target.isValid(), targets[index].isValid());
283 QCOMPARE(target.type(), targets[index].type());
284 QCOMPARE(target.name(), targets[index].name());
285 QCOMPARE(target.handle(), targets[index].handle());
286 QCOMPARE(target.uuid(), targets[index].uuid());
287 QCOMPARE(target.value(), targets[index].value());
288
289 // test copy constructor
290 QLowEnergyDescriptor copyConstructed(target);
291 QCOMPARE(copyConstructed.isValid(), targets[index].isValid());
292 QCOMPARE(copyConstructed.type(), targets[index].type());
293 QCOMPARE(copyConstructed.name(), targets[index].name());
294 QCOMPARE(copyConstructed.handle(), targets[index].handle());
295 QCOMPARE(copyConstructed.uuid(), targets[index].uuid());
296 QCOMPARE(copyConstructed.value(), targets[index].value());
297
298 QVERIFY(copyConstructed == target);
299 QVERIFY(target == copyConstructed);
300 QVERIFY(!(copyConstructed != target));
301 QVERIFY(!(target != copyConstructed));
302
303 // test invalidation
304 QLowEnergyDescriptor invalid;
305 target = invalid;
306 QVERIFY(!target.isValid());
307 QCOMPARE(target.value(), QByteArray());
308 QVERIFY(target.uuid().isNull());
309 QVERIFY(target.handle() == 0);
310 QCOMPARE(target.name(), QString());
311 QCOMPARE(target.type(), QBluetoothUuid::UnknownDescriptorType);
312
313 QVERIFY(invalid == target);
314 QVERIFY(target == invalid);
315 QVERIFY(!(invalid != target));
316 QVERIFY(!(target != invalid));
317
318 QVERIFY(!(targets[index] == target));
319 QVERIFY(!(target == targets[index]));
320 QVERIFY(targets[index] != target);
321 QVERIFY(target != targets[index]);
322
323 if (targets.count() >= 2) {
324 QLowEnergyDescriptor second = targets[(index+1)%2];
325 // at least two descriptors
326 QVERIFY(!(targets[index] == second));
327 QVERIFY(!(second == targets[index]));
328 QVERIFY(targets[index] != second);
329 QVERIFY(second != targets[index]);
330 }
331}
332
333QTEST_MAIN(tst_QLowEnergyDescriptor)
334
335#include "tst_qlowenergydescriptor.moc"
336
337

source code of qtconnectivity/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp