Add test for AlbumCoverFetcher.

Comes with infrastructure for testing network requests.
This commit is contained in:
John Maguire 2010-03-03 19:14:14 +00:00
parent 15797e07c6
commit 7763d7da89
8 changed files with 248 additions and 5 deletions

View File

@ -9,11 +9,15 @@
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent)
AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent, QNetworkAccessManager* network)
: QObject(parent),
network_(network),
next_id_(0),
request_starter_(new QTimer(this))
{
if (!network_.get()) {
network_.reset(new QNetworkAccessManager);
}
request_starter_->setInterval(1000);
connect(request_starter_, SIGNAL(timeout()), SLOT(StartRequests()));
}
@ -68,7 +72,7 @@ void AlbumCoverFetcher::AlbumGetInfoFinished() {
lastfm::XmlQuery query(lastfm::ws::parse(reply));
QUrl image_url(query["album"]["image size=large"].text());
QNetworkReply* image_reply = network_.get(QNetworkRequest(image_url));
QNetworkReply* image_reply = network_->get(QNetworkRequest(image_url));
connect(image_reply, SIGNAL(finished()), SLOT(AlbumCoverFetchFinished()));
active_requests_[image_reply] = id;

View File

@ -9,6 +9,8 @@
#include <lastfm/Album>
#include <boost/scoped_ptr.hpp>
class QNetworkReply;
class QString;
@ -16,7 +18,7 @@ class AlbumCoverFetcher : public QObject {
Q_OBJECT
public:
AlbumCoverFetcher(QObject* parent = 0);
AlbumCoverFetcher(QObject* parent = 0, QNetworkAccessManager* network_ = 0);
virtual ~AlbumCoverFetcher() {}
static const int kMaxConcurrentRequests;
@ -40,7 +42,7 @@ class AlbumCoverFetcher : public QObject {
QString album;
};
QNetworkAccessManager network_;
boost::scoped_ptr<QNetworkAccessManager> network_;
quint64 next_id_;
QQueue<QueuedRequest> queued_requests_;

View File

@ -29,6 +29,17 @@ set(GMOCK-SOURCES
add_library(gmock ${GMOCK-SOURCES})
target_link_libraries(gmock gtest)
set(MOCK-SOURCES
mock_networkaccessmanager.cpp)
set(MOCK-MOC-HEADERS
mock_networkaccessmanager.h)
qt4_wrap_cpp(MOCK-SOURCES-MOC ${MOCK-MOC-HEADERS})
add_library(mocks ${MOCK-SOURCES} ${MOCK-SOURCES-MOC})
target_link_libraries(mocks gmock)
add_custom_target(test
echo "Running tests"
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
@ -41,7 +52,7 @@ macro(add_test_file test_source)
${ARGV0}
main.cpp
)
target_link_libraries(${TEST_NAME} gmock clementine_lib)
target_link_libraries(${TEST_NAME} gmock clementine_lib mocks)
add_custom_command(TARGET test POST_BUILD
COMMAND ./${TEST_NAME})
add_dependencies(test ${TEST_NAME})
@ -51,3 +62,4 @@ endmacro (add_test_file)
add_test_file(m3uparser_test.cpp)
add_test_file(song_test.cpp)
add_test_file(librarybackend_test.cpp)
add_test_file(albumcoverfetcher_test.cpp)

View File

@ -0,0 +1,59 @@
#include "albumcoverfetcher.h"
#include <lastfm/ws.h>
#include <QCoreApplication>
#include <QEventLoop>
#include <QSignalSpy>
#include "mock_networkaccessmanager.h"
#include "gtest/gtest.h"
int argc = 1;
char* argv[] = { "test", 0 };
class AlbumCoverFetcherTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
lastfm::ws::ApiKey = "foobar";
}
AlbumCoverFetcherTest()
: app_(argc, (char**)argv) {
}
void SetUp() {
network_ = new MockNetworkAccessManager;
lastfm::setNetworkAccessManager(network_);
}
MockNetworkAccessManager* network_;
QCoreApplication app_;
};
TEST_F(AlbumCoverFetcherTest, FetchesAlbumCover) {
const char* data = "<lfm status=\"ok\"><album><name>Bar</name><artist>Foo</artist>"
"<image size=\"large\">http://example.com/image.jpg</image></album></lfm>";
QMap<QString, QString> params;
params["artist"] = "Foo";
params["album"] = "Bar";
params["api_key"] = "foobar";
MockNetworkReply* get_info_reply = network_->ExpectGet("audioscrobbler", params, 200, data);
params.clear();
MockNetworkReply* album_reply = network_->ExpectGet("http://example.com/image.jpg", params, 200, "");
AlbumCoverFetcher fetcher(NULL, network_);
QSignalSpy spy(&fetcher, SIGNAL(AlbumCoverFetched(quint64, const QImage&)));
ASSERT_TRUE(spy.isValid());
fetcher.FetchAlbumCover("Foo", "Bar");
get_info_reply->Done();
app_.processEvents(QEventLoop::AllEvents);
album_reply->Done();
app_.processEvents(QEventLoop::AllEvents);
EXPECT_EQ(1, spy.count());
}

View File

@ -0,0 +1,110 @@
#include "mock_networkaccessmanager.h"
#include <QtDebug>
#include <algorithm>
using std::min;
using ::testing::MakeMatcher;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::Return;
class RequestForUrlMatcher : public MatcherInterface<const QNetworkRequest&> {
public:
RequestForUrlMatcher(const QString& contains,
const QMap<QString, QString>& expected_params)
: contains_(contains),
expected_params_(expected_params) {
}
virtual ~RequestForUrlMatcher() {}
virtual bool Matches(const QNetworkRequest& req) const {
const QUrl& url = req.url();
if (!url.toString().contains(contains_)) {
return false;
}
for (QMap<QString, QString>::const_iterator it = expected_params_.begin();
it != expected_params_.end(); ++it) {
if (!url.hasQueryItem(it.key()) ||
url.queryItemValue(it.key()) != it.value()) {
return false;
}
}
return true;
}
virtual void DescribeTo(::std::ostream* os) const {
*os << "matches url";
}
private:
QString contains_;
QMap<QString, QString> expected_params_;
};
inline Matcher<const QNetworkRequest&> RequestForUrl(
const QString& contains,
const QMap<QString, QString>& params) {
return MakeMatcher(new RequestForUrlMatcher(contains, params));
}
MockNetworkReply* MockNetworkAccessManager::ExpectGet(
const QString& contains,
const QMap<QString, QString>& expected_params,
int status,
const char* data) {
MockNetworkReply* reply = new MockNetworkReply(data);
reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
EXPECT_CALL(*this, createRequest(
GetOperation, RequestForUrl(contains, expected_params), NULL)).
WillOnce(Return(reply));
return reply;
}
MockNetworkAccessManager::~MockNetworkAccessManager() {
}
MockNetworkReply::MockNetworkReply()
: data_(NULL),
size_(0) {
}
MockNetworkReply::MockNetworkReply(const char* data)
: data_(data),
size_(strlen(data)),
pos_(0) {
}
void MockNetworkReply::SetData(const char* data) {
data_ = data;
size_ = strlen(data);
pos_ = 0;
}
qint64 MockNetworkReply::readData(char* data, qint64 size) {
if (size_ == pos_) {
return -1;
}
qint64 bytes_to_read = min(size_ - pos_, size);
memcpy(data, data_, bytes_to_read);
pos_ += bytes_to_read;
return bytes_to_read;
}
qint64 MockNetworkReply::writeData(const char* data, qint64) {
ADD_FAILURE() << "Something tried to write to a QNetworkReply";
}
void MockNetworkReply::Done() {
setOpenMode(QIODevice::ReadOnly);
emit finished();
}
void MockNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant& value) {
QNetworkReply::setAttribute(code, value);
}

View File

@ -0,0 +1,49 @@
#ifndef MOCK_NETWORKACCESSAMANGER_H
#define MOCK_NETWORKACCESSMANAGER_H
#include <QList>
#include <QMap>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
#include "test_utils.h"
#include "gmock/gmock.h"
class MockNetworkReply : public QNetworkReply {
Q_OBJECT
public:
MockNetworkReply();
MockNetworkReply(const char* data);
virtual ~MockNetworkReply() {}
void SetData(const char* data);
virtual void setAttribute(QNetworkRequest::Attribute code, const QVariant& value);
void Done();
protected:
MOCK_METHOD0(abort, void());
virtual qint64 readData(char* data, qint64);
virtual qint64 writeData(const char* data, qint64);
const char* data_;
qint64 size_;
qint64 pos_;
};
class MockNetworkAccessManager : public QNetworkAccessManager {
Q_OBJECT
public:
virtual ~MockNetworkAccessManager();
MockNetworkReply* ExpectGet(
const QString& contains,
const QMap<QString, QString>& params,
int status,
const char* ret_data);
protected:
MOCK_METHOD3(createRequest, QNetworkReply*(Operation, const QNetworkRequest&, QIODevice*));
};
#endif

View File

@ -1,5 +1,6 @@
#include "test_utils.h"
#include <QNetworkRequest>
#include <QString>
#include <QUrl>
@ -13,3 +14,7 @@ std::ostream& operator <<(std::ostream& stream, const QUrl& url) {
return stream;
}
std::ostream& operator <<(std::ostream& stream, const QNetworkRequest& req) {
stream << req.url().toString().toStdString();
return stream;
}

View File

@ -3,10 +3,12 @@
#include <iostream>
class QNetworkRequest;
class QString;
class QUrl;
std::ostream& operator <<(std::ostream& stream, const QString& str);
std::ostream& operator <<(std::ostream& stream, const QUrl& url);
std::ostream& operator <<(std::ostream& stream, const QNetworkRequest& req);
#endif // TEST_UTILS_H