summaryrefslogtreecommitdiffstats
path: root/tests/manual/qlowenergycontroller_peripheral/tst_qlowenergycontroller_peripheral.cpp
blob: 061f614eb281b5abe57c2deb6c0131ffe273f39c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QTest>

#include <QBluetoothLocalDevice>
#include <QLowEnergyController>
#include <QLowEnergyServiceData>
#include <QLowEnergyCharacteristicData>
#include <QLowEnergyDescriptorData>

#if QT_CONFIG(permissions)
#include <QtTest/qtesteventloop.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qpermissions.h>
#include <QtCore/qnamespace.h>
#endif // permissions

using namespace Qt::Literals::StringLiterals;

static constexpr auto leServiceUuid{"10f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
static constexpr auto leCharUuid1{"11f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
static const qsizetype leCharacteristicSize = 4; // Set to 1...512 bytes
static QByteArray leCharacteristicValue = QByteArray{leCharacteristicSize, 1};
static QByteArray leDescriptorValue = "a descriptor value"_ba;

class tst_qlowenergycontroller_peripheral : public QObject
{
    Q_OBJECT
private slots:
    void initTestCase();
    void init();
    void cleanup();

    void localCharacteristicReadAfterUpdate();
    void localDescriptorReadAfterUpdate();

private:
    QBluetoothHostInfo mDevice;
    std::unique_ptr<QLowEnergyController> mController;
    QList<QSharedPointer<QLowEnergyService>> mServices;
};

void tst_qlowenergycontroller_peripheral::initTestCase()
{
#ifndef Q_OS_IOS
    auto devices = QBluetoothLocalDevice::allDevices();
    if (devices.isEmpty())
        QSKIP("Failed to find local adapter");
    else
        mDevice = devices.back();
#endif // Q_OS_IOS

#if QT_CONFIG(permissions)
    Qt::PermissionStatus permissionStatus = qApp->checkPermission(QBluetoothPermission{});
    // FIXME: Android will add more specific BT permissions, fix when appropriate
    // change is in qtbase.
    if (qApp->checkPermission(QBluetoothPermission{}) == Qt::PermissionStatus::Undetermined) {
        QTestEventLoop loop;
        qApp->requestPermission(QBluetoothPermission{}, [&permissionStatus, &loop](const QPermission &permission){
            permissionStatus = permission.status();
            loop.exitLoop();
        });
        if (permissionStatus == Qt::PermissionStatus::Undetermined)
            loop.enterLoopMSecs(30000);
    }
    if (permissionStatus != Qt::PermissionStatus::Granted)
        QSKIP("This manual test requires Blutooth permissions granted.");
#endif // permissions
}

void tst_qlowenergycontroller_peripheral::init()
{
    QList<QLowEnergyServiceData> serviceDefinitions;

    QLowEnergyServiceData sd;
    sd.setType(QLowEnergyServiceData::ServiceTypePrimary);
    sd.setUuid(QBluetoothUuid(leServiceUuid));

    QLowEnergyCharacteristicData charData;
    charData.setUuid(QBluetoothUuid(leCharUuid1));
    charData.setValue(leCharacteristicValue);
    charData.setValueLength(leCharacteristicSize, leCharacteristicSize);
    charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
                           | QLowEnergyCharacteristic::PropertyType::Write
                           | QLowEnergyCharacteristic::PropertyType::Notify
                           | QLowEnergyCharacteristic::ExtendedProperty);

    const QLowEnergyDescriptorData clientConfig(
            QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
            QLowEnergyCharacteristic::CCCDDisable);
    charData.addDescriptor(clientConfig);

    const QLowEnergyDescriptorData userDescription(
                QBluetoothUuid::DescriptorType::CharacteristicUserDescription,
                leDescriptorValue);
    charData.addDescriptor(userDescription);

    const QLowEnergyDescriptorData extendedProperties(
                QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties,
                // From bluetooth specs: length 2 bytes
                // bit 0: reliable write, bit 1: writable auxiliaries
                QByteArray::fromHex("0300"));
    charData.addDescriptor(extendedProperties);

    sd.addCharacteristic(charData);

    serviceDefinitions << sd;


#ifndef Q_OS_IOS
    mController.reset(QLowEnergyController::createPeripheral(mDevice.address()));
#else
    mController.reset(QLowEnergyController::createPeripheral());
#endif // Q_OS_IOS
    QVERIFY(mController);

    for (const auto &serviceData : serviceDefinitions) {
        mServices.emplaceBack(mController->addService(serviceData));
    }
}

void tst_qlowenergycontroller_peripheral::cleanup()
{
    if (mController)
        mController->stopAdvertising();

    mController.reset();
    mServices.clear();
}

void tst_qlowenergycontroller_peripheral::localCharacteristicReadAfterUpdate()
{
#ifdef Q_OS_WINDOWS
    QSKIP("Windows does not support peripheral");
#endif
    auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
    QVERIFY(characteristic.isValid());
    const auto initialValue = characteristic.value();
    auto newValue = initialValue;
    newValue[0] = newValue[0] + 1;
    mServices[0]->writeCharacteristic(characteristic, newValue);
    QTRY_COMPARE(characteristic.value(), newValue);
}

void tst_qlowenergycontroller_peripheral::localDescriptorReadAfterUpdate()
{
#ifdef Q_OS_WINDOWS
    QSKIP("Windows does not support peripheral");
#elif defined Q_OS_DARWIN
    QSKIP("Apple devices do not support descriptor value update");
#endif
    auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
    QVERIFY(characteristic.isValid());
    auto descriptor = characteristic.descriptor(
                QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
    QVERIFY(descriptor.isValid());
    const auto initialValue = descriptor.value();
    auto newValue = initialValue;
    newValue[0] = newValue[0] + 1;
    mServices[0]->writeDescriptor(descriptor, newValue);
    QCOMPARE(descriptor.value(), newValue);
}

QTEST_MAIN(tst_qlowenergycontroller_peripheral)

#include "tst_qlowenergycontroller_peripheral.moc"