1 /*
2 * Strawberry Music Player
3 * This file was part of Clementine.
4 * Copyright 2010, David Sansome <me@davidsansome.com>
5 * Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
6 *
7 * Strawberry is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Strawberry is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <memory>
23
24 #include <gtest/gtest.h>
25
26 #include <QMap>
27 #include <QString>
28 #include <QUrl>
29 #include <QThread>
30 #include <QSignalSpy>
31 #include <QSortFilterProxyModel>
32 #include <QtDebug>
33
34 #include "test_utils.h"
35
36 #include "core/logging.h"
37 #include "core/database.h"
38 #include "collection/collectionmodel.h"
39 #include "collection/collectionbackend.h"
40 #include "collection/collection.h"
41
42 // clazy:excludeall=non-pod-global-static,returning-void-expression
43
44 namespace {
45
46 class CollectionModelTest : public ::testing::Test {
47 public:
CollectionModelTest()48 CollectionModelTest() : added_dir_(false) {}
49
50 protected:
SetUp()51 void SetUp() override {
52 database_ = std::make_shared<MemoryDatabase>(nullptr);
53 backend_ = std::make_unique<CollectionBackend>();
54 backend_->Init(database_.get(), nullptr, Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
55 model_ = std::make_unique<CollectionModel>(backend_.get(), nullptr);
56
57 added_dir_ = false;
58
59 model_sorted_ = std::make_unique<QSortFilterProxyModel>();
60 model_sorted_->setSourceModel(model_.get());
61 model_sorted_->setSortRole(CollectionModel::Role_SortText);
62 model_sorted_->setDynamicSortFilter(true);
63 model_sorted_->sort(0);
64
65 }
66
AddSong(Song & song)67 Song AddSong(Song &song) {
68 song.set_directory_id(1);
69 if (song.mtime() == 0) song.set_mtime(1);
70 if (song.ctime() == 0) song.set_ctime(1);
71 if (song.url().isEmpty()) song.set_url(QUrl("file:///tmp/foo"));
72 if (song.filesize() == -1) song.set_filesize(1);
73
74 if (!added_dir_) {
75 backend_->AddDirectory("/tmp");
76 added_dir_ = true;
77 }
78
79 backend_->AddOrUpdateSongs(SongList() << song);
80 return song;
81 }
82
AddSong(const QString & title,const QString & artist,const QString & album,const int length)83 Song AddSong(const QString &title, const QString &artist, const QString &album, const int length) {
84 Song song;
85 song.Init(title, artist, album, length);
86 song.set_mtime(0);
87 song.set_ctime(0);
88 return AddSong(song);
89 }
90
91 std::shared_ptr<Database> database_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
92 std::unique_ptr<CollectionBackend> backend_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
93 std::unique_ptr<CollectionModel> model_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
94 std::unique_ptr<QSortFilterProxyModel> model_sorted_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
95
96 bool added_dir_; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
97 };
98
TEST_F(CollectionModelTest,Initialization)99 TEST_F(CollectionModelTest, Initialization) {
100 EXPECT_EQ(0, model_->rowCount(QModelIndex()));
101 }
102
TEST_F(CollectionModelTest,WithInitialArtists)103 TEST_F(CollectionModelTest, WithInitialArtists) {
104
105 AddSong("Title", "Artist 1", "Album", 123);
106 AddSong("Title", "Artist 2", "Album", 123);
107 AddSong("Title", "Foo", "Album", 123);
108 model_->Init(false);
109
110 ASSERT_EQ(5, model_sorted_->rowCount(QModelIndex()));
111 EXPECT_EQ("A", model_sorted_->index(0, 0, QModelIndex()).data().toString());
112 EXPECT_EQ("Artist 1", model_sorted_->index(1, 0, QModelIndex()).data().toString());
113 EXPECT_EQ("Artist 2", model_sorted_->index(2, 0, QModelIndex()).data().toString());
114 EXPECT_EQ("F", model_sorted_->index(3, 0, QModelIndex()).data().toString());
115 EXPECT_EQ("Foo", model_sorted_->index(4, 0, QModelIndex()).data().toString());
116
117 }
118
TEST_F(CollectionModelTest,CompilationAlbums)119 TEST_F(CollectionModelTest, CompilationAlbums) {
120
121 Song song;
122 song.Init("Title", "Artist", "Album", 123);
123 song.set_compilation(true);
124 song.set_mtime(0);
125 song.set_ctime(0);
126
127 AddSong(song);
128 model_->Init(false);
129 model_->fetchMore(model_->index(0, 0));
130
131 ASSERT_EQ(1, model_->rowCount(QModelIndex()));
132
133 QModelIndex va_index = model_->index(0, 0, QModelIndex());
134 EXPECT_EQ("Various artists", va_index.data().toString());
135 EXPECT_TRUE(model_->hasChildren(va_index));
136 ASSERT_EQ(model_->rowCount(va_index), 1);
137
138 QModelIndex album_index = model_->index(0, 0, va_index);
139 EXPECT_EQ(model_->data(album_index).toString(), "Album");
140 EXPECT_TRUE(model_->hasChildren(album_index));
141
142 }
143
TEST_F(CollectionModelTest,NumericHeaders)144 TEST_F(CollectionModelTest, NumericHeaders) {
145
146 AddSong("Title", "1artist", "Album", 123);
147 AddSong("Title", "2artist", "Album", 123);
148 AddSong("Title", "0artist", "Album", 123);
149 AddSong("Title", "zartist", "Album", 123);
150 model_->Init(false);
151
152 ASSERT_EQ(6, model_sorted_->rowCount(QModelIndex()));
153 EXPECT_EQ("0-9", model_sorted_->index(0, 0, QModelIndex()).data().toString());
154 EXPECT_EQ("0artist", model_sorted_->index(1, 0, QModelIndex()).data().toString());
155 EXPECT_EQ("1artist", model_sorted_->index(2, 0, QModelIndex()).data().toString());
156 EXPECT_EQ("2artist", model_sorted_->index(3, 0, QModelIndex()).data().toString());
157 EXPECT_EQ("Z", model_sorted_->index(4, 0, QModelIndex()).data().toString());
158 EXPECT_EQ("zartist", model_sorted_->index(5, 0, QModelIndex()).data().toString());
159
160 }
161
TEST_F(CollectionModelTest,MixedCaseHeaders)162 TEST_F(CollectionModelTest, MixedCaseHeaders) {
163
164 AddSong("Title", "Artist", "Album", 123);
165 AddSong("Title", "artist", "Album", 123);
166 model_->Init(false);
167
168 ASSERT_EQ(3, model_sorted_->rowCount(QModelIndex()));
169 EXPECT_EQ("A", model_sorted_->index(0, 0, QModelIndex()).data().toString());
170 EXPECT_EQ("Artist", model_sorted_->index(1, 0, QModelIndex()).data().toString());
171 EXPECT_EQ("artist", model_sorted_->index(2, 0, QModelIndex()).data().toString());
172
173 }
174
TEST_F(CollectionModelTest,UnknownArtists)175 TEST_F(CollectionModelTest, UnknownArtists) {
176
177 AddSong("Title", "", "Album", 123);
178 model_->Init(false);
179 model_->fetchMore(model_->index(0, 0));
180
181 ASSERT_EQ(1, model_->rowCount(QModelIndex()));
182 QModelIndex unknown_index = model_->index(0, 0, QModelIndex());
183 EXPECT_EQ("Unknown", unknown_index.data().toString());
184
185 ASSERT_EQ(1, model_->rowCount(unknown_index));
186 EXPECT_EQ("Album", model_->index(0, 0, unknown_index).data().toString());
187
188 }
189
TEST_F(CollectionModelTest,UnknownAlbums)190 TEST_F(CollectionModelTest, UnknownAlbums) {
191
192 AddSong("Title", "Artist", "", 123);
193 AddSong("Title", "Artist", "Album", 123);
194 model_->Init(false);
195 model_->fetchMore(model_->index(0, 0));
196
197 QModelIndex artist_index = model_->index(0, 0, QModelIndex());
198 ASSERT_EQ(2, model_->rowCount(artist_index));
199
200 QModelIndex unknown_album_index = model_->index(0, 0, artist_index);
201 QModelIndex real_album_index = model_->index(1, 0, artist_index);
202
203 EXPECT_EQ("Unknown", unknown_album_index.data().toString());
204 EXPECT_EQ("Album", real_album_index.data().toString());
205
206 }
207
TEST_F(CollectionModelTest,VariousArtistSongs)208 TEST_F(CollectionModelTest, VariousArtistSongs) {
209
210 SongList songs;
211 for (int i=0 ; i < 4 ; ++i) {
212 QString n = QString::number(i+1);
213 Song song;
214 song.Init("Title " + n, "Artist " + n, "Album", 0);
215 song.set_mtime(0);
216 song.set_ctime(0);
217 songs << song; // clazy:exclude=reserve-candidates
218 }
219
220 // Different ways of putting songs in "Various Artist". Make sure they all work
221 songs[0].set_compilation_detected(true);
222 songs[1].set_compilation(true);
223 songs[2].set_compilation_on(true);
224 songs[3].set_compilation_detected(true); songs[3].set_artist("Various Artists");
225
226 for (int i=0 ; i < 4 ; ++i)
227 AddSong(songs[i]);
228 model_->Init(false);
229
230 QModelIndex artist_index = model_->index(0, 0, QModelIndex());
231 model_->fetchMore(artist_index);
232 ASSERT_EQ(1, model_->rowCount(artist_index));
233
234 QModelIndex album_index = model_->index(0, 0, artist_index);
235 model_->fetchMore(album_index);
236 ASSERT_EQ(4, model_->rowCount(album_index));
237
238 EXPECT_EQ("Artist 1 - Title 1", model_->index(0, 0, album_index).data().toString());
239 EXPECT_EQ("Artist 2 - Title 2", model_->index(1, 0, album_index).data().toString());
240 EXPECT_EQ("Artist 3 - Title 3", model_->index(2, 0, album_index).data().toString());
241 EXPECT_EQ("Title 4", model_->index(3, 0, album_index).data().toString());
242
243 }
244
TEST_F(CollectionModelTest,RemoveSongsLazyLoaded)245 TEST_F(CollectionModelTest, RemoveSongsLazyLoaded) {
246
247 Song one = AddSong("Title 1", "Artist", "Album", 123); one.set_id(1);
248 Song two = AddSong("Title 2", "Artist", "Album", 123); two.set_id(2);
249 AddSong("Title 3", "Artist", "Album", 123);
250 model_->Init(false);
251
252 // Lazy load the items
253 QModelIndex artist_index = model_->index(0, 0, QModelIndex());
254 model_->fetchMore(artist_index);
255 ASSERT_EQ(1, model_->rowCount(artist_index));
256 QModelIndex album_index = model_->index(0, 0, artist_index);
257 model_->fetchMore(album_index);
258 ASSERT_EQ(3, model_->rowCount(album_index));
259
260 // Remove the first two songs
261 QSignalSpy spy_preremove(model_.get(), &CollectionModel::rowsAboutToBeRemoved);
262 QSignalSpy spy_remove(model_.get(), &CollectionModel::rowsRemoved);
263 QSignalSpy spy_reset(model_.get(), &CollectionModel::modelReset);
264
265 backend_->DeleteSongs(SongList() << one << two);
266
267 ASSERT_EQ(2, spy_preremove.count());
268 ASSERT_EQ(2, spy_remove.count());
269 ASSERT_EQ(0, spy_reset.count());
270
271 artist_index = model_->index(0, 0, QModelIndex());
272 ASSERT_EQ(1, model_->rowCount(artist_index));
273 album_index = model_->index(0, 0, artist_index);
274 ASSERT_EQ(1, model_->rowCount(album_index));
275 EXPECT_EQ("Title 3", model_->index(0, 0, album_index).data().toString());
276
277 }
278
TEST_F(CollectionModelTest,RemoveSongsNotLazyLoaded)279 TEST_F(CollectionModelTest, RemoveSongsNotLazyLoaded) {
280
281 Song one = AddSong("Title 1", "Artist", "Album", 123); one.set_id(1);
282 Song two = AddSong("Title 2", "Artist", "Album", 123); two.set_id(2);
283 model_->Init(false);
284
285 // Remove the first two songs
286 QSignalSpy spy_preremove(model_.get(), &CollectionModel::rowsAboutToBeRemoved);
287 QSignalSpy spy_remove(model_.get(), &CollectionModel::rowsRemoved);
288 QSignalSpy spy_reset(model_.get(), &CollectionModel::modelReset);
289
290 backend_->DeleteSongs(SongList() << one << two);
291
292 ASSERT_EQ(0, spy_preremove.count());
293 ASSERT_EQ(0, spy_remove.count());
294 ASSERT_EQ(1, spy_reset.count());
295
296 }
297
TEST_F(CollectionModelTest,RemoveEmptyAlbums)298 TEST_F(CollectionModelTest, RemoveEmptyAlbums) {
299
300 Song one = AddSong("Title 1", "Artist", "Album 1", 123); one.set_id(1);
301 Song two = AddSong("Title 2", "Artist", "Album 2", 123); two.set_id(2);
302 Song three = AddSong("Title 3", "Artist", "Album 2", 123); three.set_id(3);
303 model_->Init(false);
304
305 QModelIndex artist_index = model_->index(0, 0, QModelIndex());
306 model_->fetchMore(artist_index);
307 ASSERT_EQ(2, model_->rowCount(artist_index));
308
309 // Remove one song from each album
310 backend_->DeleteSongs(SongList() << one << two);
311
312 // Check the model
313 artist_index = model_->index(0, 0, QModelIndex());
314 model_->fetchMore(artist_index);
315 ASSERT_EQ(1, model_->rowCount(artist_index));
316 QModelIndex album_index = model_->index(0, 0, artist_index);
317 model_->fetchMore(album_index);
318 EXPECT_EQ("Album 2", album_index.data().toString());
319
320 ASSERT_EQ(1, model_->rowCount(album_index));
321 EXPECT_EQ("Title 3", model_->index(0, 0, album_index).data().toString());
322
323 }
324
TEST_F(CollectionModelTest,RemoveEmptyArtists)325 TEST_F(CollectionModelTest, RemoveEmptyArtists) {
326
327 Song one = AddSong("Title", "Artist", "Album", 123); one.set_id(1);
328 model_->Init(false);
329
330 // Lazy load the items
331 QModelIndex artist_index = model_->index(0, 0, QModelIndex());
332 model_->fetchMore(artist_index);
333 ASSERT_EQ(1, model_->rowCount(artist_index));
334 QModelIndex album_index = model_->index(0, 0, artist_index);
335 model_->fetchMore(album_index);
336 ASSERT_EQ(1, model_->rowCount(album_index));
337
338 // The artist header is there too right?
339 ASSERT_EQ(2, model_->rowCount(QModelIndex()));
340
341 // Remove the song
342 backend_->DeleteSongs(SongList() << one);
343
344 // Everything should be gone - even the artist header
345 ASSERT_EQ(0, model_->rowCount(QModelIndex()));
346
347 }
348
349 // Test to check that the container nodes are created identical and unique all through the model with all possible collection groupings.
350 // model1 - Nodes are created from a complete reset done through lazy-loading.
351 // model2 - Initial container nodes are created in SongsDiscovered.
352 // model3 - All container nodes are created in SongsDiscovered.
353
354 // WARNING: This test can take up to 30 minutes to complete.
355 #if 0
356 TEST_F(CollectionModelTest, TestContainerNodes) {
357
358 SongList songs;
359 int year = 1960;
360 // Add some normal albums.
361 for (int artist_number = 1; artist_number <= 3 ; ++artist_number) {
362 Song song(Song::Source_Collection);
363 song.set_artist(QString("Artist %1").arg(artist_number));
364 song.set_composer(QString("Composer %1").arg(artist_number));
365 song.set_performer(QString("Performer %1").arg(artist_number));
366 song.set_mtime(1);
367 song.set_ctime(1);
368 song.set_directory_id(1);
369 song.set_filetype(Song::FileType_FLAC);
370 song.set_filesize(1);
371 for (int album_number = 1; album_number <= 3 ; ++album_number) {
372 if (year > 2020) year = 1960;
373 song.set_album(QString("Artist %1 - Album %2").arg(artist_number).arg(album_number));
374 song.set_album_id(QString::number(album_number));
375 song.set_year(year++);
376 song.set_genre("Rock");
377 for (int song_number = 1; song_number <= 5 ; ++song_number) {
378 song.set_url(QUrl(QString("file:///mnt/music/Artist %1/Album %2/%3 - artist song-n-%3").arg(artist_number).arg(album_number).arg(song_number)));
379 song.set_title(QString("Title %1").arg(song_number));
380 song.set_track(song_number);
381 songs << song;
382 }
383 }
384 }
385
386 // Add some albums with 'album artist'.
387 for (int album_artist_number = 1; album_artist_number <= 3 ; ++album_artist_number) {
388 Song song(Song::Source_Collection);
389 song.set_albumartist(QString("Album Artist %1").arg(album_artist_number));
390 song.set_composer(QString("Composer %1").arg(album_artist_number));
391 song.set_performer(QString("Performer %1").arg(album_artist_number));
392 song.set_mtime(1);
393 song.set_ctime(1);
394 song.set_directory_id(1);
395 song.set_filetype(Song::FileType_FLAC);
396 song.set_filesize(1);
397 for (int album_number = 1; album_number <= 3 ; ++album_number) {
398 if (year > 2020) year = 1960;
399 song.set_album(QString("Album Artist %1 - Album %2").arg(album_artist_number).arg(album_number));
400 song.set_album_id(QString::number(album_number));
401 song.set_year(year++);
402 song.set_genre("Rock");
403 int artist_number = 1;
404 for (int song_number = 1; song_number <= 5 ; ++song_number) {
405 song.set_url(QUrl(QString("file:///mnt/music/Album Artist %1/Album %2/%3 - album artist song-n-%3").arg(album_artist_number).arg(album_number).arg(QString::number(song_number))));
406 song.set_title("Title " + QString::number(song_number));
407 song.set_track(song_number);
408 song.set_artist("Artist " + QString::number(artist_number));
409 songs << song;
410 ++artist_number;
411 }
412 }
413 }
414
415 // Add some compilation albums.
416 for (int album_number = 1; album_number <= 3 ; ++album_number) {
417 if (year > 2020) year = 1960;
418 Song song(Song::Source_Collection);
419 song.set_mtime(1);
420 song.set_ctime(1);
421 song.set_directory_id(1);
422 song.set_filetype(Song::FileType_FLAC);
423 song.set_filesize(1);
424 song.set_album(QString("Compilation Album %1").arg(album_number));
425 song.set_album_id(QString::number(album_number));
426 song.set_year(year++);
427 song.set_genre("Pop");
428 song.set_compilation(true);
429 int artist_number = 1;
430 for (int song_number = 1; song_number <= 4 ; ++song_number) {
431 song.set_url(QUrl(QString("file:///mnt/music/Compilation Artist %1/Compilation Album %2/%3 - compilation song-n-%3").arg(artist_number).arg(album_number).arg(QString::number(song_number))));
432 song.set_artist(QString("Compilation Artist %1").arg(artist_number));
433 song.set_composer(QString("Composer %1").arg(artist_number));
434 song.set_performer(QString("Performer %1").arg(artist_number));
435 song.set_title(QString("Title %1").arg(song_number));
436 song.set_track(song_number);
437 songs << song;
438 ++artist_number;
439 }
440 }
441
442 // Songs with only title
443 {
444 Song song(Song::Source_Collection);
445 song.set_mtime(1);
446 song.set_ctime(1);
447 song.set_directory_id(1);
448 song.set_filetype(Song::FileType_FLAC);
449 song.set_filesize(1);
450 song.set_url(QUrl(QString("file:///mnt/music/no album song 1/song-only-1")));
451 song.set_title("Only Title 1");
452 songs << song;
453 song.set_url(QUrl(QString("file:///mnt/music/no album song 2/song-only-2")));
454 song.set_title("Only Title 2");
455 songs << song;
456 }
457
458 // Song with only artist, album and title.
459 {
460 Song song(Song::Source_Collection);
461 song.set_url(QUrl(QString("file:///tmp/artist-album-title-song")));
462 song.set_artist("Not Only Artist");
463 song.set_album("Not Only Album");
464 song.set_title("Not Only Title");
465 song.set_mtime(1);
466 song.set_ctime(1);
467 song.set_directory_id(1);
468 song.set_filetype(Song::FileType_FLAC);
469 song.set_filesize(1);
470 song.set_year(1970);
471 song.set_track(1);
472 songs << song;
473 }
474
475 // Add possible Various artists conflicting songs.
476 {
477 Song song(Song::Source_Collection);
478 song.set_url(QUrl(QString("file:///tmp/song-va-conflicting-1")));
479 song.set_artist("Various artists");
480 song.set_album("VA Album");
481 song.set_title("VA Title");
482 song.set_mtime(1);
483 song.set_ctime(1);
484 song.set_directory_id(1);
485 song.set_filetype(Song::FileType_FLAC);
486 song.set_filesize(1);
487 song.set_year(1970);
488 song.set_track(1);
489 songs << song;
490 }
491
492 {
493 Song song(Song::Source_Collection);
494 song.set_url(QUrl(QString("file:///tmp/song-va-conflicting-2")));
495 song.set_artist("Various artists");
496 song.set_albumartist("Various artists");
497 song.set_album("VA Album");
498 song.set_title("VA Title");
499 song.set_mtime(1);
500 song.set_ctime(1);
501 song.set_directory_id(1);
502 song.set_filetype(Song::FileType_FLAC);
503 song.set_filesize(1);
504 song.set_year(1970);
505 song.set_track(1);
506 songs << song;
507 }
508
509 {
510 Song song(Song::Source_Collection);
511 song.set_url(QUrl(QString("file:///tmp/song-va-conflicting-3")));
512 song.set_albumartist("Various artists");
513 song.set_album("VA Album");
514 song.set_title("VA Title");
515 song.set_mtime(1);
516 song.set_ctime(1);
517 song.set_directory_id(1);
518 song.set_filetype(Song::FileType_FLAC);
519 song.set_filesize(1);
520 song.set_year(1970);
521 song.set_track(1);
522 songs << song;
523 }
524
525 // Albums with Album ID.
526 for (int album_id = 0; album_id <= 2 ; ++album_id) {
527 Song song(Song::Source_Collection);
528 song.set_url(QUrl(QString("file:///tmp/song-with-album-id-1")));
529 song.set_artist("Artist with Album ID");
530 song.set_album(QString("Album %1 with Album ID").arg(album_id));
531 song.set_album_id(QString("Album ID %1").arg(album_id));
532 song.set_mtime(1);
533 song.set_ctime(1);
534 song.set_directory_id(1);
535 song.set_filetype(Song::FileType_FLAC);
536 song.set_filesize(1);
537 song.set_year(1970);
538 for (int i = 0; i <= 3 ; ++i) {
539 song.set_title(QString("Title %1 %2").arg(album_id).arg(i));
540 song.set_track(i);
541 songs << song;
542 }
543 }
544
545 for (int f = CollectionModel::GroupBy_None + 1 ; f < CollectionModel::GroupByCount ; ++f) {
546 for (int s = CollectionModel::GroupBy_None ; s < CollectionModel::GroupByCount ; ++s) {
547 for (int t = CollectionModel::GroupBy_None ; t < CollectionModel::GroupByCount ; ++t) {
548
549 qLog(Debug) << "Testing collection model grouping: " << f << s << t;
550
551 std::unique_ptr<Database> database1;
552 std::unique_ptr<Database> database2;
553 std::unique_ptr<Database> database3;
554 std::unique_ptr<CollectionBackend> backend1;
555 std::unique_ptr<CollectionBackend> backend2;
556 std::unique_ptr<CollectionBackend> backend3;
557 std::unique_ptr<CollectionModel> model1;
558 std::unique_ptr<CollectionModel> model2;
559 std::unique_ptr<CollectionModel> model3;
560
561 database1 = std::make_unique<MemoryDatabase>(nullptr);
562 database2 = std::make_unique<MemoryDatabase>(nullptr);
563 database3 = std::make_unique<MemoryDatabase>(nullptr);
564 backend1 = std::make_unique<CollectionBackend>();
565 backend2= std::make_unique<CollectionBackend>();
566 backend3 = std::make_unique<CollectionBackend>();
567 backend1->Init(database1.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
568 backend2->Init(database2.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
569 backend3->Init(database3.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kFtsTable, SCollection::kDirsTable, SCollection::kSubdirsTable);
570 model1 = std::make_unique<CollectionModel>(backend1.get(), nullptr);
571 model2 = std::make_unique<CollectionModel>(backend2.get(), nullptr);
572 model3 = std::make_unique<CollectionModel>(backend3.get(), nullptr);
573
574 backend1->AddDirectory("/mnt/music");
575 backend2->AddDirectory("/mnt/music");
576 backend3->AddDirectory("/mut/music");
577
578 model1->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy(f), CollectionModel::GroupBy(s), CollectionModel::GroupBy(t)));
579 model2->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy(f), CollectionModel::GroupBy(s), CollectionModel::GroupBy(t)));
580 model3->SetGroupBy(CollectionModel::Grouping(CollectionModel::GroupBy(f), CollectionModel::GroupBy(s), CollectionModel::GroupBy(t)));
581
582 model3->set_use_lazy_loading(false);
583
584 QSignalSpy model1_update(model1.get(), &CollectionModel::rowsInserted);
585 QSignalSpy model2_update(model2.get(), &CollectionModel::rowsInserted);
586 QSignalSpy model3_update(model3.get(), &CollectionModel::rowsInserted);
587
588 backend1->AddOrUpdateSongs(songs);
589 backend2->AddOrUpdateSongs(songs);
590 backend3->AddOrUpdateSongs(songs);
591
592 ASSERT_EQ(model1->song_nodes().count(), 0);
593 ASSERT_EQ(model2->song_nodes().count(), 0);
594 ASSERT_EQ(model3->song_nodes().count(), songs.count());
595
596 model1->Init(false);
597
598 model1->ExpandAll();
599 model2->ExpandAll();
600 // All nodes in model3 should be created already.
601
602 ASSERT_EQ(model1->song_nodes().count(), songs.count());
603 ASSERT_EQ(model2->song_nodes().count(), songs.count());
604 ASSERT_EQ(model3->song_nodes().count(), songs.count());
605
606 // Container nodes for all models should now be identical.
607 for (int i = 0 ; i < 3 ; ++i) {
608 for (CollectionItem *node : model1->container_nodes(i).values()) {
609 ASSERT_TRUE(model2->container_nodes(i).keys().contains(node->key));
610 CollectionItem *node2 = model2->container_nodes(i)[node->key];
611 ASSERT_EQ(node->key, node2->key);
612 ASSERT_EQ(node->display_text, node2->display_text);
613 ASSERT_EQ(node->sort_text, node2->sort_text);
614 }
615 for (CollectionItem *node : model1->container_nodes(i).values()) {
616 ASSERT_TRUE(model3->container_nodes(i).keys().contains(node->key));
617 CollectionItem *node2 = model2->container_nodes(i)[node->key];
618 ASSERT_EQ(node->key, node2->key);
619 ASSERT_EQ(node->display_text, node2->display_text);
620 ASSERT_EQ(node->sort_text, node2->sort_text);
621 }
622
623 for (CollectionItem *node : model2->container_nodes(i).values()) {
624 ASSERT_TRUE(model1->container_nodes(i).keys().contains(node->key));
625 CollectionItem *node2 = model2->container_nodes(i)[node->key];
626 ASSERT_EQ(node->key, node2->key);
627 ASSERT_EQ(node->display_text, node2->display_text);
628 ASSERT_EQ(node->sort_text, node2->sort_text);
629 }
630 for (CollectionItem *node : model2->container_nodes(i).values()) {
631 ASSERT_TRUE(model3->container_nodes(i).keys().contains(node->key));
632 CollectionItem *node2 = model2->container_nodes(i)[node->key];
633 ASSERT_EQ(node->key, node2->key);
634 ASSERT_EQ(node->display_text, node2->display_text);
635 ASSERT_EQ(node->sort_text, node2->sort_text);
636 }
637
638 for (CollectionItem *node : model3->container_nodes(i).values()) {
639 ASSERT_TRUE(model1->container_nodes(i).keys().contains(node->key));
640 CollectionItem *node2 = model2->container_nodes(i)[node->key];
641 ASSERT_EQ(node->key, node2->key);
642 ASSERT_EQ(node->display_text, node2->display_text);
643 ASSERT_EQ(node->sort_text, node2->sort_text);
644 }
645 for (CollectionItem *node : model3->container_nodes(i).values()) {
646 ASSERT_TRUE(model2->container_nodes(i).keys().contains(node->key));
647 CollectionItem *node2 = model2->container_nodes(i)[node->key];
648 ASSERT_EQ(node->key, node2->key);
649 ASSERT_EQ(node->display_text, node2->display_text);
650 ASSERT_EQ(node->sort_text, node2->sort_text);
651 }
652 }
653
654 QSignalSpy database_reset_1(backend1.get(), &CollectionBackend::DatabaseReset);
655 QSignalSpy database_reset_2(backend2.get(), &CollectionBackend::DatabaseReset);
656 QSignalSpy database_reset_3(backend3.get(), &CollectionBackend::DatabaseReset);
657
658 backend1->DeleteAll();
659 backend2->DeleteAll();
660 backend3->DeleteAll();
661
662 ASSERT_EQ(database_reset_1.count(), 1);
663 ASSERT_EQ(database_reset_2.count(), 1);
664 ASSERT_EQ(database_reset_3.count(), 1);
665
666 // Make sure all nodes are deleted.
667
668 ASSERT_EQ(model1->container_nodes(0).count(), 0);
669 ASSERT_EQ(model1->container_nodes(1).count(), 0);
670 ASSERT_EQ(model1->container_nodes(2).count(), 0);
671
672 ASSERT_EQ(model2->container_nodes(0).count(), 0);
673 ASSERT_EQ(model2->container_nodes(1).count(), 0);
674 ASSERT_EQ(model2->container_nodes(2).count(), 0);
675
676 ASSERT_EQ(model3->container_nodes(0).count(), 0);
677 ASSERT_EQ(model3->container_nodes(1).count(), 0);
678 ASSERT_EQ(model3->container_nodes(2).count(), 0);
679
680 ASSERT_EQ(model1->song_nodes().count(), 0);
681 ASSERT_EQ(model2->song_nodes().count(), 0);
682 ASSERT_EQ(model3->song_nodes().count(), 0);
683
684 ASSERT_EQ(model1->divider_nodes_count(), 0);
685 ASSERT_EQ(model2->divider_nodes_count(), 0);
686 ASSERT_EQ(model3->divider_nodes_count(), 0);
687
688 backend1->Close();
689 backend2->Close();
690 backend3->Close();
691
692 }
693 }
694 }
695
696 }
697 #endif
698
699 } // namespace
700