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
38QT_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
47class tst_QBluetoothLocalDevice : public QObject
48{
49 Q_OBJECT
50
51public:
52 tst_QBluetoothLocalDevice();
53 ~tst_QBluetoothLocalDevice();
54
55private 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
71private:
72 QBluetoothAddress remoteDevice;
73 bool expectRemoteDevice;
74};
75
76tst_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
96tst_QBluetoothLocalDevice::~tst_QBluetoothLocalDevice()
97{
98}
99
100void 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
108void 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
136void 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
169void 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
187void 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
230void 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}
239void 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}
247void 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
293void tst_QBluetoothLocalDevice::tst_allDevices()
294{
295 //nothing we can really test here
296}
297void 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
311void 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
350void 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
405void 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
423void 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}
434QTEST_MAIN(tst_QBluetoothLocalDevice)
435
436#include "tst_qbluetoothlocaldevice.moc"
437

source code of qtconnectivity/tests/auto/qbluetoothlocaldevice/tst_qbluetoothlocaldevice.cpp