1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtBluetooth module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | |
31 | #include <QDebug> |
32 | #include <QVariant> |
33 | |
34 | #include <private/qtbluetoothglobal_p.h> |
35 | #include <qbluetoothaddress.h> |
36 | #include <qbluetoothlocaldevice.h> |
37 | |
38 | QT_USE_NAMESPACE |
39 | |
40 | /* |
41 | * Running the manual tests requires another Bluetooth device in the vincinity. |
42 | * The remote device's address must be passed via the BT_TEST_DEVICE env variable. |
43 | * Every pairing request must be accepted within a 10s interval of appearing. |
44 | * If BT_TEST_DEVICE is not set manual tests will be skipped. |
45 | **/ |
46 | |
47 | class tst_QBluetoothLocalDevice : public QObject |
48 | { |
49 | Q_OBJECT |
50 | |
51 | public: |
52 | tst_QBluetoothLocalDevice(); |
53 | ~tst_QBluetoothLocalDevice(); |
54 | |
55 | private slots: |
56 | void initTestCase(); |
57 | void tst_powerOn(); |
58 | void tst_powerOff(); |
59 | void tst_hostModes(); |
60 | void tst_hostModes_data(); |
61 | void tst_address(); |
62 | void tst_name(); |
63 | void tst_isValid(); |
64 | void tst_allDevices(); |
65 | void tst_construction(); |
66 | void tst_pairingStatus_data(); |
67 | void tst_pairingStatus(); |
68 | void tst_pairDevice_data(); |
69 | void tst_pairDevice(); |
70 | |
71 | private: |
72 | QBluetoothAddress remoteDevice; |
73 | bool expectRemoteDevice; |
74 | }; |
75 | |
76 | tst_QBluetoothLocalDevice::tst_QBluetoothLocalDevice() |
77 | : expectRemoteDevice(false) |
78 | { |
79 | const QString remote = qgetenv(varName: "BT_TEST_DEVICE" ); |
80 | if (!remote.isEmpty()) { |
81 | remoteDevice = QBluetoothAddress(remote); |
82 | expectRemoteDevice = true; |
83 | qWarning() << "Using remote device " << remote << " for testing. Ensure that the device is discoverable for pairing requests" ; |
84 | } else { |
85 | qWarning() << "Not using any remote device for testing. Set BT_TEST_DEVICE env to run manual tests involving a remote device" ; |
86 | } |
87 | |
88 | // start with host powered off |
89 | QBluetoothLocalDevice *device = new QBluetoothLocalDevice(); |
90 | device->setHostMode(QBluetoothLocalDevice::HostPoweredOff); |
91 | delete device; |
92 | // wait for the device to switch bluetooth mode. |
93 | QTest::qWait(ms: 1000); |
94 | } |
95 | |
96 | tst_QBluetoothLocalDevice::~tst_QBluetoothLocalDevice() |
97 | { |
98 | } |
99 | |
100 | void tst_QBluetoothLocalDevice::initTestCase() |
101 | { |
102 | if (expectRemoteDevice) { |
103 | //test passed Bt address here since we cannot do that in the ctor |
104 | QVERIFY2(!remoteDevice.isNull(), "BT_TEST_DEVICE is not a valid Bluetooth address" ); |
105 | } |
106 | } |
107 | |
108 | void tst_QBluetoothLocalDevice::tst_powerOn() |
109 | { |
110 | #ifdef Q_OS_OSX |
111 | QSKIP("Not possible on OS X" ); |
112 | #endif |
113 | #ifdef Q_OS_WIN |
114 | QSKIP("Not possible on Windows" ); |
115 | #endif |
116 | |
117 | QBluetoothLocalDevice localDevice; |
118 | |
119 | QSignalSpy hostModeSpy(&localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode))); |
120 | // there should be no changes yet |
121 | QVERIFY(hostModeSpy.isValid()); |
122 | QVERIFY(hostModeSpy.isEmpty()); |
123 | |
124 | if (!QBluetoothLocalDevice::allDevices().count()) |
125 | QSKIP("Skipping test due to missing Bluetooth device" ); |
126 | |
127 | localDevice.powerOn(); |
128 | // async, wait for it |
129 | QTRY_VERIFY(hostModeSpy.count() > 0); |
130 | QBluetoothLocalDevice::HostMode hostMode= localDevice.hostMode(); |
131 | // we should not be powered off |
132 | QVERIFY(hostMode == QBluetoothLocalDevice::HostConnectable |
133 | || hostMode == QBluetoothLocalDevice::HostDiscoverable); |
134 | } |
135 | |
136 | void tst_QBluetoothLocalDevice::tst_powerOff() |
137 | { |
138 | #ifdef Q_OS_OSX |
139 | QSKIP("Not possible on OS X" ); |
140 | #endif |
141 | #ifdef Q_OS_WIN |
142 | QSKIP("Not possible on Windows" ); |
143 | #endif |
144 | |
145 | if (!QBluetoothLocalDevice::allDevices().count()) |
146 | QSKIP("Skipping test due to missing Bluetooth device" ); |
147 | |
148 | { |
149 | QBluetoothLocalDevice *device = new QBluetoothLocalDevice(); |
150 | device->powerOn(); |
151 | delete device; |
152 | // wait for the device to switch bluetooth mode. |
153 | QTest::qWait(ms: 1000); |
154 | } |
155 | QBluetoothLocalDevice localDevice; |
156 | QSignalSpy hostModeSpy(&localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode))); |
157 | // there should be no changes yet |
158 | QVERIFY(hostModeSpy.isValid()); |
159 | QVERIFY(hostModeSpy.isEmpty()); |
160 | |
161 | localDevice.setHostMode(QBluetoothLocalDevice::HostPoweredOff); |
162 | // async, wait for it |
163 | QTRY_VERIFY(hostModeSpy.count() > 0); |
164 | // we should not be powered off |
165 | QVERIFY(localDevice.hostMode() == QBluetoothLocalDevice::HostPoweredOff); |
166 | |
167 | } |
168 | |
169 | void tst_QBluetoothLocalDevice::tst_hostModes_data() |
170 | { |
171 | QTest::addColumn<QBluetoothLocalDevice::HostMode>(name: "hostModeExpected" ); |
172 | QTest::addColumn<bool>(name: "expectSignal" ); |
173 | |
174 | QTest::newRow(dataTag: "HostDiscoverable1" ) << QBluetoothLocalDevice::HostDiscoverable << true; |
175 | QTest::newRow(dataTag: "HostPoweredOff1" ) << QBluetoothLocalDevice::HostPoweredOff << true; |
176 | QTest::newRow(dataTag: "HostPoweredOff2" ) << QBluetoothLocalDevice::HostPoweredOff << false; |
177 | QTest::newRow(dataTag: "HostConnectable1" ) << QBluetoothLocalDevice::HostConnectable << true; |
178 | QTest::newRow(dataTag: "HostConnectable2" ) << QBluetoothLocalDevice::HostConnectable << false; |
179 | QTest::newRow(dataTag: "HostDiscoverable2" ) << QBluetoothLocalDevice::HostDiscoverable << true; |
180 | QTest::newRow(dataTag: "HostConnectable3" ) << QBluetoothLocalDevice::HostConnectable << true; |
181 | QTest::newRow(dataTag: "HostPoweredOff3" ) << QBluetoothLocalDevice::HostPoweredOff << true; |
182 | QTest::newRow(dataTag: "HostDiscoverable3" ) << QBluetoothLocalDevice::HostDiscoverable << true; |
183 | QTest::newRow(dataTag: "HostDiscoverable4" ) << QBluetoothLocalDevice::HostDiscoverable << false; |
184 | QTest::newRow(dataTag: "HostConnectable4" ) << QBluetoothLocalDevice::HostConnectable << true; |
185 | } |
186 | |
187 | void tst_QBluetoothLocalDevice::tst_hostModes() |
188 | { |
189 | #ifdef Q_OS_OSX |
190 | QSKIP("Not possible on OS X" ); |
191 | #endif |
192 | #ifdef Q_OS_WIN |
193 | QSKIP("Not possible on Windows" ); |
194 | #endif |
195 | |
196 | QFETCH(QBluetoothLocalDevice::HostMode, hostModeExpected); |
197 | QFETCH(bool, expectSignal); |
198 | |
199 | if (!QBluetoothLocalDevice::allDevices().count()) |
200 | QSKIP("Skipping test due to missing Bluetooth device" ); |
201 | |
202 | QBluetoothLocalDevice localDevice; |
203 | QSignalSpy hostModeSpy(&localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode))); |
204 | // there should be no changes yet |
205 | QVERIFY(hostModeSpy.isValid()); |
206 | QVERIFY(hostModeSpy.isEmpty()); |
207 | |
208 | QTest::qWait(ms: 1000); |
209 | localDevice.setHostMode(hostModeExpected); |
210 | // wait for the device to switch bluetooth mode. |
211 | QTest::qWait(ms: 1000); |
212 | if (hostModeExpected != localDevice.hostMode()) { |
213 | QTRY_VERIFY(hostModeSpy.count() > 0); |
214 | } |
215 | // test the actual signal values. |
216 | if (expectSignal) |
217 | QVERIFY(hostModeSpy.count() > 0); |
218 | else |
219 | QVERIFY(hostModeSpy.count() == 0); |
220 | |
221 | if (expectSignal) { |
222 | QList<QVariant> arguments = hostModeSpy.takeLast(); |
223 | QBluetoothLocalDevice::HostMode hostMode = qvariant_cast<QBluetoothLocalDevice::HostMode>(v: arguments.at(i: 0)); |
224 | QCOMPARE(hostModeExpected, hostMode); |
225 | } |
226 | // test actual |
227 | QCOMPARE(hostModeExpected, localDevice.hostMode()); |
228 | } |
229 | |
230 | void tst_QBluetoothLocalDevice::tst_address() |
231 | { |
232 | if (!QBluetoothLocalDevice::allDevices().count()) |
233 | QSKIP("Skipping test due to missing Bluetooth device" ); |
234 | |
235 | QBluetoothLocalDevice localDevice; |
236 | QVERIFY(!localDevice.address().toString().isEmpty()); |
237 | QVERIFY(!localDevice.address().isNull()); |
238 | } |
239 | void tst_QBluetoothLocalDevice::tst_name() |
240 | { |
241 | if (!QBluetoothLocalDevice::allDevices().count()) |
242 | QSKIP("Skipping test due to missing Bluetooth device" ); |
243 | |
244 | QBluetoothLocalDevice localDevice; |
245 | QVERIFY(!localDevice.name().isEmpty()); |
246 | } |
247 | void tst_QBluetoothLocalDevice::tst_isValid() |
248 | { |
249 | #if defined(Q_OS_MACOS) || QT_CONFIG(winrt_bt) |
250 | // On OS X we can have a valid device (device.isValid() == true), |
251 | // that has neither a name nor a valid address - this happens |
252 | // if a Bluetooth adapter is OFF. |
253 | if (!QBluetoothLocalDevice::allDevices().count()) |
254 | QSKIP("Skipping test due to missing Bluetooth device" ); |
255 | #endif |
256 | |
257 | QBluetoothLocalDevice localDevice; |
258 | QBluetoothAddress invalidAddress("FF:FF:FF:FF:FF:FF" ); |
259 | |
260 | const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices(); |
261 | if (devices.count()) { |
262 | QVERIFY(localDevice.isValid()); |
263 | bool defaultFound = false; |
264 | for (int i = 0; i<devices.count(); i++) { |
265 | QVERIFY(devices.at(i).address() != invalidAddress); |
266 | if (devices.at(i).address() == localDevice.address() ) { |
267 | defaultFound = true; |
268 | } else { |
269 | QBluetoothLocalDevice otherDevice(devices.at(i).address()); |
270 | QVERIFY(otherDevice.isValid()); |
271 | } |
272 | } |
273 | QVERIFY(defaultFound); |
274 | } else { |
275 | QVERIFY(!localDevice.isValid()); |
276 | } |
277 | |
278 | //ensure common behavior of invalid local device |
279 | QBluetoothLocalDevice invalidLocalDevice(invalidAddress); |
280 | QVERIFY(!invalidLocalDevice.isValid()); |
281 | QCOMPARE(invalidLocalDevice.address(), QBluetoothAddress()); |
282 | QCOMPARE(invalidLocalDevice.name(), QString()); |
283 | #if !QT_CONFIG(winrt_bt) |
284 | QCOMPARE(invalidLocalDevice.pairingStatus(QBluetoothAddress()), QBluetoothLocalDevice::Unpaired ); |
285 | QCOMPARE(invalidLocalDevice.hostMode(), QBluetoothLocalDevice::HostPoweredOff); |
286 | #else |
287 | // When QTBUG-62294 is fixed, the pairingStatus part is consistent across platforms |
288 | QCOMPARE(invalidLocalDevice.pairingStatus(QBluetoothAddress()), QBluetoothLocalDevice::Paired); |
289 | QCOMPARE(invalidLocalDevice.hostMode(), QBluetoothLocalDevice::HostConnectable); |
290 | #endif |
291 | } |
292 | |
293 | void tst_QBluetoothLocalDevice::tst_allDevices() |
294 | { |
295 | //nothing we can really test here |
296 | } |
297 | void tst_QBluetoothLocalDevice::tst_construction() |
298 | { |
299 | if (!QBluetoothLocalDevice::allDevices().count()) |
300 | QSKIP("Skipping test due to missing Bluetooth device" ); |
301 | |
302 | QBluetoothLocalDevice localDevice; |
303 | QVERIFY(localDevice.isValid()); |
304 | |
305 | QBluetoothLocalDevice anotherDevice(QBluetoothAddress(000000000000)); |
306 | QVERIFY(anotherDevice.isValid()); |
307 | QVERIFY(anotherDevice.address().toUInt64() != 0); |
308 | |
309 | } |
310 | |
311 | void tst_QBluetoothLocalDevice::tst_pairDevice_data() |
312 | { |
313 | QTest::addColumn<QBluetoothAddress>(name: "deviceAddress" ); |
314 | QTest::addColumn<QBluetoothLocalDevice::Pairing>(name: "pairingExpected" ); |
315 | //waiting time larger for manual tests -> requires device interaction |
316 | QTest::addColumn<int>(name: "pairingWaitTime" ); |
317 | QTest::addColumn<bool>(name: "expectErrorSignal" ); |
318 | |
319 | QTest::newRow(dataTag: "UnPaired Device: DUMMY->unpaired" ) << QBluetoothAddress("11:00:00:00:00:00" ) |
320 | << QBluetoothLocalDevice::Unpaired << 1000 << false; |
321 | //Bluez5 may have to do a device search which can take up to 20s |
322 | QTest::newRow(dataTag: "UnPaired Device: DUMMY->paired" ) << QBluetoothAddress("11:00:00:00:00:00" ) |
323 | << QBluetoothLocalDevice::Paired << 21000 << true; |
324 | QTest::newRow(dataTag: "UnPaired Device: DUMMY" ) << QBluetoothAddress() |
325 | << QBluetoothLocalDevice::Unpaired << 1000 << true; |
326 | |
327 | if (!remoteDevice.isNull()) { |
328 | QTest::newRow(dataTag: "UnParing Test device 1" ) << QBluetoothAddress(remoteDevice) |
329 | << QBluetoothLocalDevice::Unpaired << 1000 << false; |
330 | //Bluez5 may have to do a device search which can take up to 20s |
331 | QTest::newRow(dataTag: "Pairing Test Device" ) << QBluetoothAddress(remoteDevice) |
332 | << QBluetoothLocalDevice::Paired << 21000 << false; |
333 | QTest::newRow(dataTag: "Pairing upgrade for Authorization" ) << QBluetoothAddress(remoteDevice) |
334 | << QBluetoothLocalDevice::AuthorizedPaired << 1000 << false; |
335 | QTest::newRow(dataTag: "Unpairing Test device 2" ) << QBluetoothAddress(remoteDevice) |
336 | << QBluetoothLocalDevice::Unpaired << 1000 << false; |
337 | QTest::newRow(dataTag: "Authorized Pairing" ) << QBluetoothAddress(remoteDevice) |
338 | << QBluetoothLocalDevice::AuthorizedPaired << 10000 << false; |
339 | QTest::newRow(dataTag: "Pairing Test Device after Authorization Pairing" ) << QBluetoothAddress(remoteDevice) |
340 | << QBluetoothLocalDevice::Paired << 1000 << false; |
341 | QTest::newRow(dataTag: "Pairing Test Device after Authorization2" ) << QBluetoothAddress(remoteDevice) |
342 | << QBluetoothLocalDevice::Paired << 1000 << false; //same again |
343 | QTest::newRow(dataTag: "Unpairing Test device 3" ) << QBluetoothAddress(remoteDevice) |
344 | << QBluetoothLocalDevice::Unpaired << 1000 << false; |
345 | QTest::newRow(dataTag: "UnParing Test device 4" ) << QBluetoothAddress(remoteDevice) |
346 | << QBluetoothLocalDevice::Unpaired << 1000 << false; |
347 | } |
348 | } |
349 | |
350 | void tst_QBluetoothLocalDevice::tst_pairDevice() |
351 | { |
352 | #ifdef Q_OS_WIN |
353 | QSKIP("Programmatic pairing not supported on Windows" ); |
354 | #endif |
355 | |
356 | QFETCH(QBluetoothAddress, deviceAddress); |
357 | QFETCH(QBluetoothLocalDevice::Pairing, pairingExpected); |
358 | QFETCH(int, pairingWaitTime); |
359 | QFETCH(bool, expectErrorSignal); |
360 | |
361 | if (!QBluetoothLocalDevice::allDevices().count()) |
362 | QSKIP("Skipping test due to missing Bluetooth device" ); |
363 | |
364 | QBluetoothLocalDevice localDevice; |
365 | //powerOn if not already |
366 | localDevice.powerOn(); |
367 | QVERIFY(localDevice.hostMode() != QBluetoothLocalDevice::HostPoweredOff); |
368 | |
369 | QSignalSpy pairingSpy(&localDevice, SIGNAL(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing)) ); |
370 | QSignalSpy errorSpy(&localDevice, SIGNAL(error(QBluetoothLocalDevice::Error))); |
371 | // there should be no signals yet |
372 | QVERIFY(pairingSpy.isValid()); |
373 | QVERIFY(pairingSpy.isEmpty()); |
374 | QVERIFY(errorSpy.isValid()); |
375 | QVERIFY(errorSpy.isEmpty()); |
376 | |
377 | QVERIFY(localDevice.isValid()); |
378 | |
379 | localDevice.requestPairing(address: deviceAddress, pairing: pairingExpected); |
380 | // async, wait for it |
381 | QTest::qWait(ms: pairingWaitTime); |
382 | |
383 | if (expectErrorSignal) { |
384 | QTRY_VERIFY(!errorSpy.isEmpty()); |
385 | QVERIFY(pairingSpy.isEmpty()); |
386 | QList<QVariant> arguments = errorSpy.first(); |
387 | QBluetoothLocalDevice::Error e = qvariant_cast<QBluetoothLocalDevice::Error>(v: arguments.at(i: 0)); |
388 | QCOMPARE(e, QBluetoothLocalDevice::PairingError); |
389 | } else { |
390 | QTRY_VERIFY(!pairingSpy.isEmpty()); |
391 | QVERIFY(errorSpy.isEmpty()); |
392 | |
393 | // test the actual signal values. |
394 | QList<QVariant> arguments = pairingSpy.takeFirst(); |
395 | QBluetoothAddress address = qvariant_cast<QBluetoothAddress>(v: arguments.at(i: 0)); |
396 | QBluetoothLocalDevice::Pairing pairingResult = qvariant_cast<QBluetoothLocalDevice::Pairing>(v: arguments.at(i: 1)); |
397 | QCOMPARE(deviceAddress, address); |
398 | QCOMPARE(pairingExpected, pairingResult); |
399 | } |
400 | |
401 | if (!expectErrorSignal) |
402 | QCOMPARE(pairingExpected, localDevice.pairingStatus(deviceAddress)); |
403 | } |
404 | |
405 | void tst_QBluetoothLocalDevice::tst_pairingStatus_data() |
406 | { |
407 | QTest::addColumn<QBluetoothAddress>(name: "deviceAddress" ); |
408 | QTest::addColumn<QBluetoothLocalDevice::Pairing>(name: "pairingExpected" ); |
409 | |
410 | #if !QT_CONFIG(winrt_bt) |
411 | QTest::newRow(dataTag: "UnPaired Device: DUMMY" ) << QBluetoothAddress("11:00:00:00:00:00" ) |
412 | << QBluetoothLocalDevice::Unpaired; |
413 | QTest::newRow(dataTag: "Invalid device" ) << QBluetoothAddress() << QBluetoothLocalDevice::Unpaired; |
414 | #else |
415 | // Remove special case when QTBUG-62294 is fixed |
416 | QTest::newRow("UnPaired Device: DUMMY" ) << QBluetoothAddress("11:00:00:00:00:00" ) |
417 | << QBluetoothLocalDevice::Paired; |
418 | QTest::newRow("Invalid device" ) << QBluetoothAddress() << QBluetoothLocalDevice::Paired; |
419 | #endif |
420 | //valid devices are already tested by tst_pairDevice() |
421 | } |
422 | |
423 | void tst_QBluetoothLocalDevice::tst_pairingStatus() |
424 | { |
425 | QFETCH(QBluetoothAddress, deviceAddress); |
426 | QFETCH(QBluetoothLocalDevice::Pairing, pairingExpected); |
427 | |
428 | if (!QBluetoothLocalDevice::allDevices().count()) |
429 | QSKIP("Skipping test due to missing Bluetooth device" ); |
430 | |
431 | QBluetoothLocalDevice localDevice; |
432 | QCOMPARE(pairingExpected, localDevice.pairingStatus(deviceAddress)); |
433 | } |
434 | QTEST_MAIN(tst_QBluetoothLocalDevice) |
435 | |
436 | #include "tst_qbluetoothlocaldevice.moc" |
437 | |