diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index f44f547c..98794a37 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,332 +1,346 @@ enable_testing() include_directories(${elisa_BINARY_DIR}) set(playListTest_SOURCES ../src/mediaplaylist.cpp ../src/playlistcontroler.cpp ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/trackslistener.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/file/filelistener.cpp ../src/file/localfilelisting.cpp playlisttests.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(playListTest_SOURCES ${playListTest_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() if (UPNPQT_FOUND) set(playListTest_SOURCES ${playListTest_SOURCES} ../src/upnp/upnpcontrolcontentdirectory.cpp ../src/upnp/upnpcontentdirectorymodel.cpp ../src/upnp/upnpcontrolconnectionmanager.cpp ../src/upnp/upnpcontrolmediaserver.cpp ../src/upnp/didlparser.cpp ../src/upnp/upnplistener.cpp ../src/upnp/upnpdiscoverallmusic.cpp ) endif() add_executable(playListTest ${playListTest_SOURCES}) target_link_libraries(playListTest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) if (KF5Baloo_FOUND) target_link_libraries(playListTest KF5::Baloo Qt5::DBus) endif() if (KF5FileMetaData_FOUND) target_link_libraries(playListTest KF5::FileMetaData) endif() if (UPNPQT_FOUND) target_link_libraries(playListTest Qt5::Xml UPNP::upnpQt) endif() target_include_directories(playListTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(playListTest playListTest) set(databaseInterfaceTest_SOURCES ../src/databaseinterface.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp databaseinterfacetest.cpp ) add_executable(databaseInterfaceTest ${databaseInterfaceTest_SOURCES}) target_link_libraries(databaseInterfaceTest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) target_include_directories(databaseInterfaceTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(databaseInterfaceTest databaseInterfaceTest) set(playListControlerTest_SOURCES ../src/playlistcontroler.cpp ../src/mediaplaylist.cpp ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/trackslistener.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/file/filelistener.cpp ../src/file/localfilelisting.cpp playlistcontrolertest.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(playListControlerTest_SOURCES ${playListControlerTest_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() if (UPNPQT_FOUND) set(playListControlerTest_SOURCES ${playListControlerTest_SOURCES} ../src/upnp/upnpcontrolcontentdirectory.cpp ../src/upnp/upnpcontentdirectorymodel.cpp ../src/upnp/upnpcontrolconnectionmanager.cpp ../src/upnp/upnpcontrolmediaserver.cpp ../src/upnp/didlparser.cpp ../src/upnp/upnplistener.cpp ../src/upnp/upnpdiscoverallmusic.cpp ) endif() add_executable(playListControlerTest ${playListControlerTest_SOURCES}) target_link_libraries(playListControlerTest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) if (KF5Baloo_FOUND) target_link_libraries(playListControlerTest KF5::Baloo Qt5::DBus) endif() if (KF5FileMetaData_FOUND) target_link_libraries(playListControlerTest KF5::FileMetaData) endif() if (UPNPQT_FOUND) target_link_libraries(playListControlerTest Qt5::Xml UPNP::upnpQt) endif() target_include_directories(playListControlerTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(playListControlerTest playListControlerTest) set(managemediaplayercontrolTest_SOURCES ../src/managemediaplayercontrol.cpp ../src/mediaplaylist.cpp ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/trackslistener.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/file/filelistener.cpp ../src/file/localfilelisting.cpp managemediaplayercontroltest.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(managemediaplayercontrolTest_SOURCES ${managemediaplayercontrolTest_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() if (UPNPQT_FOUND) set(managemediaplayercontrolTest_SOURCES ${managemediaplayercontrolTest_SOURCES} ../src/upnp/upnpcontrolcontentdirectory.cpp ../src/upnp/upnpcontentdirectorymodel.cpp ../src/upnp/upnpcontrolconnectionmanager.cpp ../src/upnp/upnpcontrolmediaserver.cpp ../src/upnp/didlparser.cpp ../src/upnp/upnplistener.cpp ../src/upnp/upnpdiscoverallmusic.cpp ) endif() add_executable(managemediaplayercontrolTest ${managemediaplayercontrolTest_SOURCES}) target_link_libraries(managemediaplayercontrolTest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) if (KF5Baloo_FOUND) target_link_libraries(managemediaplayercontrolTest KF5::Baloo Qt5::DBus) endif() if (KF5FileMetaData_FOUND) target_link_libraries(managemediaplayercontrolTest KF5::FileMetaData) endif() if (UPNPQT_FOUND) target_link_libraries(managemediaplayercontrolTest Qt5::Xml UPNP::upnpQt) endif() target_include_directories(managemediaplayercontrolTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(managemediaplayercontrolTest managemediaplayercontrolTest) set(manageheaderbarTest_SOURCES ../src/manageheaderbar.cpp ../src/mediaplaylist.cpp ../src/databaseinterface.cpp ../src/musiclistenersmanager.cpp ../src/trackslistener.cpp ../src/trackslistener.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/file/filelistener.cpp ../src/file/localfilelisting.cpp manageheaderbartest.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(manageheaderbarTest_SOURCES ${manageheaderbarTest_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() if (UPNPQT_FOUND) set(manageheaderbarTest_SOURCES ${manageheaderbarTest_SOURCES} ../src/upnp/upnpcontrolcontentdirectory.cpp ../src/upnp/upnpcontentdirectorymodel.cpp ../src/upnp/upnpcontrolconnectionmanager.cpp ../src/upnp/upnpcontrolmediaserver.cpp ../src/upnp/didlparser.cpp ../src/upnp/upnplistener.cpp ../src/upnp/upnpdiscoverallmusic.cpp ) endif() add_executable(manageheaderbarTest ${manageheaderbarTest_SOURCES}) target_link_libraries(manageheaderbarTest Qt5::Test Qt5::Core Qt5::Sql Qt5::Gui KF5::I18n) if (KF5Baloo_FOUND) target_link_libraries(manageheaderbarTest KF5::Baloo Qt5::DBus) endif() if (KF5FileMetaData_FOUND) target_link_libraries(manageheaderbarTest KF5::FileMetaData) endif() if (UPNPQT_FOUND) target_link_libraries(manageheaderbarTest Qt5::Xml UPNP::upnpQt) endif() target_include_directories(manageheaderbarTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(manageheaderbarTest manageheaderbarTest) set(manageaudioplayerTest_SOURCES ../src/manageaudioplayer.cpp manageaudioplayertest.cpp ) add_executable(manageaudioplayerTest ${manageaudioplayerTest_SOURCES}) target_link_libraries(manageaudioplayerTest Qt5::Test Qt5::Core Qt5::Gui) target_include_directories(manageaudioplayerTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(manageaudioplayerTest manageaudioplayerTest) set(mediaplaylistTest_SOURCES ../src/mediaplaylist.cpp ../src/databaseinterface.cpp ../src/trackslistener.cpp ../src/musiclistenersmanager.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/file/filelistener.cpp ../src/file/localfilelisting.cpp mediaplaylisttest.cpp ) if (KF5Baloo_FOUND) if (Qt5DBus_FOUND) set(mediaplaylistTest_SOURCES ${mediaplaylistTest_SOURCES} ../src/baloo/baloolistener.cpp ../src/baloo/localbaloofilelisting.cpp ) endif() endif() if (UPNPQT_FOUND) set(mediaplaylistTest_SOURCES ${mediaplaylistTest_SOURCES} ../src/upnp/upnpcontrolcontentdirectory.cpp ../src/upnp/upnpcontentdirectorymodel.cpp ../src/upnp/upnpcontrolconnectionmanager.cpp ../src/upnp/upnpcontrolmediaserver.cpp ../src/upnp/didlparser.cpp ../src/upnp/upnplistener.cpp ../src/upnp/upnpdiscoverallmusic.cpp ) endif() add_executable(mediaplaylistTest ${mediaplaylistTest_SOURCES}) target_link_libraries(mediaplaylistTest Qt5::Test Qt5::Core Qt5::Sql Qt5::Gui KF5::I18n) if (KF5Baloo_FOUND) target_link_libraries(mediaplaylistTest KF5::Baloo Qt5::DBus) endif() if (KF5FileMetaData_FOUND) target_link_libraries(mediaplaylistTest KF5::FileMetaData) endif() if (UPNPQT_FOUND) target_link_libraries(mediaplaylistTest Qt5::Xml UPNP::upnpQt) endif() target_include_directories(mediaplaylistTest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(mediaplaylistTest mediaplaylistTest) set(allalbumsmodeltest_SOURCES ../src/databaseinterface.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/allalbumsmodel.cpp allalbumsmodeltest.cpp ) add_executable(allalbumsmodeltest ${allalbumsmodeltest_SOURCES}) target_link_libraries(allalbumsmodeltest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) target_include_directories(allalbumsmodeltest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(allalbumsmodeltest allalbumsmodeltest) set(albummodeltest_SOURCES ../src/databaseinterface.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/albummodel.cpp albummodeltest.cpp ) add_executable(albummodeltest ${albummodeltest_SOURCES}) target_link_libraries(albummodeltest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) target_include_directories(albummodeltest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(albummodeltest albummodeltest) set(allartistsmodeltest_SOURCES ../src/databaseinterface.cpp ../src/musicartist.cpp ../src/musicalbum.cpp ../src/musicaudiotrack.cpp ../src/allartistsmodel.cpp allartistsmodeltest.cpp ) add_executable(allartistsmodeltest ${allartistsmodeltest_SOURCES}) target_link_libraries(allartistsmodeltest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) target_include_directories(allartistsmodeltest PRIVATE ${CMAKE_SOURCE_DIR}/src) add_test(allartistsmodeltest allartistsmodeltest) + +set(localfilelistingtest_SOURCES + ../src/file/localfilelisting.cpp + ../src/musicaudiotrack.cpp + localfilelistingtest.cpp +) + +add_executable(localfilelistingtest ${localfilelistingtest_SOURCES}) +target_link_libraries(localfilelistingtest Qt5::Test Qt5::Core Qt5::Sql KF5::I18n) +if (KF5FileMetaData_FOUND) + target_link_libraries(localfilelistingtest KF5::FileMetaData) +endif() +target_include_directories(localfilelistingtest PRIVATE ${CMAKE_SOURCE_DIR}/src) +add_test(localfilelistingtest localfilelistingtest) diff --git a/autotests/data/music/cover.jpg b/autotests/data/music/cover.jpg new file mode 100644 index 00000000..5dff3d50 Binary files /dev/null and b/autotests/data/music/cover.jpg differ diff --git a/autotests/data/music/test.m4a b/autotests/data/music/test.m4a new file mode 100644 index 00000000..1bd94652 Binary files /dev/null and b/autotests/data/music/test.m4a differ diff --git a/autotests/data/music/test.mp3 b/autotests/data/music/test.mp3 new file mode 100644 index 00000000..addf5329 Binary files /dev/null and b/autotests/data/music/test.mp3 differ diff --git a/autotests/data/music/test.ogg b/autotests/data/music/test.ogg new file mode 100644 index 00000000..5ac45aa1 Binary files /dev/null and b/autotests/data/music/test.ogg differ diff --git a/autotests/localfilelistingtest.cpp b/autotests/localfilelistingtest.cpp new file mode 100644 index 00000000..4da883c2 --- /dev/null +++ b/autotests/localfilelistingtest.cpp @@ -0,0 +1,341 @@ +/* + * Copyright 2015-2017 Matthieu Gallien + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "file/localfilelisting.h" +#include "musicaudiotrack.h" + +#include "config-upnp-qt.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +class LocalFileListingTests: public QObject +{ + Q_OBJECT + +private: + + QHash> mNewTracks; + QHash mNewCovers; + +private Q_SLOTS: + + void initTestCase() + { + qRegisterMetaType>("QHash"); + qRegisterMetaType>("QHash"); + qRegisterMetaType>>("QHash>"); + qRegisterMetaType>("QVector"); + qRegisterMetaType>("QHash"); + qRegisterMetaType>("QList"); + } + + void initialTestWithNoTrack() + { + LocalFileListing myListing; + + QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); + QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); + QSignalSpy rootPathChangedSpy(&myListing, &LocalFileListing::rootPathChanged); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.init(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.setRootPath(QStringLiteral("/directoryNotExist")); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QCOMPARE(myListing.rootPath(), QStringLiteral("/directoryNotExist")); + + myListing.refreshContent(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + } + + void initialTestWithTracks() + { + LocalFileListing myListing; + + QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); + + QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); + QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); + QSignalSpy rootPathChangedSpy(&myListing, &LocalFileListing::rootPathChanged); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.init(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.setRootPath(musicPath); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QCOMPARE(myListing.rootPath(), musicPath); + + myListing.refreshContent(); + + QCOMPARE(tracksListSpy.count(), 1); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto newTracksSignal = tracksListSpy.at(0); + auto newTracks = newTracksSignal.at(0).value>>(); + auto newCovers = newTracksSignal.at(1).value>(); + + QCOMPARE(newTracks.count(), 1); + QCOMPARE(newTracks[newTracks.keys()[0]].count(), 3); + QCOMPARE(newCovers.count(), 1); + } + + void addAndRemoveTracks() + { + LocalFileListing myListing; + + QString musicOriginPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); + + QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data/innerData"); + QDir musicDirectory(musicPath); + + QString musicParentPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2"); + QDir musicParentDirectory(musicParentPath); + QDir rootDirectory(QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH)); + + musicParentDirectory.removeRecursively(); + rootDirectory.mkpath(QStringLiteral("music2/data/innerData")); + + QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); + QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); + QSignalSpy rootPathChangedSpy(&myListing, &LocalFileListing::rootPathChanged); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.init(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.setRootPath(musicParentPath); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QCOMPARE(myListing.rootPath(), musicParentPath); + + myListing.refreshContent(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QFile myTrack(musicOriginPath + QStringLiteral("/test.ogg")); + myTrack.copy(musicPath + QStringLiteral("/test.ogg")); + QFile myCover(musicOriginPath + QStringLiteral("/cover.jpg")); + myCover.copy(musicPath + QStringLiteral("/cover.jpg")); + + QCOMPARE(tracksListSpy.wait(), true); + + QCOMPARE(tracksListSpy.count(), 1); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto newTracksSignal = tracksListSpy.at(0); + auto newTracks = newTracksSignal.at(0).value>>(); + auto newCovers = newTracksSignal.at(1).value>(); + + QCOMPARE(newTracks.count(), 1); + QCOMPARE(newTracks[newTracks.keys()[0]].count(), 1); + QCOMPARE(newCovers.count(), 1); + + QString commandLine(QStringLiteral("rm -rf ") + musicPath); + system(commandLine.toLatin1().data()); + + QCOMPARE(removedTracksListSpy.wait(), true); + + QCOMPARE(tracksListSpy.count(), 1); + QCOMPARE(removedTracksListSpy.count(), 1); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto removeSignal = removedTracksListSpy.at(0); + auto removedTracks = removeSignal.at(0).value>(); + QCOMPARE(removedTracks.isEmpty(), false); + + QCOMPARE(rootDirectory.mkpath(QStringLiteral("music2/data/innerData")), true); + QCOMPARE(myTrack.copy(musicPath + QStringLiteral("/test.ogg")), true); + QCOMPARE(myCover.copy(musicPath + QStringLiteral("/cover.jpg")), true); + + if (tracksListSpy.count() == 1) { + QCOMPARE(tracksListSpy.wait(), true); + } + + QCOMPARE(tracksListSpy.count(), 2); + QCOMPARE(removedTracksListSpy.count(), 1); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto newTracksSignalLast = tracksListSpy.at(1); + auto newTracksLast = newTracksSignalLast.at(0).value>>(); + auto newCoversLast = newTracksSignalLast.at(1).value>(); + + QCOMPARE(newTracksLast.count(), 1); + QCOMPARE(newTracksLast[newTracksLast.keys()[0]].count(), 1); + QCOMPARE(newCoversLast.count(), 1); + } + + void addAndMoveTracks() + { + LocalFileListing myListing; + + QString musicOriginPath = QStringLiteral(LOCAL_FILE_TESTS_SAMPLE_FILES_PATH) + QStringLiteral("/music"); + + QString musicPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2/data/innerData"); + QDir musicDirectory(musicPath); + + QString musicParentPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music2"); + QDir musicParentDirectory(musicParentPath); + + QString musicFriendPath = QStringLiteral(LOCAL_FILE_TESTS_WORKING_PATH) + QStringLiteral("/music3"); + QDir musicFriendDirectory(musicFriendPath); + + QCOMPARE(musicFriendDirectory.removeRecursively(), true); + QCOMPARE(musicParentDirectory.removeRecursively(), true); + + musicFriendDirectory.mkpath(musicFriendPath); + musicDirectory.mkpath(musicPath); + + QSignalSpy tracksListSpy(&myListing, &LocalFileListing::tracksList); + QSignalSpy removedTracksListSpy(&myListing, &LocalFileListing::removedTracksList); + QSignalSpy rootPathChangedSpy(&myListing, &LocalFileListing::rootPathChanged); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.init(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 0); + + myListing.setRootPath(musicParentPath); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QCOMPARE(myListing.rootPath(), musicParentPath); + + myListing.refreshContent(); + + QCOMPARE(tracksListSpy.count(), 0); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + QFile myTrack(musicOriginPath + QStringLiteral("/test.ogg")); + myTrack.copy(musicPath + QStringLiteral("/test.ogg")); + QFile myCover(musicOriginPath + QStringLiteral("/cover.jpg")); + myCover.copy(musicPath + QStringLiteral("/cover.jpg")); + + QCOMPARE(tracksListSpy.wait(), true); + + QCOMPARE(tracksListSpy.count(), 1); + QCOMPARE(removedTracksListSpy.count(), 0); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto newTracksSignal = tracksListSpy.at(0); + auto newTracks = newTracksSignal.at(0).value>>(); + auto newCovers = newTracksSignal.at(1).value>(); + + QCOMPARE(newTracks.count(), 1); + QCOMPARE(newTracks[newTracks.keys()[0]].count(), 1); + QCOMPARE(newCovers.count(), 1); + + QString commandLine(QStringLiteral("mv ") + musicPath + QStringLiteral(" ") + musicFriendPath); + system(commandLine.toLatin1().data()); + + QCOMPARE(removedTracksListSpy.wait(), true); + + QCOMPARE(tracksListSpy.count(), 1); + QCOMPARE(removedTracksListSpy.count(), 1); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto removeSignal = removedTracksListSpy.at(0); + auto removedTracks = removeSignal.at(0).value>(); + QCOMPARE(removedTracks.isEmpty(), false); + + QCOMPARE(musicFriendDirectory.mkpath(musicFriendPath), true); + QCOMPARE(musicDirectory.mkpath(musicPath), true); + QCOMPARE(myTrack.copy(musicPath + QStringLiteral("/test.ogg")), true); + QCOMPARE(myCover.copy(musicPath + QStringLiteral("/cover.jpg")), true); + + if (tracksListSpy.count() == 1) { + QCOMPARE(tracksListSpy.wait(), true); + } + + QCOMPARE(tracksListSpy.count(), 2); + QCOMPARE(removedTracksListSpy.count(), 1); + QCOMPARE(rootPathChangedSpy.count(), 1); + + auto newTracksSignalLast = tracksListSpy.at(1); + auto newTracksLast = newTracksSignalLast.at(0).value>>(); + auto newCoversLast = newTracksSignalLast.at(1).value>(); + + QCOMPARE(newTracksLast.count(), 1); + QCOMPARE(newTracksLast[newTracksLast.keys()[0]].count(), 1); + QCOMPARE(newCoversLast.count(), 1); + } +}; + +QTEST_MAIN(LocalFileListingTests) + + +#include "localfilelistingtest.moc" diff --git a/config-upnp-qt.h.cmake b/config-upnp-qt.h.cmake index 8ec85efd..71c59f45 100644 --- a/config-upnp-qt.h.cmake +++ b/config-upnp-qt.h.cmake @@ -1,17 +1,21 @@ #cmakedefine01 KF5Declarative_FOUND #cmakedefine01 KF5I18n_FOUND #cmakedefine01 KF5CoreAddons_FOUND #cmakedefine01 KF5Baloo_FOUND #cmakedefine01 KF5XmlGui_FOUND #cmakedefine01 KF5ConfigWidgets_FOUND #cmakedefine01 KF5Config_FOUND #cmakedefine01 UPNPQT_FOUND #cmakedefine01 Qt5DBus_FOUND + +#define LOCAL_FILE_TESTS_SAMPLE_FILES_PATH "@CMAKE_CURRENT_SOURCE_DIR@/autotests/data" + +#define LOCAL_FILE_TESTS_WORKING_PATH "@CMAKE_CURRENT_BINARY_DIR@/autotests/data" diff --git a/src/file/localfilelisting.cpp b/src/file/localfilelisting.cpp index a0293018..bb13ea05 100644 --- a/src/file/localfilelisting.cpp +++ b/src/file/localfilelisting.cpp @@ -1,299 +1,355 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "localfilelisting.h" #include "musicaudiotrack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class LocalFileListingPrivate { public: QString mRootPath; QFileSystemWatcher mFileSystemWatcher; QHash> mAllAlbums; QHash mAllAlbumCover; QHash mAlbumNameFromTrackFile; int mCptTracks = 0; QHash> mDiscoveredFiles; + QHash> mDiscoveredDirectories; + + bool mNewTracks = false; + }; LocalFileListing::LocalFileListing(QObject *parent) : QObject(parent), d(new LocalFileListingPrivate) { connect(&d->mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &LocalFileListing::directoryChanged); connect(&d->mFileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LocalFileListing::fileChanged); const auto &musicLocations(QStandardPaths::standardLocations(QStandardPaths::MusicLocation)); if (musicLocations.isEmpty()) { return; } d->mRootPath = musicLocations.first(); } LocalFileListing::~LocalFileListing() { } +QString LocalFileListing::rootPath() const +{ + return d->mRootPath; +} + void LocalFileListing::init() { } +void LocalFileListing::setRootPath(QString rootPath) +{ + if (d->mRootPath == rootPath) { + return; + } + + d->mRootPath = rootPath; + Q_EMIT rootPathChanged(); +} + void LocalFileListing::scanDirectory(const QString &path) { QDir rootDirectory(path); + rootDirectory.refresh(); - d->mFileSystemWatcher.addPath(path); + if (rootDirectory.exists()) { + d->mFileSystemWatcher.addPath(path); + } - auto ¤tDirectoryListing = d->mDiscoveredFiles[path]; + auto ¤tDirectoryListingFiles = d->mDiscoveredFiles[path]; + auto ¤tDirectoryListingDirectories = d->mDiscoveredDirectories[path]; auto currentFilesList = QList(); + rootDirectory.refresh(); const auto entryList = rootDirectory.entryInfoList(); for (auto oneEntry : entryList) { auto newFilePath = oneEntry.canonicalFilePath(); if (newFilePath == path) { continue; } if (newFilePath.size() < path.size()) { continue; } - if (oneEntry.isDir()) { - if (d->mDiscoveredFiles.find(newFilePath) == d->mDiscoveredFiles.end()) { - scanDirectory(newFilePath); - } - continue; - } - if (oneEntry.isFile()) { + if (oneEntry.isDir() || oneEntry.isFile()) { currentFilesList.push_back(QUrl::fromLocalFile(newFilePath)); } } + for (const auto &removedDirectoryPath : currentDirectoryListingDirectories) { + auto itFilePath = std::find(currentFilesList.begin(), currentFilesList.end(), removedDirectoryPath); + + if (itFilePath != currentFilesList.end()) { + continue; + } + + scanDirectory(removedDirectoryPath.toLocalFile()); + + auto itRemovedDirectory = std::find(currentDirectoryListingDirectories.begin(), currentDirectoryListingDirectories.end(), removedDirectoryPath); + currentDirectoryListingDirectories.erase(itRemovedDirectory); + + auto itRemovedFullDirectory = d->mDiscoveredDirectories.find(removedDirectoryPath.toLocalFile()); + d->mDiscoveredDirectories.erase(itRemovedFullDirectory); + } auto removedTracks = QList(); - for (const auto &removedFilePath : currentDirectoryListing) { + for (const auto &removedFilePath : currentDirectoryListingFiles) { auto itFilePath = std::find(currentFilesList.begin(), currentFilesList.end(), removedFilePath); if (itFilePath != currentFilesList.end()) { continue; } removedTracks.push_back(removedFilePath); } for (const auto &oneRemovedTrack : removedTracks) { auto itAlbumName = d->mAlbumNameFromTrackFile.find(oneRemovedTrack); if (itAlbumName != d->mAlbumNameFromTrackFile.end()) { auto itAlbum = d->mAllAlbums.find(*itAlbumName); if (itAlbum != d->mAllAlbums.end()) { for (auto itTrack = itAlbum->begin(); itTrack != itAlbum->end(); ) { if (itTrack->resourceURI() == oneRemovedTrack) { itTrack = itAlbum->erase(itTrack); } else { ++itTrack; } } } if (itAlbum->isEmpty()) { d->mAllAlbums.erase(itAlbum); } } - auto itRemovedTrack = std::find(currentDirectoryListing.begin(), currentDirectoryListing.end(), oneRemovedTrack); - currentDirectoryListing.erase(itRemovedTrack); + auto itRemovedTrack = std::find(currentDirectoryListingFiles.begin(), currentDirectoryListingFiles.end(), oneRemovedTrack); + currentDirectoryListingFiles.erase(itRemovedTrack); } if (!removedTracks.isEmpty()) { Q_EMIT removedTracksList(removedTracks); } for (auto newFilePath : currentFilesList) { - auto itFilePath = std::find(currentDirectoryListing.begin(), currentDirectoryListing.end(), newFilePath); + auto itFilePath = std::find(currentDirectoryListingFiles.begin(), currentDirectoryListingFiles.end(), newFilePath); - if (itFilePath != currentDirectoryListing.end()) { + if (itFilePath != currentDirectoryListingFiles.end()) { continue; } - currentDirectoryListing.push_back(newFilePath); + QFileInfo oneEntry(newFilePath.toLocalFile()); + if (oneEntry.isDir()) { + if (d->mDiscoveredDirectories.find(newFilePath.toLocalFile()) == d->mDiscoveredDirectories.end()) { + currentDirectoryListingDirectories.push_back(newFilePath); + scanDirectory(newFilePath.toLocalFile()); + } + continue; + } + if (!oneEntry.isFile()) { + continue; + } + + currentDirectoryListingFiles.push_back(newFilePath); auto newTrack = scanOneFile(newFilePath); if (newTrack.isValid()) { QFileInfo trackFilePath(newTrack.resourceURI().toLocalFile()); QFileInfo coverFilePath(trackFilePath.dir().filePath(QStringLiteral("cover.jpg"))); if (coverFilePath.exists()) { d->mAllAlbumCover[newTrack.albumName()] = QUrl::fromLocalFile(coverFilePath.absoluteFilePath()); } auto &allTracks = d->mAllAlbums[newTrack.albumName()]; + d->mNewTracks = true; allTracks.push_back(newTrack); d->mAlbumNameFromTrackFile[newTrack.resourceURI()] = newTrack.albumName(); } } + if (currentDirectoryListingDirectories.isEmpty()) { + auto itRemovedFullDirectory = d->mDiscoveredDirectories.find(path); + d->mDiscoveredDirectories.erase(itRemovedFullDirectory); + } } void LocalFileListing::directoryChanged(const QString &path) { + d->mNewTracks = false; + scanDirectory(path); - Q_EMIT tracksList(d->mAllAlbums, d->mAllAlbumCover); + if (d->mNewTracks) { + Q_EMIT tracksList(d->mAllAlbums, d->mAllAlbumCover); + } } void LocalFileListing::fileChanged(const QString &modifiedFileName) { auto modifiedFile = QUrl::fromLocalFile(modifiedFileName); auto itAlbumName = d->mAlbumNameFromTrackFile.find(modifiedFile); if (itAlbumName != d->mAlbumNameFromTrackFile.end()) { auto itAlbum = d->mAllAlbums.find(*itAlbumName); if (itAlbum != d->mAllAlbums.end()) { for (auto itTrack : *itAlbum) { if (itTrack.resourceURI() == modifiedFile) { auto modifiedTrack = scanOneFile(modifiedFile); if (modifiedTrack.isValid()) { auto removedTracks = QList{modifiedFile}; Q_EMIT removedTracksList(removedTracks); QHash> modifiedAlbum; auto ¤tAlbum = modifiedAlbum[modifiedTrack.albumName()]; currentAlbum.push_back(modifiedTrack); Q_EMIT tracksList(modifiedAlbum, d->mAllAlbumCover); } break; } } } } } void LocalFileListing::refreshContent() { - scanDirectory(d->mRootPath); - Q_EMIT tracksList(d->mAllAlbums, d->mAllAlbumCover); + directoryChanged(d->mRootPath); } MusicAudioTrack LocalFileListing::scanOneFile(QUrl scanFile) { MusicAudioTrack newTrack; QMimeDatabase mimeDb; QString mimetype = mimeDb.mimeTypeForFile(scanFile.toLocalFile()).name(); - d->mFileSystemWatcher.addPath(scanFile.toLocalFile()); + QFileInfo scanFileInfo(scanFile.toLocalFile()); + if (scanFileInfo.exists()) { + d->mFileSystemWatcher.addPath(scanFile.toLocalFile()); + } KFileMetaData::ExtractorCollection extractors; QList exList = extractors.fetchExtractors(mimetype); if (exList.isEmpty()) { qDebug() << "LocalFileListing::scanOneFile" << "empty extractors list" << scanFile; return newTrack; } KFileMetaData::Extractor* ex = exList.first(); KFileMetaData::SimpleExtractionResult result(scanFile.toLocalFile(), mimetype, KFileMetaData::ExtractionResult::ExtractMetaData); ex->extract(&result); const auto &allProperties = result.properties(); auto titleProperty = allProperties.find(KFileMetaData::Property::Title); auto durationProperty = allProperties.find(KFileMetaData::Property::Duration); auto artistProperty = allProperties.find(KFileMetaData::Property::Artist); auto albumProperty = allProperties.find(KFileMetaData::Property::Album); auto albumArtistProperty = allProperties.find(KFileMetaData::Property::AlbumArtist); auto trackNumberProperty = allProperties.find(KFileMetaData::Property::TrackNumber); if (albumProperty != allProperties.end()) { auto albumValue = albumProperty->toString(); newTrack.setAlbumName(albumValue); ++d->mCptTracks; if (artistProperty != allProperties.end()) { newTrack.setArtist(artistProperty->toString()); } if (durationProperty != allProperties.end()) { newTrack.setDuration(QTime::fromMSecsSinceStartOfDay(1000 * durationProperty->toDouble())); } if (titleProperty != allProperties.end()) { newTrack.setTitle(titleProperty->toString()); } if (trackNumberProperty != allProperties.end()) { newTrack.setTrackNumber(trackNumberProperty->toInt()); } if (albumArtistProperty != allProperties.end()) { newTrack.setAlbumArtist(albumArtistProperty->toString()); } if (newTrack.albumArtist().isEmpty()) { newTrack.setAlbumArtist(newTrack.artist()); } if (newTrack.artist().isEmpty()) { newTrack.setArtist(newTrack.albumArtist()); } newTrack.setResourceURI(scanFile); newTrack.setValid(true); } return newTrack; } #include "moc_localfilelisting.cpp" diff --git a/src/file/localfilelisting.h b/src/file/localfilelisting.h index 26f3f026..8e03bb17 100644 --- a/src/file/localfilelisting.h +++ b/src/file/localfilelisting.h @@ -1,76 +1,87 @@ /* * Copyright 2016-2017 Matthieu Gallien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef LOCALFILELISTING_H #define LOCALFILELISTING_H #include #include #include #include #include #include class LocalFileListingPrivate; class MusicAudioTrack; class LocalFileListing : public QObject { Q_OBJECT + Q_PROPERTY(QString rootPath + READ rootPath + WRITE setRootPath + NOTIFY rootPathChanged) + public: explicit LocalFileListing(QObject *parent = 0); virtual ~LocalFileListing(); + QString rootPath() const; + Q_SIGNALS: void tracksList(const QHash> &tracks, const QHash &covers); void removedTracksList(const QList &removedTracks); + void rootPathChanged(); + public Q_SLOTS: void refreshContent(); void init(); + void setRootPath(QString rootPath); + private Q_SLOTS: void directoryChanged(const QString &path); void fileChanged(const QString &modifiedFileName); private: void scanDirectory(const QString &path); MusicAudioTrack scanOneFile(QUrl scanFile); std::unique_ptr d; }; #endif // LOCALFILELISTING_H