/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include QT_USE_NAMESPACE // Maximum time to for bluetooth device scan const int MaxScanTime = 5 * 60 * 1000; // 5 minutes in ms class tst_QBluetoothServiceDiscoveryAgent : public QObject { Q_OBJECT public: tst_QBluetoothServiceDiscoveryAgent(); ~tst_QBluetoothServiceDiscoveryAgent(); public slots: void deviceDiscoveryDebug(const QBluetoothDeviceInfo &info); void serviceDiscoveryDebug(const QBluetoothServiceInfo &info); void serviceError(const QBluetoothServiceDiscoveryAgent::Error err); private slots: void initTestCase(); void tst_invalidBtAddress(); void tst_serviceDiscovery_data(); void tst_serviceDiscovery(); void tst_serviceDiscoveryAdapters(); private: QList devices; bool localDeviceAvailable; }; tst_QBluetoothServiceDiscoveryAgent::tst_QBluetoothServiceDiscoveryAgent() { QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); // start Bluetooth if not started #ifndef Q_OS_OSX QBluetoothLocalDevice *device = new QBluetoothLocalDevice(); localDeviceAvailable = device->isValid(); if (localDeviceAvailable) { device->powerOn(); // wait for the device to switch bluetooth mode. QTest::qWait(1000); } delete device; #else QBluetoothLocalDevice device; localDeviceAvailable = QBluetoothLocalDevice().hostMode() != QBluetoothLocalDevice::HostPoweredOff; #endif qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType >(); qRegisterMetaType(); qRegisterMetaType(); } tst_QBluetoothServiceDiscoveryAgent::~tst_QBluetoothServiceDiscoveryAgent() { } void tst_QBluetoothServiceDiscoveryAgent::deviceDiscoveryDebug(const QBluetoothDeviceInfo &info) { qDebug() << "Discovered device:" << info.address().toString() << info.name(); } void tst_QBluetoothServiceDiscoveryAgent::serviceError(const QBluetoothServiceDiscoveryAgent::Error err) { qDebug() << "Service discovery error" << err; } void tst_QBluetoothServiceDiscoveryAgent::initTestCase() { if (localDeviceAvailable) { QBluetoothDeviceDiscoveryAgent discoveryAgent; QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error))); QSignalSpy discoveredSpy(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo))); // connect(&discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), // this, SLOT(deviceDiscoveryDebug(QBluetoothDeviceInfo))); discoveryAgent.start(); // Wait for up to MaxScanTime for the scan to finish int scanTime = MaxScanTime; while (finishedSpy.count() == 0 && scanTime > 0) { QTest::qWait(1000); scanTime -= 1000; } // qDebug() << "Scan time left:" << scanTime; // Expect finished signal with no error QVERIFY(finishedSpy.count() == 1); QVERIFY(errorSpy.isEmpty()); devices = discoveryAgent.discoveredDevices(); } } void tst_QBluetoothServiceDiscoveryAgent::tst_invalidBtAddress() { #ifdef Q_OS_OSX if (!localDeviceAvailable) QSKIP("On OS X this test requires Bluetooth adapter in powered ON state"); #endif QBluetoothServiceDiscoveryAgent *discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress("11:11:11:11:11:11")); QCOMPARE(discoveryAgent->error(), QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError); discoveryAgent->start(); QCOMPARE(discoveryAgent->isActive(), false); delete discoveryAgent; discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress()); QCOMPARE(discoveryAgent->error(), QBluetoothServiceDiscoveryAgent::NoError); if (QBluetoothLocalDevice::allDevices().count() > 0) { discoveryAgent->start(); QCOMPARE(discoveryAgent->isActive(), true); } delete discoveryAgent; } void tst_QBluetoothServiceDiscoveryAgent::serviceDiscoveryDebug(const QBluetoothServiceInfo &info) { qDebug() << "Discovered service on" << info.device().name() << info.device().address().toString(); qDebug() << "\tService name:" << info.serviceName() << "cached" << info.device().isCached(); qDebug() << "\tDescription:" << info.attribute(QBluetoothServiceInfo::ServiceDescription).toString(); qDebug() << "\tProvider:" << info.attribute(QBluetoothServiceInfo::ServiceProvider).toString(); qDebug() << "\tL2CAP protocol service multiplexer:" << info.protocolServiceMultiplexer(); qDebug() << "\tRFCOMM server channel:" << info.serverChannel(); } static void dumpAttributeVariant(const QVariant &var, const QString indent) { if (!var.isValid()) { qDebug("%sEmpty", indent.toLocal8Bit().constData()); return; } if (var.userType() == qMetaTypeId()) { qDebug("%sSequence", indent.toLocal8Bit().constData()); const QBluetoothServiceInfo::Sequence *sequence = static_cast(var.data()); foreach (const QVariant &v, *sequence) dumpAttributeVariant(v, indent + '\t'); } else if (var.userType() == qMetaTypeId()) { qDebug("%sAlternative", indent.toLocal8Bit().constData()); const QBluetoothServiceInfo::Alternative *alternative = static_cast(var.data()); foreach (const QVariant &v, *alternative) dumpAttributeVariant(v, indent + '\t'); } else if (var.userType() == qMetaTypeId()) { QBluetoothUuid uuid = var.value(); switch (uuid.minimumSize()) { case 0: qDebug("%suuid NULL", indent.toLocal8Bit().constData()); break; case 2: qDebug("%suuid %04x", indent.toLocal8Bit().constData(), uuid.toUInt16()); break; case 4: qDebug("%suuid %08x", indent.toLocal8Bit().constData(), uuid.toUInt32()); break; case 16: { qDebug("%suuid %s", indent.toLocal8Bit().constData(), QByteArray(reinterpret_cast(uuid.toUInt128().data), 16).toHex().constData()); break; } default: qDebug("%suuid ???", indent.toLocal8Bit().constData()); } } else { switch (var.userType()) { case QVariant::UInt: qDebug("%suint %u", indent.toLocal8Bit().constData(), var.toUInt()); break; case QVariant::Int: qDebug("%sint %d", indent.toLocal8Bit().constData(), var.toInt()); break; case QVariant::String: qDebug("%sstring %s", indent.toLocal8Bit().constData(), var.toString().toLocal8Bit().constData()); break; case QVariant::Bool: qDebug("%sbool %d", indent.toLocal8Bit().constData(), var.toBool()); break; case QVariant::Url: qDebug("%surl %s", indent.toLocal8Bit().constData(), var.toUrl().toString().toLocal8Bit().constData()); break; default: qDebug("%sunknown", indent.toLocal8Bit().constData()); } } } static inline void dumpServiceInfoAttributes(const QBluetoothServiceInfo &info) { foreach (quint16 id, info.attributes()) { dumpAttributeVariant(info.attribute(id), QString("\t")); } } void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscovery_data() { if (devices.isEmpty()) QSKIP("This test requires an in-range bluetooth device"); QTest::addColumn("deviceInfo"); QTest::addColumn >("uuidFilter"); QTest::addColumn("serviceDiscoveryError"); // Only need to test the first 5 live devices int max = 5; foreach (const QBluetoothDeviceInfo &info, devices) { if (info.isCached()) continue; QTest::newRow("default filter") << info << QList() << QBluetoothServiceDiscoveryAgent::NoError; if (!--max) break; //QTest::newRow("public browse group") << info << (QList() << QBluetoothUuid::PublicBrowseGroup); //QTest::newRow("l2cap") << info << (QList() << QBluetoothUuid::L2cap); } QTest::newRow("all devices") << QBluetoothDeviceInfo() << QList() << QBluetoothServiceDiscoveryAgent::NoError; } void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscoveryAdapters() { QBluetoothLocalDevice localDevice; int numberOfAdapters = (localDevice.allDevices()).size(); if (numberOfAdapters>1) { if (devices.isEmpty()) QSKIP("This test requires an in-range bluetooth device"); QList addresses; for (int i=0; i 0) { QTest::qWait(1000); scanTime -= 1000; } QList discServices = discoveryAgent.discoveredServices(); QVERIFY(!discServices.empty()); int counter = 0; for (int i = 0; i, uuidFilter); QFETCH(QBluetoothServiceDiscoveryAgent::Error, serviceDiscoveryError); QBluetoothLocalDevice localDevice; qDebug() << "Scanning address" << deviceInfo.address().toString(); QBluetoothServiceDiscoveryAgent discoveryAgent(localDevice.address()); bool setAddress = discoveryAgent.setRemoteAddress(deviceInfo.address()); QVERIFY(setAddress); QVERIFY(!discoveryAgent.isActive()); QVERIFY(discoveryAgent.discoveredServices().isEmpty()); QVERIFY(discoveryAgent.uuidFilter().isEmpty()); discoveryAgent.setUuidFilter(uuidFilter); QVERIFY(discoveryAgent.uuidFilter() == uuidFilter); QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished())); QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error))); QSignalSpy discoveredSpy(&discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo))); // connect(&discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), // this, SLOT(serviceDiscoveryDebug(QBluetoothServiceInfo))); connect(&discoveryAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), this, SLOT(serviceError(QBluetoothServiceDiscoveryAgent::Error))); discoveryAgent.start(QBluetoothServiceDiscoveryAgent::FullDiscovery); /* * Either we wait for discovery agent to run its course (e.g. Bluez 4) or * we have an immediate result (e.g. Bluez 5) */ QVERIFY(discoveryAgent.isActive() || !finishedSpy.isEmpty()); // Wait for up to MaxScanTime for the scan to finish int scanTime = MaxScanTime; while (finishedSpy.count() == 0 && scanTime > 0) { QTest::qWait(1000); scanTime -= 1000; } if (discoveryAgent.error() && expected_failures++ < 2){ qDebug() << "Device failed to respond to SDP, skipping device" << discoveryAgent.error() << discoveryAgent.errorString(); return; } QVERIFY(discoveryAgent.error() == serviceDiscoveryError); QVERIFY(discoveryAgent.errorString() == QString()); // Expect finished signal with no error QVERIFY(finishedSpy.count() == 1); QVERIFY(errorSpy.isEmpty()); //if (discoveryAgent.discoveredServices().count() && expected_failures++ <2){ if (discoveredSpy.isEmpty() && expected_failures++ < 2){ qDebug() << "Device failed to return any results, skipping device" << discoveryAgent.discoveredServices().count(); return; } // All returned QBluetoothServiceInfo should be valid. bool servicesFound = !discoveredSpy.isEmpty(); while (!discoveredSpy.isEmpty()) { const QVariant v = discoveredSpy.takeFirst().at(0); // Work around limitation in QMetaType and moc. // QBluetoothServiceInfo is registered with metatype as QBluetoothServiceInfo // moc sees it as the unqualified QBluetoothServiceInfo. if (v.userType() == qMetaTypeId()) { const QBluetoothServiceInfo info = *reinterpret_cast(v.constData()); QVERIFY(info.isValid()); QVERIFY(!info.isRegistered()); #if 0 qDebug() << info.device().name() << info.device().address().toString(); qDebug() << "\tService name:" << info.serviceName(); if (info.protocolServiceMultiplexer() >= 0) qDebug() << "\tL2CAP protocol service multiplexer:" << info.protocolServiceMultiplexer(); if (info.serverChannel() >= 0) qDebug() << "\tRFCOMM server channel:" << info.serverChannel(); //dumpServiceInfoAttributes(info); #endif } else { QFAIL("Unknown type returned by service discovery"); } } if (servicesFound) QVERIFY(discoveryAgent.discoveredServices().count() != 0); discoveryAgent.clear(); QVERIFY(discoveryAgent.discoveredServices().count() == 0); discoveryAgent.stop(); QVERIFY(!discoveryAgent.isActive()); } QTEST_MAIN(tst_QBluetoothServiceDiscoveryAgent) #include "tst_qbluetoothservicediscoveryagent.moc"