1 /****************************************************************************************
2  * Copyright (c) 2009 Maximilian Kossick <maximilian.kossick@googlemail.com>            *
3  * Copyright (c) 2010-2013 Ralf Engels <ralf-engels@gmx.de>                             *
4  *                                                                                      *
5  * This program is free software; you can redistribute it and/or modify it under        *
6  * the terms of the GNU General Public License as published by the Free Software        *
7  * Foundation; either version 2 of the License, or (at your option) any later           *
8  * version.                                                                             *
9  *                                                                                      *
10  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
12  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
13  *                                                                                      *
14  * You should have received a copy of the GNU General Public License along with         *
15  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
16  ****************************************************************************************/
17 
18 #include "TestGenericScanManager.h"
19 
20 #include "amarokconfig.h"
21 #include "MetaTagLib.h"
22 #include "scanner/GenericScanManager.h"
23 
24 #include "config-amarok-test.h"
25 
26 #include <QImage>
27 #include <QScopedPointer>
28 #include <QTest>
29 
30 #include <unistd.h>
31 
QTEST_MAIN(TestGenericScanManager)32 QTEST_MAIN( TestGenericScanManager )
33 
34 TestGenericScanManager::TestGenericScanManager()
35     : QObject()
36 {
37     // QString help = i18n("Amarok"); // prevent a bug when the scanner is the first thread creating a translator
38 }
39 
40 void
initTestCase()41 TestGenericScanManager::initTestCase()
42 {
43     AmarokConfig::instance("amarokrc");
44 
45     // setenv( "LC_ALL", "", 1 ); // this breaks the test
46     // Amarok does not force LC_ALL=C but obviously the test does it which
47     // will prevent scanning of files with umlauts.
48 
49     // that is the original mp3 file that we use to generate the "real" tracks
50     m_sourcePath = QDir::toNativeSeparators( QString( AMAROK_TEST_DIR ) + "/data/audio/Platz 01.mp3" );
51     QVERIFY( QFile::exists( m_sourcePath ) );
52 
53     m_scanManager = new GenericScanManager( this );
54 
55     connect( m_scanManager, &GenericScanManager::started,
56              this, &TestGenericScanManager::slotStarted );
57     connect( m_scanManager, &GenericScanManager::directoryCount, this, &TestGenericScanManager::slotDirectoryCount );
58     connect( m_scanManager, &GenericScanManager::directoryScanned,
59              this, &TestGenericScanManager::slotDirectoryScanned );
60     connect( m_scanManager, &GenericScanManager::succeeded, this, &TestGenericScanManager::slotSucceeded );
61     connect( m_scanManager, &GenericScanManager::failed, this, &TestGenericScanManager::slotFailed );
62 
63     AmarokConfig::setScanRecursively( true );
64     AmarokConfig::setMonitorChanges( false );
65 
66     // switch on writing back so that we can create the test files with all the information
67     AmarokConfig::setWriteBack( true );
68     AmarokConfig::setWriteBackStatistics( true );
69     AmarokConfig::setWriteBackCover( true );
70 }
71 
72 void
cleanupTestCase()73 TestGenericScanManager::cleanupTestCase()
74 { }
75 
76 void
init()77 TestGenericScanManager::init()
78 {
79     m_tmpCollectionDir = new QTemporaryDir;
80 
81     QStringList collectionFolders;
82     collectionFolders << m_tmpCollectionDir->path();
83 
84     m_started = false;
85     m_finished = false;
86 
87     m_scannedDirsCount = 0;
88     m_scannedTracksCount = 0;
89     m_scannedCoversCount = 0;
90 }
91 
92 void
cleanup()93 TestGenericScanManager::cleanup()
94 {
95     m_scanManager->abort();
96     delete m_tmpCollectionDir;
97 }
98 
99 void
testScanSingle()100 TestGenericScanManager::testScanSingle()
101 {
102     createSingleTrack();
103     fullScanAndWait();
104 
105     QCOMPARE( m_scannedDirsCount, 3 );
106     QCOMPARE( m_scannedTracksCount, 1 );
107     QCOMPARE( m_scannedCoversCount, 0 );
108 }
109 
110 void
testScanDirectory()111 TestGenericScanManager::testScanDirectory()
112 {
113     createAlbum();
114     fullScanAndWait();
115 
116     // -- check the commit
117     QCOMPARE( m_scannedDirsCount, 3 );
118     QCOMPARE( m_scannedTracksCount, 9 );
119     QCOMPARE( m_scannedCoversCount, 0 );
120 }
121 
122 void
testRestartScanner()123 TestGenericScanManager::testRestartScanner()
124 {
125 #ifndef QT_NO_DEBUG
126     createAlbum();
127 
128     // the scanner crashes at a special file:
129     Meta::FieldHash values;
130     values.clear();
131     values.insert( Meta::valUniqueId, QVariant("c6c29f50279ab9523a0f44928bc1e96b") );
132     values.insert( Meta::valUrl, QVariant("Thriller/crash_amarok_here.ogg") );
133     createTrack( values );
134 
135     fullScanAndWait();
136 
137     // -- check the commit
138     QCOMPARE( m_scannedDirsCount, 4 );
139     QCOMPARE( m_scannedTracksCount, 9 );
140     QCOMPARE( m_scannedCoversCount, 0 );
141 
142 #else
143     QSKIP( "Collection scanner only crashes in debug build.", SkipAll );
144 #endif
145 }
146 
147 void
testAlbumImage()148 TestGenericScanManager::testAlbumImage()
149 {
150     createSingleTrack();
151     createAlbum();
152 
153     // put an image into the album directory
154     QString imageSourcePath = QDir::toNativeSeparators( QString( AMAROK_TEST_DIR ) + "/data/playlists/no-playlist.png" );
155     QVERIFY( QFile::exists( imageSourcePath ) );
156     QString targetPath;
157     targetPath = m_tmpCollectionDir->path() + '/' + "Pop/Thriller/cover.png";
158     QVERIFY( QFile::copy( m_sourcePath, targetPath ) );
159 
160     // set an embedded image
161     targetPath = m_tmpCollectionDir->path() + '/' + "Various Artists/Big Screen Adventures/28 - Theme From Armageddon.mp3";
162     Meta::Tag::setEmbeddedCover( targetPath, QImage( 200, 200, QImage::Format_RGB32 ) );
163 
164     fullScanAndWait();
165 
166     // -- check the commit
167     QCOMPARE( m_scannedDirsCount, 5 );
168     QCOMPARE( m_scannedTracksCount, 10 );
169     QCOMPARE( m_scannedCoversCount, 1 );
170 }
171 
172 void
slotStarted(GenericScanManager::ScanType type)173 TestGenericScanManager::slotStarted( GenericScanManager::ScanType type )
174 {
175     Q_UNUSED( type );
176 
177     QVERIFY( !m_started );
178     QVERIFY( !m_finished );
179 
180     m_started = true;
181 }
182 
183 void
slotDirectoryCount(int count)184 TestGenericScanManager::slotDirectoryCount( int count )
185 {
186     Q_UNUSED( count );
187 
188     QVERIFY( m_started );
189     QVERIFY( !m_finished );
190 }
191 
192 void
slotDirectoryScanned(QSharedPointer<CollectionScanner::Directory> dir)193 TestGenericScanManager::slotDirectoryScanned( QSharedPointer<CollectionScanner::Directory> dir )
194 {
195     QVERIFY( m_started );
196     QVERIFY( !m_finished );
197 
198     m_scannedDirsCount += 1;
199     m_scannedTracksCount += dir->tracks().count();
200     m_scannedCoversCount += dir->covers().count();
201 }
202 
203 void
slotSucceeded()204 TestGenericScanManager::slotSucceeded()
205 {
206     QVERIFY( m_started );
207     QVERIFY( !m_finished );
208 
209     m_finished = true;
210 }
211 
212 void
slotFailed(const QString & message)213 TestGenericScanManager::slotFailed( const QString& message )
214 {
215     Q_UNUSED( message );
216 
217     QVERIFY( m_started );
218     QVERIFY( !m_finished );
219 
220     m_finished = true;
221 }
222 
223 void
fullScanAndWait()224 TestGenericScanManager::fullScanAndWait()
225 {
226     QList<QUrl> urls;
227     urls << QUrl::fromLocalFile( m_tmpCollectionDir->path() );
228 
229     QSignalSpy spy( m_scanManager, &GenericScanManager::succeeded );
230     m_scanManager->requestScan( urls );
231     waitScannerFinished( spy );
232 
233     QVERIFY( m_started );
234     QVERIFY( m_finished );
235 }
236 
237 void
waitScannerFinished(QSignalSpy & spy)238 TestGenericScanManager::waitScannerFinished( QSignalSpy &spy )
239 {
240     QVERIFY( m_scanManager->isRunning() );
241     QVERIFY2( spy.wait( 5000 ), "ScanManager didn't finish scan within timeout" );
242     // m_scanManager needs a little time to delete its worker job after it emits succeeded.
243     int wait = 0;
244     while( wait < 50 && m_scanManager->isRunning() )
245     {
246         wait++;
247         usleep( 100 );
248     }
249     QVERIFY( !m_scanManager->isRunning() );
250 }
251 
252 void
createTrack(const Meta::FieldHash & values)253 TestGenericScanManager::createTrack( const Meta::FieldHash &values )
254 {
255     // -- copy the file from our original
256     QVERIFY( values.contains( Meta::valUrl ) );
257     const QString targetPath = m_tmpCollectionDir->path() + '/' + values.value( Meta::valUrl ).toString();
258     QVERIFY( QDir( m_tmpCollectionDir->path() ).mkpath( QFileInfo( values.value( Meta::valUrl ).toString() ).path() ) );
259 
260     QVERIFY( QFile::copy( m_sourcePath, targetPath ) );
261 
262     // -- set all the values that we need
263     Meta::Tag::writeTags( targetPath, values, true );
264 }
265 
266 void
createSingleTrack()267 TestGenericScanManager::createSingleTrack()
268 {
269     Meta::FieldHash values;
270 
271     values.insert( Meta::valUniqueId, QVariant("794b1bd040d5dd9b5b45c1494d84cc82") );
272     values.insert( Meta::valUrl, QVariant("Various Artists/Big Screen Adventures/28 - Theme From Armageddon.mp3") );
273     values.insert( Meta::valFormat, QVariant("1") );
274     values.insert( Meta::valTitle, QVariant("Theme From Armageddon") );
275     values.insert( Meta::valArtist, QVariant("Soundtrack & Theme Orchestra") );
276     values.insert( Meta::valAlbumArtist, QVariant("Theme Orchestra") );
277     values.insert( Meta::valAlbum, QVariant("Big Screen Adventures") );
278     values.insert( Meta::valComposer, QVariant("Unknown Composer") );
279     values.insert( Meta::valComment, QVariant("Amazon.com Song ID: 210541237") );
280     values.insert( Meta::valGenre, QVariant("Broadway & Vocalists") );
281     values.insert( Meta::valYear, QVariant(2009) );
282     values.insert( Meta::valTrackNr, QVariant(28) );
283     // values.insert( Meta::valBitrate, QVariant(216) ); // the bitrate can not be set. it's computed
284     // values.insert( Meta::valLength, QVariant(184000) ); // also can't be set
285     // values.insert( Meta::valSamplerate, QVariant(44100) ); // again
286     // values.insert( Meta::valFilesize, QVariant(5094892) ); // again
287     values.insert( Meta::valScore, QVariant(0.875) );
288     values.insert( Meta::valPlaycount, QVariant(5) );
289     // TODO: set an embedded cover
290 
291     createTrack( values );
292 }
293 
294 void
createAlbum()295 TestGenericScanManager::createAlbum()
296 {
297     Meta::FieldHash values;
298 
299     values.insert( Meta::valUniqueId, QVariant("1dc7022c52a3e4c51b46577da9b3c8ff") );
300     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 01 - Michael Jackson - Track01.mp3") );
301     values.insert( Meta::valTitle, QVariant("Wanna Be Startin' Somethin'") );
302     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
303     values.insert( Meta::valAlbum, QVariant("Thriller") );
304     values.insert( Meta::valYear, QVariant(1982) );
305     values.insert( Meta::valTrackNr, QVariant(1) );
306     createTrack( values );
307 
308     values.clear();
309     values.insert( Meta::valUniqueId, QVariant("1dc708934a3e4c51b46577da9b3ab11") );
310     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 02 - Michael Jackson - Track02.mp3") );
311     values.insert( Meta::valTitle, QVariant("Baby Be Mine") );
312     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
313     values.insert( Meta::valAlbum, QVariant("Thriller") );
314     values.insert( Meta::valYear, QVariant(1982) );
315     values.insert( Meta::valTrackNr, QVariant(2) );
316     createTrack( values );
317 
318     values.clear();
319     values.insert( Meta::valUniqueId, QVariant("15a6b1bf79747fdc8e9c6b6f06203017") );
320     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 03 - Michael Jackson - Track03.mp3") );
321     values.insert( Meta::valTitle, QVariant("The Girl Is Mine") );
322     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
323     values.insert( Meta::valAlbum, QVariant("Thriller") );
324     values.insert( Meta::valYear, QVariant(1982) );
325     values.insert( Meta::valTrackNr, QVariant(3) );
326     createTrack( values );
327 
328     values.clear();
329     values.insert( Meta::valUniqueId, QVariant("4aba4c8b1d1893c03c112cc3c01221e9") );
330     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 04 - Michael Jackson - Track04.mp3") );
331     values.insert( Meta::valTitle, QVariant("Thriller") );
332     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
333     values.insert( Meta::valAlbum, QVariant("Thriller") );
334     values.insert( Meta::valYear, QVariant(1982) );
335     values.insert( Meta::valTrackNr, QVariant(4) );
336     createTrack( values );
337 
338     values.clear();
339     values.insert( Meta::valUniqueId, QVariant("cb44d2a3d8053829b04672723bf0bd6e") );
340     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 05 - Michael Jackson - Track05.mp3") );
341     values.insert( Meta::valTitle, QVariant("Beat It") );
342     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
343     values.insert( Meta::valAlbum, QVariant("Thriller") );
344     values.insert( Meta::valYear, QVariant(1982) );
345     values.insert( Meta::valTrackNr, QVariant(5) );
346     createTrack( values );
347 
348     values.clear();
349     values.insert( Meta::valUniqueId, QVariant("eba1858eeeb3c6d97fe3385200114d86") );
350     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 06 - Michael Jackson - Track06.mp3") );
351     values.insert( Meta::valTitle, QVariant("Billy Jean") );
352     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
353     values.insert( Meta::valAlbum, QVariant("Thriller") );
354     values.insert( Meta::valYear, QVariant(1982) );
355     values.insert( Meta::valTrackNr, QVariant(6) );
356     createTrack( values );
357 
358     values.clear();
359     values.insert( Meta::valUniqueId, QVariant("4623850290998486b0f7b39a2719904e") );
360     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 07 - Michael Jackson - Track07.mp3") );
361     values.insert( Meta::valTitle, QVariant("Human Nature") );
362     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
363     values.insert( Meta::valAlbum, QVariant("Thriller") );
364     values.insert( Meta::valYear, QVariant(1982) );
365     values.insert( Meta::valTrackNr, QVariant(7) );
366     createTrack( values );
367 
368     values.clear();
369     values.insert( Meta::valUniqueId, QVariant("6d9a7de13af1e16bb13a6208e44b046d") );
370     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 08 - Michael Jackson - Track08.mp3") );
371     values.insert( Meta::valTitle, QVariant("P.Y.T. (Pretty Young Thing)") );
372     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
373     values.insert( Meta::valAlbum, QVariant("Thriller") );
374     values.insert( Meta::valYear, QVariant(1982) );
375     values.insert( Meta::valTrackNr, QVariant(8) );
376     createTrack( values );
377 
378     values.clear();
379     values.insert( Meta::valUniqueId, QVariant("91cf9a7c0d255399f9f6babfacae432b") );
380     values.insert( Meta::valUrl, QVariant("Pop/Thriller/Thriller - 09 - Michael Jackson - Track09.mp3") );
381     values.insert( Meta::valTitle, QVariant("The Lady In My Life") );
382     values.insert( Meta::valArtist, QVariant("Michael Jackson") );
383     values.insert( Meta::valAlbum, QVariant("Thriller") );
384     values.insert( Meta::valYear, QVariant(1982) );
385     values.insert( Meta::valTrackNr, QVariant(9) );
386     createTrack( values );
387 }
388 
389