diff options
author | Alex Merry <dev@randomguy3.me.uk> | 2011-11-02 13:30:24 +0000 |
---|---|---|
committer | Alex Merry <dev@randomguy3.me.uk> | 2011-11-02 13:30:24 +0000 |
commit | aac6b1fa7a85badf7bbb458dcd75a708cae16cc3 (patch) | |
tree | 14ea7fd32de70cd472185cb69b5e5a88fc56ce3b | |
parent | be5b3bf09f01b488bbc0429a7cbd8e7980a9bb7e (diff) |
Player interface testing class is mostly complete now
-rw-r--r-- | mpris2/interfacetest.cpp | 13 | ||||
-rw-r--r-- | mpris2/interfacetest.h | 1 | ||||
-rw-r--r-- | mpris2/playerinterfacetest.cpp | 278 | ||||
-rw-r--r-- | mpris2/playerinterfacetest.h | 27 | ||||
-rw-r--r-- | mpris2/rootinterfacetest.cpp | 2 |
5 files changed, 314 insertions, 7 deletions
diff --git a/mpris2/interfacetest.cpp b/mpris2/interfacetest.cpp index 50638dd..8422921 100644 --- a/mpris2/interfacetest.cpp +++ b/mpris2/interfacetest.cpp @@ -42,6 +42,10 @@ InterfaceTest::~InterfaceTest() { } +void InterfaceTest::connectSignals() +{ +} + QVariantMap InterfaceTest::properties() const { return props; @@ -159,9 +163,14 @@ void InterfaceTest::initialTest() checkProps(); - QDBusConnection::sessionBus().connect(iface->service(), iface->path(), iface->interface(), + QDBusConnection::sessionBus().connect( + iface->service(), + iface->path(), + DBUS_PROPS_IFACE, "propertiesChanged", /* signature, */ - this, SLOT( _m_propertiesChanged(QString,QVariantMap,QStringList))); + this, + SLOT( _m_propertiesChanged(QString,QVariantMap,QStringList,QDBusMessage))); + connectSignals(); emit propertiesChanged(properties().keys()); } diff --git a/mpris2/interfacetest.h b/mpris2/interfacetest.h index 2de85e3..8d62b2c 100644 --- a/mpris2/interfacetest.h +++ b/mpris2/interfacetest.h @@ -125,6 +125,7 @@ namespace Mpris2 virtual void checkProps(const QVariantMap& oldProps = QVariantMap()) = 0; virtual void checkUpdatedProperty(const QString& propName) = 0; virtual void checkConsistency(const QVariantMap& oldProps = QVariantMap()) = 0; + virtual void connectSignals(); QDBusInterface* iface; QVariantMap props; diff --git a/mpris2/playerinterfacetest.cpp b/mpris2/playerinterfacetest.cpp index 0731eac..d663809 100644 --- a/mpris2/playerinterfacetest.cpp +++ b/mpris2/playerinterfacetest.cpp @@ -27,12 +27,25 @@ using namespace Mpris2; PlayerInterfaceTest::PlayerInterfaceTest(const QString& service, QObject* parent) : InterfaceTest(MPRIS2_PLAYER_IFACE, service, parent) { + m_pos = -1; + m_currentRate = 0.0; } PlayerInterfaceTest::~PlayerInterfaceTest() { } +void PlayerInterfaceTest::connectSignals() +{ + QDBusConnection::sessionBus().connect( + iface->service(), + iface->path(), + iface->interface(), + "Seeked", /* signature, */ + this, + SLOT( _m_seeked(qint64,QDBusMessage))); +} + void PlayerInterfaceTest::checkUpdatedProperty(const QString& propName) { if (propName == "CanControl") { @@ -225,12 +238,12 @@ void PlayerInterfaceTest::checkMetadata(const QVariantMap& oldProps) if (!metadata.contains("mpris:trackid")) { emit interfaceError(Property, "Metadata", "No mpris:trackid entry for the current track"); - } else if (metadata.value("mpris:trackid").type() != QVariant::String) { + } else if (metadata.value("mpris:trackid").userType() != qMetaTypeId<QDBusObjectPath>()) { emit interfaceError(Property, "Metadata", - "mpris:trackid entry is not a string"); + "mpris:trackid entry is not a D-Bus object path"); } else if (metadata.value("mpris:trackid").toString().isEmpty()) { emit interfaceError(Property, "Metadata", - "mpris:trackid entry is an empty string"); + "mpris:trackid entry is an empty path"); } checkMetadataEntry(metadata, "mpris:length", QVariant::LongLong); @@ -360,7 +373,9 @@ bool PlayerInterfaceTest::checkMetadataEntry(const QVariantMap& metadata, const void PlayerInterfaceTest::checkPosition(const QVariantMap& oldProps) { - if (!checkPropValid("Position", QVariant::LongLong, oldProps)) + // don't pass in oldProps: Position doesn't get updated + // automatically + if (!checkPropValid("Position", QVariant::LongLong)) return; qint64 position = properties().value("Position").toLongLong(); @@ -454,8 +469,261 @@ void PlayerInterfaceTest::checkRateConsistency(const QVariantMap& oldProps) } } +void PlayerInterfaceTest::updateCurrentRate() +{ + QString playbackStatus = properties().value("PlaybackStatus").toString(); + if (playbackStatus == "Playing") { + m_currentRate = properties().value("Rate").toDouble(); + } else { + m_currentRate = 0.0; + } +} + +void PlayerInterfaceTest::_m_seeked(qint64 position, const QDBusMessage& message) +{ + m_pos = position; + m_posLastUpdated = QTime::currentTime(); + properties()["Position"] = position; + checkPosition(); + emit Seeked(position); +} + +qint64 PlayerInterfaceTest::predictedPosition() +{ + qint64 elapsed = (qint64)m_posLastUpdated.elapsed() * 1000; + return m_pos + (m_currentRate * elapsed); +} + void PlayerInterfaceTest::checkPredictedPosition() { - // FIXME: check predicted position + qint64 position = properties().value("Position").toLongLong(); + + // if this is the initial fetch + if (m_pos == -1) { + m_pos = position; + m_posLastUpdated = QTime::currentTime(); + updateCurrentRate(); + return; + } + + qint64 predictedPos = predictedPosition(); + m_pos = position; + m_posLastUpdated = QTime::currentTime(); + updateCurrentRate(); + + // allow 1s of error + const qint64 allowance = 1000000; + if (position - predictedPos > allowance || + position - predictedPos < -allowance) + { + qint64 diffMillis = (position - predictedPos) / 1000; + qreal diffSecs = (qreal)diffMillis / 1000.0; + if (diffMillis > 0) { + emit interfaceWarning(Property, "Position", + "Position is " + + QString::number(diffSecs, 'g', 2) + + "ms ahead of what was predicted from Rate"); + } else { + emit interfaceWarning(Property, "Position", + "Position is " + QString::number(-diffMillis) + + QString::number(-diffSecs, 'g', 2) + + "ms behind of what was predicted from Rate"); + } + } +} + +void PlayerInterfaceTest::testNext() +{ + QDBusReply<void> reply = iface->call("Next"); + if (!reply.isValid()) { + emit interfaceError(Method, "Next", "Call to Next failed with error " + reply.error().message()); + } else { + if (!props["CanGoNext"].toBool()) { + emit interfaceInfo(Method, "Next", "Next called, but CanGoNext is false, so this should have no effect"); + // TODO: check to see that the track does not change in the next second or so + } else { + emit interfaceInfo(Method, "Next", "Next called; the media player should now move to the next track"); + // TODO: check to see if the track changes + // TODO: check to make sure the PlaybackStatus does not change + } + } } +void PlayerInterfaceTest::testPrevious() +{ + QDBusReply<void> reply = iface->call("Previous"); + if (!reply.isValid()) { + emit interfaceError(Method, "Previous", "Call to Previous failed with error " + reply.error().message()); + } else { + if (!props["CanGoNext"].toBool()) { + emit interfaceInfo(Method, "Previous", "Previous called, but CanGoPrevious is false, so this should have no effect"); + // TODO: check to see that the track does not change in the next second or so + } else { + emit interfaceInfo(Method, "Previous", "Previous called; the media player should now move to the next track"); + // TODO: check to see if the track changes + // TODO: check to make sure the PlaybackStatus does not change + } + } +} + +void PlayerInterfaceTest::testPause() +{ + QDBusReply<void> reply = iface->call("Pause"); + if (!reply.isValid()) { + emit interfaceError(Method, "Pause", "Call to Pause failed with error " + reply.error().message()); + } else { + if (!props["CanPause"].toBool()) { + emit interfaceInfo(Method, "Pause", "Pause called, but CanPause is false, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else if (props["PlaybackStatus"] == "Paused") { + emit interfaceInfo(Method, "Pause", "Pause called, but already paused, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else { + emit interfaceInfo(Method, "Pause", "Pause called; the media player should now be paused"); + // TODO: check to see if the PlaybackStatus changes to Paused + } + } +} + +void PlayerInterfaceTest::testPlayPause() +{ + QDBusReply<void> reply = iface->call("PlayPause"); + if (!reply.isValid()) { + emit interfaceError(Method, "PlayPause", "Call to PlayPause failed with error " + reply.error().message()); + } else { + if (!props["CanPause"].toBool()) { + emit interfaceInfo(Method, "PlayPause", "PlayPause called, but CanPause is false, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else if (props["PlaybackStatus"] == "Playing") { + emit interfaceInfo(Method, "PlayPause", "PlayPause called; the media player should now be paused"); + // TODO: check to see if the PlaybackStatus changes to Paused + } else { + emit interfaceInfo(Method, "PlayPause", "PlayPause called; the media player should now be playing"); + // TODO: check to see if the PlaybackStatus changes to Playing + } + } +} + +void PlayerInterfaceTest::testPlay() +{ + QDBusReply<void> reply = iface->call("Play"); + if (!reply.isValid()) { + emit interfaceError(Method, "Play", "Call to Play failed with error " + reply.error().message()); + } else { + if (!props["CanPlay"].toBool()) { + emit interfaceInfo(Method, "Play", "Play called, but CanPlay is false, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else if (props["PlaybackStatus"] == "Playing") { + emit interfaceInfo(Method, "Play", "Play called, but already playing, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else { + emit interfaceInfo(Method, "Play", "Play called; the media player should start playback"); + // TODO: check to see if the PlaybackStatus changes to Playing + } + } +} + +void PlayerInterfaceTest::testStop() +{ + QDBusReply<void> reply = iface->call("Stop"); + if (!reply.isValid()) { + emit interfaceError(Method, "Stop", "Call to Stop failed with error " + reply.error().message()); + } else { + if (!props["CanControl"].toBool()) { + emit interfaceInfo(Method, "Stop", "Stop called, but CanControl is false, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else if (props["PlaybackStatus"] == "Stopped") { + emit interfaceInfo(Method, "Stop", "Stop called, but already stopped, so this should have no effect"); + // TODO: check to see that the PlaybackStatus does not change + } else { + emit interfaceInfo(Method, "Stop", "Stop called; the media player should now stop"); + // TODO: check to see if the PlaybackStatus changes to Stopped + } + } +} + +void PlayerInterfaceTest::testSeek(qint64 offset) +{ + QDBusReply<void> reply = iface->call("Seek", QVariant::fromValue<qint64>(offset)); + if (!reply.isValid()) { + emit interfaceError(Method, "Seek", "Call to Seek failed with error " + reply.error().message()); + } else { + if (!props["CanSeek"].toBool()) { + emit interfaceInfo(Method, "Seek", "Seek called, but CanSeek is false, so this should have no effect"); + // TODO: check to see that Seeked is not emitted; PlaybackStatus does not change + } else { + qint64 newPos = predictedPosition() + offset; + if (newPos < 0.0) { + emit interfaceInfo(Method, "Seek", "Seek called with a value that moved beyond the start of the track; the media player should be at the start of the track"); + // TODO: check to see that Seeked is emitted with value 0 (fuzzy) + } else if (props.contains("mpris:length") && + props["mpris:length"].toLongLong() <= newPos) { + if (!props["CanGoNext"].toBool()) { + emit interfaceInfo(Method, "Seek", "Seek called with a value that would move beyond the end of the track, but CanGoNext is false, so playback should stop"); + // TODO: check to see that PlaybackStatus changes to Stopped + } else { + emit interfaceInfo(Method, "Seek", "Seek called with a value that would move beyond the end of the track; the media player should now move to the next track"); + // TODO: check to see if the track changes + // TODO: check to make sure the PlaybackStatus does not change + } + } else { + emit interfaceInfo(Method, "Seek", "Seek called; the media player should seek to " + + QString::number(newPos)); + // TODO: check that Seeked is emitted with the relevant value (fuzzy) + } + } + } +} + +void PlayerInterfaceTest::testSetPosition(const QDBusObjectPath& trackId, qint64 position) +{ + QDBusReply<void> reply = iface->call("SetPosition", + QVariant::fromValue<QDBusObjectPath>(trackId), + QVariant::fromValue<qint64>(position)); + if (!reply.isValid()) { + emit interfaceError(Method, "SetPosition", "Call to SetPosition failed with error " + reply.error().message()); + } else { + if (!props["CanSeek"].toBool()) { + emit interfaceInfo(Method, "SetPosition", "SetPosition called, but CanSeek is false, so this should have no effect"); + // TODO: check to see that Seeked is not emitted; PlaybackStatus does not change + } else { + if (trackId.path() != props["mpris:trackid"].toString()) { + emit interfaceInfo(Method, "SetPosition", "SetPosition called with the wrong trackid; nothing should happen"); + // TODO: check to see that Seeked is not emitted; PlaybackStatus does not change + } else if (position < 0.0) { + emit interfaceInfo(Method, "SetPosition", "SetPosition called with a negative value; the media player should be at the start of the track"); + // TODO: check to see that Seeked is emitted with value 0 + } else if (props.contains("mpris:length") && + props["mpris:length"].toLongLong() <= position) { + if (!props["CanGoNext"].toBool()) { + emit interfaceInfo(Method, "SetPosition", "SetPosition called with a value that would move beyond the end of the track, but CanGoNext is false, so playback should stop"); + // TODO: check to see that PlaybackStatus changes to Stopped + } else { + emit interfaceInfo(Method, "SetPosition", "SetPosition called with a value that would move beyond the end of the track; the media player should now move to the next track"); + // TODO: check to see if the track changes + // TODO: check to make sure the PlaybackStatus does not change + } + } else { + emit interfaceInfo(Method, "SetPosition", "SetPosition called; the media player should seek to the new position"); + // TODO: check that Seeked is emitted with the relevant value + } + } + } +} + +void PlayerInterfaceTest::testOpenUri(const QString& uri) +{ + QDBusReply<void> reply = iface->call("OpenUri", + QVariant::fromValue<QString>(uri)); + if (!reply.isValid()) { + emit interfaceInfo(Method, "OpenUri", "Call to OpenUri failed with error " + reply.error().message()); + // TODO: check SupportedUriSchemes - if empty, this method may be unimplemented + // if uri scheme is not in the list of schemes or not a valid uri, argument + // error may be returned + } else { + emit interfaceInfo(Method, "OpenUri", "Call to OpenUri did not return an error"); + // ? + } +} + + diff --git a/mpris2/playerinterfacetest.h b/mpris2/playerinterfacetest.h index 33e682d..21f1281 100644 --- a/mpris2/playerinterfacetest.h +++ b/mpris2/playerinterfacetest.h @@ -20,7 +20,9 @@ #define MPRIS2_PLAYERINTERFACETEST_H #include "interfacetest.h" +#include <QTime> +class QDBusObjectPath; namespace Mpris2 { class PlayerInterfaceTest : public InterfaceTest @@ -31,10 +33,30 @@ namespace Mpris2 { PlayerInterfaceTest(const QString& service, QObject* parent = 0); virtual ~PlayerInterfaceTest(); + qint64 predictedPosition(); + + public slots: + void testNext(); + void testPrevious(); + void testPause(); + void testPlayPause(); + void testPlay(); + void testStop(); + void testSeek(qint64 offset); + void testSetPosition(const QDBusObjectPath& trackId, qint64 offset); + void testOpenUri(const QString& uri); + + signals: + void Seeked(qint64 newPosition); + protected: virtual void checkUpdatedProperty(const QString& propName); virtual void checkProps(const QVariantMap& oldProps = QVariantMap()); virtual void checkConsistency(const QVariantMap& oldProps = QVariantMap()); + virtual void connectSignals(); + + private slots: + void _m_seeked(qint64 position, const QDBusMessage& message); private: void checkControlProp(const QString& propName, const QVariantMap& oldProps = QVariantMap()); @@ -50,6 +72,11 @@ namespace Mpris2 { void checkPositionConsistency(const QVariantMap& oldProps = QVariantMap()); void checkPredictedPosition(); bool checkMetadataEntry(const QVariantMap& metadata, const QString& entry, QVariant::Type type); + void updateCurrentRate(); + + qint64 m_pos; + qreal m_currentRate; // 0.0 if not playing, Rate otherwise + QTime m_posLastUpdated; }; } diff --git a/mpris2/rootinterfacetest.cpp b/mpris2/rootinterfacetest.cpp index f7e8475..664b8bb 100644 --- a/mpris2/rootinterfacetest.cpp +++ b/mpris2/rootinterfacetest.cpp @@ -59,6 +59,7 @@ void RootInterfaceTest::checkPropertySupportedUriSchemes(const QVariantMap& oldP emit interfaceWarning(Property, "SupportedUriSchemes", "\"file\" is not listed as a supported URI scheme (this is unusual)"); } // TODO: check valid protocols + // check duplicates? } } @@ -70,6 +71,7 @@ void RootInterfaceTest::checkPropertySupportedMimeTypes(const QVariantMap& oldPr emit interfaceWarning(Property, "SupportedMimeTypes", "The media player claims not to support any mime types"); } // TODO: check valid mimetypes + // check duplicates? } } |