Rearrange the automatic tests.

Split them into unit and integration tests.  Integration tests really
need to be run on the real platform (not in a VM etc) since they are
somewhat unstable or nonfunctional otherwise.

A few tests were previously broken by QUrl changes and they were repaired.
Removed one test since it was not providing a lot of value.

There are still a number of tests that rely on Q_AUTOTEST_EXPORT symbols.

Change-Id: Ic402abf0af946baa5945075d975b3f584f9ef280
Reviewed-by: Kalle Lehtonen <kalle.ju.lehtonen@nokia.com>
This commit is contained in:
Michael Goddard
2011-11-04 13:38:44 +10:00
committed by Qt by Nokia
parent 7dfb883df6
commit e3a8c165ea
190 changed files with 39 additions and 596 deletions

View File

@@ -0,0 +1,12 @@
TARGET = tst_qaudioinput
QT += core multimedia-private testlib
CONFIG += no_private_qt_headers_warning
# This is more of a system test
# CONFIG += testcase
DEFINES += SRCDIR=\\\"$$PWD/\\\"
HEADERS += wavheader.h
SOURCES += wavheader.cpp tst_qaudioinput.cpp

View File

@@ -0,0 +1,850 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/qlocale.h>
#include <qaudioinput.h>
#include <qaudiodeviceinfo.h>
#include <qaudioformat.h>
#include <qaudio.h>
#include "wavheader.h"
//TESTED_COMPONENT=src/multimedia
#define AUDIO_BUFFER 192000
#ifndef QTRY_VERIFY2
#define QTRY_VERIFY2(__expr,__msg) \
do { \
const int __step = 50; \
const int __timeout = 5000; \
if (!(__expr)) { \
QTest::qWait(0); \
} \
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
QTest::qWait(__step); \
} \
QVERIFY2(__expr,__msg); \
} while(0)
#endif
class tst_QAudioInput : public QObject
{
Q_OBJECT
public:
tst_QAudioInput(QObject* parent=0) : QObject(parent) {}
private slots:
void initTestCase();
void format();
void invalidFormat_data();
void invalidFormat();
void bufferSize();
void notifyInterval();
void disableNotifyInterval();
void stopWhileStopped();
void suspendWhileStopped();
void resumeWhileStopped();
void pull();
void pullSuspendResume();
void push();
void pushSuspendResume();
void reset();
void cleanupTestCase();
private:
QString formatToFileName(const QAudioFormat &format);
QString workingDir();
QAudioDeviceInfo audioDevice;
QList<QAudioFormat> testFormats;
QList<QFile*> audioFiles;
QScopedPointer<QByteArray> m_byteArray;
QScopedPointer<QBuffer> m_buffer;
};
QString tst_QAudioInput::formatToFileName(const QAudioFormat &format)
{
const QString formatEndian = (format.byteOrder() == QAudioFormat::LittleEndian)
? QString("LE") : QString("BE");
const QString formatSigned = (format.sampleType() == QAudioFormat::SignedInt)
? QString("signed") : QString("unsigned");
return QString("%1_%2_%3_%4_%5")
.arg(format.frequency())
.arg(format.sampleSize())
.arg(formatSigned)
.arg(formatEndian)
.arg(format.channels());
}
QString tst_QAudioInput::workingDir()
{
QDir working(QString(SRCDIR));
if (working.exists())
return QString(SRCDIR);
return QDir::currentPath();
}
void tst_QAudioInput::initTestCase()
{
qRegisterMetaType<QAudioFormat>();
// Only perform tests if audio output device exists
const QList<QAudioDeviceInfo> devices =
QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if (devices.size() <= 0)
QSKIP("No audio backend", SkipAll);
audioDevice = QAudioDeviceInfo::defaultInputDevice();
QAudioFormat format;
format.setCodec("audio/pcm");
if (audioDevice.isFormatSupported(audioDevice.preferredFormat()))
testFormats.append(audioDevice.preferredFormat());
// PCM 8000 mono S8
format.setFrequency(8000);
format.setSampleSize(8);
format.setSampleType(QAudioFormat::SignedInt);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setChannels(1);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
// PCM 11025 mono S16LE
format.setFrequency(11025);
format.setSampleSize(16);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
// PCM 22050 mono S16LE
format.setFrequency(22050);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
// PCM 22050 stereo S16LE
format.setChannels(2);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
// PCM 44100 stereo S16LE
format.setFrequency(44100);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
// PCM 48000 stereo S16LE
format.setFrequency(48000);
if (audioDevice.isFormatSupported(format))
testFormats.append(format);
QVERIFY(testFormats.size());
foreach (const QAudioFormat &format, testFormats) {
QFile* file = new QFile(workingDir() + formatToFileName(format) + QString(".wav"));
audioFiles.append(file);
}
}
void tst_QAudioInput::format()
{
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QAudioFormat requested = audioDevice.preferredFormat();
QAudioFormat actual = audioInput.format();
QVERIFY2((requested.channels() == actual.channels()),
QString("channels: requested=%1, actual=%2").arg(requested.channels()).arg(actual.channels()).toLocal8Bit().constData());
QVERIFY2((requested.frequency() == actual.frequency()),
QString("frequency: requested=%1, actual=%2").arg(requested.frequency()).arg(actual.frequency()).toLocal8Bit().constData());
QVERIFY2((requested.sampleSize() == actual.sampleSize()),
QString("sampleSize: requested=%1, actual=%2").arg(requested.sampleSize()).arg(actual.sampleSize()).toLocal8Bit().constData());
QVERIFY2((requested.codec() == actual.codec()),
QString("codec: requested=%1, actual=%2").arg(requested.codec()).arg(actual.codec()).toLocal8Bit().constData());
QVERIFY2((requested.byteOrder() == actual.byteOrder()),
QString("byteOrder: requested=%1, actual=%2").arg(requested.byteOrder()).arg(actual.byteOrder()).toLocal8Bit().constData());
QVERIFY2((requested.sampleType() == actual.sampleType()),
QString("sampleType: requested=%1, actual=%2").arg(requested.sampleType()).arg(actual.sampleType()).toLocal8Bit().constData());
}
void tst_QAudioInput::invalidFormat_data()
{
QTest::addColumn<QAudioFormat>("invalidFormat");
QAudioFormat format;
QTest::newRow("Null Format")
<< format;
format = audioDevice.preferredFormat();
format.setChannelCount(0);
QTest::newRow("Channel count 0")
<< format;
format = audioDevice.preferredFormat();
format.setSampleRate(0);
QTest::newRow("Sample rate 0")
<< format;
format = audioDevice.preferredFormat();
format.setSampleSize(0);
QTest::newRow("Sample size 0")
<< format;
}
void tst_QAudioInput::invalidFormat()
{
QFETCH(QAudioFormat, invalidFormat);
QVERIFY2(!audioDevice.isFormatSupported(invalidFormat),
"isFormatSupported() is returning true on an invalid format");
QAudioInput audioInput(invalidFormat, this);
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
audioInput.start();
// Check that error is raised
QTRY_VERIFY2((audioInput.error() == QAudio::OpenError),"error() was not set to QAudio::OpenError after start()");
}
void tst_QAudioInput::bufferSize()
{
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
audioInput.setBufferSize(512);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
QVERIFY2((audioInput.bufferSize() == 512),
QString("bufferSize: requested=512, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
audioInput.setBufferSize(4096);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
QVERIFY2((audioInput.bufferSize() == 4096),
QString("bufferSize: requested=4096, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
audioInput.setBufferSize(8192);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
QVERIFY2((audioInput.bufferSize() == 8192),
QString("bufferSize: requested=8192, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
}
void tst_QAudioInput::notifyInterval()
{
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
audioInput.setNotifyInterval(50);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(50)");
QVERIFY2((audioInput.notifyInterval() == 50),
QString("notifyInterval: requested=50, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
audioInput.setNotifyInterval(100);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(100)");
QVERIFY2((audioInput.notifyInterval() == 100),
QString("notifyInterval: requested=100, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
audioInput.setNotifyInterval(250);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(250)");
QVERIFY2((audioInput.notifyInterval() == 250),
QString("notifyInterval: requested=250, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
audioInput.setNotifyInterval(1000);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(1000)");
QVERIFY2((audioInput.notifyInterval() == 1000),
QString("notifyInterval: requested=1000, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
}
void tst_QAudioInput::disableNotifyInterval()
{
// Sets an invalid notification interval (QAudioInput::setNotifyInterval(0))
// Checks that
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
// - if <= 0, set to zero and disable notify signal
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
audioInput.setNotifyInterval(0);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(0)");
QVERIFY2((audioInput.notifyInterval() == 0),
"notifyInterval() is not zero after setNotifyInterval(0)");
audioInput.setNotifyInterval(-1);
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(-1)");
QVERIFY2((audioInput.notifyInterval() == 0),
"notifyInterval() is not zero after setNotifyInterval(-1)");
//start and run to check if notify() is emitted
if (audioFiles.size() > 0) {
QAudioInput audioInputCheck(testFormats.at(0), this);
audioInputCheck.setNotifyInterval(0);
QSignalSpy notifySignal(&audioInputCheck, SIGNAL(notify()));
audioFiles.at(0)->open(QIODevice::WriteOnly);
audioInputCheck.start(audioFiles.at(0));
QTest::qWait(3000); // 3 seconds should be plenty
audioInputCheck.stop();
QVERIFY2((notifySignal.count() == 0),
QString("didn't disable notify interval: shouldn't have got any but got %1").arg(notifySignal.count()).toLocal8Bit().constData());
audioFiles.at(0)->close();
}
}
void tst_QAudioInput::stopWhileStopped()
{
// Calls QAudioInput::stop() when object is already in StoppedState
// Checks that
// - No state change occurs
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
audioInput.stop();
// Check that no state transition occurred
QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
void tst_QAudioInput::suspendWhileStopped()
{
// Calls QAudioInput::suspend() when object is already in StoppedState
// Checks that
// - No state change occurs
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
audioInput.suspend();
// Check that no state transition occurred
QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
}
void tst_QAudioInput::resumeWhileStopped()
{
// Calls QAudioInput::resume() when object is already in StoppedState
// Checks that
// - No state change occurs
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
QAudioInput audioInput(audioDevice.preferredFormat(), this);
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
audioInput.resume();
// Check that no state transition occurred
QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
}
void tst_QAudioInput::pull()
{
for(int i=0; i<audioFiles.count(); i++) {
QAudioInput audioInput(testFormats.at(i), this);
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
audioFiles.at(i)->close();
audioFiles.at(i)->open(QIODevice::WriteOnly);
WavHeader wavHeader(testFormats.at(i));
QVERIFY(wavHeader.write(*audioFiles.at(i)));
audioInput.start(audioFiles.at(i));
// Check that QAudioInput immediately transitions to ActiveState or IdleState
QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
stateSignal.clear();
// Check that 'elapsed' increases
QTest::qWait(40);
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
// Allow some recording to happen
QTest::qWait(3000); // 3 seconds should be plenty
stateSignal.clear();
qint64 processedUs = audioInput.processedUSecs();
audioInput.stop();
QTest::qWait(40);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((processedUs > 2800000 && processedUs < 3200000),
QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
audioFiles.at(i)->close();
}
}
void tst_QAudioInput::pullSuspendResume()
{
for(int i=0; i<audioFiles.count(); i++) {
QAudioInput audioInput(testFormats.at(i), this);
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
audioFiles.at(i)->close();
audioFiles.at(i)->open(QIODevice::WriteOnly);
WavHeader wavHeader(testFormats.at(i));
QVERIFY(wavHeader.write(*audioFiles.at(i)));
audioInput.start(audioFiles.at(i));
// Check that QAudioInput immediately transitions to ActiveState or IdleState
QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
stateSignal.clear();
// Check that 'elapsed' increases
QTest::qWait(40);
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
// Allow some recording to happen
QTest::qWait(3000); // 3 seconds should be plenty
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after some recording");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after some recording");
stateSignal.clear();
audioInput.suspend();
// Give backends running in separate threads a chance to suspend.
QTest::qWait(100);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
stateSignal.clear();
// Check that only 'elapsed', and not 'processed' increases while suspended
qint64 elapsedUs = audioInput.elapsedUSecs();
qint64 processedUs = audioInput.processedUSecs();
QTest::qWait(1000);
QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
QVERIFY(audioInput.processedUSecs() == processedUs);
audioInput.resume();
// Give backends running in separate threads a chance to resume.
QTest::qWait(100);
// Check that QAudioInput immediately transitions to ActiveState
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
stateSignal.clear();
processedUs = audioInput.processedUSecs();
audioInput.stop();
QTest::qWait(40);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((processedUs > 2800000 && processedUs < 3200000),
QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
audioFiles.at(i)->close();
}
}
void tst_QAudioInput::push()
{
for(int i=0; i<audioFiles.count(); i++) {
QAudioInput audioInput(testFormats.at(i), this);
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
audioFiles.at(i)->close();
audioFiles.at(i)->open(QIODevice::WriteOnly);
WavHeader wavHeader(testFormats.at(i));
QVERIFY(wavHeader.write(*audioFiles.at(i)));
QIODevice* feed = audioInput.start();
// Check that QAudioInput immediately transitions to IdleState
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState),
"didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
stateSignal.clear();
// Check that 'elapsed' increases
QTest::qWait(40);
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
qint64 totalBytesRead = 0;
bool firstBuffer = true;
QByteArray buffer(AUDIO_BUFFER, 0);
qint64 len = (testFormats.at(i).frequency()*testFormats.at(i).channels()*(testFormats.at(i).sampleSize()/8)*2); // 2 seconds
while (totalBytesRead < len) {
if (audioInput.bytesReady() >= audioInput.periodSize()) {
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
audioFiles.at(i)->write(buffer.constData(),bytesRead);
totalBytesRead+=bytesRead;
if (firstBuffer && bytesRead) {
// Check for transition to ActiveState when data is provided
QVERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after data");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
firstBuffer = false;
}
} else
QTest::qWait(20);
}
QTest::qWait(1000);
stateSignal.clear();
qint64 processedUs = audioInput.processedUSecs();
audioInput.stop();
QTest::qWait(40);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((processedUs > 1800000 && processedUs < 2200000),
QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
audioFiles.at(i)->close();
}
}
void tst_QAudioInput::pushSuspendResume()
{
for(int i=0; i<audioFiles.count(); i++) {
QAudioInput audioInput(testFormats.at(i), this);
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
audioFiles.at(i)->close();
audioFiles.at(i)->open(QIODevice::WriteOnly);
WavHeader wavHeader(testFormats.at(i));
QVERIFY(wavHeader.write(*audioFiles.at(i)));
QIODevice* feed = audioInput.start();
// Check that QAudioInput immediately transitions to IdleState
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState),
"didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
stateSignal.clear();
// Check that 'elapsed' increases
QTest::qWait(40);
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
qint64 totalBytesRead = 0;
bool firstBuffer = true;
QByteArray buffer(AUDIO_BUFFER, 0);
qint64 len = (testFormats.at(i).frequency()*testFormats.at(i).channels()*(testFormats.at(i).sampleSize()/8)); // 1 seconds
while (totalBytesRead < len) {
if (audioInput.bytesReady() >= audioInput.periodSize()) {
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
audioFiles.at(i)->write(buffer.constData(),bytesRead);
totalBytesRead+=bytesRead;
if (firstBuffer && bytesRead) {
// Check for transition to ActiveState when data is provided
QVERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
QVERIFY2((audioInput.state() == QAudio::ActiveState),
"didn't transition to ActiveState after data");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
firstBuffer = false;
}
} else
QTest::qWait(20);
}
stateSignal.clear();
audioInput.suspend();
// Give backends running in separate threads a chance to suspend
QTest::qWait(100);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
stateSignal.clear();
// Check that only 'elapsed', and not 'processed' increases while suspended
qint64 elapsedUs = audioInput.elapsedUSecs();
qint64 processedUs = audioInput.processedUSecs();
QTest::qWait(1000);
QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
QVERIFY(audioInput.processedUSecs() == processedUs);
audioInput.resume();
// Give backends running in separate threads a chance to resume.
QTest::qWait(100);
// Check that QAudioInput immediately transitions to Active or IdleState
QVERIFY2((stateSignal.count() > 0),"didn't emit signals on resume()");
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
"didn't transition to ActiveState or IdleState after resume()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
QVERIFY(audioInput.periodSize() > 0);
// Let it play out what is in buffer and go to Idle before continue
QTest::qWait(1000);
stateSignal.clear();
// Read another seconds worth
totalBytesRead = 0;
firstBuffer = true;
while (totalBytesRead < len) {
if (audioInput.bytesReady() >= audioInput.periodSize()) {
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
audioFiles.at(i)->write(buffer.constData(),bytesRead);
totalBytesRead+=bytesRead;
} else
QTest::qWait(20);
}
stateSignal.clear();
processedUs = audioInput.processedUSecs();
audioInput.stop();
QTest::qWait(40);
QVERIFY2((stateSignal.count() == 1),
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
QVERIFY2((processedUs > 1800000 && processedUs < 2200000),
QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
audioFiles.at(i)->close();
}
}
void tst_QAudioInput::reset()
{
for(int i=0; i<audioFiles.count(); i++) {
// Try both push/pull.. the vagaries of Active vs Idle are tested elsewhere
{
QAudioInput audioInput(testFormats.at(i), this);
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
QIODevice* device = audioInput.start();
// Check that QAudioInput immediately transitions to IdleState
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
QVERIFY2((audioInput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
QTRY_VERIFY2((audioInput.bytesReady() > 0), "no bytes available after starting");
// Trigger a read
QByteArray data = device->read(1);
QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after read()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
stateSignal.clear();
audioInput.reset();
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
}
{
QAudioInput audioInput(testFormats.at(i), this);
QBuffer buffer;
audioInput.setNotifyInterval(100);
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
// Check that we are in the default state before calling start
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
audioInput.start(&buffer);
// Check that QAudioInput immediately transitions to ActiveState
QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit state changed signal on start()");
QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
QVERIFY(audioInput.periodSize() > 0);
QTRY_VERIFY2((audioInput.bytesReady() > 0), "no bytes available after starting");
stateSignal.clear();
audioInput.reset();
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
}
}
}
void tst_QAudioInput::cleanupTestCase()
{
QFile* file;
foreach (file, audioFiles) {
file->remove();
delete file;
}
}
QTEST_MAIN(tst_QAudioInput)
#include "tst_qaudioinput.moc"

View File

@@ -0,0 +1,205 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qendian.h>
#include "wavheader.h"
struct chunk
{
char id[4];
quint32 size;
};
struct RIFFHeader
{
chunk descriptor; // "RIFF"
char type[4]; // "WAVE"
};
struct WAVEHeader
{
chunk descriptor;
quint16 audioFormat;
quint16 numChannels;
quint32 sampleRate;
quint32 byteRate;
quint16 blockAlign;
quint16 bitsPerSample;
};
struct DATAHeader
{
chunk descriptor;
};
struct CombinedHeader
{
RIFFHeader riff;
WAVEHeader wave;
DATAHeader data;
};
static const int HeaderLength = sizeof(CombinedHeader);
WavHeader::WavHeader(const QAudioFormat &format, qint64 dataLength)
: m_format(format)
, m_dataLength(dataLength)
{
}
bool WavHeader::read(QIODevice &device)
{
bool result = true;
if (!device.isSequential())
result = device.seek(0);
// else, assume that current position is the start of the header
if (result) {
CombinedHeader header;
result = (device.read(reinterpret_cast<char *>(&header), HeaderLength) == HeaderLength);
if (result) {
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
&& memcmp(&header.riff.type, "WAVE", 4) == 0
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
&& header.wave.audioFormat == 1 // PCM
) {
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
m_format.setByteOrder(QAudioFormat::LittleEndian);
else
m_format.setByteOrder(QAudioFormat::BigEndian);
m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
m_format.setCodec("audio/pcm");
m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
switch(header.wave.bitsPerSample) {
case 8:
m_format.setSampleType(QAudioFormat::UnSignedInt);
break;
case 16:
m_format.setSampleType(QAudioFormat::SignedInt);
break;
default:
result = false;
}
m_dataLength = device.size() - HeaderLength;
} else {
result = false;
}
}
}
return result;
}
bool WavHeader::write(QIODevice &device)
{
CombinedHeader header;
memset(&header, 0, HeaderLength);
// RIFF header
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
memcpy(header.riff.descriptor.id,"RIFF",4);
else
memcpy(header.riff.descriptor.id,"RIFX",4);
qToLittleEndian<quint32>(quint32(m_dataLength + HeaderLength - 8),
reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
memcpy(header.riff.type, "WAVE",4);
// WAVE header
memcpy(header.wave.descriptor.id,"fmt ",4);
qToLittleEndian<quint32>(quint32(16),
reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
qToLittleEndian<quint16>(quint16(1),
reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
qToLittleEndian<quint16>(quint16(m_format.channels()),
reinterpret_cast<unsigned char*>(&header.wave.numChannels));
qToLittleEndian<quint32>(quint32(m_format.frequency()),
reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8),
reinterpret_cast<unsigned char*>(&header.wave.byteRate));
qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8),
reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
qToLittleEndian<quint16>(quint16(m_format.sampleSize()),
reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
// DATA header
memcpy(header.data.descriptor.id,"data",4);
qToLittleEndian<quint32>(quint32(m_dataLength),
reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
return (device.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength);
}
const QAudioFormat& WavHeader::format() const
{
return m_format;
}
qint64 WavHeader::dataLength() const
{
return m_dataLength;
}
qint64 WavHeader::headerLength()
{
return HeaderLength;
}
bool WavHeader::writeDataLength(QIODevice &device, qint64 dataLength)
{
bool result = false;
if (!device.isSequential()) {
device.seek(40);
unsigned char dataLengthLE[4];
qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE);
result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4);
}
return result;
}

View File

@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef WAVHEADER_H
#define WAVHEADER_H
#include <QtCore/qobject.h>
#include <QtCore/qfile.h>
#include <qaudioformat.h>
/**
* Helper class for parsing WAV file headers.
*
* See https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
*/
class WavHeader
{
public:
WavHeader(const QAudioFormat &format = QAudioFormat(),
qint64 dataLength = 0);
// Reads WAV header and seeks to start of data
bool read(QIODevice &device);
// Writes WAV header
bool write(QIODevice &device);
const QAudioFormat& format() const;
qint64 dataLength() const;
static qint64 headerLength();
static bool writeDataLength(QIODevice &device, qint64 dataLength);
private:
QAudioFormat m_format;
qint64 m_dataLength;
};
#endif