summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Buhr <andreas.buhr@qt.io>2021-05-03 13:45:52 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-10-26 11:46:04 +0000
commit435c9c9ab0b708db1b309829f9d586b207fe6ef8 (patch)
tree2848b781d720c5acb0320605f5a93a55ec5c2b1e
parentded09dd38793db5cd0c4f42a172c049bbf2eed8a (diff)
downloadqtconnectivity-435c9c9ab0b708db1b309829f9d586b207fe6ef8.tar.gz
Add bluetoothtestdevice tool
This patch adds a tool "bluetoothtestdevice" which acts as a partner device in QtBluetooth unit tests. Change-Id: I5be89cf555a94a209de093d328fb29f91c2fea9a Reviewed-by: Alex Blasche <alexander.blasche@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> (cherry picked from commit f3fcf88a0dc03114fc7bce32b87a866c54eede7d) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/bluetoothtestdevice/CMakeLists.txt49
-rw-r--r--tests/bluetoothtestdevice/bluetoothtestdevice.cpp368
3 files changed, 419 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 33962f6d..4e7d7c2e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -7,6 +7,8 @@ if(TARGET Qt::Bluetooth)
if(TARGET Qt::Quick)
add_subdirectory(bttestui)
endif()
+
+ add_subdirectory(bluetoothtestdevice)
endif()
qt_build_tests()
diff --git a/tests/bluetoothtestdevice/CMakeLists.txt b/tests/bluetoothtestdevice/CMakeLists.txt
new file mode 100644
index 00000000..ff9edead
--- /dev/null
+++ b/tests/bluetoothtestdevice/CMakeLists.txt
@@ -0,0 +1,49 @@
+#####################################################################
+## bluetoothtestdevice Tool:
+#####################################################################
+
+cmake_minimum_required(VERSION 3.16...3.21)
+
+if(NOT TARGET Qt::Bluetooth)
+ # for standalone build
+ project(bluetoothtestdevice LANGUAGES CXX)
+
+ set(CMAKE_AUTOMOC ON)
+
+ find_package(Qt6 COMPONENTS Bluetooth Core)
+ if(ANDROID OR IOS)
+ find_package(Qt6 COMPONENTS Gui)
+ endif()
+
+ qt_add_executable(
+ bluetoothtestdevice
+ bluetoothtestdevice.cpp
+ )
+
+else()
+
+ qt_internal_add_executable(bluetoothtestdevice
+ SOURCES
+ bluetoothtestdevice.cpp
+ )
+endif()
+
+set_target_properties(bluetoothtestdevice PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(
+ bluetoothtestdevice
+ PUBLIC
+ Qt::Core
+ Qt::Bluetooth
+)
+
+if(ANDROID OR IOS)
+ target_link_libraries(
+ bluetoothtestdevice
+ PUBLIC
+ Qt::Gui
+ )
+endif()
diff --git a/tests/bluetoothtestdevice/bluetoothtestdevice.cpp b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
new file mode 100644
index 00000000..ef3a15fc
--- /dev/null
+++ b/tests/bluetoothtestdevice/bluetoothtestdevice.cpp
@@ -0,0 +1,368 @@
+/***************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QBluetoothLocalDevice>
+#include <QLatin1String>
+#include <QLoggingCategory>
+#include <QLowEnergyAdvertisingData>
+#include <QLowEnergyAdvertisingParameters>
+#include <QLowEnergyCharacteristicData>
+#include <QLowEnergyController>
+#include <QLowEnergyDescriptorData>
+#include <QLowEnergyServiceData>
+#include <QLowEnergyDescriptorData>
+#include <QTimer>
+
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+#include <QGuiApplication>
+#else
+#include <QCoreApplication>
+#endif // Q_OS_ANDROID || Q_OS_IOS
+
+#include <thread>
+
+static const QLatin1String largeCharacteristicServiceUuid("1f85e37c-ac16-11eb-ae5c-93d3a763feed");
+static const QLatin1String largeCharacteristicCharUuid("40e4f68e-ac16-11eb-9956-cfe55a8c370c");
+
+static const QLatin1String
+ notificationIndicationTestServiceUuid("bb137ac5-5716-4b80-873b-e2d11d29efe2");
+static const QLatin1String
+ notificationIndicationTestChar1Uuid("6da4d652-0248-478a-a5a8-1e2f076158cc");
+static const QLatin1String
+ notificationIndicationTestChar2Uuid("990930f0-b9cc-4c27-8c1b-ebc2bcae5c95");
+static const QLatin1String
+ notificationIndicationTestChar3Uuid("9a60486b-de5b-4e03-b914-4e158c0bd388");
+static const QLatin1String
+ notificationIndicationTestChar4Uuid("d92435d4-6c2e-43f8-a6be-bbb66b5a3e28");
+
+static const QLatin1String connectionCountServiceUuid("78c61a07-a0f9-4b92-be2d-2570d8dbf010");
+static const QLatin1String connectionCountCharUuid("9414ec2d-792f-46a2-a19e-186d0fb38a08");
+
+static const QLatin1String mtuServiceUuid("9a9483eb-cf4f-4c32-9a6b-794238d5b483");
+static const QLatin1String mtuCharUuid("960d7e2a-a850-4a70-8064-cd74e9ccb6ff");
+
+int main(int argc, char *argv[])
+{
+ qDebug() << "build:" << __DATE__ << __TIME__;
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
+ QGuiApplication app(argc, argv);
+#else
+ QCoreApplication app(argc, argv);
+#endif // Q_OS_ANDROID || Q_OS_IOS
+
+ // prepare list of services
+ QList<QLowEnergyServiceData> serviceDefinitions;
+
+ int connectioncount = 0;
+ {
+ // connection count service
+ QLowEnergyServiceData serviceData;
+ serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ serviceData.setUuid(QBluetoothUuid(connectionCountServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(connectionCountCharUuid));
+ size_t size = sizeof(int);
+ QByteArray initialValue(size, 0);
+ charData.setValue(initialValue);
+ charData.setValueLength(size, size);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read);
+
+ serviceData.addCharacteristic(charData);
+
+ serviceDefinitions << serviceData;
+ }
+
+ {
+ // mtu size service
+ QLowEnergyServiceData serviceData;
+ serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ serviceData.setUuid(QBluetoothUuid(mtuServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(mtuCharUuid));
+ size_t size = sizeof(int);
+ int mtu = 23;
+ QByteArray initialValue((const char *)&mtu, sizeof(int));
+ charData.setValue(initialValue);
+ charData.setValueLength(size, size);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Notify);
+
+ serviceData.addCharacteristic(charData);
+
+ serviceDefinitions << serviceData;
+ }
+ {
+ // large characteristic service
+ //
+ // This is just a service offering a 512 bytes large characteristic which can
+ // be read and written. It is used to test reading and writing at MTU sizes smaller
+ // than the size of the characteristic.
+ QLowEnergyServiceData serviceData;
+ serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ serviceData.setUuid(QBluetoothUuid(largeCharacteristicServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(largeCharacteristicCharUuid));
+ QByteArray initialValue(512, 0);
+ initialValue[0] = 0x0b;
+ charData.setValue(initialValue);
+ charData.setValueLength(512, 512);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Write);
+
+ serviceData.addCharacteristic(charData);
+
+ serviceDefinitions << serviceData;
+ }
+
+ {
+
+ // notification service
+ //
+ // This is a service which offers:
+ // - one characteristic which does not support notification or indication
+ // - one characteristic which does only support notification
+ // - one characteristic which does only support indication
+ // - one characteristic which supports both notification or indication
+ // to test their discovery
+ QLowEnergyServiceData serviceData;
+
+ serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ serviceData.setUuid(QBluetoothUuid(notificationIndicationTestServiceUuid));
+
+ {
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(notificationIndicationTestChar1Uuid));
+ QByteArray initialValue(8, 0);
+ initialValue[0] = 0x0b;
+ charData.setValue(initialValue);
+ charData.setValueLength(8, 8);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read);
+
+ serviceData.addCharacteristic(charData);
+ }
+ {
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(notificationIndicationTestChar2Uuid));
+ QByteArray initialValue(8, 0);
+ initialValue[0] = 0x0b;
+ charData.setValue(initialValue);
+ charData.setValueLength(8, 8);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Notify);
+
+ const QLowEnergyDescriptorData clientConfig(
+ QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
+ QLowEnergyCharacteristic::CCCDDisable);
+ charData.addDescriptor(clientConfig);
+
+ serviceData.addCharacteristic(charData);
+ }
+ {
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(notificationIndicationTestChar3Uuid));
+ QByteArray initialValue(8, 0);
+ initialValue[0] = 0x0b;
+ charData.setValue(initialValue);
+ charData.setValueLength(8, 8);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Indicate);
+
+ const QLowEnergyDescriptorData clientConfig(
+ QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
+ QLowEnergyCharacteristic::CCCDDisable);
+ charData.addDescriptor(clientConfig);
+
+ serviceData.addCharacteristic(charData);
+ }
+ {
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(notificationIndicationTestChar4Uuid));
+ QByteArray initialValue(8, 0);
+ initialValue[0] = 0x0b;
+ charData.setValue(initialValue);
+ charData.setValueLength(8, 8);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Notify
+ | QLowEnergyCharacteristic::PropertyType::Indicate);
+
+ const QLowEnergyDescriptorData clientConfig(
+ QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
+ QLowEnergyCharacteristic::CCCDDisable);
+ charData.addDescriptor(clientConfig);
+
+ serviceData.addCharacteristic(charData);
+ }
+
+ serviceDefinitions << serviceData;
+ }
+
+#ifndef Q_OS_IOS
+ auto localAdapters = QBluetoothLocalDevice::allDevices();
+ QBluetoothLocalDevice adapter(localAdapters.back().address());
+ adapter.setHostMode(QBluetoothLocalDevice::HostDiscoverable);
+#endif // Q_OS_IOS
+
+ // Advertising data
+ QLowEnergyAdvertisingData advertisingData;
+ advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
+ advertisingData.setIncludePowerLevel(true);
+ advertisingData.setLocalName("BluetoothTestDevice");
+ QList<QBluetoothUuid> serviceUuids;
+ for (const auto &serviceData : serviceDefinitions) {
+ serviceUuids << serviceData.uuid();
+ }
+ // leads to too large advertising data
+ // advertisingData.setServices(serviceUuids);
+
+ // start advertising
+#ifndef Q_OS_IOS
+ auto devices = QBluetoothLocalDevice::allDevices();
+ qDebug() << "advertising on" << devices.back().address();
+ const QScopedPointer<QLowEnergyController> leController(
+ QLowEnergyController::createPeripheral(devices.back().address()));
+#else
+ const QScopedPointer<QLowEnergyController> leController(
+ QLowEnergyController::createPeripheral());
+#endif // Q_OS_IOS
+ QList<QSharedPointer<QLowEnergyService>> services;
+
+ for (const auto &serviceData : serviceDefinitions) {
+ services.emplaceBack(leController->addService(serviceData));
+ std::this_thread::sleep_for (std::chrono::seconds(1));
+ }
+
+ leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
+ advertisingData);
+
+
+ auto reconnect = [&connectioncount, &leController, advertisingData, &services, serviceDefinitions]() {
+ connectioncount++;
+ for (int i = 0; i < services.size(); ++i) {
+ services[i].reset(leController->addService(serviceDefinitions[i]));
+ std::this_thread::sleep_for (std::chrono::seconds(1));
+ }
+
+
+ {
+ // set connection counter
+ Q_ASSERT(services[0]->serviceUuid()
+ == QBluetoothUuid(connectionCountServiceUuid));
+ QByteArray value((const char *)&connectioncount, sizeof(int));
+ QLowEnergyCharacteristic characteristic = services[0]->characteristic(
+ QBluetoothUuid(connectionCountCharUuid));
+ Q_ASSERT(characteristic.isValid());
+ services[0]->writeCharacteristic(characteristic, value);
+ }
+
+ bool allValid = true;
+ for (const auto &service : services) {
+ allValid = allValid & !service.isNull();
+ }
+
+ if (allValid){
+ leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,
+ advertisingData);
+ qDebug() << "starting advertising for " << connectioncount << "th time.";
+ }
+ else
+ qDebug() << "Cannot start advertising: Service not valid.";
+ };
+ QObject::connect(leController.data(), &QLowEnergyController::disconnected, reconnect);
+
+ QObject::connect(leController.data(), &QLowEnergyController::mtuChanged, [&services](int mtu) {
+ qDebug() << "MTU changed, callback called.";
+ Q_ASSERT(services[1]->serviceUuid() == QBluetoothUuid(mtuServiceUuid));
+ QByteArray value((const char *)&mtu, sizeof(int));
+ QLowEnergyCharacteristic characteristic =
+ services[1]->characteristic(QBluetoothUuid(mtuCharUuid));
+ Q_ASSERT(characteristic.isValid());
+ services[1]->writeCharacteristic(characteristic, value);
+ });
+
+ QTimer notificationTestTimer;
+ quint64 currentCharacteristicValue = 0;
+ const auto characteristicChanger = [&services, &currentCharacteristicValue]() {
+ QByteArray value((const char *)&currentCharacteristicValue, 8);
+
+ Q_ASSERT(services[3]->serviceUuid()
+ == QBluetoothUuid(notificationIndicationTestServiceUuid));
+ {
+ QLowEnergyCharacteristic characteristic = services[3]->characteristic(
+ QBluetoothUuid(notificationIndicationTestChar2Uuid));
+ Q_ASSERT(characteristic.isValid());
+ services[3]->writeCharacteristic(characteristic,
+ value); // Potentially causes notification.
+ }
+ {
+ QLowEnergyCharacteristic characteristic = services[3]->characteristic(
+ QBluetoothUuid(notificationIndicationTestChar3Uuid));
+ Q_ASSERT(characteristic.isValid());
+ services[3]->writeCharacteristic(characteristic,
+ value); // Potentially causes notification.
+ }
+ {
+ QLowEnergyCharacteristic characteristic = services[3]->characteristic(
+ QBluetoothUuid(notificationIndicationTestChar4Uuid));
+ Q_ASSERT(characteristic.isValid());
+ services[3]->writeCharacteristic(characteristic,
+ value); // Potentially causes notification.
+ }
+
+ ++currentCharacteristicValue;
+ };
+ QObject::connect(&notificationTestTimer, &QTimer::timeout, characteristicChanger);
+ notificationTestTimer.start(100);
+
+ return app.exec();
+}