| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtBluetooth module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtTest/QtTest> |
| |
| #include <QDebug> |
| #include <QVariant> |
| #include <QList> |
| #include <QLoggingCategory> |
| |
| #include <private/qtbluetoothglobal_p.h> |
| #include <qbluetoothaddress.h> |
| #include <qbluetoothdevicediscoveryagent.h> |
| #include <qbluetoothlocaldevice.h> |
| |
| QT_USE_NAMESPACE |
| |
| /* |
| * Some parts of this test require a remote and discoverable Bluetooth |
| * device. Setting the BT_TEST_DEVICE environment variable will |
| * set the test up to fail if it cannot find a remote device. |
| * BT_TEST_DEVICE should contain the address of the device the |
| * test expects to find. Ensure that the device is running |
| * in discovery mode. |
| **/ |
| |
| // Maximum time to for bluetooth device scan |
| const int MaxScanTime = 5 * 60 * 1000; // 5 minutes in ms |
| |
| //Bluez needs at least 10s for a device discovery to be cancelled |
| const int MaxWaitForCancelTime = 15 * 1000; // 15 seconds in ms |
| |
| class tst_QBluetoothDeviceDiscoveryAgent : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| tst_QBluetoothDeviceDiscoveryAgent(); |
| ~tst_QBluetoothDeviceDiscoveryAgent(); |
| |
| public slots: |
| void deviceDiscoveryDebug(const QBluetoothDeviceInfo &info); |
| void finished(); |
| |
| private slots: |
| void initTestCase(); |
| |
| void tst_properties(); |
| |
| void tst_invalidBtAddress(); |
| |
| void tst_startStopDeviceDiscoveries(); |
| |
| void tst_deviceDiscovery_data(); |
| void tst_deviceDiscovery(); |
| |
| void tst_discoveryTimeout(); |
| |
| void tst_discoveryMethods(); |
| private: |
| int noOfLocalDevices; |
| bool isBluez5Runtime = false; |
| }; |
| |
| tst_QBluetoothDeviceDiscoveryAgent::tst_QBluetoothDeviceDiscoveryAgent() |
| { |
| QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); |
| qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>(); |
| } |
| |
| tst_QBluetoothDeviceDiscoveryAgent::~tst_QBluetoothDeviceDiscoveryAgent() |
| { |
| } |
| |
| #if QT_CONFIG(bluez) |
| // This section was adopted from tst_qloggingcategory.cpp |
| QString logMessage; |
| |
| QByteArray qMyMessageFormatString(QtMsgType type, const QMessageLogContext &context, |
| const QString &str) |
| { |
| QByteArray message; |
| message.append(context.category); |
| switch (type) { |
| case QtDebugMsg: message.append(".debug"); break; |
| case QtInfoMsg: message.append(".info"); break; |
| case QtWarningMsg: message.append(".warning"); break; |
| case QtCriticalMsg:message.append(".critical"); break; |
| case QtFatalMsg: message.append(".fatal"); break; |
| } |
| message.append(": "); |
| message.append(qPrintable(str)); |
| |
| return message.simplified(); |
| } |
| |
| static void myCustomMessageHandler(QtMsgType type, |
| const QMessageLogContext &context, |
| const QString &msg) |
| { |
| logMessage = qMyMessageFormatString(type, context, msg); |
| } |
| #endif |
| |
| |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::initTestCase() |
| { |
| qRegisterMetaType<QBluetoothDeviceInfo>(); |
| qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::InquiryType>(); |
| |
| #if QT_CONFIG(bluez) |
| // To distinguish Bluez 4 and 5 we peek into the debug output |
| // of first Bluetooth ctor. It executes a runtime test and prints the result |
| // as logging output. This avoids more complex runtime detection logic within this unit test. |
| QtMessageHandler oldMessageHandler; |
| oldMessageHandler = qInstallMessageHandler(myCustomMessageHandler); |
| |
| noOfLocalDevices = QBluetoothLocalDevice::allDevices().count(); |
| qInstallMessageHandler(oldMessageHandler); |
| isBluez5Runtime = logMessage.contains(QStringLiteral("Bluez 5")); |
| if (isBluez5Runtime) |
| qDebug() << "BlueZ 5 runtime detected."; |
| #else |
| noOfLocalDevices = QBluetoothLocalDevice::allDevices().count(); |
| #endif |
| |
| if (!noOfLocalDevices) |
| return; |
| |
| // turn on BT in case it is not on |
| QBluetoothLocalDevice *device = new QBluetoothLocalDevice(); |
| if (device->hostMode() == QBluetoothLocalDevice::HostPoweredOff) { |
| QSignalSpy hostModeSpy(device, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode))); |
| QVERIFY(hostModeSpy.isEmpty()); |
| device->powerOn(); |
| int connectTime = 5000; // ms |
| while (hostModeSpy.count() < 1 && connectTime > 0) { |
| QTest::qWait(500); |
| connectTime -= 500; |
| } |
| QVERIFY(hostModeSpy.count() > 0); |
| } |
| QBluetoothLocalDevice::HostMode hostMode= device->hostMode(); |
| QVERIFY(hostMode == QBluetoothLocalDevice::HostConnectable |
| || hostMode == QBluetoothLocalDevice::HostDiscoverable |
| || hostMode == QBluetoothLocalDevice::HostDiscoverableLimitedInquiry); |
| delete device; |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_properties() |
| { |
| QBluetoothDeviceDiscoveryAgent discoveryAgent; |
| |
| QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); |
| discoveryAgent.setInquiryType(QBluetoothDeviceDiscoveryAgent::LimitedInquiry); |
| QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::LimitedInquiry); |
| discoveryAgent.setInquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); |
| QCOMPARE(discoveryAgent.inquiryType(), QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry); |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_invalidBtAddress() |
| { |
| QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(QBluetoothAddress("11:11:11:11:11:11")); |
| |
| QCOMPARE(discoveryAgent->error(), QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError); |
| discoveryAgent->start(); |
| QCOMPARE(discoveryAgent->isActive(), false); |
| delete discoveryAgent; |
| |
| discoveryAgent = new QBluetoothDeviceDiscoveryAgent(QBluetoothAddress()); |
| QCOMPARE(discoveryAgent->error(), QBluetoothDeviceDiscoveryAgent::NoError); |
| if (QBluetoothLocalDevice::allDevices().count() > 0) { |
| discoveryAgent->start(); |
| QCOMPARE(discoveryAgent->isActive(), true); |
| } |
| delete discoveryAgent; |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::deviceDiscoveryDebug(const QBluetoothDeviceInfo &info) |
| { |
| qDebug() << "Discovered device:" << info.address().toString() << info.name(); |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_startStopDeviceDiscoveries() |
| { |
| QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType = QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; |
| QBluetoothDeviceDiscoveryAgent discoveryAgent; |
| |
| QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); |
| QVERIFY(discoveryAgent.errorString().isEmpty()); |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(discoveryAgent.discoveredDevices().isEmpty()); |
| |
| QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); |
| QSignalSpy cancelSpy(&discoveryAgent, SIGNAL(canceled())); |
| QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); |
| |
| // Starting case 1: start-stop, expecting cancel signal |
| discoveryAgent.setInquiryType(inquiryType); |
| // we should have no errors at this point. |
| QVERIFY(errorSpy.isEmpty()); |
| |
| discoveryAgent.start(); |
| |
| if (errorSpy.isEmpty()) { |
| QVERIFY(discoveryAgent.isActive()); |
| QCOMPARE(discoveryAgent.errorString(), QString()); |
| QCOMPARE(discoveryAgent.error(), QBluetoothDeviceDiscoveryAgent::NoError); |
| } else { |
| QCOMPARE(noOfLocalDevices, 0); |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(!discoveryAgent.errorString().isEmpty()); |
| QVERIFY(discoveryAgent.error() != QBluetoothDeviceDiscoveryAgent::NoError); |
| QSKIP("No local Bluetooth device available. Skipping remaining part of test."); |
| } |
| // cancel current request. |
| discoveryAgent.stop(); |
| |
| // Wait for up to MaxWaitForCancelTime for the cancel to finish |
| int waitTime = MaxWaitForCancelTime; |
| while (cancelSpy.count() == 0 && waitTime > 0) { |
| QTest::qWait(100); |
| waitTime-=100; |
| } |
| |
| // we should not be active anymore |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| QCOMPARE(cancelSpy.count(), 1); |
| cancelSpy.clear(); |
| // Starting case 2: start-start-stop, expecting cancel signal |
| discoveryAgent.start(); |
| // we should be active now |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // start again. should this be error? |
| discoveryAgent.start(); |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // stop |
| discoveryAgent.stop(); |
| |
| // Wait for up to MaxWaitForCancelTime for the cancel to finish |
| waitTime = MaxWaitForCancelTime; |
| while (cancelSpy.count() == 0 && waitTime > 0) { |
| QTest::qWait(100); |
| waitTime-=100; |
| } |
| |
| // we should not be active anymore |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| QVERIFY(cancelSpy.count() == 1); |
| cancelSpy.clear(); |
| |
| // Starting case 3: stop |
| discoveryAgent.stop(); |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| |
| // Don't expect finished signal and no error |
| QVERIFY(finishedSpy.count() == 0); |
| QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); |
| QVERIFY(discoveryAgent.errorString().isEmpty()); |
| |
| /* |
| Starting case 4: start-stop-start-stop: |
| We are testing that two subsequent stop() calls reduce total number |
| of cancel() signals to 1 if the true cancellation requires |
| asynchronous function calls (signal consolidation); otherwise we |
| expect 2x cancel() signal. |
| |
| Examples are: |
| - Bluez4 (event loop needs to run for cancel) |
| - Bluez5 (no event loop required) |
| */ |
| |
| bool immediateSignal = false; |
| discoveryAgent.start(); |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // cancel current request. |
| discoveryAgent.stop(); |
| //should only have triggered cancel() if stop didn't involve the event loop |
| if (cancelSpy.count() == 1) immediateSignal = true; |
| |
| // start a new one |
| discoveryAgent.start(); |
| // we should be active now |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // stop |
| discoveryAgent.stop(); |
| if (immediateSignal) |
| QVERIFY(cancelSpy.count() == 2); |
| |
| // Wait for up to MaxWaitForCancelTime for the cancel to finish |
| waitTime = MaxWaitForCancelTime; |
| while (cancelSpy.count() == 0 && waitTime > 0) { |
| QTest::qWait(100); |
| waitTime-=100; |
| } |
| // we should not be active anymore |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // should only have 1 cancel |
| |
| if (immediateSignal) |
| QVERIFY(cancelSpy.count() == 2); |
| else |
| QVERIFY(cancelSpy.count() == 1); |
| cancelSpy.clear(); |
| |
| // Starting case 5: start-stop-start: expecting finished signal & no cancel |
| discoveryAgent.start(); |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // cancel current request. |
| discoveryAgent.stop(); |
| // start a new one |
| discoveryAgent.start(); |
| // we should be active now |
| QVERIFY(discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| |
| // Wait for up to MaxScanTime for the cancel to finish |
| waitTime = MaxScanTime; |
| while (finishedSpy.count() == 0 && waitTime > 0) { |
| QTest::qWait(1000); |
| waitTime-=1000; |
| } |
| |
| // we should not be active anymore |
| QVERIFY(!discoveryAgent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| // should only have 1 cancel |
| QVERIFY(finishedSpy.count() == 1); |
| |
| // On OS X, stop is synchronous (signal will be emitted immediately). |
| if (!immediateSignal) |
| QVERIFY(cancelSpy.isEmpty()); |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::finished() |
| { |
| qDebug() << "Finished called"; |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_deviceDiscovery_data() |
| { |
| QTest::addColumn<QBluetoothDeviceDiscoveryAgent::InquiryType>("inquiryType"); |
| |
| QTest::newRow("general unlimited inquiry") << QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry; |
| QTest::newRow("limited inquiry") << QBluetoothDeviceDiscoveryAgent::LimitedInquiry; |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_deviceDiscovery() |
| { |
| { |
| QFETCH(QBluetoothDeviceDiscoveryAgent::InquiryType, inquiryType); |
| |
| //Run test in case of multiple Bluetooth adapters |
| QBluetoothLocalDevice localDevice; |
| //We will use default adapter if there is no other adapter |
| QBluetoothAddress address = localDevice.address(); |
| int numberOfAdapters = (localDevice.allDevices()).size(); |
| QList<QBluetoothAddress> addresses; |
| if (numberOfAdapters > 1) { |
| |
| for (int i=0; i < numberOfAdapters; i++) { |
| addresses.append(((QBluetoothHostInfo)localDevice.allDevices().at(i)).address()); |
| } |
| address = (QBluetoothAddress)addresses.at(0); |
| } |
| |
| QBluetoothDeviceDiscoveryAgent discoveryAgent(address); |
| QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); |
| QVERIFY(discoveryAgent.errorString().isEmpty()); |
| QVERIFY(!discoveryAgent.isActive()); |
| |
| QVERIFY(discoveryAgent.discoveredDevices().isEmpty()); |
| |
| QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); |
| QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); |
| QSignalSpy discoveredSpy(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo))); |
| // connect(&discoveryAgent, SIGNAL(finished()), this, SLOT(finished())); |
| // connect(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), |
| // this, SLOT(deviceDiscoveryDebug(QBluetoothDeviceInfo))); |
| |
| discoveryAgent.setInquiryType(inquiryType); |
| discoveryAgent.start(); |
| if (!errorSpy.isEmpty()) { |
| QCOMPARE(noOfLocalDevices, 0); |
| QVERIFY(!discoveryAgent.isActive()); |
| QSKIP("No local Bluetooth device available. Skipping remaining part of test."); |
| } |
| |
| QVERIFY(discoveryAgent.isActive()); |
| |
| // Wait for up to MaxScanTime for the scan to finish |
| int scanTime = MaxScanTime; |
| while (finishedSpy.count() == 0 && scanTime > 0) { |
| QTest::qWait(15000); |
| scanTime -= 15000; |
| } |
| |
| // verify that we are finished |
| QVERIFY(!discoveryAgent.isActive()); |
| // stop |
| discoveryAgent.stop(); |
| QVERIFY(!discoveryAgent.isActive()); |
| qDebug() << "Scan time left:" << scanTime; |
| // Expect finished signal with no error |
| QVERIFY(finishedSpy.count() == 1); |
| QVERIFY(errorSpy.isEmpty()); |
| QVERIFY(discoveryAgent.error() == discoveryAgent.NoError); |
| QVERIFY(discoveryAgent.errorString().isEmpty()); |
| |
| // verify that the list is as big as the signals received. |
| QVERIFY(discoveredSpy.count() == discoveryAgent.discoveredDevices().length()); |
| // verify that there really was some devices in the array |
| |
| const QString remote = qgetenv("BT_TEST_DEVICE"); |
| QBluetoothAddress remoteDevice; |
| if (!remote.isEmpty()) { |
| remoteDevice = QBluetoothAddress(remote); |
| QVERIFY2(!remote.isNull(), "Expecting valid Bluetooth address to be passed via BT_TEST_DEVICE"); |
| } else { |
| qWarning() << "Not using a remote device for testing. Set BT_TEST_DEVICE env to run extended tests involving a remote device"; |
| } |
| |
| if (!remoteDevice.isNull()) |
| QVERIFY(discoveredSpy.count() > 0); |
| int counter = 0; |
| // All returned QBluetoothDeviceInfo should be valid. |
| while (!discoveredSpy.isEmpty()) { |
| const QBluetoothDeviceInfo info = |
| qvariant_cast<QBluetoothDeviceInfo>(discoveredSpy.takeFirst().at(0)); |
| QVERIFY(info.isValid()); |
| qDebug() << "Discovered device:" << info.address().toString() << info.name(); |
| |
| if (numberOfAdapters > 1) { |
| for (int i= 1; i < numberOfAdapters; i++) { |
| if (info.address().toString() == addresses[i].toString()) |
| counter++; |
| } |
| } |
| } |
| #if defined(Q_OS_IOS) || defined(Q_OS_TVOS) || QT_CONFIG(winrt_bt) |
| //On iOS/WinRT, we do not have access to the local device/adapter, numberOfAdapters is 0, |
| //so we skip this test at all. |
| QSKIP("iOS/WinRT: no local Bluetooth device available. Skipping remaining part of test."); |
| #endif |
| |
| //For multiple Bluetooth adapter do the check only for GeneralUnlimitedInquiry. |
| if (!(inquiryType == QBluetoothDeviceDiscoveryAgent::LimitedInquiry)) |
| QVERIFY((numberOfAdapters-1) == counter); |
| } |
| } |
| |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_discoveryTimeout() |
| { |
| QBluetoothDeviceDiscoveryAgent agent; |
| |
| // check default values |
| #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || QT_CONFIG(winrt_bt) |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); |
| agent.setLowEnergyDiscoveryTimeout(-1); // negative ignored |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); |
| agent.setLowEnergyDiscoveryTimeout(20000); |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); |
| #elif QT_CONFIG(bluez) |
| if (isBluez5Runtime) { |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); |
| agent.setLowEnergyDiscoveryTimeout(-1); // negative ignored |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 20000); |
| agent.setLowEnergyDiscoveryTimeout(25000); |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), 25000); |
| } else { |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); |
| agent.setLowEnergyDiscoveryTimeout(20000); // feature not supported -> ignored |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); |
| } |
| #else |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); |
| agent.setLowEnergyDiscoveryTimeout(20000); // feature not supported -> ignored |
| QCOMPARE(agent.lowEnergyDiscoveryTimeout(), -1); |
| #endif |
| } |
| |
| void tst_QBluetoothDeviceDiscoveryAgent::tst_discoveryMethods() |
| { |
| const QBluetoothLocalDevice localDevice; |
| if (localDevice.allDevices().size() != 1) { |
| // On iOS it returns 0 but we still have working BT. |
| #ifndef Q_OS_IOS |
| QSKIP("This test expects exactly one local device working"); |
| #endif |
| } |
| |
| const QBluetoothDeviceDiscoveryAgent::DiscoveryMethods |
| supportedMethods = QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods(); |
| |
| QVERIFY(supportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod); |
| |
| QBluetoothDeviceDiscoveryAgent::DiscoveryMethod |
| unsupportedMethods = QBluetoothDeviceDiscoveryAgent::NoMethod; |
| QBluetoothDeviceInfo::CoreConfiguration |
| expectedConfiguration = QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration; |
| |
| if (supportedMethods == QBluetoothDeviceDiscoveryAgent::ClassicMethod) { |
| unsupportedMethods = QBluetoothDeviceDiscoveryAgent::LowEnergyMethod; |
| expectedConfiguration = QBluetoothDeviceInfo::BaseRateCoreConfiguration; |
| } else if (supportedMethods == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { |
| unsupportedMethods = QBluetoothDeviceDiscoveryAgent::ClassicMethod; |
| expectedConfiguration = QBluetoothDeviceInfo::LowEnergyCoreConfiguration; |
| } |
| |
| QBluetoothDeviceDiscoveryAgent agent; |
| QSignalSpy finishedSpy(&agent, SIGNAL(finished())); |
| QSignalSpy errorSpy(&agent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); |
| QSignalSpy discoveredSpy(&agent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo))); |
| |
| // NoMethod - should just immediately return: |
| agent.start(QBluetoothDeviceDiscoveryAgent::NoMethod); |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); |
| QVERIFY(!agent.isActive()); |
| QCOMPARE(finishedSpy.size(), 0); |
| QCOMPARE(errorSpy.size(), 0); |
| QCOMPARE(discoveredSpy.size(), 0); |
| |
| if (unsupportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod) { |
| agent.start(unsupportedMethods); |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); |
| QVERIFY(!agent.isActive()); |
| QVERIFY(finishedSpy.isEmpty()); |
| QCOMPARE(errorSpy.size(), 1); |
| errorSpy.clear(); |
| QVERIFY(discoveredSpy.isEmpty()); |
| } |
| |
| // Start discovery, probably both Classic and LE methods: |
| agent.start(supportedMethods); |
| QVERIFY(agent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| |
| |
| #define RUN_DISCOVERY(maxTimeout, step, condition) \ |
| for (int scanTime = maxTimeout; (condition) && scanTime > 0; scanTime -= step) \ |
| QTest::qWait(step); |
| |
| // Wait for up to MaxScanTime for the scan to finish |
| const int timeStep = 15000; |
| RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty()) |
| |
| QVERIFY(!agent.isActive()); |
| QVERIFY(errorSpy.size() <= 1); |
| |
| if (errorSpy.size()) { |
| // For example, old iOS device could report it supports LE method, |
| // but it actually does not. |
| QVERIFY(supportedMethods == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); |
| } else { |
| QVERIFY(finishedSpy.count() == 1); |
| QVERIFY(agent.error() == QBluetoothDeviceDiscoveryAgent::NoError); |
| QVERIFY(agent.errorString().isEmpty()); |
| |
| while (!discoveredSpy.isEmpty()) { |
| const QBluetoothDeviceInfo info = |
| qvariant_cast<QBluetoothDeviceInfo>(discoveredSpy.takeFirst().at(0)); |
| QVERIFY(info.isValid()); |
| QVERIFY(info.coreConfigurations() & expectedConfiguration); |
| } |
| } |
| |
| if (unsupportedMethods != QBluetoothDeviceDiscoveryAgent::NoMethod) |
| return; |
| |
| // Both methods were reported as supported. We already tested them |
| // above, now let's test first Classic then LE. |
| finishedSpy.clear(); |
| errorSpy.clear(); |
| discoveredSpy.clear(); |
| |
| agent.start(QBluetoothDeviceDiscoveryAgent::ClassicMethod); |
| QVERIFY(agent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); |
| |
| RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty()) |
| |
| QVERIFY(!agent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| QCOMPARE(finishedSpy.size(), 1); |
| |
| finishedSpy.clear(); |
| discoveredSpy.clear(); |
| |
| agent.start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); |
| QVERIFY(agent.isActive()); |
| QVERIFY(errorSpy.isEmpty()); |
| |
| RUN_DISCOVERY(MaxScanTime, timeStep, finishedSpy.isEmpty() && errorSpy.isEmpty()) |
| |
| QVERIFY(!agent.isActive()); |
| QVERIFY(errorSpy.size() <= 1); |
| |
| if (errorSpy.size()) { |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::UnsupportedDiscoveryMethod); |
| qDebug() << "QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods is inaccurate" |
| " on your platform/with your device, LowEnergyMethod is not supported"; |
| } else { |
| QCOMPARE(agent.error(), QBluetoothDeviceDiscoveryAgent::NoError); |
| QCOMPARE(finishedSpy.size(), 1); |
| } |
| } |
| |
| QTEST_MAIN(tst_QBluetoothDeviceDiscoveryAgent) |
| |
| #include "tst_qbluetoothdevicediscoveryagent.moc" |