1 /****************************************************************************************
2  * Copyright (c) 2011 Ralf Engels <ralf-engels@gmx.de>                                  *
3  *                                                                                      *
4  * This program is free software; you can redistribute it and/or modify it under        *
5  * the terms of the GNU General Public License as published by the Free Software        *
6  * Foundation; either version 2 of the License, or (at your option) any later           *
7  * version.                                                                             *
8  *                                                                                      *
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
11  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
12  *                                                                                      *
13  * You should have received a copy of the GNU General Public License along with         *
14  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
15  ****************************************************************************************/
16 
17 #include "TestDynamicModel.h"
18 
19 #include "amarokconfig.h"
20 #include "dynamic/Bias.h"
21 #include "dynamic/BiasedPlaylist.h"
22 #include "dynamic/DynamicModel.h"
23 
24 #include "core/support/Amarok.h"
25 #include "core/support/Debug.h"
26 
27 #include <QByteArray>
28 #include <QDataStream>
29 #include <QMimeData>
30 #include <QSignalSpy>
31 #include <QTemporaryDir>
32 
33 
34 Q_DECLARE_METATYPE(QModelIndex);
35 
36 QTemporaryDir *s_tmpDir = 0;   // Memory leak here now, but if it's deleted, we have a segfault
37 
38 // We return a special saveLocation.
saveLocation(const QString & directory)39 QString Amarok::saveLocation( const QString &directory )
40 {
41     return s_tmpDir->path() + directory;
42 }
43 
QTEST_GUILESS_MAIN(TestDynamicModel)44 QTEST_GUILESS_MAIN( TestDynamicModel )
45 
46 TestDynamicModel::TestDynamicModel()
47 {
48     qRegisterMetaType<QModelIndex>();
49 }
50 
51 void
init()52 TestDynamicModel::init()
53 {
54     AmarokConfig::instance("amarokrc");
55 
56     s_tmpDir = new QTemporaryDir();
57     QVERIFY( s_tmpDir->isValid() );
58 }
59 
60 void
cleanup()61 TestDynamicModel::cleanup()
62 {
63 }
64 
65 void
testData()66 TestDynamicModel::testData()
67 {
68     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
69 
70     // load from the empty directory
71     model->loadPlaylists();
72 
73     // now we should have the four default playlists
74     QModelIndex playlistIndex = model->index( 0, 0 );
75     QCOMPARE( model->data( playlistIndex ).toString(), QString("Random") );
76     QCOMPARE( model->data( playlistIndex, Qt::EditRole ).toString(), QString("Random") );
77     QVERIFY(  model->data( playlistIndex, Dynamic::DynamicModel::PlaylistRole ).isValid() );
78     QVERIFY( !model->data( playlistIndex, Dynamic::DynamicModel::BiasRole ).isValid() );
79 
80     QModelIndex biasIndex = model->index( 0, 0, playlistIndex );
81     QVERIFY( !model->data( biasIndex ).toString().isEmpty() );
82     QVERIFY( !model->data( biasIndex, Dynamic::DynamicModel::PlaylistRole ).isValid() );
83     QVERIFY(  model->data( biasIndex, Dynamic::DynamicModel::BiasRole ).isValid() );
84 }
85 
86 void
testPlaylistIndex()87 TestDynamicModel::testPlaylistIndex()
88 {
89     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
90 
91     // load from the empty directory
92     model->loadPlaylists();
93 
94     // now we should have the four default playlists
95     QCOMPARE( model->rowCount(), 4 );
96     QCOMPARE( model->columnCount(), 1 );
97 
98     // -- random playlist with one bias
99     QModelIndex playlistIndex = model->index( 0, 0 );
100     QModelIndex biasIndex = model->index( 0, 0, playlistIndex );
101 
102     QCOMPARE( model->rowCount( playlistIndex ), 1 );
103     QCOMPARE( model->rowCount( biasIndex ), 0 );
104     QCOMPARE( playlistIndex.parent(), QModelIndex() );
105     QCOMPARE( biasIndex.parent(), playlistIndex );
106 
107     // -- albumplay playlist with bias structure
108     playlistIndex = model->index( 2, 0 );
109     biasIndex = model->index( 0, 0, playlistIndex );
110     QModelIndex subBiasIndex = model->index( 1, 0, biasIndex );
111 
112     QCOMPARE( model->rowCount( playlistIndex ), 1 );
113     QCOMPARE( model->rowCount( biasIndex ), 2 );
114     QCOMPARE( model->rowCount( subBiasIndex ), 0 );
115     QCOMPARE( playlistIndex.parent(), QModelIndex() );
116     QCOMPARE( biasIndex.parent(), playlistIndex );
117     QCOMPARE( subBiasIndex.parent(), biasIndex );
118 
119 
120     // and now the non-model index functions:
121     model->setActivePlaylist( 2 );
122     Dynamic::DynamicPlaylist* playlist = model->activePlaylist();
123     playlistIndex = model->index( model->activePlaylistIndex(), 0 );
124     QCOMPARE( model->index( playlist ), playlistIndex );
125 
126     Dynamic::BiasPtr bias = qobject_cast<Dynamic::BiasedPlaylist*>(playlist)->bias();
127     biasIndex = model->index( 0, 0, playlistIndex );
128     QCOMPARE( model->index( bias ), biasIndex );
129 
130     Dynamic::BiasPtr subBias = qobject_cast<Dynamic::AndBias*>(bias.data())->biases().at(0);
131     subBiasIndex = model->index( 0, 0, biasIndex );
132     QCOMPARE( model->index( subBias ), subBiasIndex );
133 }
134 
135 void
testSlots()136 TestDynamicModel::testSlots()
137 {
138     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
139 
140     // load from the empty directory
141     model->loadPlaylists();
142 
143     QSignalSpy spy1( model, &Dynamic::DynamicModel::rowsAboutToBeRemoved );
144     QSignalSpy spy2( model, &Dynamic::DynamicModel::rowsRemoved );
145     QSignalSpy spy3( model, &Dynamic::DynamicModel::rowsAboutToBeInserted );
146     QSignalSpy spy4( model, &Dynamic::DynamicModel::rowsInserted );
147 
148     // -- removeAt with playlist
149     QModelIndex playlistIndex = model->index( 1, 0 );
150     QString oldName = model->data( playlistIndex ).toString();
151 
152     model->removeAt( playlistIndex );
153 
154     QCOMPARE( spy1.count(), 1 );
155     QCOMPARE( spy3.count(), 0 );
156     QList<QVariant> args1 = spy1.takeFirst();
157     QVERIFY( args1.value(0).canConvert<QModelIndex>() );
158     QCOMPARE( args1.value(0).value<QModelIndex>(), QModelIndex() );
159     QCOMPARE( args1.value(1).toInt(), 1 );
160     QCOMPARE( args1.value(2).toInt(), 1 );
161     QCOMPARE( spy2.count(), 1 );
162     spy2.takeFirst();
163 
164     // name should be different
165     playlistIndex = model->index( 1, 0 );
166     QVERIFY( model->data( playlistIndex ).toString() != oldName );
167 
168     QCOMPARE( model->rowCount(), 3 );
169 
170     // -- removeAt with bias
171     playlistIndex = model->index( 1, 0 );
172     QModelIndex biasIndex = model->index( 0, 0, playlistIndex );
173     QModelIndex subBiasIndex = model->index( 0, 0, biasIndex );
174     QCOMPARE( model->rowCount( biasIndex ), 2 );
175 
176     model->removeAt( subBiasIndex );
177 
178     QCOMPARE( spy1.count(), 1 );
179     QCOMPARE( spy3.count(), 0 );
180     args1 = spy1.takeFirst();
181     QCOMPARE( args1.count(), 3 );
182     QVERIFY( args1.value(0).canConvert<QModelIndex>() );
183     QCOMPARE( args1.value(0).value<QModelIndex>(), biasIndex );
184     QCOMPARE( args1.value(1).toInt(), 0 );
185     QCOMPARE( args1.value(2).toInt(), 0 );
186     QCOMPARE( spy2.count(), 1 );
187     spy2.takeFirst();
188 
189     QCOMPARE( model->rowCount( biasIndex ), 1 );
190     QCOMPARE( model->rowCount(), 3 ); // only the bias was removed
191 
192     // -- cloneAt with level 2 bias
193     playlistIndex = model->index( 1, 0 );
194 
195     biasIndex = model->index( 0, 0, playlistIndex );
196     subBiasIndex = model->index( 0, 0, biasIndex );
197     QCOMPARE( model->rowCount( biasIndex ), 1 );
198 
199     QModelIndex resultIndex = model->cloneAt(subBiasIndex);
200     QCOMPARE( resultIndex.row(), 1 );
201     QCOMPARE( resultIndex.parent(), biasIndex );
202 
203     QCOMPARE( spy3.count(), 1 );
204     args1 = spy3.takeFirst();
205     QVERIFY( args1.value(0).canConvert<QModelIndex>() );
206     QCOMPARE( args1.value(0).value<QModelIndex>(), biasIndex );
207     QCOMPARE( args1.value(1).toInt(), 1 );
208     QCOMPARE( args1.value(2).toInt(), 1 );
209     QCOMPARE( spy4.count(), 1 );
210     spy4.takeFirst();
211 
212     QCOMPARE( model->rowCount( biasIndex ), 2 );
213     QCOMPARE( model->rowCount(), 3 ); // only the bias was cloned
214 
215     // -- newPlaylist
216     QCOMPARE( spy1.count(), 0 );
217     QCOMPARE( spy3.count(), 0 );
218 
219     QCOMPARE( model->rowCount(), 3 );
220     resultIndex = model->newPlaylist();
221     QCOMPARE( model->rowCount(), 4 );
222 
223     QCOMPARE( resultIndex.row(), 3 );
224     QCOMPARE( resultIndex.parent(), QModelIndex() );
225 
226     QCOMPARE( spy1.count(), 0 );
227     QCOMPARE( spy3.count(), 1 );
228     args1 = spy3.takeFirst();
229     QVERIFY( args1.value(0).canConvert<QModelIndex>() );
230     QCOMPARE( args1.value(0).value<QModelIndex>(), QModelIndex() );
231     QCOMPARE( args1.value(1).toInt(), 3 );
232     QCOMPARE( args1.value(2).toInt(), 3 );
233     QCOMPARE( spy4.count(), 1 );
234     spy4.takeFirst();
235 
236     // -- cloneAt with playlist
237     playlistIndex = model->index( 1, 0 );
238 
239     QCOMPARE( model->rowCount(), 4 );
240     resultIndex = model->cloneAt(playlistIndex);
241     QCOMPARE( model->rowCount(), 5 );
242 
243     QCOMPARE( resultIndex.row(), 4 );
244     QCOMPARE( resultIndex.parent(), QModelIndex() );
245     QCOMPARE( model->rowCount( resultIndex ), 1 );
246 
247     QCOMPARE( spy3.count(), 1 );
248     args1 = spy3.takeFirst();
249     QVERIFY( args1.value(0).canConvert<QModelIndex>() );
250     QCOMPARE( args1.value(0).value<QModelIndex>(), QModelIndex() );
251     QCOMPARE( args1.value(1).toInt(), 4 );
252     QCOMPARE( args1.value(2).toInt(), 4 );
253     QCOMPARE( spy4.count(), 1 );
254     spy4.takeFirst();
255 }
256 
257 QModelIndex
serializeUnserialize(const QModelIndex & index)258 TestDynamicModel::serializeUnserialize( const QModelIndex& index )
259 {
260     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
261 
262     QByteArray bytes;
263     QDataStream stream( &bytes, QIODevice::WriteOnly );
264     model->serializeIndex( &stream, index );
265 
266     QDataStream stream2( &bytes, QIODevice::ReadOnly );
267     return model->unserializeIndex( &stream2 );
268 }
269 
270 void
testSerializeIndex()271 TestDynamicModel::testSerializeIndex()
272 {
273     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
274 
275     // load from the empty directory
276     model->loadPlaylists();
277 
278     QModelIndex playlistIndex = model->index( 2, 0 );
279     QModelIndex biasIndex = model->index( 0, 0, playlistIndex );
280     QModelIndex subBiasIndex = model->index( 0, 0, biasIndex );
281 
282     QCOMPARE( QModelIndex(), serializeUnserialize( QModelIndex() ) );
283     QCOMPARE( biasIndex, serializeUnserialize( biasIndex ) );
284     QCOMPARE( subBiasIndex, serializeUnserialize( subBiasIndex ) );
285 }
286 
287 void
testDnD()288 TestDynamicModel::testDnD()
289 {
290     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
291 
292     // load from the empty directory
293     model->loadPlaylists();
294 
295     // -- copy a playlist
296     QModelIndex playlistIndex = model->index( 2, 0 );
297     QModelIndexList indexes;
298     indexes << playlistIndex;
299     int oldRowCount = model->rowCount();
300     QString oldName = model->data( playlistIndex ).toString();
301     QMimeData* data = model->mimeData( indexes );
302     QVERIFY( model->dropMimeData( data, Qt::CopyAction, 0, 0, QModelIndex() ) );
303 
304     QCOMPARE( model->rowCount(), oldRowCount + 1 );
305     playlistIndex = model->index( 0, 0 );
306     QCOMPARE( oldName, model->data( playlistIndex ).toString() );
307     delete data;
308 
309     // -- move a playlist (to the end)
310     playlistIndex = model->index( 0, 0 );
311     indexes.clear();
312     indexes << playlistIndex;
313 
314     oldRowCount = model->rowCount();
315     oldName = model->data( playlistIndex ).toString();
316     data = model->mimeData( indexes );
317     QVERIFY( model->dropMimeData( data, Qt::MoveAction, oldRowCount, 0, QModelIndex() ) );
318 
319     QCOMPARE( model->rowCount(), oldRowCount );
320     playlistIndex = model->index( oldRowCount - 1, 0 );
321     QCOMPARE( oldName, model->data( playlistIndex ).toString() );
322     delete data;
323 
324 
325     // -- copy a bias
326     // TODO
327 //     QModelIndex biasIndex = model->index( 0, 0, playlistIndex );
328 //     QModelIndex subBiasIndex = model->index( 0, 0, biasIndex );
329 }
330 
331 void
testRemoveActive()332 TestDynamicModel::testRemoveActive()
333 {
334     Dynamic::DynamicModel *model = Dynamic::DynamicModel::instance();
335 
336     // load from the empty directory
337     model->loadPlaylists();
338     QCOMPARE( model->rowCount(), 4 );
339 
340     // -- try to remove the active playlist
341     model->setActivePlaylist( 2 );
342     QCOMPARE( model->activePlaylistIndex(), 2 );
343     Dynamic::DynamicPlaylist* pl = model->activePlaylist();
344 
345     model->removeAt( model->index( pl ) );
346     QCOMPARE( model->rowCount(), 3 );
347     QVERIFY( model->activePlaylist() != pl );
348 
349     // -- now remove all playlists remaining three playlists
350     model->removeAt( model->index( model->activePlaylist() ) );
351     model->removeAt( model->index( model->activePlaylist() ) );
352     model->removeAt( model->index( model->activePlaylist() ) );
353 
354     QCOMPARE( model->rowCount(), 0 );
355     QCOMPARE( model->activePlaylist(), static_cast<Dynamic::DynamicPlaylist*>(0) );
356 }
357 
358