From e14c59948f336f475414a9a3975cf4abfa4ada80 Mon Sep 17 00:00:00 2001 From: Mattias Andersson Date: Sat, 24 May 2014 13:33:32 +0200 Subject: [PATCH] Add test for MusicBrainzClient. --- src/musicbrainz/musicbrainzclient.cpp | 5 +- src/musicbrainz/musicbrainzclient.h | 6 +- tests/CMakeLists.txt | 1 + tests/data/discid_2cd.xml | 399 ++++++++++++++++++ tests/data/recording.xml | 48 +++ .../data/recording_with_multiple_releases.xml | 285 +++++++++++++ tests/data/testdata.qrc | 3 + tests/musicbrainzclient_test.cpp | 248 +++++++++++ 8 files changed, 991 insertions(+), 4 deletions(-) create mode 100644 tests/data/discid_2cd.xml create mode 100644 tests/data/recording.xml create mode 100644 tests/data/recording_with_multiple_releases.xml create mode 100644 tests/musicbrainzclient_test.cpp diff --git a/src/musicbrainz/musicbrainzclient.cpp b/src/musicbrainz/musicbrainzclient.cpp index a82279cc1..0f5797ed3 100644 --- a/src/musicbrainz/musicbrainzclient.cpp +++ b/src/musicbrainz/musicbrainzclient.cpp @@ -34,9 +34,10 @@ const char* MusicBrainzClient::kDiscUrl = const char* MusicBrainzClient::kDateRegex = "^[12]\\d{3}"; const int MusicBrainzClient::kDefaultTimeout = 5000; // msec -MusicBrainzClient::MusicBrainzClient(QObject* parent) +MusicBrainzClient::MusicBrainzClient(QObject* parent, + QNetworkAccessManager* network) : QObject(parent), - network_(new NetworkAccessManager(this)), + network_(network ? network : new NetworkAccessManager(this)), timeouts_(new NetworkTimeouts(kDefaultTimeout, this)) {} void MusicBrainzClient::Start(int id, const QString& mbid) { diff --git a/src/musicbrainz/musicbrainzclient.h b/src/musicbrainz/musicbrainzclient.h index cdaf20094..6b526b68b 100644 --- a/src/musicbrainz/musicbrainzclient.h +++ b/src/musicbrainz/musicbrainzclient.h @@ -38,7 +38,10 @@ class MusicBrainzClient : public QObject { // the Finished signal - they have no meaning to MusicBrainzClient. public: - MusicBrainzClient(QObject* parent = nullptr); + // The second argument allows for specifying a custom network access + // manager. It is used in tests. + MusicBrainzClient(QObject* parent = nullptr, + QNetworkAccessManager* network = nullptr); struct Result { Result() : duration_msec_(0), track_(0), year_(-1) {} @@ -133,5 +136,4 @@ inline uint qHash(const MusicBrainzClient::Result& result) { return qHash(result.album_) ^ qHash(result.artist_) ^ result.duration_msec_ ^ qHash(result.title_) ^ result.track_ ^ result.year_; } - #endif // MUSICBRAINZCLIENT_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9149e4b1f..f7d0a42ac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -131,6 +131,7 @@ add_test_file(fmpsparser_test.cpp false) #add_test_file(librarymodel_test.cpp true) #add_test_file(m3uparser_test.cpp false) add_test_file(mergedproxymodel_test.cpp false) +add_test_file(musicbrainzclient_test.cpp false) add_test_file(organiseformat_test.cpp false) add_test_file(organisedialog_test.cpp false) #add_test_file(playlist_test.cpp true) diff --git a/tests/data/discid_2cd.xml b/tests/data/discid_2cd.xml new file mode 100644 index 000000000..e234b7d9c --- /dev/null +++ b/tests/data/discid_2cd.xml @@ -0,0 +1,399 @@ + + + + 267785 + + + Live on the Edge of Forever + Official + normal + + eng + + + + + + Symphony X + Symphony X + + + + 2001-11-13 + US + + + 2001-11-13 + + United States + United States + + US + + + + + + false + 0 + false + false + + + + 1 + CD + + + 213610 + + + + + 1 + 1 + 98266 + + Prelude + 98266 + + + + 2 + 2 + 318400 + + Evolution (The Grand Design) + 318400 + + + + 3 + 3 + 390826 + + Fallen / Transcendence + 390826 + + + + 4 + 4 + 459040 + + Communion and the Oracle + 459040 + + + + 5 + 5 + 219760 + + The Bird-Serpent War + 219760 + + + + 6 + 6 + 309573 + + On the Breath of Poseidon + 309573 + + + + 7 + 7 + 425226 + + Egypt + 425226 + + + + 8 + 8 + 352666 + + The Death of Balance / Candlelight Fantasia + 352666 + + + + 9 + 9 + 272373 + + The Eyes of Medusa + 272373 + + + + + + 2 + CD + + + 267785 + + + + + 1 + 1 + 394600 + + Smoke and Mirrors + 394600 + + + + 2 + 2 + 441866 + + Church of the Machine + 441866 + + + + 3 + 3 + 849426 + + Through the Looking Glass + 849426 + + + + 4 + 4 + 442773 + + Of Sins and Shadows + 442773 + + + + 5 + 5 + 245160 + + Sea of Lies + 245160 + + + + 6 + 6 + 1194640 + + The Divine Wings of Tragedy + 1194640 + + + + + + + + Live on the Edge of Forever + Official + normal + + eng + + + + + + Symphony X + Symphony X + + + + 2001-10-22 + DE + + + 2001-10-22 + + Germany + Germany + + DE + + + + + B00005Q8VB + + false + 0 + false + false + + + + 1 + + + 213610 + + + + + 1 + 1 + 98266 + + Prelude + 98266 + + + + 2 + 2 + 318400 + + Evolution (The Grand Design) + 318400 + + + + 3 + 3 + 390826 + + Fallen / Transcendence + 390826 + + + + 4 + 4 + 459040 + + Communion and the Oracle + 459040 + + + + 5 + 5 + 219760 + + The Bird-Serpent War + 219760 + + + + 6 + 6 + 309573 + + On the Breath of Poseidon + 309573 + + + + 7 + 7 + 425226 + + Egypt + 425226 + + + + 8 + 8 + 352666 + + The Death of Balance / Candlelight Fantasia + 352666 + + + + 9 + 9 + 272373 + + The Eyes of Medusa + 272373 + + + + + + 2 + + + 267785 + + + + + 1 + 1 + 394600 + + Smoke and Mirrors + 394600 + + + + 2 + 2 + 441866 + + Church of the Machine + 441866 + + + + 3 + 3 + 849426 + + Through the Looking Glass + 849426 + + + + 4 + 4 + 442773 + + Of Sins and Shadows + 442773 + + + + 5 + 5 + 245160 + + Sea of Lies + 245160 + + + + 6 + 6 + 1194640 + + The Divine Wings of Tragedy + 1194640 + + + + + + + + + diff --git a/tests/data/recording.xml b/tests/data/recording.xml new file mode 100644 index 000000000..cd7ee1b13 --- /dev/null +++ b/tests/data/recording.xml @@ -0,0 +1,48 @@ + + + + Victoria und ihr Husar: Pardon Madame + 203906 + + + + Paul Abraham + Abraham, Paul + + + + + + An Evening at the Operetta + Official + normal + Jewel Case + + deu + + + 1992 + + + 1992 + + + 8712157906266 + + + 1 + CD + + + 6 + 6 + Victoria und ihr Husar: Pardon Madame + 203906 + + + + + + + + diff --git a/tests/data/recording_with_multiple_releases.xml b/tests/data/recording_with_multiple_releases.xml new file mode 100644 index 000000000..da34590f1 --- /dev/null +++ b/tests/data/recording_with_multiple_releases.xml @@ -0,0 +1,285 @@ + + + + Symphony no. 40 in G minor, K. 550 "Great": I. Allegro molto + 458386 + + + + Hans Graf + Graf, Hans + + + + + Mozarteum Orchester Salzburg + Mozarteum Orchester Salzburg + + + + + + Masters of Classical Music, Volume 1 + Official + normal + Jewel Case + + eng + + + 1988 + US + + + 1988 + + United States + United States + + US + + + + + 018111580120 + + + 1 + CD + + + 4 + 4 + Symphony no. 40 in G minor, K. 550 "Great": I. Allegro molto + 458960 + + + + + + + The Classic Composers, Volume 3: Musical Masterpieces + Promotion + normal + + eng + + + 2005 + US + + + 2005 + + United States + United States + + US + + + + + + + 1 + CD + + + 9 + 9 + Symphony No. 40 in G minor with clarinets, K. 550: I. Allegro molto + 458386 + + + + + + + Masters of Classical Music, Volume 1 + Official + normal + Jewel Case + + eng + + + 2010-10-18 + DE + + + 2010-10-18 + + Germany + Germany + + DE + + + + + 4006408158011 + + + 1 + CD + + + 4 + 4 + Symphony no. 40 in G minor, K. 550 "Great": I. Allegro molto + 458960 + + + + + + + Les grands compositeurs : Mozart prodige musical + Official + normal + Other + + + + 2003 + FR + + + 2003 + + France + France + + FR + + + + + + + + 1 + CD + + + 9 + 9 + Symphonie n° 40, K 550 : 1er mouvement + 456000 + + + + + + + The Classic Composers, Volume 3: Musical Masterpieces + Promotion + normal + + eng + + + 2002 + GB + + + 2002 + + United Kingdom + United Kingdom + + GB + + + + + + + 1 + + + 9 + 9 + Symphony No. 40 in G minor with clarinets, K. 550: I. Allegro molto + 458386 + + + + + + + Klassiset säveltäjät: Taitoa ja mielikuvitusta + normal + Other + + fin + + + FI + + + + Finland + Finland + + FI + + + + + + + + 1 + CD + + + 9 + 9 + Sinfonia nro 40 G-molli, KV 550: 1. osa + 458386 + + + + + + + The Classic Composers, Volume 3: Musical Masterpieces + Official + normal + Other + + eng + + + DE + + + + Germany + Germany + + DE + + + + + + + + 1 + CD + + + 9 + 9 + Symphony no. 40 in G minor, K. 550 "Great": I. Allegro molto + 458000 + + + + + + + + diff --git a/tests/data/testdata.qrc b/tests/data/testdata.qrc index 42716808b..235240b04 100644 --- a/tests/data/testdata.qrc +++ b/tests/data/testdata.qrc @@ -8,6 +8,7 @@ beep.wma beep.m4a brokensong.cue + discid_2cd.xml fmpsplaycount.mp3 fmpsplaycountboth.mp3 fmpsplaycountuser.mp3 @@ -22,6 +23,8 @@ popmrating.mp3 pls_one.pls pls_somafm.pls + recording.xml + recording_with_multiple_releases.xml secretagent.asx secretagent.pls test.m3u diff --git a/tests/musicbrainzclient_test.cpp b/tests/musicbrainzclient_test.cpp new file mode 100644 index 000000000..6b08bc790 --- /dev/null +++ b/tests/musicbrainzclient_test.cpp @@ -0,0 +1,248 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine 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 3 of the License, or + (at your option) any later version. + + Clementine 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 Clementine. If not, see . +*/ + +#include "core/logging.h" +#include "musicbrainz/musicbrainzclient.h" + +#include +#include +#include +#include +#include +#include + +#include "mock_networkaccessmanager.h" +#include "gtest/gtest.h" +#include "test_utils.h" + +namespace { +typedef QList ResultList; +Q_DECLARE_METATYPE(ResultList); +}; + +class MusicBrainzClientTest : public ::testing::Test { + protected: + static void SetUpTestCase() { + mock_network_ = new MockNetworkAccessManager; + qRegisterMetaType("MusicBrainzClient::ResultList"); + } + + static void TearDownTestCase() { delete mock_network_; } + + // Reads the data from a file into a QByteArray and returns it. + QByteArray ReadDataFromFile(const QString& filename) { + QFile file(filename); + file.open(QIODevice::ReadOnly); + QByteArray data = file.readAll(); + return data; + } + + static MockNetworkAccessManager* mock_network_; +}; + +MockNetworkAccessManager* MusicBrainzClientTest::mock_network_; + +// Test if a discid that do not exist in the musicbrainz database +// generates an empty result. +TEST_F(MusicBrainzClientTest, DiscIdNotFound) { + QByteArray data = + "Not " + "FoundFor usage, please see: " + "http://musicbrainz.org/development/mmd"; + + // Create a MusicBrainzClient instance with mock_network_. + MusicBrainzClient musicbrainz_client(nullptr, mock_network_); + + // Hook the data as the response to a query of a given type. + QMap params; + params["inc"] = "artists+recordings"; + MockNetworkReply* discid_reply = + mock_network_->ExpectGet("discid", params, 200, data); + + // Set up a QSignalSpy which stores the result. + QSignalSpy spy(&musicbrainz_client, + SIGNAL(Finished(const QString&, const QString, + const MusicBrainzClient::ResultList&))); + ASSERT_TRUE(spy.isValid()); + EXPECT_EQ(0, spy.count()); + + // Start the request and get a result. The argument doesn't matter + // in the test. + musicbrainz_client.StartDiscIdRequest("fooDiscid"); + discid_reply->Done(); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + EXPECT_EQ(1, spy.count()); + + QList result = spy.takeFirst(); + QString artist = result.takeFirst().toString(); + QString album = result.takeFirst().toString(); + ResultList tracks = result.takeFirst().value(); + + // Check that title and artist are empty, and that there are zero tracks. + EXPECT_TRUE(artist.isEmpty()); + EXPECT_TRUE(album.isEmpty()); + EXPECT_EQ(0, tracks.count()); +} + +// Test if MusicBrainzClient::StartDiscIdRequest() parses a discid +// correctly. +TEST_F(MusicBrainzClientTest, ParseDiscID) { + QByteArray data = ReadDataFromFile(":testdata/discid_2cd.xml"); + ASSERT_FALSE(data.isEmpty()); + + // The following are the expected values given for the test file + // discid_2cd.xml. The discid corresponds to the 2nd disc in the + // set. The test file contains two releases but we only parse the first. + const QString expected_artist = "Symphony X"; + const QString expected_title = "Live on the Edge of Forever"; + const int expected_number_of_tracks = 6; + + // Create a MusicBrainzClient instance with mock_network_. + MusicBrainzClient musicbrainz_client(nullptr, mock_network_); + + // Hook the data as the response to a query of a given type. + QMap params; + params["inc"] = "artists+recordings"; + MockNetworkReply* discid_reply = + mock_network_->ExpectGet("discid", params, 200, data); + + // Set up a QSignalSpy which stores the result. + QSignalSpy spy(&musicbrainz_client, + SIGNAL(Finished(const QString&, const QString, + const MusicBrainzClient::ResultList&))); + ASSERT_TRUE(spy.isValid()); + EXPECT_EQ(0, spy.count()); + + // Start the request and get a result. The argument doesn't matter + // in the test. It is here set to the discid of the requested disc. + musicbrainz_client.StartDiscIdRequest("lvcH9_vbw_rJAbXieTOo1CbyNmQ-"); + discid_reply->Done(); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + EXPECT_EQ(1, spy.count()); + + QList result = spy.takeFirst(); + QString artist = result.takeFirst().toString(); + QString album = result.takeFirst().toString(); + ResultList tracks = result.takeFirst().value(); + + // Check that title and artist are correct. + EXPECT_EQ(expected_artist, artist); + EXPECT_EQ(expected_title, album); + + // Check that we get the correct number of tracks, i.e. that the + // correct disc is chosen in a multi-disc release. + EXPECT_EQ(expected_number_of_tracks, tracks.count()); + + // Check that the tracks is ordered by track number in ascending + // order. + for (int i = 0; i < tracks.count(); ++i) { + EXPECT_EQ(i + 1, tracks[i].track_); + } + + // Check some track information. + EXPECT_EQ("Smoke and Mirrors", tracks[0].title_); + EXPECT_EQ(1, tracks[0].track_); + EXPECT_EQ(394600, tracks[0].duration_msec_); + + EXPECT_EQ("Church of the Machine", tracks[1].title_); + EXPECT_EQ(2, tracks[1].track_); + EXPECT_EQ(441866, tracks[1].duration_msec_); +} + +// Test if MusicBrainzClient::Start() parses a track correctly. +TEST_F(MusicBrainzClientTest, ParseTrack) { + QByteArray data = ReadDataFromFile(":testdata/recording.xml"); + ASSERT_FALSE(data.isEmpty()); + + // Expected results from the test file recording.xml: + const int expected_track_number = 6; + const QString expected_title = "Victoria und ihr Husar: Pardon Madame"; + const QString expected_artist = "Paul Abraham"; + const QString expected_album = "An Evening at the Operetta"; + + // Create a MusicBrainzClient instance with mock_network_. + MusicBrainzClient musicbrainz_client(nullptr, mock_network_); + + // Hook the data as the response to a query of a given type. + QMap params; + params["inc"] = "artists+releases+media"; + MockNetworkReply* discid_reply = + mock_network_->ExpectGet("recording", params, 200, data); + + QSignalSpy spy(&musicbrainz_client, + SIGNAL(Finished(int, const MusicBrainzClient::ResultList&))); + ASSERT_TRUE(spy.isValid()); + EXPECT_EQ(0, spy.count()); + + // Start the request and get a result. + // The mbid argument doesn't matter in the test. + const int sent_id = 0; + musicbrainz_client.Start(sent_id, "fooMbid"); + discid_reply->Done(); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + EXPECT_EQ(1, spy.count()); + + QList result = spy.takeFirst(); + int id = result.takeFirst().toInt(); + EXPECT_EQ(sent_id, id); + + ResultList tracks = result.takeFirst().value(); + for (const MusicBrainzClient::Result& track : tracks) { + EXPECT_EQ(expected_track_number, track.track_); + EXPECT_EQ(expected_title, track.title_); + EXPECT_EQ(expected_artist, track.artist_); + EXPECT_EQ(expected_album, track.album_); + } +} + +// For a recording with multiple releases, we should get them all. +TEST_F(MusicBrainzClientTest, ParseTrackWithMultipleReleases) { + QByteArray data = + ReadDataFromFile(":testdata/recording_with_multiple_releases.xml"); + ASSERT_FALSE(data.isEmpty()); + + const int expected_number_of_releases = 7; + + // Create a MusicBrainzClient instance with mock_network_. + MusicBrainzClient musicbrainz_client(nullptr, mock_network_); + + // Hook the data as the response to a query of a given type. + QMap params; + params["inc"] = "artists+releases+media"; + MockNetworkReply* discid_reply = + mock_network_->ExpectGet("recording", params, 200, data); + + QSignalSpy spy(&musicbrainz_client, + SIGNAL(Finished(int, const MusicBrainzClient::ResultList&))); + ASSERT_TRUE(spy.isValid()); + EXPECT_EQ(0, spy.count()); + + // Start the request and get a result. + // The mbid argument doesn't matter in the test. + const int sent_id = 0; + musicbrainz_client.Start(sent_id, "fooMbid"); + discid_reply->Done(); + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + EXPECT_EQ(1, spy.count()); + + QList result = spy.takeFirst(); + int id = result.takeFirst().toInt(); + EXPECT_EQ(sent_id, id); + + ResultList tracks = result.takeFirst().value(); + EXPECT_EQ(expected_number_of_releases, tracks.count()); +}