1/*
2 * SPDX-FileCopyrightText: 2019 Manuel Weichselbaumer <mincequi@web.de>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7#include "mediatransporttest.h"
8#include "a2dp-codecs.h"
9#include "autotests.h"
10#include "initmanagerjob.h"
11#include "pendingcall.h"
12#include "services.h"
13
14#include <QSignalSpy>
15#include <QTest>
16
17namespace BluezQt
18{
19extern void bluezqt_initFakeBluezTestRun();
20}
21
22using namespace BluezQt;
23
24Q_DECLARE_METATYPE(MediaTransport::State)
25
26static a2dp_sbc_t sbcConfiguration;
27static a2dp_aac_t aacConfiguration;
28
29static AudioCodec charToCodec(uchar value)
30{
31 switch (value) {
32 case A2DP_CODEC_SBC:
33 return AudioCodec::Sbc;
34 break;
35 case A2DP_CODEC_MPEG24:
36 return AudioCodec::Aac;
37 break;
38 }
39
40 return AudioCodec::Invalid;
41}
42
43static AudioSampleRate byteArrayToSampleRate(AudioCodec codec, const QByteArray &buffer)
44{
45 switch (codec) {
46 case AudioCodec::Sbc: {
47 if (buffer.size() != sizeof(a2dp_sbc_t)) {
48 return AudioSampleRate::Invalid;
49 }
50
51 a2dp_sbc_t sbcConfig = *reinterpret_cast<const a2dp_sbc_t *>(buffer.constData());
52 switch (sbcConfig.frequency) {
53 case SBC_SAMPLING_FREQ_44100:
54 return AudioSampleRate::Rate44100;
55 break;
56 case SBC_SAMPLING_FREQ_48000:
57 return AudioSampleRate::Rate48000;
58 break;
59 }
60 break;
61 }
62 case AudioCodec::Aac: {
63 if (buffer.size() != sizeof(a2dp_aac_t)) {
64 return AudioSampleRate::Invalid;
65 }
66
67 a2dp_aac_t aacConfig = *reinterpret_cast<const a2dp_aac_t *>(buffer.constData());
68 switch (AAC_GET_FREQUENCY(aacConfig)) {
69 case AAC_SAMPLING_FREQ_44100:
70 return AudioSampleRate::Rate44100;
71 break;
72 case AAC_SAMPLING_FREQ_48000:
73 return AudioSampleRate::Rate48000;
74 break;
75 }
76 break;
77 }
78 }
79
80 return AudioSampleRate::Invalid;
81}
82
83QString stateString(MediaTransport::State state)
84{
85 switch (state) {
86 case MediaTransport::State::Idle:
87 return QStringLiteral("idle");
88 case MediaTransport::State::Pending:
89 return QStringLiteral("pending");
90 case MediaTransport::State::Active:
91 return QStringLiteral("active");
92 }
93
94 return {};
95}
96
97MediaTransportTest::MediaTransportTest()
98 : m_manager(nullptr)
99{
100 Autotests::registerMetatypes();
101
102 qRegisterMetaType<MediaTransport::State>("State");
103
104 sbcConfiguration.frequency = SBC_SAMPLING_FREQ_44100;
105 AAC_SET_FREQUENCY(aacConfiguration, AAC_SAMPLING_FREQ_48000);
106}
107
108void MediaTransportTest::initTestCase()
109{
110 bluezqt_initFakeBluezTestRun();
111
112 FakeBluez::start();
113 FakeBluez::runTest(QStringLiteral("bluez-standard"));
114
115 // Create adapter
116 QString adapter = QStringLiteral("/org/bluez/hci0");
117 QVariantMap adapterProps;
118 adapterProps[QStringLiteral("Path")] = QVariant::fromValue(QDBusObjectPath(adapter));
119 adapterProps[QStringLiteral("Address")] = QStringLiteral("1C:E5:C3:BC:94:7E");
120 adapterProps[QStringLiteral("Name")] = QStringLiteral("TestAdapter");
121 FakeBluez::runAction(QStringLiteral("devicemanager"), QStringLiteral("create-adapter"), adapterProps);
122
123 // Create devices
124 QVariantMap deviceProps;
125 QVariantMap mediaTransportProps;
126
127 QString device1 = adapter + QLatin1String("/dev_40_79_6A_0C_39_75");
128 deviceProps[QStringLiteral("Path")] = QVariant::fromValue(QDBusObjectPath(device1));
129 deviceProps[QStringLiteral("Adapter")] = QVariant::fromValue(QDBusObjectPath(adapter));
130 deviceProps[QStringLiteral("Address")] = QStringLiteral("40:79:6A:0C:39:75");
131 deviceProps[QStringLiteral("Name")] = QStringLiteral("TestDevice");
132 deviceProps[QStringLiteral("UUIDs")] = QStringList(Services::AudioSink);
133 mediaTransportProps[QStringLiteral("Path")] = QVariant::fromValue(QDBusObjectPath(device1 + QLatin1String("/fd0")));
134 mediaTransportProps[QStringLiteral("Device")] = QVariant::fromValue(QDBusObjectPath(device1));
135 mediaTransportProps[QStringLiteral("UUID")] = Services::AudioSink;
136 mediaTransportProps[QStringLiteral("Codec")] = QVariant::fromValue(quint8(A2DP_CODEC_SBC)); // SBC
137 mediaTransportProps[QStringLiteral("Configuration")] =
138 QVariant::fromValue(QByteArray(reinterpret_cast<const char *>(&sbcConfiguration), sizeof(sbcConfiguration)));
139 mediaTransportProps[QStringLiteral("State")] = QStringLiteral("pending");
140 mediaTransportProps[QStringLiteral("Volume")] = QVariant::fromValue(quint16(63));
141 deviceProps[QStringLiteral("MediaTransport")] = mediaTransportProps;
142 FakeBluez::runAction(QStringLiteral("devicemanager"), QStringLiteral("create-device"), deviceProps);
143
144 QString device2 = adapter + QLatin1String("/dev_50_79_6A_0C_39_75");
145 deviceProps[QStringLiteral("Path")] = QVariant::fromValue(QDBusObjectPath(device2));
146 deviceProps[QStringLiteral("Adapter")] = QVariant::fromValue(QDBusObjectPath(adapter));
147 deviceProps[QStringLiteral("Address")] = QStringLiteral("50:79:6A:0C:39:75");
148 deviceProps[QStringLiteral("Name")] = QStringLiteral("TestDevice2");
149 deviceProps[QStringLiteral("UUIDs")] = QStringList(Services::AudioSink);
150 mediaTransportProps[QStringLiteral("Path")] = QVariant::fromValue(QDBusObjectPath(device2 + QLatin1String("/fd0")));
151 mediaTransportProps[QStringLiteral("Device")] = QVariant::fromValue(QDBusObjectPath(device2));
152 mediaTransportProps[QStringLiteral("UUID")] = Services::AudioSink;
153 mediaTransportProps[QStringLiteral("Codec")] = QVariant::fromValue(quint8(A2DP_CODEC_MPEG24)); // AAC
154 mediaTransportProps[QStringLiteral("Configuration")] =
155 QVariant::fromValue(QByteArray(reinterpret_cast<const char *>(&aacConfiguration), sizeof(aacConfiguration)));
156 mediaTransportProps[QStringLiteral("State")] = QStringLiteral("active");
157 mediaTransportProps[QStringLiteral("Volume")] = QVariant::fromValue(quint16(127));
158 deviceProps[QStringLiteral("MediaTransport")] = mediaTransportProps;
159 FakeBluez::runAction(QStringLiteral("devicemanager"), QStringLiteral("create-device"), deviceProps);
160
161 m_manager = new Manager();
162 InitManagerJob *initJob = m_manager->init();
163 initJob->exec();
164 QVERIFY(!initJob->error());
165
166 QCOMPARE(m_manager->adapters().count(), 1);
167 QCOMPARE(m_manager->devices().count(), 2);
168}
169
170void MediaTransportTest::cleanupTestCase()
171{
172 for (const MediaTransportUnit &unit : m_units) {
173 delete unit.dbusMediaTransport;
174 delete unit.dbusProperties;
175 }
176
177 delete m_manager;
178
179 FakeBluez::stop();
180}
181
182void MediaTransportTest::connectTest()
183{
184 for (DevicePtr device : m_manager->devices()) {
185 QVERIFY(!device->ubi().isEmpty());
186 QVERIFY(!device->mediaTransport());
187
188 QSignalSpy deviceSpy(device.data(), SIGNAL(mediaTransportChanged(MediaTransportPtr)));
189
190 device->connectToDevice();
191 QTRY_COMPARE(deviceSpy.count(), 1);
192 QVERIFY(device->mediaTransport());
193 }
194}
195
196void MediaTransportTest::disconnectTest()
197{
198 for (DevicePtr device : m_manager->devices()) {
199 QVERIFY(device->mediaTransport());
200
201 QSignalSpy deviceSpy(device.data(), SIGNAL(mediaTransportChanged(MediaTransportPtr)));
202
203 device->disconnectFromDevice();
204 QTRY_COMPARE(deviceSpy.count(), 1);
205 QVERIFY(!device->mediaTransport());
206 }
207}
208
209void MediaTransportTest::connectProfileTest()
210{
211 QDBusConnection connection = QDBusConnection::sessionBus();
212 QString service = QStringLiteral("org.kde.bluezqt.fakebluez");
213
214 for (DevicePtr device : m_manager->devices()) {
215 QVERIFY(!device->ubi().isEmpty());
216 QVERIFY(!device->mediaTransport());
217
218 QSignalSpy deviceSpy(device.data(), SIGNAL(mediaTransportChanged(MediaTransportPtr)));
219
220 device->connectProfile(Services::AudioSink);
221 QTRY_COMPARE(deviceSpy.count(), 1);
222 QVERIFY(device->mediaTransport());
223
224 QString path = device->ubi() + QLatin1String("/fd0");
225
226 MediaTransportUnit unit;
227 unit.device = device;
228 unit.dbusMediaTransport = new org::bluez::MediaTransport1(service, path, connection, this);
229 unit.dbusProperties = new org::freedesktop::DBus::Properties(service, path, connection, this);
230 m_units.append(unit);
231 }
232}
233
234void MediaTransportTest::getPropertiesTest()
235{
236 for (const MediaTransportUnit &unit : m_units) {
237 QCOMPARE(unit.device->mediaTransport()->audioConfiguration().codec, charToCodec(unit.dbusMediaTransport->codec()));
238 QCOMPARE(unit.device->mediaTransport()->audioConfiguration().sampleRate,
239 byteArrayToSampleRate(unit.device->mediaTransport()->audioConfiguration().codec, unit.dbusMediaTransport->configuration()));
240 QCOMPARE(stateString(unit.device->mediaTransport()->state()), unit.dbusMediaTransport->state());
241 QCOMPARE(unit.device->mediaTransport()->volume(), unit.dbusMediaTransport->volume());
242 }
243}
244
245void MediaTransportTest::disconnectProfileTest()
246{
247 for (const MediaTransportUnit &unit : m_units) {
248 QVERIFY(unit.device->mediaTransport());
249
250 QSignalSpy deviceSpy(unit.device.data(), SIGNAL(mediaTransportChanged(MediaTransportPtr)));
251
252 unit.device->disconnectProfile(Services::AudioSink);
253 QTRY_COMPARE(deviceSpy.count(), 1);
254 QVERIFY(!unit.device->mediaTransport());
255 }
256}
257
258QTEST_MAIN(MediaTransportTest)
259
260#include "moc_mediatransporttest.cpp"
261

source code of bluez-qt/autotests/mediatransporttest.cpp