Skip to content

Commit 7b60552

Browse files
authored
Merge pull request overte-org#177 from daleglass-overte/audio-testing
Create audio tests for AudioClient and codecs
2 parents 918452d + 342554d commit 7b60552

File tree

5 files changed

+272
-1
lines changed

5 files changed

+272
-1
lines changed

tests/audio/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Declare dependencies
22
macro (SETUP_TESTCASE_DEPENDENCIES)
33
# link in the shared libraries
4-
link_hifi_libraries(shared audio networking)
4+
link_hifi_libraries(shared audio audio-client plugins networking)
55

66
package_libraries_for_deployment()
77
endmacro ()

tests/audio/src/AudioTests.cpp

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <QSignalSpy>
2+
#include <QDebug>
3+
4+
#include "AudioTests.h"
5+
#include "AudioClient.h"
6+
#include "DependencyManager.h"
7+
#include "NodeList.h"
8+
#include "plugins/CodecPlugin.h"
9+
#include "plugins/PluginManager.h"
10+
11+
QTEST_MAIN(AudioTests)
12+
13+
14+
Q_DECLARE_METATYPE(QList<HifiAudioDeviceInfo>)
15+
16+
17+
18+
19+
void AudioTests::initTestCase() {
20+
// AudioClient starts networking, but for the purposes of the tests here we don't care,
21+
// so just got to use some port.
22+
int listenPort = 10000;
23+
24+
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
25+
DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
26+
DependencyManager::set<AudioClient>();
27+
DependencyManager::set<PluginManager>();
28+
QSharedPointer<AudioClient> ac = DependencyManager::get<AudioClient>();
29+
30+
qRegisterMetaType<QList<HifiAudioDeviceInfo>>();
31+
32+
ac->startThread();
33+
}
34+
35+
void AudioTests::listAudioDevices() {
36+
QSharedPointer<AudioClient> ac = DependencyManager::get<AudioClient>();
37+
QVERIFY(!ac.isNull());
38+
39+
/*
40+
// AudioClient::devicesChanged is declared as:
41+
// void devicesChanged(QAudio::Mode mode, const QList<HifiAudioDeviceInfo>& devices);
42+
//
43+
// Unfortunately with QSignalSpy we have to use the old SIGNAL() syntax, so it was a bit tricky
44+
// to figure out how to get the signal to connect. The snippet below lists signals in the format
45+
// that Qt understands. Turns out we lose the 'const &'.
46+
47+
const QMetaObject *mo = ac->metaObject();
48+
QList<QString> signalSignatures;
49+
50+
// Start from MyClass members
51+
for(int methodIdx = mo->methodOffset(); methodIdx < mo->methodCount(); ++methodIdx) {
52+
QMetaMethod mmTest = mo->method(methodIdx);
53+
switch((int)mmTest.methodType()) {
54+
case QMetaMethod::Signal:
55+
signalSignatures.append(QString(mmTest.methodSignature()));
56+
qDebug() << "SIG: " << QString(mmTest.methodSignature());
57+
break;
58+
}
59+
}
60+
*/
61+
62+
QSignalSpy spy(ac.get(), SIGNAL(devicesChanged(QAudio::Mode,QList<HifiAudioDeviceInfo>)));
63+
64+
QVERIFY(spy.isValid()); // This checks that the signal has connected
65+
spy.wait(15000);
66+
67+
// We always get two events here, one for audio input, and one for output,
68+
// but signals keep coming and we could potentially get more repetitions.
69+
QVERIFY(spy.count() > 0);
70+
qDebug() << "Received" << spy.count() << "device events";
71+
72+
// QSignalSpy is a QList, which stores the received signals. We can then examine it to see
73+
// what we got.
74+
for(auto event : spy) {
75+
QAudio::Mode mode = qvariant_cast<QAudio::Mode>(event.at(0));
76+
QList<HifiAudioDeviceInfo> devs = qvariant_cast<QList<HifiAudioDeviceInfo>>(event.at(1));
77+
78+
QVERIFY(devs.count() > 0);
79+
80+
qDebug() << "Mode:" << mode;
81+
for(auto dev : devs) {
82+
qDebug() << "\t" << dev.deviceName();
83+
}
84+
}
85+
86+
87+
}

tests/audio/src/AudioTests.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// AudioTests.h
3+
// tests/audio/src
4+
//
5+
// Created by Dale Glass on 27/8/2022
6+
// Copyright 2022 Overte e.V.
7+
//
8+
// Distributed under the Apache License, Version 2.0.
9+
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+
//
11+
12+
#ifndef hifi_AudioTests_h
13+
#define hifi_AudioTests_h
14+
15+
#include <QtTest/QtTest>
16+
17+
18+
19+
class AudioTests : public QObject {
20+
Q_OBJECT
21+
private slots:
22+
void initTestCase();
23+
24+
void listAudioDevices();
25+
};
26+
27+
#endif // hifi_AudioTests_h

tests/audio/src/CodecTests.cpp

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include <QSignalSpy>
2+
#include <QDebug>
3+
#include <QCoreApplication>
4+
#include <QFile>
5+
6+
7+
#include "CodecTests.h"
8+
#include "AudioClient.h"
9+
#include "DependencyManager.h"
10+
#include "NodeList.h"
11+
#include "plugins/CodecPlugin.h"
12+
#include "plugins/PluginManager.h"
13+
14+
QTEST_MAIN(CodecTests)
15+
16+
17+
18+
19+
void CodecTests::initTestCase() {
20+
DependencyManager::set<PluginManager>();
21+
22+
QDir testPath (QCoreApplication::applicationDirPath());
23+
QDir interfacePluginPath = testPath;
24+
25+
26+
27+
qDebug() << "Our directory is" << testPath;
28+
29+
interfacePluginPath.cdUp();
30+
interfacePluginPath.cdUp();
31+
interfacePluginPath.cd("interface");
32+
interfacePluginPath.cd("plugins");
33+
interfacePluginPath.makeAbsolute();
34+
35+
QString ourPluginPath = testPath.filePath("plugins");
36+
37+
38+
qDebug() << "Interface plugins are at" << interfacePluginPath;
39+
qDebug() << "Our plugins are at" << ourPluginPath;
40+
41+
42+
QFile::link(interfacePluginPath.path(), ourPluginPath);
43+
44+
}
45+
46+
void CodecTests::loadCodecs() {
47+
const auto& codecPlugins = PluginManager::getInstance()->getCodecPlugins();
48+
49+
QVERIFY(codecPlugins.size() > 0);
50+
51+
52+
for (const auto& plugin : codecPlugins) {
53+
auto codecName = plugin->getName();
54+
qDebug() << "Codec:" << codecName << ", supported=" << plugin->isSupported();
55+
}
56+
}
57+
58+
59+
60+
void CodecTests::testEncoders() {
61+
const auto& codecPlugins = PluginManager::getInstance()->getCodecPlugins();
62+
63+
QVERIFY(codecPlugins.size() > 0);
64+
65+
66+
for (const auto& plugin : codecPlugins) {
67+
if (!plugin->isSupported()) {
68+
qWarning() << "Skipping unsupported plugin" << plugin->getName();
69+
continue;
70+
}
71+
72+
Encoder* encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO);
73+
QVERIFY(encoder != nullptr);
74+
75+
QByteArray data(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0);
76+
QByteArray encoded;
77+
78+
encoder->encode(data, encoded);
79+
80+
QVERIFY(encoded.size() > 0);
81+
82+
qDebug() << "Codec" << plugin->getName() << "encoded empty buffer of" << data.size() << "bytes into" << encoded.size();
83+
}
84+
}
85+
86+
void CodecTests::testDecoders () {
87+
const auto& codecPlugins = PluginManager::getInstance()->getCodecPlugins();
88+
89+
QVERIFY(codecPlugins.size() > 0);
90+
91+
92+
for (const auto& plugin : codecPlugins) {
93+
if (!plugin->isSupported()) {
94+
qWarning() << "Skipping unsupported plugin" << plugin->getName();
95+
continue;
96+
}
97+
98+
Encoder* encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO);
99+
Decoder* decoder = plugin->createDecoder(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO);
100+
QVERIFY(encoder != nullptr);
101+
102+
QByteArray data(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0);
103+
QByteArray encoded;
104+
QByteArray decoded;
105+
QByteArray lost(AudioConstants::NETWORK_FRAME_BYTES_STEREO, 0);
106+
107+
108+
encoder->encode(data, encoded);
109+
decoder->decode(encoded, decoded);
110+
111+
112+
QVERIFY(encoded.size() > 0);
113+
QVERIFY(decoded.size() > 0);
114+
QVERIFY(decoded.size() == data.size());
115+
QVERIFY(lost.size() > 0);
116+
117+
118+
qDebug() << "Codec" << plugin->getName() << "encoded empty buffer of" << data.size() << "bytes into" << encoded.size() << "and decoded back into" << decoded.size();
119+
120+
// This is here mostly for valgrind testing -- we can't really validate anything, but we can see if it crashes.
121+
decoder->lostFrame(lost);
122+
QVERIFY(lost.size() > 0);
123+
qDebug() << "Codec" << plugin->getName() << "decoded a lost frame";
124+
}
125+
}

tests/audio/src/CodecTests.h

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//
2+
// AudioTests.h
3+
// tests/audio/src
4+
//
5+
// Created by Dale Glass on 27/8/2022
6+
// Copyright 2022 Overte e.V.
7+
//
8+
// Distributed under the Apache License, Version 2.0.
9+
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10+
//
11+
12+
#ifndef hifi_AudioTests_h
13+
#define hifi_AudioTests_h
14+
15+
#include <QtTest/QtTest>
16+
17+
18+
19+
class CodecTests : public QObject {
20+
Q_OBJECT
21+
private slots:
22+
void initTestCase();
23+
24+
void loadCodecs();
25+
26+
27+
void testEncoders();
28+
void testDecoders();
29+
30+
};
31+
32+
#endif // hifi_AudioTests_h

0 commit comments

Comments
 (0)