summaryrefslogtreecommitdiffstats
path: root/tests/auto/integration
diff options
context:
space:
mode:
authorMichael Goddard <michael.goddard@nokia.com>2011-11-04 13:38:44 +1000
committerQt by Nokia <qt-info@nokia.com>2011-11-04 08:19:17 +0100
commite3a8c165eabe139d71a762089ab396e5b492c70b (patch)
tree16d9960d2d20d65e0a5d1becc181c0a7b095d0aa /tests/auto/integration
parent7dfb883df639f8d80cec7bd2c51eb37561bc4522 (diff)
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>
Diffstat (limited to 'tests/auto/integration')
-rw-r--r--tests/auto/integration/integration.pro3
-rw-r--r--tests/auto/integration/multimedia.pro9
-rw-r--r--tests/auto/integration/qaudioinput/qaudioinput.pro12
-rwxr-xr-xtests/auto/integration/qaudioinput/tst_qaudioinput.cpp850
-rwxr-xr-xtests/auto/integration/qaudioinput/wavheader.cpp205
-rwxr-xr-xtests/auto/integration/qaudioinput/wavheader.h80
-rw-r--r--tests/auto/integration/qaudiooutput/qaudiooutput.pro12
-rwxr-xr-xtests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp957
-rwxr-xr-xtests/auto/integration/qaudiooutput/wavheader.cpp205
-rwxr-xr-xtests/auto/integration/qaudiooutput/wavheader.h80
-rw-r--r--tests/auto/integration/qcamerabackend/qcamerabackend.pro10
-rw-r--r--tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp621
-rw-r--r--tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro12
-rw-r--r--tests/auto/integration/qmediaplayerbackend/testdata/test.wavbin0 -> 88232 bytes
-rw-r--r--tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp462
-rw-r--r--tests/auto/integration/qsoundeffect/qsoundeffect.pro17
-rw-r--r--tests/auto/integration/qsoundeffect/test.wavbin0 -> 38316 bytes
-rw-r--r--tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp195
18 files changed, 3730 insertions, 0 deletions
diff --git a/tests/auto/integration/integration.pro b/tests/auto/integration/integration.pro
new file mode 100644
index 000000000..c61fbd4ee
--- /dev/null
+++ b/tests/auto/integration/integration.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += multimedia.pro
diff --git a/tests/auto/integration/multimedia.pro b/tests/auto/integration/multimedia.pro
new file mode 100644
index 000000000..f09ce9410
--- /dev/null
+++ b/tests/auto/integration/multimedia.pro
@@ -0,0 +1,9 @@
+
+TEMPLATE = subdirs
+SUBDIRS += \
+ qaudioinput \
+ qaudiooutput \
+ qmediaplayerbackend \
+ qcamerabackend \
+ qsoundeffect
+
diff --git a/tests/auto/integration/qaudioinput/qaudioinput.pro b/tests/auto/integration/qaudioinput/qaudioinput.pro
new file mode 100644
index 000000000..b70c6ee48
--- /dev/null
+++ b/tests/auto/integration/qaudioinput/qaudioinput.pro
@@ -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
diff --git a/tests/auto/integration/qaudioinput/tst_qaudioinput.cpp b/tests/auto/integration/qaudioinput/tst_qaudioinput.cpp
new file mode 100755
index 000000000..30ed2e10a
--- /dev/null
+++ b/tests/auto/integration/qaudioinput/tst_qaudioinput.cpp
@@ -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"
diff --git a/tests/auto/integration/qaudioinput/wavheader.cpp b/tests/auto/integration/qaudioinput/wavheader.cpp
new file mode 100755
index 000000000..26fcd6f98
--- /dev/null
+++ b/tests/auto/integration/qaudioinput/wavheader.cpp
@@ -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;
+}
diff --git a/tests/auto/integration/qaudioinput/wavheader.h b/tests/auto/integration/qaudioinput/wavheader.h
new file mode 100755
index 000000000..4136da027
--- /dev/null
+++ b/tests/auto/integration/qaudioinput/wavheader.h
@@ -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
+
diff --git a/tests/auto/integration/qaudiooutput/qaudiooutput.pro b/tests/auto/integration/qaudiooutput/qaudiooutput.pro
new file mode 100644
index 000000000..e3b50509e
--- /dev/null
+++ b/tests/auto/integration/qaudiooutput/qaudiooutput.pro
@@ -0,0 +1,12 @@
+TARGET = tst_qaudiooutput
+
+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_qaudiooutput.cpp
diff --git a/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp b/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
new file mode 100755
index 000000000..8810a5969
--- /dev/null
+++ b/tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
@@ -0,0 +1,957 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+//TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <QtCore/qlocale.h>
+
+#include <qaudiooutput.h>
+#include <qaudiodeviceinfo.h>
+#include <qaudioformat.h>
+#include <qaudio.h>
+
+#include "wavheader.h"
+
+#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_QAudioOutput : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QAudioOutput(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 pushUnderrun();
+
+ void cleanupTestCase();
+
+private:
+ QString formatToFileName(const QAudioFormat &format);
+ QString workingDir();
+ void createSineWaveData(const QAudioFormat &format, qint64 length, int frequency = 440);
+
+ QAudioDeviceInfo audioDevice;
+ QList<QAudioFormat> testFormats;
+ QList<QFile*> audioFiles;
+
+ QScopedPointer<QByteArray> m_byteArray;
+ QScopedPointer<QBuffer> m_buffer;
+};
+
+QString tst_QAudioOutput::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_QAudioOutput::workingDir()
+{
+ QDir working(QString(SRCDIR));
+
+ if (working.exists())
+ return QString(SRCDIR);
+
+ return QDir::currentPath();
+}
+
+void tst_QAudioOutput::createSineWaveData(const QAudioFormat &format, qint64 length, int frequency)
+{
+ const int channelBytes = format.sampleSize() / 8;
+ const int sampleBytes = format.channels() * channelBytes;
+
+ Q_ASSERT(length % sampleBytes == 0);
+ Q_UNUSED(sampleBytes) // suppress warning in release builds
+
+ m_byteArray.reset(new QByteArray(length, 0));
+ unsigned char *ptr = reinterpret_cast<unsigned char *>(m_byteArray->data());
+ int sampleIndex = 0;
+
+ while (length) {
+ const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency());
+ for (int i=0; i<format.channels(); ++i) {
+ if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) {
+ const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
+ *reinterpret_cast<quint8*>(ptr) = value;
+ } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) {
+ const qint8 value = static_cast<qint8>(x * 127);
+ *reinterpret_cast<quint8*>(ptr) = value;
+ } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) {
+ quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
+ if (format.byteOrder() == QAudioFormat::LittleEndian)
+ qToLittleEndian<quint16>(value, ptr);
+ else
+ qToBigEndian<quint16>(value, ptr);
+ } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) {
+ qint16 value = static_cast<qint16>(x * 32767);
+ if (format.byteOrder() == QAudioFormat::LittleEndian)
+ qToLittleEndian<qint16>(value, ptr);
+ else
+ qToBigEndian<qint16>(value, ptr);
+ }
+
+ ptr += channelBytes;
+ length -= channelBytes;
+ }
+ ++sampleIndex;
+ }
+
+ m_buffer.reset(new QBuffer(m_byteArray.data(), this));
+ Q_ASSERT(m_buffer->open(QIODevice::ReadOnly));
+}
+
+void tst_QAudioOutput::initTestCase()
+{
+ qRegisterMetaType<QAudioFormat>();
+
+ // Only perform tests if audio output device exists
+ const QList<QAudioDeviceInfo> devices =
+ QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+
+ if (devices.size() <= 0)
+ QSKIP("No audio backend", SkipAll);
+
+ audioDevice = QAudioDeviceInfo::defaultOutputDevice();
+
+
+ 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) {
+ qint64 len = (format.frequency()*format.channels()*(format.sampleSize()/8)*2); // 2 seconds
+ createSineWaveData(format, len);
+ // Write generate sine wave data to file
+ QFile* file = new QFile(workingDir() + QString("generated") + formatToFileName(format) + QString(".wav"));
+ if (file->open(QIODevice::WriteOnly)) {
+ WavHeader wavHeader(format, len);
+ wavHeader.write(*file);
+ file->write(m_byteArray->data(), len);
+ file->close();
+ audioFiles.append(file);
+ }
+ }
+}
+
+void tst_QAudioOutput::format()
+{
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QAudioFormat requested = audioDevice.preferredFormat();
+ QAudioFormat actual = audioOutput.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_QAudioOutput::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_QAudioOutput::invalidFormat()
+{
+ QFETCH(QAudioFormat, invalidFormat);
+
+ QVERIFY2(!audioDevice.isFormatSupported(invalidFormat),
+ "isFormatSupported() is returning true on an invalid format");
+
+ QAudioOutput audioOutput(invalidFormat, this);
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+
+ audioOutput.start();
+ // Check that error is raised
+ QTRY_VERIFY2((audioOutput.error() == QAudio::OpenError),"error() was not set to QAudio::OpenError after start()");
+}
+
+void tst_QAudioOutput::bufferSize()
+{
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
+
+ audioOutput.setBufferSize(512);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
+ QVERIFY2((audioOutput.bufferSize() == 512),
+ QString("bufferSize: requested=512, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
+
+ audioOutput.setBufferSize(4096);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
+ QVERIFY2((audioOutput.bufferSize() == 4096),
+ QString("bufferSize: requested=4096, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
+
+ audioOutput.setBufferSize(8192);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
+ QVERIFY2((audioOutput.bufferSize() == 8192),
+ QString("bufferSize: requested=8192, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
+}
+
+void tst_QAudioOutput::notifyInterval()
+{
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
+
+ audioOutput.setNotifyInterval(50);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(50)");
+ QVERIFY2((audioOutput.notifyInterval() == 50),
+ QString("notifyInterval: requested=50, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
+
+ audioOutput.setNotifyInterval(100);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(100)");
+ QVERIFY2((audioOutput.notifyInterval() == 100),
+ QString("notifyInterval: requested=100, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
+
+ audioOutput.setNotifyInterval(250);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(250)");
+ QVERIFY2((audioOutput.notifyInterval() == 250),
+ QString("notifyInterval: requested=250, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
+
+ audioOutput.setNotifyInterval(1000);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(1000)");
+ QVERIFY2((audioOutput.notifyInterval() == 1000),
+ QString("notifyInterval: requested=1000, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
+}
+
+void tst_QAudioOutput::disableNotifyInterval()
+{
+ // Sets an invalid notification interval (QAudioOutput::setNotifyInterval(0))
+ // Checks that
+ // - No error is raised (QAudioOutput::error() returns QAudio::NoError)
+ // - if <= 0, set to zero and disable notify signal
+
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
+
+ audioOutput.setNotifyInterval(0);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(0)");
+ QVERIFY2((audioOutput.notifyInterval() == 0),
+ "notifyInterval() is not zero after setNotifyInterval(0)");
+
+ audioOutput.setNotifyInterval(-1);
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(-1)");
+ QVERIFY2((audioOutput.notifyInterval() == 0),
+ "notifyInterval() is not zero after setNotifyInterval(-1)");
+
+ //start and run to check if notify() is emitted
+ if (audioFiles.size() > 0) {
+ QAudioOutput audioOutputCheck(testFormats.at(0), this);
+ audioOutputCheck.setNotifyInterval(0);
+ QSignalSpy notifySignal(&audioOutputCheck, SIGNAL(notify()));
+ audioFiles.at(0)->open(QIODevice::ReadOnly);
+ audioOutputCheck.start(audioFiles.at(0));
+ QTest::qWait(3000); // 3 seconds should be plenty
+ audioOutputCheck.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_QAudioOutput::stopWhileStopped()
+{
+ // Calls QAudioOutput::stop() when object is already in StoppedState
+ // Checks that
+ // - No state change occurs
+ // - No error is raised (QAudioOutput::error() returns QAudio::NoError)
+
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ audioOutput.stop();
+
+ // Check that no state transition occurred
+ QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
+}
+
+void tst_QAudioOutput::suspendWhileStopped()
+{
+ // Calls QAudioOutput::suspend() when object is already in StoppedState
+ // Checks that
+ // - No state change occurs
+ // - No error is raised (QAudioOutput::error() returns QAudio::NoError)
+
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ audioOutput.suspend();
+
+ // Check that no state transition occurred
+ QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
+}
+
+void tst_QAudioOutput::resumeWhileStopped()
+{
+ // Calls QAudioOutput::resume() when object is already in StoppedState
+ // Checks that
+ // - No state change occurs
+ // - No error is raised (QAudioOutput::error() returns QAudio::NoError)
+
+ QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
+
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+ audioOutput.resume();
+
+ // Check that no state transition occurred
+ QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
+}
+
+void tst_QAudioOutput::pull()
+{
+ for(int i=0; i<audioFiles.count(); i++) {
+ QAudioOutput audioOutput(testFormats.at(i), this);
+
+ audioOutput.setNotifyInterval(100);
+
+ QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+ QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
+
+ audioFiles.at(i)->close();
+ audioFiles.at(i)->open(QIODevice::ReadOnly);
+ audioFiles.at(i)->seek(WavHeader::headerLength());
+
+ audioOutput.start(audioFiles.at(i));
+
+ // Check that QAudioOutput immediately transitions to ActiveState
+ QTRY_VERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
+ QVERIFY(audioOutput.periodSize() > 0);
+ stateSignal.clear();
+
+ // Check that 'elapsed' increases
+ QTest::qWait(40);
+ QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
+
+ // Wait until playback finishes
+ QTest::qWait(3000); // 3 seconds should be plenty
+
+ QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
+ stateSignal.clear();
+
+ qint64 processedUs = audioOutput.processedUSecs();
+
+ audioOutput.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((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
+
+ QVERIFY2((processedUs == 2000000),
+ QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
+ QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
+ QVERIFY2((notifySignal.count() > 15 && notifySignal.count() < 25),
+ QString("too many notify() signals emitted (%1)").arg(notifySignal.count()).toLocal8Bit().constData());
+
+ audioFiles.at(i)->close();
+ }
+}
+
+void tst_QAudioOutput::pullSuspendResume()
+{
+ for(int i=0; i<audioFiles.count(); i++) {
+ QAudioOutput audioOutput(testFormats.at(i), this);
+
+ audioOutput.setNotifyInterval(100);
+
+ QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+ QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
+
+ audioFiles.at(i)->close();
+ audioFiles.at(i)->open(QIODevice::ReadOnly);
+ audioFiles.at(i)->seek(WavHeader::headerLength());
+
+ audioOutput.start(audioFiles.at(i));
+ // Check that QAudioOutput immediately transitions to ActiveState
+ QTRY_VERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
+ QVERIFY(audioOutput.periodSize() > 0);
+ stateSignal.clear();
+
+ // Wait for half of clip to play
+ QTest::qWait(1000);
+
+ audioOutput.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((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
+ stateSignal.clear();
+
+ // Check that only 'elapsed', and not 'processed' increases while suspended
+ qint64 elapsedUs = audioOutput.elapsedUSecs();
+ qint64 processedUs = audioOutput.processedUSecs();
+ QTest::qWait(1000);
+ QVERIFY(audioOutput.elapsedUSecs() > elapsedUs);
+ QVERIFY(audioOutput.processedUSecs() == processedUs);
+
+ audioOutput.resume();
+
+ // Give backends running in separate threads a chance to suspend.
+ QTest::qWait(100);
+
+ // Check that QAudioOutput 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((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
+ stateSignal.clear();
+
+ // Wait until playback finishes
+ QTest::qWait(3000); // 3 seconds should be plenty
+
+ QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
+ stateSignal.clear();
+
+ processedUs = audioOutput.processedUSecs();
+
+ audioOutput.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((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
+
+ QVERIFY2((processedUs == 2000000),
+ QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
+ QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
+
+ audioFiles.at(i)->close();
+ }
+}
+
+void tst_QAudioOutput::push()
+{
+ for(int i=0; i<audioFiles.count(); i++) {
+ QAudioOutput audioOutput(testFormats.at(i), this);
+
+ audioOutput.setNotifyInterval(100);
+
+ QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+ QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
+
+ audioFiles.at(i)->close();
+ audioFiles.at(i)->open(QIODevice::ReadOnly);
+ audioFiles.at(i)->seek(WavHeader::headerLength());
+
+ QIODevice* feed = audioOutput.start();
+
+ // Check that QAudioOutput immediately transitions to IdleState
+ QTRY_VERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
+ QVERIFY(audioOutput.periodSize() > 0);
+ stateSignal.clear();
+
+ // Check that 'elapsed' increases
+ QTest::qWait(40);
+ QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
+ QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
+
+ qint64 written = 0;
+ bool firstBuffer = true;
+ QByteArray buffer(AUDIO_BUFFER, 0);
+
+ while (written < audioFiles.at(i)->size()-WavHeader::headerLength()) {
+
+ if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
+ qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
+ written += feed->write(buffer.constData(), len);
+
+ if (firstBuffer) {
+ // Check for transition to ActiveState when data is provided
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
+ firstBuffer = false;
+ }
+ } else
+ QTest::qWait(20);
+ }
+ stateSignal.clear();
+
+ // Wait until playback finishes
+ QTest::qWait(3000); // 3 seconds should be plenty
+
+ QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
+ stateSignal.clear();
+
+ qint64 processedUs = audioOutput.processedUSecs();
+
+ audioOutput.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((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
+
+ QVERIFY2((processedUs == 2000000),
+ QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
+ QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
+ QVERIFY2((notifySignal.count() > 15 && notifySignal.count() < 25),
+ QString("too many notify() signals emitted (%1)").arg(notifySignal.count()).toLocal8Bit().constData());
+
+ audioFiles.at(i)->close();
+ }
+}
+
+void tst_QAudioOutput::pushSuspendResume()
+{
+ for(int i=0; i<audioFiles.count(); i++) {
+ QAudioOutput audioOutput(testFormats.at(i), this);
+
+ audioOutput.setNotifyInterval(100);
+
+ QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+ QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
+
+ audioFiles.at(i)->close();
+ audioFiles.at(i)->open(QIODevice::ReadOnly);
+ audioFiles.at(i)->seek(WavHeader::headerLength());
+
+ QIODevice* feed = audioOutput.start();
+
+ // Check that QAudioOutput immediately transitions to IdleState
+ QTRY_VERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
+ QVERIFY(audioOutput.periodSize() > 0);
+ stateSignal.clear();
+
+ // Check that 'elapsed' increases
+ QTest::qWait(40);
+ QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
+ QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
+
+ qint64 written = 0;
+ bool firstBuffer = true;
+ QByteArray buffer(AUDIO_BUFFER, 0);
+
+ // Play half of the clip
+ while (written < (audioFiles.at(i)->size()-WavHeader::headerLength())/2) {
+
+ if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
+ qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
+ written += feed->write(buffer.constData(), len);
+
+ if (firstBuffer) {
+ // Check for transition to ActiveState when data is provided
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
+ firstBuffer = false;
+ }
+ } else
+ QTest::qWait(20);
+ }
+ stateSignal.clear();
+
+ audioOutput.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((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
+ stateSignal.clear();
+
+ // Check that only 'elapsed', and not 'processed' increases while suspended
+ qint64 elapsedUs = audioOutput.elapsedUSecs();
+ qint64 processedUs = audioOutput.processedUSecs();
+ QTest::qWait(1000);
+ QVERIFY(audioOutput.elapsedUSecs() > elapsedUs);
+ QVERIFY(audioOutput.processedUSecs() == processedUs);
+
+ audioOutput.resume();
+
+ // Give backends running in separate threads a chance to suspend.
+ QTest::qWait(100);
+
+ // Check that QAudioOutput 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((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
+ stateSignal.clear();
+
+ // Play rest of the clip
+ while (!audioFiles.at(i)->atEnd()) {
+ if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
+ qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
+ written += feed->write(buffer.constData(), len);
+ } else
+ QTest::qWait(20);
+ }
+ stateSignal.clear();
+
+ // Wait until playback finishes
+ QTest::qWait(1000); // 1 seconds should be plenty
+
+ QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
+ stateSignal.clear();
+
+ processedUs = audioOutput.processedUSecs();
+
+ audioOutput.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((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
+
+ QVERIFY2((processedUs == 2000000),
+ QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
+ QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
+
+ audioFiles.at(i)->close();
+ }
+}
+
+void tst_QAudioOutput::pushUnderrun()
+{
+ for(int i=0; i<audioFiles.count(); i++) {
+ QAudioOutput audioOutput(testFormats.at(i), this);
+
+ audioOutput.setNotifyInterval(100);
+
+ QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
+ QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
+
+ // Check that we are in the default state before calling start
+ QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
+ QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
+
+ audioFiles.at(i)->close();
+ audioFiles.at(i)->open(QIODevice::ReadOnly);
+ audioFiles.at(i)->seek(WavHeader::headerLength());
+
+ QIODevice* feed = audioOutput.start();
+
+ // Check that QAudioOutput immediately transitions to IdleState
+ QTRY_VERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
+ QVERIFY(audioOutput.periodSize() > 0);
+ stateSignal.clear();
+
+ // Check that 'elapsed' increases
+ QTest::qWait(40);
+ QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
+ QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
+
+ qint64 written = 0;
+ bool firstBuffer = true;
+ QByteArray buffer(AUDIO_BUFFER, 0);
+
+ // Play half of the clip
+ while (written < (audioFiles.at(i)->size()-WavHeader::headerLength())/2) {
+
+ if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
+ qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
+ written += feed->write(buffer.constData(), len);
+
+ if (firstBuffer) {
+ // Check for transition to ActiveState when data is provided
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
+ firstBuffer = false;
+ }
+ } else
+ QTest::qWait(20);
+ }
+ stateSignal.clear();
+
+ // Wait for data to be played
+ QTest::qWait(1000);
+
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal after suspend(), got %1 signals instead")
+ .arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState, no data");
+ QVERIFY2((audioOutput.error() == QAudio::UnderrunError), "error state is not equal to QAudio::UnderrunError, no data");
+ stateSignal.clear();
+
+ firstBuffer = true;
+ // Play rest of the clip
+ while (!audioFiles.at(i)->atEnd()) {
+ if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
+ qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
+ written += feed->write(buffer.constData(), len);
+ if (firstBuffer) {
+ // Check for transition to ActiveState when data is provided
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit signal after receiving data, got %1 signals instead")
+ .arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
+ firstBuffer = false;
+ }
+ } else
+ QTest::qWait(20);
+ }
+ stateSignal.clear();
+
+ // Wait until playback finishes
+ QTest::qWait(1000); // 1 seconds should be plenty
+
+ QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
+ QVERIFY2((stateSignal.count() == 1),
+ QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
+ stateSignal.clear();
+
+ qint64 processedUs = audioOutput.processedUSecs();
+
+ audioOutput.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((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
+
+ QVERIFY2((processedUs == 2000000),
+ QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
+ QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
+ QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
+
+ audioFiles.at(i)->close();
+ }
+}
+
+void tst_QAudioOutput::cleanupTestCase()
+{
+ QFile* file;
+
+ foreach (file, audioFiles) {
+ file->remove();
+ delete file;
+ }
+}
+
+QTEST_MAIN(tst_QAudioOutput)
+
+#include "tst_qaudiooutput.moc"
diff --git a/tests/auto/integration/qaudiooutput/wavheader.cpp b/tests/auto/integration/qaudiooutput/wavheader.cpp
new file mode 100755
index 000000000..26fcd6f98
--- /dev/null
+++ b/tests/auto/integration/qaudiooutput/wavheader.cpp
@@ -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;
+}
diff --git a/tests/auto/integration/qaudiooutput/wavheader.h b/tests/auto/integration/qaudiooutput/wavheader.h
new file mode 100755
index 000000000..5212eca67
--- /dev/null
+++ b/tests/auto/integration/qaudiooutput/wavheader.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+
+#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
+
diff --git a/tests/auto/integration/qcamerabackend/qcamerabackend.pro b/tests/auto/integration/qcamerabackend/qcamerabackend.pro
new file mode 100644
index 000000000..147885ffc
--- /dev/null
+++ b/tests/auto/integration/qcamerabackend/qcamerabackend.pro
@@ -0,0 +1,10 @@
+CONFIG += testcase
+TARGET = tst_qcamerabackend
+
+QT += multimedia-private multimediawidgets-private testlib
+CONFIG += no_private_qt_headers_warning
+
+# This is more of a system test
+# CONFIG += testcase
+
+SOURCES += tst_qcamerabackend.cpp
diff --git a/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
new file mode 100644
index 000000000..25e960d44
--- /dev/null
+++ b/tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
@@ -0,0 +1,621 @@
+/****************************************************************************
+**
+** 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 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$
+**
+****************************************************************************/
+
+//TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <QtGui/QImageReader>
+#include <QDebug>
+
+#include <qabstractvideosurface.h>
+#include <qcameracontrol.h>
+#include <qcameralockscontrol.h>
+#include <qcameraexposurecontrol.h>
+#include <qcameraflashcontrol.h>
+#include <qcamerafocuscontrol.h>
+#include <qcameraimagecapturecontrol.h>
+#include <qimageencodercontrol.h>
+#include <qcameraimageprocessingcontrol.h>
+#include <qcameracapturebufferformatcontrol.h>
+#include <qcameracapturedestinationcontrol.h>
+#include <qmediaservice.h>
+#include <qcamera.h>
+#include <qcameraimagecapture.h>
+#include <qgraphicsvideoitem.h>
+#include <qvideorenderercontrol.h>
+#include <qvideowidget.h>
+#include <qvideowindowcontrol.h>
+
+QT_USE_NAMESPACE
+
+// Eventually these will make it into qtestcase.h
+// but we might need to tweak the timeout values here.
+#ifndef QTRY_COMPARE
+#define QTRY_COMPARE(__expr, __expected) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 10000; \
+ if ((__expr) != (__expected)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
+ QTest::qWait(__step); \
+ } \
+ QCOMPARE(__expr, __expected); \
+ } while(0)
+#endif
+
+#ifndef QTRY_VERIFY
+#define QTRY_VERIFY(__expr) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 10000; \
+ if (!(__expr)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
+ QTest::qWait(__step); \
+ } \
+ QVERIFY(__expr); \
+ } while(0)
+#endif
+
+
+#define QTRY_WAIT(code, __expr) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 10000; \
+ if (!(__expr)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
+ do { code } while(0); \
+ QTest::qWait(__step); \
+ } \
+ } while(0)
+
+
+/*
+ This is the backend conformance test.
+
+ Since it relies on platform media framework and sound hardware
+ it may be less stable.
+*/
+
+class tst_QCameraBackend: public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+private slots:
+ void testAvailableDevices();
+ void testDeviceDescription();
+ void testCtorWithDevice();
+
+ void testCameraStates();
+ void testCaptureMode();
+ void testCameraCapture();
+ void testCaptureToBuffer();
+ void testCameraCaptureMetadata();
+ void testExposureCompensation();
+ void testExposureMode();
+private:
+};
+
+void tst_QCameraBackend::initTestCase()
+{
+ qRegisterMetaType<QtMultimedia::MetaData>("QtMultimedia::MetaData");
+
+ QCamera camera;
+ if (!camera.isAvailable())
+ QSKIP("Camera is not available", SkipAll);
+}
+
+void tst_QCameraBackend::cleanupTestCase()
+{
+}
+
+void tst_QCameraBackend::testAvailableDevices()
+{
+ int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
+ QCOMPARE(QCamera::availableDevices().count(), deviceCount);
+}
+
+void tst_QCameraBackend::testDeviceDescription()
+{
+ int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
+
+ if (deviceCount == 0)
+ QVERIFY(QCamera::deviceDescription(QByteArray("random")).isNull());
+ else {
+ foreach (const QByteArray &device, QCamera::availableDevices())
+ QVERIFY(QCamera::deviceDescription(device).length() > 0);
+ }
+}
+
+void tst_QCameraBackend::testCtorWithDevice()
+{
+ int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
+ QCamera *camera = 0;
+
+ if (deviceCount == 0) {
+ camera = new QCamera("random");
+ QCOMPARE(camera->error(), QCamera::ServiceMissingError);
+ }
+ else {
+ camera = new QCamera(QCamera::availableDevices().first());
+ QCOMPARE(camera->error(), QCamera::NoError);
+ }
+
+ delete camera;
+}
+
+void tst_QCameraBackend::testCameraStates()
+{
+ QCamera camera;
+ QCameraImageCapture imageCapture(&camera);
+
+ QSignalSpy errorSignal(&camera, SIGNAL(error(QCamera::Error)));
+ QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State)));
+ QSignalSpy statusChangedSignal(&camera, SIGNAL(statusChanged(QCamera::Status)));
+
+ QCOMPARE(camera.state(), QCamera::UnloadedState);
+ QCOMPARE(camera.status(), QCamera::UnloadedStatus);
+
+ camera.load();
+ QCOMPARE(camera.state(), QCamera::LoadedState);
+ QCOMPARE(stateChangedSignal.count(), 1);
+ QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState);
+ QVERIFY(stateChangedSignal.count() > 0);
+
+ QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
+ QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus);
+
+ camera.unload();
+ QCOMPARE(camera.state(), QCamera::UnloadedState);
+ QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState);
+ QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
+ QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus);
+
+#ifdef Q_WS_MAEMO_6
+ //resource policy doesn't work correctly when resource is released and immediately requested again.
+ QTest::qWait(250);
+#endif
+
+ camera.start();
+ QCOMPARE(camera.state(), QCamera::ActiveState);
+ QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::ActiveState);
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::ActiveStatus);
+
+ camera.stop();
+ QCOMPARE(camera.state(), QCamera::LoadedState);
+ QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState);
+ QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
+ QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus);
+
+ camera.unload();
+ QCOMPARE(camera.state(), QCamera::UnloadedState);
+ QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState);
+ QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
+ QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus);
+
+ QCOMPARE(camera.errorString(), QString());
+ QCOMPARE(errorSignal.count(), 0);
+}
+
+void tst_QCameraBackend::testCaptureMode()
+{
+ QCamera camera;
+
+ QSignalSpy errorSignal(&camera, SIGNAL(error(QCamera::Error)));
+ QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State)));
+ QSignalSpy captureModeSignal(&camera, SIGNAL(captureModeChanged(QCamera::CaptureMode)));
+
+ QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
+
+ if (!camera.isCaptureModeSupported(QCamera::CaptureVideo)) {
+ camera.setCaptureMode(QCamera::CaptureVideo);
+ QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
+ QSKIP("Video capture not supported", SkipAll);
+ }
+
+ camera.setCaptureMode(QCamera::CaptureVideo);
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+ QTRY_COMPARE(captureModeSignal.size(), 1);
+ QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
+ captureModeSignal.clear();
+
+ camera.load();
+ QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
+ //capture mode should still be video
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+
+ //it should be possible to switch capture mode in Loaded state
+ camera.setCaptureMode(QCamera::CaptureStillImage);
+ QTRY_COMPARE(captureModeSignal.size(), 1);
+ QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureStillImage);
+ captureModeSignal.clear();
+
+ camera.setCaptureMode(QCamera::CaptureVideo);
+ QTRY_COMPARE(captureModeSignal.size(), 1);
+ QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
+ captureModeSignal.clear();
+
+ camera.start();
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ //capture mode should still be video
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+
+ stateChangedSignal.clear();
+ //it should be possible to switch capture mode in Active state
+ camera.setCaptureMode(QCamera::CaptureStillImage);
+ //camera may leave Active status, but should return to Active
+ QTest::qWait(10); //camera may leave Active status async
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
+ QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes");
+
+ QCOMPARE(captureModeSignal.size(), 1);
+ QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureStillImage);
+ captureModeSignal.clear();
+
+ camera.setCaptureMode(QCamera::CaptureVideo);
+ //camera may leave Active status, but should return to Active
+ QTest::qWait(10); //camera may leave Active status async
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+
+ QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes");
+
+ QCOMPARE(captureModeSignal.size(), 1);
+ QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
+ captureModeSignal.clear();
+
+ camera.stop();
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+ camera.unload();
+ QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
+
+ QVERIFY2(errorSignal.isEmpty(), QString("Camera error: %1").arg(camera.errorString()).toLocal8Bit());
+}
+
+void tst_QCameraBackend::testCameraCapture()
+{
+ QCamera camera;
+ QCameraImageCapture imageCapture(&camera);
+ //prevents camera to flash during the test
+ camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
+
+ QVERIFY(!imageCapture.isReadyForCapture());
+
+ QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
+ QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
+ QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int, QCameraImageCapture::Error,QString)));
+
+ imageCapture.capture();
+ QTRY_COMPARE(errorSignal.size(), 1);
+ QCOMPARE(imageCapture.error(), QCameraImageCapture::NotReadyError);
+ QCOMPARE(capturedSignal.size(), 0);
+ errorSignal.clear();
+
+ camera.start();
+
+ QTRY_VERIFY(imageCapture.isReadyForCapture());
+ QCOMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(errorSignal.size(), 0);
+
+ int id = imageCapture.capture();
+
+ QTRY_VERIFY(!savedSignal.isEmpty());
+
+ QCOMPARE(capturedSignal.size(), 1);
+ QCOMPARE(capturedSignal.last().first().toInt(), id);
+ QCOMPARE(errorSignal.size(), 0);
+ QCOMPARE(imageCapture.error(), QCameraImageCapture::NoError);
+
+ QCOMPARE(savedSignal.last().first().toInt(), id);
+ QString location = savedSignal.last().last().toString();
+ QVERIFY(!location.isEmpty());
+ QVERIFY(QFileInfo(location).exists());
+ QImageReader reader(location);
+ reader.setScaledSize(QSize(320,240));
+ QVERIFY(!reader.read().isNull());
+
+ QFile(location).remove();
+}
+
+
+void tst_QCameraBackend::testCaptureToBuffer()
+{
+ QCamera camera;
+ QCameraImageCapture imageCapture(&camera);
+ camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
+
+ camera.load();
+
+#ifdef Q_WS_MAEMO_6
+ QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer));
+#endif
+
+ if (!imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer))
+ QSKIP("Buffer capture not supported", SkipAll);
+
+ QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
+
+ QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg);
+
+ QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToFile));
+ QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer));
+ QVERIFY(imageCapture.isCaptureDestinationSupported(
+ QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile));
+
+ QSignalSpy destinationChangedSignal(&imageCapture, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)));
+
+ QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToFile);
+ imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
+ QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToBuffer);
+ QCOMPARE(destinationChangedSignal.size(), 1);
+ QCOMPARE(destinationChangedSignal.first().first().value<QCameraImageCapture::CaptureDestinations>(),
+ QCameraImageCapture::CaptureToBuffer);
+
+ QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
+ QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame)));
+ QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
+ QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int, QCameraImageCapture::Error,QString)));
+
+ camera.start();
+ QTRY_VERIFY(imageCapture.isReadyForCapture());
+
+ int id = imageCapture.capture();
+ QTRY_VERIFY(!imageAvailableSignal.isEmpty());
+
+ QVERIFY(errorSignal.isEmpty());
+ QVERIFY(!capturedSignal.isEmpty());
+ QVERIFY(!imageAvailableSignal.isEmpty());
+
+ QTest::qWait(2000);
+ QVERIFY(savedSignal.isEmpty());
+
+ QCOMPARE(capturedSignal.first().first().toInt(), id);
+ QCOMPARE(imageAvailableSignal.first().first().toInt(), id);
+
+ QVideoFrame frame = imageAvailableSignal.first().last().value<QVideoFrame>();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_Jpeg);
+ QVERIFY(!frame.size().isEmpty());
+ QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly));
+ QByteArray data((const char *)frame.bits(), frame.mappedBytes());
+ frame.unmap();
+ frame = QVideoFrame();
+
+ QVERIFY(!data.isEmpty());
+ QBuffer buffer;
+ buffer.setData(data);
+ buffer.open(QIODevice::ReadOnly);
+ QImageReader reader(&buffer, "JPG");
+ reader.setScaledSize(QSize(640,480));
+ QImage img(reader.read());
+ QVERIFY(!img.isNull());
+
+ capturedSignal.clear();
+ imageAvailableSignal.clear();
+ savedSignal.clear();
+
+ //Capture to yuv buffer
+#ifdef Q_WS_MAEMO_6
+ QVERIFY(imageCapture.supportedBufferFormats().contains(QVideoFrame::Format_UYVY));
+#endif
+
+ if (imageCapture.supportedBufferFormats().contains(QVideoFrame::Format_UYVY)) {
+ imageCapture.setBufferFormat(QVideoFrame::Format_UYVY);
+ QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_UYVY);
+
+ id = imageCapture.capture();
+ QTRY_VERIFY(!imageAvailableSignal.isEmpty());
+
+ QVERIFY(errorSignal.isEmpty());
+ QVERIFY(!capturedSignal.isEmpty());
+ QVERIFY(!imageAvailableSignal.isEmpty());
+ QVERIFY(savedSignal.isEmpty());
+
+ QTest::qWait(2000);
+ QVERIFY(savedSignal.isEmpty());
+
+ frame = imageAvailableSignal.first().last().value<QVideoFrame>();
+ QVERIFY(frame.isValid());
+
+ qDebug() << frame.pixelFormat();
+ QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_UYVY);
+ QVERIFY(!frame.size().isEmpty());
+ frame = QVideoFrame();
+
+ capturedSignal.clear();
+ imageAvailableSignal.clear();
+ savedSignal.clear();
+
+ imageCapture.setBufferFormat(QVideoFrame::Format_Jpeg);
+ QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg);
+ }
+
+ //Try to capture to both buffer and file
+#ifdef Q_WS_MAEMO_6
+ QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile));
+#endif
+ if (imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile)) {
+ imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile);
+
+ int oldId = id;
+ id = imageCapture.capture();
+ QVERIFY(id != oldId);
+ QTRY_VERIFY(!savedSignal.isEmpty());
+
+ QVERIFY(errorSignal.isEmpty());
+ QVERIFY(!capturedSignal.isEmpty());
+ QVERIFY(!imageAvailableSignal.isEmpty());
+ QVERIFY(!savedSignal.isEmpty());
+
+ QCOMPARE(capturedSignal.first().first().toInt(), id);
+ QCOMPARE(imageAvailableSignal.first().first().toInt(), id);
+
+ frame = imageAvailableSignal.first().last().value<QVideoFrame>();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_Jpeg);
+ QVERIFY(!frame.size().isEmpty());
+
+ QString fileName = savedSignal.first().last().toString();
+ QVERIFY(QFileInfo(fileName).exists());
+ }
+}
+
+void tst_QCameraBackend::testCameraCaptureMetadata()
+{
+#ifndef Q_WS_MAEMO_6
+ QSKIP("Capture metadata is supported only on harmattan", SkipAll);
+#endif
+
+ QCamera camera;
+ QCameraImageCapture imageCapture(&camera);
+ camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
+
+ QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,QtMultimedia::MetaData,QVariant)));
+ QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
+
+ camera.start();
+
+ QTRY_VERIFY(imageCapture.isReadyForCapture());
+
+ int id = imageCapture.capture(QString::fromLatin1("/dev/null"));
+ QTRY_VERIFY(!savedSignal.isEmpty());
+ QVERIFY(!metadataSignal.isEmpty());
+ QCOMPARE(metadataSignal.first().first().toInt(), id);
+}
+
+void tst_QCameraBackend::testExposureCompensation()
+{
+#if !defined(Q_WS_MAEMO_6)
+ QSKIP("Capture exposure parameters are supported only on mobile platforms", SkipAll);
+#endif
+
+ QCamera camera;
+ QCameraExposure *exposure = camera.exposure();
+
+ QSignalSpy exposureCompensationSignal(exposure, SIGNAL(exposureCompensationChanged(qreal)));
+
+ //it should be possible to set exposure parameters in Unloaded state
+ QCOMPARE(exposure->exposureCompensation()+1.0, 1.0);
+ exposure->setExposureCompensation(1.0);
+ QCOMPARE(exposure->exposureCompensation(), 1.0);
+ QTRY_COMPARE(exposureCompensationSignal.count(), 1);
+ QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0);
+
+ //exposureCompensationChanged should not be emitted when value is not changed
+ exposure->setExposureCompensation(1.0);
+ QTest::qWait(50);
+ QCOMPARE(exposureCompensationSignal.count(), 1);
+
+ //exposure compensation should be preserved during load/start
+ camera.load();
+ QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
+
+ QCOMPARE(exposure->exposureCompensation(), 1.0);
+
+ exposureCompensationSignal.clear();
+ exposure->setExposureCompensation(-1.0);
+ QCOMPARE(exposure->exposureCompensation(), -1.0);
+ QTRY_COMPARE(exposureCompensationSignal.count(), 1);
+ QCOMPARE(exposureCompensationSignal.last().first().toReal(), -1.0);
+
+ camera.start();
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+
+ QCOMPARE(exposure->exposureCompensation(), -1.0);
+
+ exposureCompensationSignal.clear();
+ exposure->setExposureCompensation(1.0);
+ QCOMPARE(exposure->exposureCompensation(), 1.0);
+ QTRY_COMPARE(exposureCompensationSignal.count(), 1);
+ QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0);
+}
+
+void tst_QCameraBackend::testExposureMode()
+{
+#if !defined(Q_WS_MAEMO_6)
+ QSKIP("Capture exposure parameters are supported only on mobile platforms", SkipAll);
+#endif
+
+ QCamera camera;
+ QCameraExposure *exposure = camera.exposure();
+
+#ifdef Q_WS_MAEMO_6
+ QEXPECT_FAIL("", "Camerabin reports Manual exposure instead of Auto", Continue);
+#endif
+ QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
+
+ // Night
+ exposure->setExposureMode(QCameraExposure::ExposureNight);
+ QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight);
+ camera.start();
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight);
+
+ camera.unload();
+ QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
+
+#ifdef Q_WS_MAEMO_6
+ //resource policy doesn't work correctly when resource is released and immediately requested again.
+ QTest::qWait(250);
+#endif
+
+ // Auto
+ exposure->setExposureMode(QCameraExposure::ExposureAuto);
+ QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
+ camera.start();
+ QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
+ QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
+}
+
+QTEST_MAIN(tst_QCameraBackend)
+
+#include "tst_qcamerabackend.moc"
diff --git a/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro b/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro
new file mode 100644
index 000000000..fdd941d69
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/qmediaplayerbackend.pro
@@ -0,0 +1,12 @@
+TARGET = tst_qmediaplayerbackend
+
+QT += multimedia-private testlib
+CONFIG += no_private_qt_headers_warning
+
+# This is more of a system test
+# CONFIG += testcase
+
+DEFINES += TESTDATA_DIR=\\\"$$PWD/\\\"
+
+SOURCES += \
+ tst_qmediaplayerbackend.cpp
diff --git a/tests/auto/integration/qmediaplayerbackend/testdata/test.wav b/tests/auto/integration/qmediaplayerbackend/testdata/test.wav
new file mode 100644
index 000000000..4dd022661
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/testdata/test.wav
Binary files differ
diff --git a/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
new file mode 100644
index 000000000..29e70c24d
--- /dev/null
+++ b/tests/auto/integration/qmediaplayerbackend/tst_qmediaplayerbackend.cpp
@@ -0,0 +1,462 @@
+/****************************************************************************
+**
+** 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 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 <QDebug>
+#include "qmediaservice.h"
+#include "qmediaplayer.h"
+
+//TESTED_COMPONENT=src/multimedia
+
+#ifndef TESTDATA_DIR
+#define TESTDATA_DIR "./"
+#endif
+
+QT_USE_NAMESPACE
+
+// Eventually these will make it into qtestcase.h
+// but we might need to tweak the timeout values here.
+#ifndef QTRY_COMPARE
+#define QTRY_COMPARE(__expr, __expected) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 5000; \
+ if ((__expr) != (__expected)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
+ QTest::qWait(__step); \
+ } \
+ QCOMPARE(__expr, __expected); \
+ } while(0)
+#endif
+
+#ifndef QTRY_VERIFY
+#define QTRY_VERIFY(__expr) \
+ 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); \
+ } \
+ QVERIFY(__expr); \
+ } while(0)
+#endif
+
+
+#define QTRY_WAIT(code, __expr) \
+ do { \
+ const int __step = 50; \
+ const int __timeout = 5000; \
+ if (!(__expr)) { \
+ QTest::qWait(0); \
+ } \
+ for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
+ do { code } while(0); \
+ QTest::qWait(__step); \
+ } \
+ } while(0)
+
+
+/*
+ This is the backend conformance test.
+
+ Since it relies on platform media framework and sound hardware
+ it may be less stable.
+*/
+
+class tst_QMediaPlayerBackend : public QObject
+{
+ Q_OBJECT
+public slots:
+ void init();
+ void cleanup();
+ void initTestCase();
+
+private slots:
+ void construction();
+ void loadMedia();
+ void unloadMedia();
+ void playPauseStop();
+ void processEOS();
+ void volumeAndMuted();
+ void volumeAcrossFiles_data();
+ void volumeAcrossFiles();
+
+private:
+ //one second local wav file
+ QMediaContent localWavFile;
+};
+
+void tst_QMediaPlayerBackend::init()
+{
+}
+
+void tst_QMediaPlayerBackend::initTestCase()
+{
+ QFileInfo wavFile(QLatin1String(TESTDATA_DIR "testdata/test.wav"));
+ QVERIFY(wavFile.exists());
+
+ localWavFile = QMediaContent(QUrl::fromLocalFile(wavFile.absoluteFilePath()));
+
+ qRegisterMetaType<QMediaContent>();
+}
+
+void tst_QMediaPlayerBackend::cleanup()
+{
+}
+
+void tst_QMediaPlayerBackend::construction()
+{
+ QMediaPlayer player;
+ QVERIFY(player.isAvailable());
+}
+
+void tst_QMediaPlayerBackend::loadMedia()
+{
+ QMediaPlayer player;
+ QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
+
+ QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
+ QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
+ QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
+
+ player.setMedia(localWavFile);
+
+ QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+
+ QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia);
+ QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia);
+ QVERIFY(player.media() == localWavFile);
+
+ QCOMPARE(stateSpy.count(), 0);
+ QVERIFY(statusSpy.count() > 0);
+ QCOMPARE(mediaSpy.count(), 1);
+ QCOMPARE(mediaSpy.last()[0].value<QMediaContent>(), localWavFile);
+
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QVERIFY(player.isAudioAvailable());
+ QVERIFY(!player.isVideoAvailable());
+}
+
+void tst_QMediaPlayerBackend::unloadMedia()
+{
+ QMediaPlayer player;
+ player.setNotifyInterval(50);
+
+ QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
+ QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
+ QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
+ QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+ QSignalSpy durationSpy(&player, SIGNAL(positionChanged(qint64)));
+
+ player.setMedia(localWavFile);
+
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QVERIFY(player.position() == 0);
+ QVERIFY(player.duration() > 0);
+
+ player.play();
+
+ QTest::qWait(250);
+ QVERIFY(player.position() > 0);
+ QVERIFY(player.duration() > 0);
+
+ stateSpy.clear();
+ statusSpy.clear();
+ mediaSpy.clear();
+ positionSpy.clear();
+ durationSpy.clear();
+
+ player.setMedia(QMediaContent());
+
+ QVERIFY(player.position() <= 0);
+ QVERIFY(player.duration() <= 0);
+ QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
+ QCOMPARE(player.media(), QMediaContent());
+
+ QVERIFY(!stateSpy.isEmpty());
+ QVERIFY(!statusSpy.isEmpty());
+ QVERIFY(!mediaSpy.isEmpty());
+ QVERIFY(!positionSpy.isEmpty());
+}
+
+
+void tst_QMediaPlayerBackend::playPauseStop()
+{
+ QMediaPlayer player;
+ player.setNotifyInterval(50);
+
+ QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
+ QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
+ QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+
+ player.setMedia(localWavFile);
+
+ QCOMPARE(player.position(), qint64(0));
+
+ player.play();
+
+ QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ QCOMPARE(stateSpy.count(), 1);
+ QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState);
+ QTRY_VERIFY(statusSpy.count() > 0 &&
+ statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia);
+
+ QTest::qWait(500);
+ QVERIFY(player.position() > 0);
+ QVERIFY(player.duration() > 0);
+ QVERIFY(positionSpy.count() > 0);
+ QVERIFY(positionSpy.last()[0].value<qint64>() > 0);
+
+ stateSpy.clear();
+ statusSpy.clear();
+
+ player.pause();
+
+ QCOMPARE(player.state(), QMediaPlayer::PausedState);
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ QCOMPARE(stateSpy.count(), 1);
+ QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PausedState);
+
+ stateSpy.clear();
+ statusSpy.clear();
+
+ player.stop();
+
+ QCOMPARE(player.state(), QMediaPlayer::StoppedState);
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QCOMPARE(stateSpy.count(), 1);
+ QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
+ //it's allowed to emit statusChanged() signal async
+ QTRY_COMPARE(statusSpy.count(), 1);
+ QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
+
+ //ensure the position is reset to 0 at stop and positionChanged(0) is emitted
+ QCOMPARE(player.position(), qint64(0));
+ QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0));
+ QVERIFY(player.duration() > 0);
+}
+
+
+void tst_QMediaPlayerBackend::processEOS()
+{
+ QMediaPlayer player;
+ player.setNotifyInterval(50);
+
+ QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
+ QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
+ QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
+
+ player.setMedia(localWavFile);
+
+ player.play();
+ player.setPosition(900);
+
+ //wait up to 5 seconds for EOS
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+
+ QVERIFY(statusSpy.count() > 0);
+ QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
+
+ //at EOS the position stays at the end of file
+ QVERIFY(player.position() > 900);
+
+ stateSpy.clear();
+ statusSpy.clear();
+
+ player.play();
+
+ //position is reset to start
+ QTRY_VERIFY(player.position() < 100);
+
+ QCOMPARE(player.state(), QMediaPlayer::PlayingState);
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
+
+ QCOMPARE(stateSpy.count(), 1);
+ QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState);
+ QVERIFY(statusSpy.count() > 0);
+ QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
+
+ player.setPosition(900);
+ //wait up to 5 seconds for EOS
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+
+ //ensure the positionChanged() signal is emitted
+ QVERIFY(positionSpy.count() > 0);
+
+ QCOMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
+ //position stays at the end of file
+ QVERIFY(player.position() > 900);
+
+ //after setPosition EndOfMedia status should be reset to Loaded
+ stateSpy.clear();
+ statusSpy.clear();
+ player.setPosition(500);
+
+ //this transition can be async, so allow backend to perform it
+ QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
+
+ QCOMPARE(stateSpy.count(), 0);
+ QTRY_VERIFY(statusSpy.count() > 0 &&
+ statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::LoadedMedia);
+}
+
+void tst_QMediaPlayerBackend::volumeAndMuted()
+{
+ //volume and muted properties should be independent
+ QMediaPlayer player;
+ QVERIFY(player.volume() > 0);
+ QVERIFY(!player.isMuted());
+
+ player.setMedia(localWavFile);
+ player.pause();
+
+ QVERIFY(player.volume() > 0);
+ QVERIFY(!player.isMuted());
+
+ QSignalSpy volumeSpy(&player, SIGNAL(volumeChanged(int)));
+ QSignalSpy mutedSpy(&player, SIGNAL(mutedChanged(bool)));
+
+ //setting volume to 0 should not trigger muted
+ player.setVolume(0);
+ QTRY_COMPARE(player.volume(), 0);
+ QVERIFY(!player.isMuted());
+ QCOMPARE(volumeSpy.count(), 1);
+ QCOMPARE(volumeSpy.last()[0].toInt(), player.volume());
+ QCOMPARE(mutedSpy.count(), 0);
+
+ player.setVolume(50);
+ QTRY_COMPARE(player.volume(), 50);
+ QVERIFY(!player.isMuted());
+ QCOMPARE(volumeSpy.count(), 2);
+ QCOMPARE(volumeSpy.last()[0].toInt(), player.volume());
+ QCOMPARE(mutedSpy.count(), 0);
+
+ player.setMuted(true);
+ QTRY_VERIFY(player.isMuted());
+ QVERIFY(player.volume() > 0);
+ QCOMPARE(volumeSpy.count(), 2);
+ QCOMPARE(mutedSpy.count(), 1);
+ QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted());
+
+ player.setMuted(false);
+ QTRY_VERIFY(!player.isMuted());
+ QVERIFY(player.volume() > 0);
+ QCOMPARE(volumeSpy.count(), 2);
+ QCOMPARE(mutedSpy.count(), 2);
+ QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted());
+
+}
+
+void tst_QMediaPlayerBackend::volumeAcrossFiles_data()
+{
+ QTest::addColumn<int>("volume");
+ QTest::addColumn<bool>("muted");
+
+ QTest::newRow("100 unmuted") << 100 << false;
+ QTest::newRow("50 unmuted") << 50 << false;
+ QTest::newRow("0 unmuted") << 0 << false;
+ QTest::newRow("100 muted") << 100 << true;
+ QTest::newRow("50 muted") << 50 << true;
+ QTest::newRow("0 muted") << 0 << true;
+}
+
+void tst_QMediaPlayerBackend::volumeAcrossFiles()
+{
+ QFETCH(int, volume);
+ QFETCH(bool, muted);
+
+ QMediaPlayer player;
+
+ //volume and muted should not be preserved between player instances
+ QVERIFY(player.volume() > 0);
+ QVERIFY(!player.isMuted());
+
+ player.setVolume(volume);
+ player.setMuted(muted);
+
+ QTRY_COMPARE(player.volume(), volume);
+ QTRY_COMPARE(player.isMuted(), muted);
+
+ player.setMedia(localWavFile);
+ QCOMPARE(player.volume(), volume);
+ QCOMPARE(player.isMuted(), muted);
+
+ player.pause();
+
+ //to ensure the backend doesn't change volume/muted
+ //async during file loading.
+ QTest::qWait(50);
+
+ QCOMPARE(player.volume(), volume);
+ QCOMPARE(player.isMuted(), muted);
+
+ player.setMedia(QMediaContent());
+ QTest::qWait(50);
+ QCOMPARE(player.volume(), volume);
+ QCOMPARE(player.isMuted(), muted);
+
+ player.setMedia(localWavFile);
+ player.pause();
+
+ QTest::qWait(50);
+
+ QCOMPARE(player.volume(), volume);
+ QCOMPARE(player.isMuted(), muted);
+}
+
+
+QTEST_MAIN(tst_QMediaPlayerBackend)
+#include "tst_qmediaplayerbackend.moc"
+
diff --git a/tests/auto/integration/qsoundeffect/qsoundeffect.pro b/tests/auto/integration/qsoundeffect/qsoundeffect.pro
new file mode 100644
index 000000000..4d7f083b1
--- /dev/null
+++ b/tests/auto/integration/qsoundeffect/qsoundeffect.pro
@@ -0,0 +1,17 @@
+TARGET = tst_qsoundeffect
+
+QT += core declarative multimedia-private testlib
+CONFIG += no_private_qt_headers_warning
+
+# This is more of a system test
+# CONFIG += testcase
+
+SOURCES += tst_qsoundeffect.cpp
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+
+unix:!mac {
+ !contains(QT_CONFIG, pulseaudio) {
+ DEFINES += QT_MULTIMEDIA_QMEDIAPLAYER
+ }
+}
diff --git a/tests/auto/integration/qsoundeffect/test.wav b/tests/auto/integration/qsoundeffect/test.wav
new file mode 100644
index 000000000..e4088a973
--- /dev/null
+++ b/tests/auto/integration/qsoundeffect/test.wav
Binary files differ
diff --git a/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
new file mode 100644
index 000000000..b3797625d
--- /dev/null
+++ b/tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+//TESTED_COMPONENT=src/multimedia
+
+#include <QtTest/QtTest>
+#include <QtCore/qlocale.h>
+#include <qaudiooutput.h>
+#include <qaudiodeviceinfo.h>
+#include <qaudio.h>
+#include "qsoundeffect.h"
+
+
+class tst_QSoundEffect : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QSoundEffect(QObject* parent=0) : QObject(parent) {}
+
+private slots:
+ void initTestCase();
+ void testSource();
+ void testLooping();
+ void testVolume();
+ void testMuting();
+
+ void testPlaying();
+ void testStatus();
+
+private:
+ QSoundEffect* sound;
+ QUrl url;
+};
+
+void tst_QSoundEffect::initTestCase()
+{
+#ifdef QT_QSOUNDEFFECT_USEAPPLICATIONPATH
+ url = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + QString("/test.wav"));
+#else
+ url = QUrl::fromLocalFile(QString(SRCDIR "test.wav"));
+#endif
+
+ sound = new QSoundEffect(this);
+
+ QVERIFY(sound->source().isEmpty());
+ QVERIFY(sound->loopCount() == 1);
+ QVERIFY(sound->volume() == 1);
+ QVERIFY(sound->isMuted() == false);
+}
+
+void tst_QSoundEffect::testSource()
+{
+ QSignalSpy readSignal(sound, SIGNAL(sourceChanged()));
+
+ sound->setSource(url);
+
+ QCOMPARE(sound->source(),url);
+ QCOMPARE(readSignal.count(),1);
+
+ QTestEventLoop::instance().enterLoop(1);
+ sound->play();
+
+ QTest::qWait(3000);
+}
+
+void tst_QSoundEffect::testLooping()
+{
+ QSignalSpy readSignal(sound, SIGNAL(loopCountChanged()));
+
+ sound->setLoopCount(5);
+ QCOMPARE(sound->loopCount(),5);
+
+ sound->play();
+
+ // test.wav is about 200ms, wait until it has finished playing 5 times
+ QTest::qWait(3000);
+
+}
+
+void tst_QSoundEffect::testVolume()
+{
+ QSignalSpy readSignal(sound, SIGNAL(volumeChanged()));
+
+ sound->setVolume(0.5);
+ QCOMPARE(sound->volume(),0.5);
+
+ QTest::qWait(20);
+ QCOMPARE(readSignal.count(),1);
+}
+
+void tst_QSoundEffect::testMuting()
+{
+ QSignalSpy readSignal(sound, SIGNAL(mutedChanged()));
+
+ sound->setMuted(true);
+ QCOMPARE(sound->isMuted(),true);
+
+ QTest::qWait(20);
+ QCOMPARE(readSignal.count(),1);
+}
+
+void tst_QSoundEffect::testPlaying()
+{
+ sound->setLoopCount(QSoundEffect::Infinite);
+ //valid source
+ sound->setSource(url);
+ QTestEventLoop::instance().enterLoop(1);
+ sound->play();
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->isPlaying(), true);
+ sound->stop();
+
+ //empty source
+ sound->setSource(QUrl());
+ QTestEventLoop::instance().enterLoop(1);
+ sound->play();
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->isPlaying(), false);
+
+ //invalid source
+ sound->setSource(QUrl((QLatin1String("invalid source"))));
+ QTestEventLoop::instance().enterLoop(1);
+ sound->play();
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->isPlaying(), false);
+
+ sound->setLoopCount(1);
+}
+
+void tst_QSoundEffect::testStatus()
+{
+ sound->setSource(QUrl());
+ QCOMPARE(sound->status(), QSoundEffect::Null);
+
+ //valid source
+ sound->setSource(url);
+
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->status(), QSoundEffect::Ready);
+
+ //empty source
+ sound->setSource(QUrl());
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->status(), QSoundEffect::Null);
+
+ //invalid source
+ sound->setLoopCount(QSoundEffect::Infinite);
+
+ sound->setSource(QUrl(QLatin1String("invalid source")));
+ QTestEventLoop::instance().enterLoop(1);
+ QCOMPARE(sound->status(), QSoundEffect::Error);
+}
+
+
+QTEST_MAIN(tst_QSoundEffect)
+
+#include "tst_qsoundeffect.moc"