diff options
Diffstat (limited to 'tests/auto/mediaobject/tst_mediaobject.cpp')
-rw-r--r-- | tests/auto/mediaobject/tst_mediaobject.cpp | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/tests/auto/mediaobject/tst_mediaobject.cpp b/tests/auto/mediaobject/tst_mediaobject.cpp new file mode 100644 index 0000000..96589b7 --- /dev/null +++ b/tests/auto/mediaobject/tst_mediaobject.cpp @@ -0,0 +1,932 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/* This file is part of the KDE project + Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include <QtTest/QtTest> +#include <QtCore/QDate> +#include <QtCore/QDebug> +#include <QtCore/QObject> +#include <QtCore/QUrl> + +#ifndef QT_NO_PHONON +#include <phonon/path.h> +#include <phonon/audiooutput.h> +#include <phonon/mediaobject.h> +#include <phonon/phononnamespace.h> +#include <phonon/audiooutput.h> +#include <phonon/seekslider.h> +#include <phonon/mediaobject.h> +#include <phonon/volumeslider.h> +#include <phonon/videowidget.h> +#include <phonon/backendcapabilities.h> + +#include "qtesthelper.h" +#include <cstdlib> +#endif + +#ifndef Q_WS_WIN +#include <unistd.h> +#endif + +#ifdef Q_OS_WINCE +#define MEDIA_FILE "/sax.wav" +#define MEDIA_FILEPATH ":/media/sax.wav" +const qint64 SEEK_BACKWARDS = 2000; +const qint64 ALLOWED_TIME_FOR_SEEKING = 1500; // 1.5s +const qint64 SEEKING_TOLERANCE = 250; +#else +#ifdef Q_OS_WIN +#define MEDIA_FILE "/sax.mp3" +#define MEDIA_FILEPATH ":/media/sax.mp3" +#else +#define MEDIA_FILE "/sax.ogg" +#define MEDIA_FILEPATH ":/media/sax.ogg" +#endif +const qint64 SEEK_BACKWARDS = 4000; +const qint64 ALLOWED_TIME_FOR_SEEKING = 1000; // 1s +const qint64 SEEKING_TOLERANCE = 0; +#endif //Q_OS_WINCE + +class tst_MediaObject : public QObject +{ + Q_OBJECT + public: + tst_MediaObject() + : m_success(false) + { + qputenv("PHONON_GST_AUDIOSINK", "fake"); + } + +#ifndef QT_NO_PHONON + + Q_SIGNALS: + void continueTestPlayOnFinish(); + + protected Q_SLOTS: + void enqueueMedia(); + void setMediaAndPlay(); + void stateChanged(Phonon::State, Phonon::State); + private Q_SLOTS: + void init(); + void cleanup(); + + void testPlayFromResource(); + void testPlayIllegalFile(); + void initTestCase(); + void checkForDefaults(); + + // state change tests + void stopToStop(); + void stopToPause(); + void stopToPlay(); + void playToPlay(); + void playToPause(); + void playToStop(); + void pauseToPause(); + void pauseToPlay(); + void pauseToStop(); + + void testPrefinishMark(); + void testSeek(); + void testTickSignal(); + void testJustInTimeQueuing(); + void testPlayOnFinish(); + void testPlayBeforeFinish(); + void testPauseOnFinish(); + void testReconnectBetweenTwoMediaObjects(); + void cleanupTestCase(); + private: + void _startPlayback(Phonon::State currentState = Phonon::StoppedState); + void _stopPlayback(Phonon::State currentState); + void _pausePlayback(); + void _testOneSeek(qint64 seekTo); + + QUrl m_url; + Phonon::MediaObject *m_media; + QSignalSpy *m_stateChangedSignalSpy; + QString m_tmpFileName; +#endif //QT_NO_PHONON + bool m_success; +}; + +#ifndef QT_NO_PHONON + +#define startPlayback() _startPlayback(); if (!m_success) return; m_success = false; +#define startPlayback2(currentState) _startPlayback(currentState); if (!m_success) return; m_success = false; +#define stopPlayback(currentState) _stopPlayback(currentState); if (!m_success) return; m_success = false; +#define pausePlayback() _pausePlayback(); if (!m_success) return; m_success = false; +#define testOneSeek(seekTo) _testOneSeek(seekTo); if (!m_success) return; m_success = false; + +const qint64 ALLOWED_SEEK_INACCURACY = 300; // 0.3s +const qint64 ALLOWED_TICK_INACCURACY = 350; // allow +/- 350 ms inaccuracy + +using namespace Phonon; + +static qint64 castQVariantToInt64(const QVariant &variant) +{ + return *reinterpret_cast<const qint64 *>(variant.constData()); +} + +static qint32 castQVariantToInt32(const QVariant &variant) +{ + return *reinterpret_cast<const qint32 *>(variant.constData()); +} + +static const char *const red = "\033[0;31m"; +static const char *const green = "\033[0;32m"; +static const char *const yellow = "\033[0;33m"; +static const char *const blue = "\033[0;34m"; +static const char *const purple = "\033[0;35m"; +static const char *const cyan = "\033[0;36m"; +static const char *const white = "\033[0;37m"; +static const char *const normal = "\033[0m"; + +void tst_MediaObject::stateChanged(Phonon::State newstate, Phonon::State oldstate) +{ + if (newstate == Phonon::ErrorState) { + QWARN(QByteArray(QByteArray(red) + ".......................................................... ") + QByteArray(QTest::toString(oldstate)) + " to " + QByteArray(QTest::toString(newstate)) + normal); + } else { + //qDebug() << ".........................................................." << cyan << QTest::toString(oldstate) << "to" << QTest::toString(newstate) << normal; + } +} + +void tst_MediaObject::testPlayFromResource() +{ + QFile file(MEDIA_FILEPATH); + MediaObject media; + media.setCurrentSource(&file); + QVERIFY(media.state() != Phonon::ErrorState); + if (media.state() != Phonon::StoppedState) + QTest::waitForSignal(&media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 10000); + QCOMPARE(media.state(), Phonon::StoppedState); + media.play(); + if (media.state() != Phonon::PlayingState) + QTest::waitForSignal(&media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 10000); + QCOMPARE(media.state(), Phonon::PlayingState); +} + +void tst_MediaObject::testPlayIllegalFile() +{ + QString filename = QDir::tempPath() + QString("/test.wav"); + QFile::remove(filename); + QFile file(filename); + file.open(QIODevice::WriteOnly); + for (int i=0;i<0xffff;i++) { + int r = qrand(); + file.write((const char*)&r, 2); + } + file.close(); + MediaObject media; + media.setCurrentSource(filename); + QTest::waitForSignal(&media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 10000); + QCOMPARE(media.state(), Phonon::ErrorState); + media.play(); + QCOMPARE(media.state(), Phonon::ErrorState); + QFile::remove(filename); +} + +void tst_MediaObject::init() +{ + QCOMPARE(m_media->outputPaths().size(), 1); + if (m_media->state() == Phonon::ErrorState) { + m_media->setCurrentSource(m_url); + if (m_media->state() == Phonon::ErrorState) { + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State))); + } + if (m_media->state() == Phonon::LoadingState) { + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State))); + } + m_stateChangedSignalSpy->clear(); + } +} + +void tst_MediaObject::cleanup() +{ + switch (m_media->state()) { + case Phonon::PlayingState: + case Phonon::BufferingState: + case Phonon::PausedState: + stopPlayback(m_media->state()); + break; + default: + break; + } + m_stateChangedSignalSpy->clear(); +} + +void tst_MediaObject::_startPlayback(Phonon::State currentState) +{ + m_stateChangedSignalSpy->clear(); + Phonon::State s = m_media->state(); + QCOMPARE(s, currentState); + m_media->play(); + while (s != Phonon::PlayingState) { + if (m_stateChangedSignalSpy->isEmpty()) { + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 3000); + QApplication::processEvents(); + } + while (!m_stateChangedSignalSpy->isEmpty()) { + QList<QVariant> args = m_stateChangedSignalSpy->takeFirst(); + Phonon::State laststate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(laststate, s); + s = qvariant_cast<Phonon::State>(args.at(0)); + QVERIFY(s == Phonon::BufferingState || s == Phonon::PlayingState); + } + } + QCOMPARE(s, Phonon::PlayingState); + QCOMPARE(m_media->state(), Phonon::PlayingState); + m_success = true; +} + +void tst_MediaObject::_stopPlayback(Phonon::State currentState) +{ + QVERIFY(currentState != Phonon::ErrorState); + m_stateChangedSignalSpy->clear(); + Phonon::State s = m_media->state(); + QCOMPARE(s, currentState); + m_media->stop(); + while (s != Phonon::StoppedState) { + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 4000)); + } + while (!m_stateChangedSignalSpy->isEmpty()) { + QList<QVariant> args = m_stateChangedSignalSpy->takeFirst(); + Phonon::State laststate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(laststate, s); + s = qvariant_cast<Phonon::State>(args.at(0)); + if (s == Phonon::StoppedState) { + QVERIFY(m_stateChangedSignalSpy->isEmpty()); + break; + } + QVERIFY(s == Phonon::BufferingState || s == Phonon::PlayingState); + } + } + QCOMPARE(s, Phonon::StoppedState); + QCOMPARE(m_media->state(), Phonon::StoppedState); + m_success = true; +} + +void tst_MediaObject::_pausePlayback() +{ + m_stateChangedSignalSpy->clear(); + Phonon::State s = m_media->state(); + m_media->pause(); + while (s != Phonon::PausedState) { + if (m_stateChangedSignalSpy->isEmpty()) { + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State))); + } + while (!m_stateChangedSignalSpy->isEmpty()) { + QList<QVariant> args = m_stateChangedSignalSpy->takeFirst(); + Phonon::State laststate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(laststate, s); + s = qvariant_cast<Phonon::State>(args.at(0)); + if (s == Phonon::PausedState) { + QVERIFY(m_stateChangedSignalSpy->isEmpty()); + break; + } + QVERIFY(s == Phonon::BufferingState || s == Phonon::PlayingState); + } + } + QCOMPARE(s, Phonon::PausedState); + QCOMPARE(m_media->state(), Phonon::PausedState); + m_success = true; +} + +void tst_MediaObject::initTestCase() +{ + QCoreApplication::setApplicationName("tst_MediaObject"); + + m_url = qgetenv("PHONON_TESTURL"); + m_media = new MediaObject(this); + connect(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(stateChanged(Phonon::State, Phonon::State))); + m_stateChangedSignalSpy = new QSignalSpy(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State))); + QVERIFY(m_stateChangedSignalSpy->isValid()); + m_stateChangedSignalSpy->clear(); + + if (m_url.isEmpty()) { + m_tmpFileName = QDir::toNativeSeparators(QDir::tempPath() + MEDIA_FILE); + QFile::remove(m_tmpFileName); + QVERIFY(QFile::copy(MEDIA_FILEPATH, m_tmpFileName)); + QFile::Permissions p = QFile::permissions(m_tmpFileName); + QFile::setPermissions(m_tmpFileName, p | QFile::WriteOther); + m_url = QUrl::fromLocalFile(m_tmpFileName); + } + + qDebug() << "Using url:" << m_url.toString(); + + // AudioOutput is needed else the backend might have no time source + AudioOutput *audioOutput = new AudioOutput(Phonon::MusicCategory, this); + //audioOutput->setVolume(0.0f); + + QSignalSpy totalTimeChangedSignalSpy(m_media, SIGNAL(totalTimeChanged(qint64))); + QVERIFY(m_media->queue().isEmpty()); + QCOMPARE(m_media->currentSource().type(), MediaSource::Empty); + QCOMPARE(m_media->state(), Phonon::LoadingState); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + + m_media->setCurrentSource(m_url); + QCOMPARE(m_media->currentSource().type(), MediaSource::Url); + QCOMPARE(m_media->currentSource().url(), m_url); + + int emits = m_stateChangedSignalSpy->count(); + Phonon::State s = m_media->state(); + if (s == Phonon::LoadingState) { + // still in LoadingState, there should be no state change + QCOMPARE(emits, 0); + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 6000); + emits = m_stateChangedSignalSpy->count(); + s = m_media->state(); + } + if (s != Phonon::LoadingState) { + // there should exactly be one state change + QCOMPARE(emits, 1); + QList<QVariant> args = m_stateChangedSignalSpy->takeFirst(); + Phonon::State newstate = qvariant_cast<Phonon::State>(args.at(0)); + Phonon::State oldstate = qvariant_cast<Phonon::State>(args.at(1)); + + QCOMPARE(Phonon::LoadingState, oldstate); + QCOMPARE(s, newstate); + if (Phonon::ErrorState == s) { +#ifdef Q_WS_WIN + if (m_media->errorString().contains(QLatin1String("no audio hardware is available"))) + QSKIP("On Windows we need an audio devide to perform the MediaObject tests", SkipAll); + else +#endif + QFAIL("Loading the URL put the MediaObject into the ErrorState. Check that PHONON_TESTURL is set to a valid URL."); + } + QCOMPARE(Phonon::StoppedState, s); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + + // check for totalTimeChanged signal + QVERIFY(totalTimeChangedSignalSpy.count() > 0); + args = totalTimeChangedSignalSpy.takeLast(); + QCOMPARE(m_media->totalTime(), castQVariantToInt64(args.at(0))); + } else { + QFAIL("Still in LoadingState after a stateChange signal was emitted. Cannot go on."); + } + + Path path = createPath(m_media, audioOutput); + QVERIFY(path.isValid()); + + + QCOMPARE(m_media->outputPaths().size(), 1); + QCOMPARE(audioOutput->inputPaths().size(), 1); + +} + +void tst_MediaObject::checkForDefaults() +{ + QCOMPARE(m_media->tickInterval(), qint32(0)); + QCOMPARE(m_media->prefinishMark(), qint32(0)); +} + +void tst_MediaObject::stopToStop() +{ + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + QCOMPARE(m_media->state(), Phonon::StoppedState); + m_media->stop(); + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 2000); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + QCOMPARE(m_media->state(), Phonon::StoppedState); +} + +void tst_MediaObject::stopToPause() +{ + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + QCOMPARE(m_media->state(), Phonon::StoppedState); + m_media->pause(); + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 6000)); + } + QCOMPARE(m_stateChangedSignalSpy->count(), 1); + QCOMPARE(m_media->state(), Phonon::PausedState); +} + +void tst_MediaObject::stopToPlay() +{ + startPlayback(); + QTest::waitForSignal(m_media, SIGNAL(finished()), 1000); + stopPlayback(Phonon::PlayingState); +} + +void tst_MediaObject::playToPlay() +{ + startPlayback(); + + m_media->play(); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + QCOMPARE(m_media->state(), Phonon::PlayingState); + + stopPlayback(Phonon::PlayingState); +} + +void tst_MediaObject::playToPause() +{ + startPlayback(); + QCOMPARE(m_media->state(), Phonon::PlayingState); + pausePlayback(); + stopPlayback(Phonon::PausedState); +} + +void tst_MediaObject::playToStop() +{ + startPlayback(); + stopPlayback(Phonon::PlayingState); +} + +void tst_MediaObject::pauseToPause() +{ + startPlayback(); + pausePlayback(); + + m_media->pause(); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + QCOMPARE(m_media->state(), Phonon::PausedState); + + stopPlayback(Phonon::PausedState); +} + +void tst_MediaObject::pauseToPlay() +{ + startPlayback(); + pausePlayback(); + startPlayback2(Phonon::PausedState); + stopPlayback(Phonon::PlayingState); +} + +void tst_MediaObject::pauseToStop() +{ + startPlayback(); + pausePlayback(); + stopPlayback(Phonon::PausedState); +} + +void tst_MediaObject::testPrefinishMark() +{ + const qint32 requestedPrefinishMarkTime = 2000; + m_media->setPrefinishMark(requestedPrefinishMarkTime); + QCOMPARE(m_media->prefinishMark(), requestedPrefinishMarkTime); + QSignalSpy prefinishMarkReachedSpy(m_media, SIGNAL(prefinishMarkReached(qint32))); + QSignalSpy finishSpy(m_media, SIGNAL(finished())); + startPlayback(); + if (m_media->isSeekable()) { + m_media->seek(m_media->totalTime() - SEEK_BACKWARDS - requestedPrefinishMarkTime); // give it 4 seconds + } + int wait = 10000; + int total = 0; + while (prefinishMarkReachedSpy.count() == 0 && (m_media->state() == Phonon::PlayingState || + m_media->state() == Phonon::BufferingState)) { + wait = qMax(1000, wait / 2); + QTest::waitForSignal(m_media, SIGNAL(prefinishMarkReached(qint32)), wait); + total += wait; + if (total >= 60*1000) // we wait 1 minute + QFAIL("Timeout failure waiting for signal"); + } + // at this point the media should be about to finish playing + qint64 r = m_media->remainingTime(); + Phonon::State state = m_media->state(); + QCOMPARE(prefinishMarkReachedSpy.count(), 1); + const qint32 prefinishMark = castQVariantToInt32(prefinishMarkReachedSpy.first().at(0)); + QVERIFY(prefinishMark <= requestedPrefinishMarkTime + 150); // allow it to be up to 150ms too early + if (state == Phonon::PlayingState || state == Phonon::BufferingState) { + if (r > prefinishMark) { + qDebug() << "remainingTime =" << r; + QFAIL("remainingTime needs to be less than or equal to prefinishMark"); + } + QVERIFY(r <= prefinishMark); + QTest::waitForSignal(m_media, SIGNAL(finished()), 10000); + } else { + QVERIFY(prefinishMark >= 0); + } + QCOMPARE(finishSpy.count(), 1); +} + +void tst_MediaObject::enqueueMedia() +{ + m_media->enqueue(m_url); +} + +Q_DECLARE_METATYPE(Phonon::MediaSource) +void tst_MediaObject::testJustInTimeQueuing() +{ +#ifdef Q_OS_WINCE + QSKIP("crashes on Windows CE", SkipAll); +#endif + qRegisterMetaType<Phonon::MediaSource>("Phonon::MediaSource"); + QSignalSpy currentSourceChanged(m_media, SIGNAL(currentSourceChanged(const Phonon::MediaSource &))); + QSignalSpy finished(m_media, SIGNAL(finished())); + connect(m_media, SIGNAL(aboutToFinish()), SLOT(enqueueMedia())); + + startPlayback(); + if (m_media->isSeekable()) { + m_media->seek(m_media->totalTime() - SEEK_BACKWARDS); + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(aboutToFinish()), 6000)); + } else { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(aboutToFinish()), 3000 + m_media->remainingTime())); + } + disconnect(m_media, SIGNAL(aboutToFinish()), this, SLOT(enqueueMedia())); + if (currentSourceChanged.isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(currentSourceChanged(const Phonon::MediaSource &)), 3000)); + } + QCOMPARE(currentSourceChanged.size(), 1); + QCOMPARE(finished.size(), 0); + QVERIFY(m_media->queue().isEmpty()); + stopPlayback(m_media->state()); +} + +void tst_MediaObject::testPauseOnFinish() +{ + startPlayback(); + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 1000); + QCOMPARE(m_media->state(), Phonon::PlayingState); + if (m_media->isSeekable() && m_media->totalTime() > 2000) + m_media->seek(m_media->totalTime() - 2000); + QTest::waitForSignal(m_media, SIGNAL(finished()), 4000); + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 1000); + + QCOMPARE(m_media->state(), Phonon::PausedState); + connect(m_media, SIGNAL(finished()), m_media, SLOT(stop())); + m_media->seek(m_media->totalTime() - 2000); + m_media->play(); + + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 1000); + QCOMPARE(m_media->state(), Phonon::PlayingState); + QTest::waitForSignal(m_media, SIGNAL(finished()), 4000); + QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 1000); + stopPlayback(Phonon::StoppedState); +} + +void tst_MediaObject::testReconnectBetweenTwoMediaObjects(){ + // Purpose: Test that phonon can handle swithing the same sink + // between different media objects. + + Phonon::MediaObject obj1; + Phonon::MediaObject obj2; + Phonon::AudioOutput audio1; + Phonon::Path p1 = Phonon::createPath(&obj1, &audio1); + QVERIFY(p1.isValid()); + + QVERIFY(p1.reconnect(&obj1, &audio1)); + QVERIFY(p1.isValid()); + QVERIFY(p1.reconnect(&obj2, &audio1)); + QVERIFY(p1.isValid()); + QVERIFY(p1.reconnect(&obj1, &audio1)); + QVERIFY(p1.isValid()); + + // Repeat the same test while playing: + QFile file(MEDIA_FILEPATH); + obj1.setCurrentSource(&file); + QFile file2(MEDIA_FILEPATH); + obj2.setCurrentSource(&file2); + obj1.play(); + obj2.play(); + + QVERIFY(p1.reconnect(&obj1, &audio1)); + QVERIFY(p1.isValid()); + QVERIFY(p1.reconnect(&obj2, &audio1)); + QVERIFY(p1.isValid()); + QVERIFY(p1.reconnect(&obj1, &audio1)); + QVERIFY(p1.isValid()); +} + +void tst_MediaObject::testPlayOnFinish() +{ + connect(m_media, SIGNAL(finished()), SLOT(setMediaAndPlay())); + startPlayback(); + if (m_media->isSeekable()) { + m_media->seek(m_media->totalTime() - SEEK_BACKWARDS); + QVERIFY(QTest::waitForSignal(this, SIGNAL(continueTestPlayOnFinish()), 6000)); + } else { + QVERIFY(QTest::waitForSignal(this, SIGNAL(continueTestPlayOnFinish()), 3000 + m_media->remainingTime())); + } + QTest::waitForSignal(m_media, SIGNAL(finished()), 1000); + stopPlayback(m_media->state()); +} + +void tst_MediaObject::testTickSignal() +{ + QTime start1; + QTime start2; +#ifdef Q_OS_WINCE //On Windows CE we only provide ticks above 400ms + for (qint32 tickInterval = 400; tickInterval <= 1000; tickInterval *= 2) +#else + for (qint32 tickInterval = 80; tickInterval <= 500; tickInterval *= 2) +#endif + { + QSignalSpy tickSpy(m_media, SIGNAL(tick(qint64))); + //qDebug() << "Test 20 ticks with an interval of" << tickInterval << "ms"; + m_media->setTickInterval(tickInterval); + QVERIFY(m_media->tickInterval() <= tickInterval); + QVERIFY(m_media->tickInterval() >= tickInterval/2); + QVERIFY(tickSpy.isEmpty()); + m_media->seek(0); //let's go back to the beginning + start1.start(); + startPlayback(); + start2.start(); + int lastCount = 0; + qint64 s1, s2 = start2.elapsed(); + while (tickSpy.count() < 20 && (m_media->state() == Phonon::PlayingState || m_media->state() == Phonon::BufferingState)) + { + if (tickSpy.count() > lastCount) + { + s1 = start1.elapsed(); + qint64 tickTime = castQVariantToInt64(tickSpy.last().at(0)); + lastCount = tickSpy.count(); + // s1 is the time from before the beginning of the playback to + // after the tick signal + // s2 is the time from after the beginning of the playback to + // before the tick signal + // so: s2 <= s1 + QVERIFY(tickTime <= m_media->currentTime()); + if (s1 + ALLOWED_TICK_INACCURACY < tickTime || s2 - ALLOWED_TICK_INACCURACY > tickTime) { + qDebug() + << "\n" << lastCount << "ticks have been received" + << "\ntime from before playback was started to after the tick signal was received:" << s1 << "ms" + << "\ntime from after playback was started to before the tick signal was received:" << s2 << "ms" + << "\nreported tick time:" << tickTime << "ms" + << "\nallowed inaccuracy: +/-" << ALLOWED_TICK_INACCURACY << "ms"; + for (int i = 0; i < tickSpy.count(); ++i) { + qDebug() << castQVariantToInt64(tickSpy[i].at(0)); + } + } + QVERIFY(s1 + ALLOWED_TICK_INACCURACY >= tickTime); + QVERIFY(s2 - ALLOWED_TICK_INACCURACY <= tickTime); +#ifndef Q_OS_WINCE + QVERIFY(s1 >= lastCount * m_media->tickInterval()); +#else + QVERIFY(s1 >= lastCount * m_media->tickInterval() - ALLOWED_TICK_INACCURACY); +#endif + if (s2 > (lastCount + 1) * m_media->tickInterval()) + QWARN(qPrintable(QString("%1. tick came too late: %2ms elapsed while this tick should have come before %3ms") + .arg(lastCount).arg(s2).arg((lastCount + 1) * m_media->tickInterval()))); + } else if (lastCount == 0 && s2 > 20 * m_media->tickInterval()) { + QFAIL("no tick signals are being received"); + } + s2 = start2.elapsed(); + QTest::waitForSignal(m_media, SIGNAL(tick(qint64)), 2000); + } +#ifndef Q_OS_WINCE //the shorter wave file is finished on Windows CE... + stopPlayback(Phonon::PlayingState); +#else + stopPlayback(m_media->state()); +#endif + } +} + +void tst_MediaObject::testSeek() +{ + m_media->seek(0); // let's seek back to the beginning + startPlayback(); + QTime timer; + timer.start(); + qint64 t = m_media->totalTime(); + qint64 c = m_media->currentTime(); + qint64 r = m_media->remainingTime(); + int elapsed = timer.elapsed(); + if (c + r > t + elapsed || c + r < t - elapsed) { + // qDebug() << "currentTime:" << c + // << "remainingTime:" << r + // << "totalTime:" << t; + QFAIL("currentTime + remainingTime doesn't come close enough to totalTime"); + } + + QVERIFY(c + r <= t + elapsed); + QVERIFY(c + r >= t - elapsed); + if (m_media->isSeekable()) + if (r > 0) + { + m_media->setTickInterval(20); + qint64 s = c + r / 2; + testOneSeek(s); + + s /= 2; + testOneSeek(s); + s = s * 3 / 2; + testOneSeek(s); + + pausePlayback(); + + s = s * 3 / 2; + testOneSeek(s); + s /= 2; + testOneSeek(s); + + m_media->setTickInterval(0); + + + stopPlayback(Phonon::PausedState); + return; + } + else + QWARN("didn't test seeking as the MediaObject reported a remaining size <= 0"); + else + QWARN("didn't test seeking as the MediaObject is not seekable"); + stopPlayback(Phonon::PlayingState); +} + + +void tst_MediaObject::setMediaAndPlay() +{ + m_stateChangedSignalSpy->clear(); + QCOMPARE(m_stateChangedSignalSpy->count(), 0); + + QSignalSpy totalTimeChangedSignalSpy(m_media, SIGNAL(totalTimeChanged(qint64))); + QVERIFY(m_media->currentSource().type() != MediaSource::Invalid); + Phonon::State state = m_media->state(); + QVERIFY(state == Phonon::StoppedState || state == Phonon::PlayingState); + m_media->setCurrentSource(m_url); + // before calling play() we better make sure that if play() finishes very fast that we don't get + // called again + disconnect(m_media, SIGNAL(finished()), this, SLOT(setMediaAndPlay())); + state = m_media->state(); + startPlayback2(state); + + emit continueTestPlayOnFinish(); +} + +void tst_MediaObject::testPlayBeforeFinish() +{ + startPlayback(); + QCOMPARE(m_stateChangedSignalSpy->size(), 0); + Phonon::State state = m_media->state(); + QCOMPARE(state, Phonon::PlayingState); + m_media->setCurrentSource(m_url); + m_media->play(); + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 4000)); + } + // first (optional) state to reach is StoppedState + QList<QVariant> args = m_stateChangedSignalSpy->takeFirst(); + Phonon::State oldstate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(oldstate, state); + state = qvariant_cast<Phonon::State>(args.at(0)); + if (state == Phonon::StoppedState) { + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 4000)); + } + args = m_stateChangedSignalSpy->takeFirst(); + oldstate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(oldstate, state); + state = qvariant_cast<Phonon::State>(args.at(0)); + } + // next LoadingState + QCOMPARE(state, Phonon::LoadingState); + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 4000)); + } + // next either BufferingState or PlayingState + args = m_stateChangedSignalSpy->takeFirst(); + oldstate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(oldstate, state); + state = qvariant_cast<Phonon::State>(args.at(0)); + if (state == Phonon::BufferingState) { + if (m_stateChangedSignalSpy->isEmpty()) { + QVERIFY(QTest::waitForSignal(m_media, SIGNAL(stateChanged(Phonon::State, Phonon::State)), 4000)); // buffering can take a while + } + args = m_stateChangedSignalSpy->takeFirst(); + oldstate = qvariant_cast<Phonon::State>(args.at(1)); + QCOMPARE(oldstate, state); + state = qvariant_cast<Phonon::State>(args.at(0)); + } +#ifdef Q_WS_MAC + // m_media->setCurrentSource(m_url) in phonon frontend will always call + // 'stop' on the backend before calling 'setSource'. So the QT7 backend + // goes into stop, and naturally remains there after setting the new source. + // So going into playing state cannot happend when the backend is synchronized. + // Thats the reason for the ifdef. + QCOMPARE(state, Phonon::StoppedState); +#else + stopPlayback(Phonon::PlayingState); +#endif +} + +void tst_MediaObject::cleanupTestCase() +{ + delete m_stateChangedSignalSpy; + delete m_media; +#ifdef Q_OS_WINCE + QTest::qWait(200); +#endif + if (!m_tmpFileName.isNull()) { + QVERIFY(QFile::remove(m_tmpFileName)); + } +} + +void tst_MediaObject::_testOneSeek(qint64 seekTo) +{ + qint64 t = m_media->totalTime(); + qint64 oldTime = m_media->currentTime(); + if (oldTime == seekTo) { + return; + } + + QTime seekDuration; + seekDuration.start(); + m_media->seek(seekTo); + + QVERIFY(oldTime == 0 || seekTo == 0 || m_media->currentTime() != 0); + + int bufferingTime = 0; + Phonon::State s = m_media->state(); + QTime timer; + if (s == Phonon::BufferingState) { + timer.start(); + } + QEventLoop loop; + connect(m_media, SIGNAL(tick(qint64)), &loop, SLOT(quit())); + connect(m_media, SIGNAL(stateChanged(Phonon::State,Phonon::State)), &loop, SLOT(quit())); + + qint64 c = m_media->currentTime(); + qint64 r = m_media->remainingTime(); + int elapsed = 0; + while (qAbs(c - seekTo) > ALLOWED_SEEK_INACCURACY){ + QTimer::singleShot(ALLOWED_TIME_FOR_SEEKING, &loop, SLOT(quit())); + + loop.exec(); + c = m_media->currentTime(); + r = m_media->remainingTime(); + if (s == Phonon::BufferingState) { + bufferingTime += timer.restart(); + } else { + timer.start(); + } + s = m_media->state(); + elapsed = seekDuration.elapsed(); + QVERIFY(elapsed - bufferingTime < (ALLOWED_TIME_FOR_SEEKING + SEEKING_TOLERANCE)); + } + + QVERIFY(c >= seekTo - ALLOWED_SEEK_INACCURACY); + if (s == Phonon::PausedState) { + QVERIFY(bufferingTime == 0); + elapsed = 0; + } + if (c > seekTo + ALLOWED_SEEK_INACCURACY + elapsed - bufferingTime) { + QFAIL("currentTime is greater than the requested time + the time that elapsed since the seek started."); + } + if (c + r > t + 200 || c + r < t - 200) { + QFAIL("currentTime + remainingTime doesn't come close enough to totalTime"); + } + m_success = true; +} + +#endif //QT_NO_PHONON + + +QTEST_MAIN(tst_MediaObject) + +#include "tst_mediaobject.moc" +// vim: sw=4 ts=4 |