File indexing completed on 2024-11-10 04:22:06

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Maximilian Kossick <maximilian.kossick@googlemail.com>       *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "TestSqlCollectionLocation.h"
0018 
0019 #include "DatabaseUpdater.h"
0020 #include "core/support/Debug.h"
0021 #include "core/support/Components.h"
0022 #include "DefaultSqlQueryMakerFactory.h"
0023 #include "core/meta/Meta.h"
0024 #include "core-impl/storage/sql/mysqlestorage/MySqlEmbeddedStorage.h"
0025 #include "SqlCollection.h"
0026 #include "SqlCollectionLocation.h"
0027 #include "SqlRegistry.h"
0028 #include "SqlMountPointManagerMock.h"
0029 #include "core/collections/MockCollectionLocationDelegate.h"
0030 
0031 #include "config-amarok-test.h"
0032 
0033 #include <QMetaObject>
0034 #include <QProcess>
0035 #include <QTimer>
0036 
0037 #include <gmock/gmock.h>
0038 #include <KConfigGroup>
0039 
0040 using ::testing::AnyNumber;
0041 using ::testing::Return;
0042 using ::testing::_;
0043 
0044 /** A SqlCollectionLocation that claims writing is possible even though it doesn't have
0045  *  a valid directory.
0046  */
0047 class MySqlCollectionLocation : public Collections::SqlCollectionLocation
0048 {
0049 public:
0050     MySqlCollectionLocation( Collections::SqlCollection *coll ) : Collections::SqlCollectionLocation( coll ) {}
0051     ~MySqlCollectionLocation() override {}
0052 
0053     bool isWritable() const override { return true; }
0054 };
0055 
0056 class MyOrganizeCollectionDelegate : public OrganizeCollectionDelegate
0057 {
0058 public:
0059     MyOrganizeCollectionDelegate() : OrganizeCollectionDelegate(), overwrite( false ), migrate( false ) {}
0060     ~MyOrganizeCollectionDelegate() override {}
0061 
0062     void setTracks( const Meta::TrackList &tracks ) override { Q_UNUSED( tracks ) }
0063     void setFolders( const QStringList &folders ) override { Q_UNUSED( folders ) }
0064     void setIsOrganizing( bool organizing ) override { Q_UNUSED( organizing ) }
0065     void setTranscodingConfiguration(const Transcoding::Configuration &configuration) override
0066     { Q_UNUSED( configuration ) }
0067     void setCaption( const QString& ) override {}
0068 
0069     void show() override { emit accepted(); }
0070 
0071     bool overwriteDestinations() const override { return overwrite; }
0072     QMap<Meta::TrackPtr, QString> destinations() const override { return dests; }
0073     bool migrateLabels() const { return migrate; }
0074 
0075     bool overwrite;
0076     bool migrate;
0077     QMap<Meta::TrackPtr, QString> dests;
0078 };
0079 
0080 class MyOrganizeCollectionDelegateFactory : public OrganizeCollectionDelegateFactory
0081 {
0082 public:
0083     MyOrganizeCollectionDelegateFactory( OrganizeCollectionDelegate *d )
0084         : OrganizeCollectionDelegateFactory()
0085         , delegate( d ) {}
0086     ~MyOrganizeCollectionDelegateFactory() override {}
0087 
0088     //warning: SqlCollectionLocation will delete the delegate
0089     OrganizeCollectionDelegate* createDelegate() override { return delegate; }
0090 
0091     OrganizeCollectionDelegate *delegate;
0092 };
0093 
0094 
0095 
0096 QTEST_GUILESS_MAIN( TestSqlCollectionLocation )
0097 
0098 TestSqlCollectionLocation::TestSqlCollectionLocation()
0099     : QObject()
0100     , m_collection( nullptr )
0101     , m_storage( nullptr )
0102     , m_tmpDir( nullptr )
0103 {
0104     int argc = 1;
0105     char **argv = (char **) malloc(sizeof(char *));
0106     argv[0] = strdup( QCoreApplication::applicationName().toLocal8Bit().data() );
0107     ::testing::InitGoogleMock( &argc, argv );
0108 }
0109 
0110 void
0111 TestSqlCollectionLocation::initTestCase()
0112 {
0113     m_tmpDir = new QTemporaryDir();
0114     m_storage = QSharedPointer<MySqlEmbeddedStorage>( new MySqlEmbeddedStorage() );
0115     QVERIFY( m_storage->init( m_tmpDir->path() ) );
0116     m_collection = new Collections::SqlCollection( m_storage );
0117     SqlMountPointManagerMock *mock = new SqlMountPointManagerMock( this, m_storage );
0118     mock->setCollectionFolders( QStringList() << m_tmpDir->path() ); // the target folder needs to have enough space and be writable
0119     m_collection->setMountPointManager( mock );
0120 
0121     // I just need the table and not the whole playlist manager
0122     m_storage->query( QString( "CREATE TABLE playlist_tracks ("
0123             " id " + m_storage->idType() +
0124             ", playlist_id INTEGER "
0125             ", track_num INTEGER "
0126             ", url " + m_storage->exactTextColumnType() +
0127             ", title " + m_storage->textColumnType() +
0128             ", album " + m_storage->textColumnType() +
0129             ", artist " + m_storage->textColumnType() +
0130             ", length INTEGER "
0131             ", uniqueid " + m_storage->textColumnType(128) + ") ENGINE = MyISAM;" ) );
0132 }
0133 
0134 void
0135 TestSqlCollectionLocation::cleanupTestCase()
0136 {
0137     delete m_collection;
0138     //m_storage is deleted by SqlCollection
0139     delete m_tmpDir;
0140 }
0141 
0142 void
0143 TestSqlCollectionLocation::init()
0144 {
0145     //setup base data
0146     m_storage->query( "INSERT INTO artists(id, name) VALUES (1, 'artist1');" );
0147     m_storage->query( "INSERT INTO artists(id, name) VALUES (2, 'artist2');" );
0148     m_storage->query( "INSERT INTO artists(id, name) VALUES (3, 'artist3');" );
0149 
0150     m_storage->query( "INSERT INTO albums(id,name,artist) VALUES(1,'album1',1);" );
0151     m_storage->query( "INSERT INTO albums(id,name,artist) VALUES(2,'album2',1);" );
0152     m_storage->query( "INSERT INTO albums(id,name,artist) VALUES(3,'album3',2);" );
0153 
0154     m_storage->query( "INSERT INTO composers(id, name) VALUES (1, 'composer1');" );
0155     m_storage->query( "INSERT INTO genres(id, name) VALUES (1, 'genre1');" );
0156     m_storage->query( "INSERT INTO years(id, name) VALUES (1, '1');" );
0157 
0158     m_storage->query( "INSERT INTO directories(id,deviceid,dir) VALUES (1, -1, '." + m_tmpDir->path() + "/ab/')");
0159     m_storage->query( "INSERT INTO directories(id,deviceid,dir) VALUES (2, -1, '." + m_tmpDir->path() + "/b/')");
0160     m_storage->query( "INSERT INTO directories(id,deviceid,dir) VALUES (3, -1, '." + m_tmpDir->path() + "/c/')");
0161 
0162     m_storage->query( QString( "INSERT INTO urls(id, deviceid, rpath, uniqueid, directory ) VALUES (1, -1, '%1', 'uid://1', 1);" ).arg( setupFileInTempDir( "ab/IDoNotExist.mp3" ) ) );
0163     m_storage->query( QString( "INSERT INTO urls(id, deviceid, rpath, uniqueid, directory ) VALUES (2, -1, '%1', 'uid://2', 2);" ).arg( setupFileInTempDir( "b/IDoNotExistAsWell.mp3") ) );
0164     m_storage->query( QString( "INSERT INTO urls(id, deviceid, rpath, uniqueid, directory ) VALUES (3, -1, '%1', 'uid:/3', 3);" ).arg( setupFileInTempDir( "c/MeNeither.mp3" ) ) );
0165 
0166     m_storage->query( "INSERT INTO tracks(id,url,title,comment,artist,album,genre,year,composer) "
0167                       "VALUES(1,1,'track1','comment1',1,1,1,1,1);" );
0168     m_storage->query( "INSERT INTO tracks(id,url,title,comment,artist,album,genre,year,composer) "
0169                       "VALUES(2,2,'track2','comment2',1,2,1,1,1);" );
0170     m_storage->query( "INSERT INTO tracks(id,url,title,comment,artist,album,genre,year,composer) "
0171                       "VALUES(3,3,'track3','comment3',2,3,1,1,1);" );
0172 
0173     m_collection->registry()->emptyCache();
0174 }
0175 
0176 void
0177 TestSqlCollectionLocation::cleanup()
0178 {
0179     delete Amarok::Components::setCollectionLocationDelegate( nullptr );
0180     m_storage->query( "TRUNCATE TABLE years;" );
0181     m_storage->query( "TRUNCATE TABLE genres;" );
0182     m_storage->query( "TRUNCATE TABLE composers;" );
0183     m_storage->query( "TRUNCATE TABLE albums;" );
0184     m_storage->query( "TRUNCATE TABLE artists;" );
0185     m_storage->query( "TRUNCATE TABLE tracks;" );
0186     m_storage->query( "TRUNCATE TABLE urls;" );
0187     m_storage->query( "TRUNCATE TABLE labels;" );
0188     m_storage->query( "TRUNCATE TABLE urls_labels;" );
0189     m_storage->query( "TRUNCATE TABLE directories;" );
0190 }
0191 
0192 void
0193 TestSqlCollectionLocation::testOrganizingCopiesLabels()
0194 {
0195     {
0196         Collections::MockCollectionLocationDelegate *d = new Collections::MockCollectionLocationDelegate();
0197         EXPECT_CALL( *d, reallyMove( _, _ ) ).Times( AnyNumber() ).WillRepeatedly( Return( true ) );
0198         EXPECT_CALL( *d, transcode( _, _, _, _, _ ) ).WillOnce( Return(
0199                 Transcoding::Configuration( Transcoding::INVALID ) ) );
0200         Amarok::Components::setCollectionLocationDelegate( d );
0201     }
0202 
0203     {
0204         Meta::TrackPtr track = m_collection->registry()->getTrackFromUid( "uid://1" );
0205         QVERIFY( track );
0206         QVERIFY( track->playableUrl().path().endsWith( "ab/IDoNotExist.mp3" ) );
0207 
0208         track->addLabel( "test" );
0209 
0210         QCOMPARE( track->labels().count(), 1 );
0211 
0212         Collections::SqlCollectionLocation *source = new MySqlCollectionLocation( m_collection );
0213         Collections::SqlCollectionLocation *dest = new MySqlCollectionLocation( m_collection );
0214         QSignalSpy spy( source, &Collections::SqlCollectionLocation::destroyed );
0215 
0216         {
0217             MyOrganizeCollectionDelegate *delegate = new MyOrganizeCollectionDelegate();
0218             delegate->overwrite = true;
0219             delegate->migrate = true;
0220             delegate->dests.insert( track, m_tmpDir->path() + "b/IDoNotExist.mp3" );
0221             dest->setOrganizeCollectionDelegateFactory( new MyOrganizeCollectionDelegateFactory( delegate ) );
0222         }
0223 
0224         source->prepareMove( track, dest );
0225 
0226         spy.wait( 1000 );
0227 
0228         QCOMPARE( track->labels().count(), 1 );
0229         QVERIFY( track->playableUrl().path().endsWith( "b/IDoNotExist.mp3" ) );
0230     }
0231 
0232     //force a reload from the database
0233     m_collection->registry()->emptyCache();
0234 
0235     {
0236         // Meta::TrackPtr track = m_collection->registry()->getTrack( m_tmpDir->path() + "/b/IDoNotExist.mp3" );
0237         Meta::TrackPtr track = m_collection->registry()->getTrack(1);
0238         QVERIFY( track );
0239         QVERIFY( track->playableUrl().path().endsWith( "b/IDoNotExist.mp3" ) );
0240         // TODO: check that the db urls entry really specifies the exiting directories entry
0241         QCOMPARE( track->labels().count(), 1 );
0242     }
0243 }
0244 
0245 void
0246 TestSqlCollectionLocation::testCopiesLabelFromExternalTracks()
0247 {
0248 
0249 }
0250 
0251 void
0252 TestSqlCollectionLocation::testCopyTrackToDirectoryWithExistingTracks()
0253 {
0254 
0255 }
0256 
0257 QString
0258 TestSqlCollectionLocation::setupFileInTempDir( const QString &relativeName )
0259 {
0260     QString absoluteName = m_tmpDir->path() + '/' + relativeName;
0261 
0262     //TODO: unix specific
0263     //create directory where necessary
0264     int index = absoluteName.lastIndexOf( '/' );
0265     if(index > 0 )
0266     {
0267         QString dir = absoluteName.left( index );
0268         QProcess::execute( "mkdir", QStringList() << "-p" << dir );
0269     }
0270     else
0271     {
0272         qDebug() << "huh? index was " << index << " relative name was " << relativeName << " tmpDir " << m_tmpDir->path();
0273     }
0274 
0275     QProcess::execute( "touch", QStringList() << absoluteName );
0276     return '.' + absoluteName;
0277 }