Skip to content

Commit

Permalink
Add tests to audio sink and source with stop upon the first state change
Browse files Browse the repository at this point in the history
The tests cover the issues described in the ticket.

Task-number: QTBUG-126816
Pick-to: 6.8
Change-Id: I2e6a9ce9b421d36d25a27134f8a2e53ac7c71fdd
Reviewed-by: Jøger Hansegård <[email protected]>
  • Loading branch information
Artemiy-d committed Oct 17, 2024
1 parent b2f4c1f commit 9bdf857
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/multimediatestlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ qt_internal_add_module(MultimediaTestLibPrivate
qscopedenvironmentvariable_p.h
qsequentialfileadaptor_p.h
qsinewavevalidator_p.h qsinewavevalidator.cpp
qmockiodevice_p.h
PUBLIC_LIBRARIES
Qt::Core
Qt::CorePrivate
Expand Down
38 changes: 38 additions & 0 deletions src/multimediatestlib/qmockiodevice_p.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#ifndef QMOCKIODEVICE_P_H
#define QMOCKIODEVICE_P_H

#include <qiodevice.h>

//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

QT_BEGIN_NAMESPACE

class MockIODevice : public QIODevice
{
public:
using QIODevice::QIODevice;

qint64 writeData(const char *, qint64 len) override { return len; }

qint64 readData(char *data, qint64 maxlen) override
{
memset(data, 0, maxlen);
return maxlen;
}
};

QT_END_NAMESPACE

#endif // QMOCKIODEVICE_P_H
86 changes: 67 additions & 19 deletions tests/auto/integration/qaudiosink/tst_qaudiosink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,32 @@
#include <qmediadevices.h>
#include <qwavedecoder.h>

#include <private/qmockiodevice_p.h>

#define AUDIO_BUFFER 192000

using AudioSinkInitializer = bool (*)(QAudioSink &);

class AudioPullSource : public QIODevice
{
public:
qint64 readData(char *data, qint64 len) override
{
qint64 read = qMin(len, available);
available -= read;
memset(data, 0, read);
return read;
}
qint64 writeData(const char *, qint64) override { return 0; }
bool isSequential() const override { return true; }

qint64 bytesAvailable() const override { return available; }
bool atEnd() const override { return signalEnd && available == 0; }

qint64 available = 0;
bool signalEnd = false;
};

class tst_QAudioSink : public QObject
{
Q_OBJECT
Expand Down Expand Up @@ -59,6 +83,9 @@ private slots:
void volume_data();
void volume();

void stop_stopsAudioSink_whenInvokedUponFirstStateChange_data();
void stop_stopsAudioSink_whenInvokedUponFirstStateChange();

private:
using FilePtr = QSharedPointer<QFile>;

Expand Down Expand Up @@ -554,25 +581,6 @@ void tst_QAudioSink::pullSuspendResume()

void tst_QAudioSink::pullResumeFromUnderrun()
{
class AudioPullSource : public QIODevice
{
public:
qint64 readData(char *data, qint64 len) override {
qint64 read = qMin(len, available);
available -= read;
memset(data, 0, read);
return read;
}
qint64 writeData(const char *, qint64) override { return 0; }
bool isSequential() const override { return true; }

qint64 bytesAvailable() const override { return available; }
bool atEnd() const override { return signalEnd && available == 0; }

qint64 available = 0;
bool signalEnd = false;
};

constexpr int chunkSize = 128;

QAudioFormat format;
Expand Down Expand Up @@ -1003,6 +1011,46 @@ void tst_QAudioSink::volume()
QTRY_VERIFY(qRound(audioOutput.volume()*10.0f) == expectedInt);
}

void tst_QAudioSink::stop_stopsAudioSink_whenInvokedUponFirstStateChange_data()
{
QTest::addColumn<AudioSinkInitializer>("initializer");

AudioSinkInitializer initPullMode = [](QAudioSink &sink) {
QIODevice *device = new MockIODevice(&sink);
device->open(QIODevice::ReadOnly);
sink.start(device);
return sink.error() == QtAudio::NoError;
};

AudioSinkInitializer initPushMode = [](QAudioSink &sink) {
QIODevice *device = sink.start();
return device && sink.error() == QtAudio::NoError;
};

QTest::newRow("pullMode") << initPullMode;
QTest::newRow("pushMode") << initPushMode;
}

void tst_QAudioSink::stop_stopsAudioSink_whenInvokedUponFirstStateChange()
{
QFETCH(const AudioSinkInitializer, initializer);

QAudioSink audioSink(testFormats.at(0));

auto stop = [&audioSink]() {
audioSink.stop();
QCOMPARE(audioSink.state(), QtAudio::State::StoppedState);
};

connect(&audioSink, &QAudioSink::stateChanged, this, stop, Qt::SingleShotConnection);

if (!initializer(audioSink))
QSKIP("Cannot start the audio sink"); // Pulse audio backend fails on some Linux CI.
// TODO: replace with QVERIFY

QTRY_COMPARE(audioSink.state(), QtAudio::State::StoppedState);
}

QTEST_MAIN(tst_QAudioSink)

#include "tst_qaudiosink.moc"
55 changes: 55 additions & 0 deletions tests/auto/integration/qaudiosource/tst_qaudiosource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <qwavedecoder.h>

#include <private/mediabackendutils_p.h>
#include <private/qmockiodevice_p.h>

#define RANGE_ERR 0.5

Expand All @@ -23,6 +24,8 @@ template<typename T> inline bool qTolerantCompare(T value, T expected)
return qAbs(value - expected) < (RANGE_ERR * expected);
}

using AudioSourceInitializer = bool (*)(QAudioSource &);

class tst_QAudioSource : public QObject
{
Q_OBJECT
Expand Down Expand Up @@ -62,6 +65,9 @@ private slots:

void stop_finishesPushMode_whenInvokedUponReadyReadSignal();

void stop_stopsAudioSource_whenInvokedUponFirstStateChange_data();
void stop_stopsAudioSource_whenInvokedUponFirstStateChange();

private:
using FilePtr = QSharedPointer<QFile>;

Expand Down Expand Up @@ -844,6 +850,55 @@ void tst_QAudioSource::stop_finishesPushMode_whenInvokedUponReadyReadSignal()
"didn't transitions to StoppedState after close()");
}

void tst_QAudioSource::stop_stopsAudioSource_whenInvokedUponFirstStateChange_data()
{
QTest::addColumn<AudioSourceInitializer>("initializer");

AudioSourceInitializer initPullMode = [](QAudioSource &source) {
QIODevice *device = new MockIODevice(&source);
device->open(QIODevice::WriteOnly);
source.start(device);
return source.error() == QtAudio::NoError;
};

AudioSourceInitializer initPushMode = [](QAudioSource &source) {
QIODevice *device = source.start();
return device && source.error() == QtAudio::NoError;
};

QTest::newRow("pullMode") << initPullMode;
QTest::newRow("pushMode") << initPushMode;
}

void tst_QAudioSource::stop_stopsAudioSource_whenInvokedUponFirstStateChange()
{
QFETCH(const AudioSourceInitializer, initializer);

const QAudioDevice defaultAudioInputDevice = QMediaDevices::defaultAudioInput();

QAudioFormat audioFormat;
audioFormat.setSampleFormat(QAudioFormat::Int16);
audioFormat.setSampleRate(qBound(defaultAudioInputDevice.minimumSampleRate(), 48000,
defaultAudioInputDevice.maximumSampleRate()));
audioFormat.setChannelCount(qBound(defaultAudioInputDevice.minimumChannelCount(), 2,
defaultAudioInputDevice.maximumChannelCount()));

QAudioSource audioSource(audioFormat);

auto stop = [&audioSource]() {
audioSource.stop();
QCOMPARE(audioSource.state(), QtAudio::State::StoppedState);
};

connect(&audioSource, &QAudioSource::stateChanged, this, stop, Qt::SingleShotConnection);

if (!initializer(audioSource))
QSKIP("Cannot start the audio source"); // Pulse audio backend fails on some Linux CI.
// TODO: replace with QVERIFY, QTBUG-130272

QTRY_COMPARE(audioSource.state(), QtAudio::State::StoppedState);
}

QTEST_MAIN(tst_QAudioSource)

#include "tst_qaudiosource.moc"

0 comments on commit 9bdf857

Please sign in to comment.