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 | |
40 | QT_USE_NAMESPACE |
41 | |
42 | class tst_QLowEnergyDescriptor : public QObject |
43 | { |
44 | Q_OBJECT |
45 | |
46 | public: |
47 | tst_QLowEnergyDescriptor(); |
48 | ~tst_QLowEnergyDescriptor(); |
49 | |
50 | protected slots: |
51 | void deviceDiscovered(const QBluetoothDeviceInfo &info); |
52 | |
53 | private slots: |
54 | void initTestCase(); |
55 | void cleanupTestCase(); |
56 | void tst_constructionDefault(); |
57 | void tst_assignCompare(); |
58 | |
59 | private: |
60 | QList<QBluetoothDeviceInfo> remoteLeDeviceInfos; |
61 | QLowEnergyController *globalControl; |
62 | QLowEnergyService *globalService; |
63 | }; |
64 | |
65 | tst_QLowEnergyDescriptor::tst_QLowEnergyDescriptor() : |
66 | globalControl(nullptr), globalService(nullptr) |
67 | { |
68 | QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true" )); |
69 | } |
70 | |
71 | tst_QLowEnergyDescriptor::~tst_QLowEnergyDescriptor() |
72 | { |
73 | } |
74 | |
75 | void 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 | |
175 | void tst_QLowEnergyDescriptor::cleanupTestCase() |
176 | { |
177 | if (globalControl) |
178 | globalControl->disconnectFromDevice(); |
179 | } |
180 | |
181 | void tst_QLowEnergyDescriptor::deviceDiscovered(const QBluetoothDeviceInfo &info) |
182 | { |
183 | if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) |
184 | remoteLeDeviceInfos.append(t: info); |
185 | } |
186 | |
187 | void 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 | |
232 | void 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 | |
333 | QTEST_MAIN(tst_QLowEnergyDescriptor) |
334 | |
335 | #include "tst_qlowenergydescriptor.moc" |
336 | |
337 | |