RSS

(root)/calliope : /tests/musicsource-tests.c (revision 453)

Line Revision Contents
1 185 /*  Calliope Music Player
2  *  Copyright 2005-09 Sam Thursfield <ssssam gmail.com>
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15 269  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 185  */
17
18 /* musicsource-tests: tests applicable to all music sources. */
19 269
20 185 #include <glib.h>
21 #include <glib/gstdio.h>
22 #include <gtk/gtk.h>
23 #include "conftool.h"
24 #include "musicsource.h"
25 #include "test-utils.h"
26 #include "musicsource-tests.h"
27
28 MusicSource *(*source_constructor)();
29
30 445 void test_checkin_1 () {
31 336         MusicSource *source = source_constructor();
32 342
33 339         // Composition and artist don't get modified (and thus checked out again) when they are added
34         // to a source. Recording at the moment does (to set default file) and some other entries do.
35 336         Entry *artist = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test"),
36               *composition = entry_new(ENTRY_TYPE_COMPOSITION, 0, NULL, "test");
37         entry_take_property (composition, COMPOSITION_ARTIST, artist);
38         music_source_add_entry (source, composition);
39 342
40 336         // 1. Simplest test case
41         artist = music_source_checkout_entry (source, ENTRY_TYPE_ARTIST, 1, "test");
42         music_source_checkin_entry (source, artist, "test");
43 342
44 336         // 2. A pair tree.
45         composition = music_source_checkout_entry (source, ENTRY_TYPE_COMPOSITION, 1, "test");
46         music_source_checkin_entry (source, composition, "test");
47 342
48 339         // 3. Adding a song (which could involve checking things out).
49 336         test_add_song (source, 1, 1, 1, 0, 0);
50
51 339         // 4. A song tree.
52 336         Entry *file = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test");
53         music_source_checkin_entry (source, file, "test");
54 342
55 336         g_object_unref (source); _entry_cleanup ();
56 };
57
58 445 void test_checkin_2 () {
59         MusicSource *source = source_constructor();
60
61         test_add_song (source, 1, 1, 1, 0, 0);
62         test_add_song (source, 2, 2, 2, 0, 0);
63
64         // There is a danger of the old artist not being unreffed properly here.
65         Entry *recording = music_source_checkout_entry (source, ENTRY_TYPE_RECORDING, 2, "test"),
66               *artist    = entry_get_property (recording, RECORDING_ARTIST);
67
68         entry_set_property (artist, ARTIST_NAME, "Test Artist 000001");
69         music_source_checkin_entry (source, recording, "test");
70
71         g_object_unref (source); _entry_cleanup ();
72 };
73
74 void test_checkin_3 () {
75         MusicSource *source = source_constructor();
76
77         test_add_song (source, 1, 1, 1, 1, 1);
78         test_add_song (source, 2, 2, 2, 2, 1);
79
80         void track_notify (MusicSource *self, Entry *old_entry, Entry *new_entry, void *user_data) {
81                 Entry *track   = music_source_checkout_entry (source, ENTRY_TYPE_TRACK,   1, "test"),
82                       *release = music_source_query_entry    (source, ENTRY_TYPE_RELEASE, 2, "test");
83                 entry_take_property (track, TRACK_RELEASE, release);
84                 music_source_checkin_entry (source, track, "test");
85         }
86
87 451         /*music_source_connect_entry_notify (source, ENTRY_TYPE_TRACK, FALSE, track_notify, NULL);*/
88 445
89         // This is a dummy edit to trigger the notify.
90         Entry *track = music_source_checkout_entry (source, ENTRY_TYPE_TRACK, 1, "test");
91         entry_set_property (track, TRACK_NUMBER, GINT_TO_POINTER (15));
92         music_source_checkin_entry (source, track, "test");
93
94 451         //g_assert_cmpint (music_source_get_n_entries (source, ENTRY_TYPE_RELEASE, NULL, NULL), ==, 1);
95 445
96         g_object_unref (source); _entry_cleanup ();
97 };
98
99 358 /* test_notify: test that entry notifications are emitted properly in simple cases. uses artist
100  *              entries because these don't have any internal triggers. */
101 void test_notify_1 () {
102         typedef enum { TEST_ADDING, TEST_CHANGING, TEST_REMOVING, TEST_FINISHED } TestState;
103         TestState test_state = TEST_ADDING;
104
105         MusicSource *source = source_constructor();
106         void artist_notify (MusicSource *music_source, Entry *old_entry, Entry *current_entry,
107                             void *user_data) {
108                 g_assert (music_source == source);
109                 g_assert (user_data == (void *)5);
110
111                 switch (test_state) {
112                         case TEST_ADDING:
113                                 g_assert (old_entry==NULL);
114                                 g_assert (current_entry->type==ENTRY_TYPE_ARTIST);
115                                 g_assert (current_entry->id==3);
116                                 g_assert_cmpstr (entry_get_property(current_entry, ARTIST_NAME), ==, "Test Artist");
117                                 test_state = TEST_CHANGING;
118                                 break;
119
120                         case TEST_CHANGING:
121                                 g_assert (old_entry->type==ENTRY_TYPE_ARTIST);
122                                 g_assert (old_entry->id==3);
123                                 g_assert (current_entry->type==ENTRY_TYPE_ARTIST);
124                                 g_assert (current_entry->id==3);
125                                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Test Artist");
126                                 g_assert_cmpstr (entry_get_property(current_entry, ARTIST_NAME), ==, "Robot Faces");
127
128                                 test_state = TEST_REMOVING;
129                                 break;
130
131                         case TEST_REMOVING:
132                                 g_assert (current_entry == NULL);
133                                 g_assert (old_entry->type==ENTRY_TYPE_ARTIST);
134                                 g_assert (old_entry->id==3);
135                                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Robot Faces");
136
137                                 test_state = TEST_FINISHED;
138                                 break;
139                 };
140         };
141
142         music_source_connect_entry_notify (source, ENTRY_TYPE_ARTIST, TRUE, artist_notify, (void *)5);
143
144         // Add a composition as well as an artist, to make sure the signal isn't emitted twice
145         Entry *artist = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test"),
146               *composition = entry_new(ENTRY_TYPE_COMPOSITION, 0, NULL, "test");
147         entry_set_property (artist, ARTIST_NAME, "Test Artist");
148         entry_set_property (composition, COMPOSITION_NAME, "Test Composition");
149         entry_take_property (composition, COMPOSITION_ARTIST, artist);
150
151         int id = music_source_add_entry (source, composition);
152         g_assert (id == 1);
153         g_assert (test_state == TEST_CHANGING);   // Callback should have advanced state
154
155         artist = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
156         entry_set_property (artist, ARTIST_NAME, "Robot Faces");
157         music_source_checkin_entry (source, artist, "test");
158         g_assert (test_state == TEST_REMOVING);
159
160         music_source_remove_entry (source, ENTRY_TYPE_ARTIST, 3);
161         g_assert (test_state == TEST_FINISHED);
162
163         g_object_unref(source);
164         _entry_cleanup();
165 };
166
167 /* test_notify_2: A more thorough test involving notifies on multiple versions. If a notify callback
168  *                modifies the entry it triggered on, the notifys already called have to be
169  *                re-called with the new version, but those that haven't already been called still
170  *                only need to be called once - but with the original value of old_entry. */
171 void test_notify_2 () {
172         void artist_notify_1 (MusicSource *source, Entry *old_entry, Entry *new_entry,
173                               void *user_data) {
174                 static int called_times = 0;
175                 switch (called_times) {
176                         case 0:
177                                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Test Artist 1");
178                                 g_assert_cmpstr (entry_get_property(new_entry, ARTIST_NAME), ==, "Test Artist 2");
179                                 break;
180                         case 1:
181                                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Test Artist 2");
182                                 g_assert_cmpstr (entry_get_property(new_entry, ARTIST_NAME), ==, "Test Artist 3");
183                                 break;
184                         default:
185                                 g_assert_not_reached ();
186                 };
187                 called_times ++;
188         };
189
190         void artist_notify_2 (MusicSource *source, Entry *old_entry, Entry *new_entry,
191                               void *user_data) {
192                 static int called_times = 0; g_assert (called_times == 0);
193                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Test Artist 1");
194                 g_assert_cmpstr (entry_get_property(new_entry, ARTIST_NAME), ==, "Test Artist 2");
195                 Entry *checked_out = music_source_checkout_entry(source, new_entry->type,
196                                                                  new_entry->id, "test");
197 360                 g_assert_cmpstr (entry_get_property(checked_out, ARTIST_NAME), ==, "Test Artist 2");
198 358                 entry_set_property (checked_out, ARTIST_NAME, "Test Artist 3");
199                 music_source_checkin_entry (source, checked_out, "test");
200                 called_times ++;
201         };
202
203         void artist_notify_3 (MusicSource *source, Entry *old_entry, Entry *new_entry,
204                               void *user_data) {
205                 static int called_times = 0; g_assert (called_times == 0);
206                 g_assert_cmpstr (entry_get_property(old_entry, ARTIST_NAME), ==, "Test Artist 1");
207                 g_assert_cmpstr (entry_get_property(new_entry, ARTIST_NAME), ==, "Test Artist 3");
208                 called_times ++;
209         };
210
211         MusicSource *source = source_constructor();
212         Entry *artist = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test");
213         entry_set_property (artist, ARTIST_NAME, "Test Artist 1");
214 360         int id = music_source_add_entry(source, artist);
215 358         g_assert (id==3);
216         music_source_connect_entry_notify (source, ENTRY_TYPE_ARTIST, FALSE, artist_notify_1, NULL);
217         music_source_connect_entry_notify (source, ENTRY_TYPE_ARTIST, FALSE, artist_notify_2, NULL);
218         music_source_connect_entry_notify (source, ENTRY_TYPE_ARTIST, TRUE, artist_notify_3, NULL);
219
220         Entry *checked_out = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
221 360         g_assert_cmpstr (entry_get_property(checked_out, ARTIST_NAME), ==, "Test Artist 1");
222 358         entry_set_property (checked_out, ARTIST_NAME, "Test Artist 2");
223         music_source_checkin_entry (source, checked_out, "test");
224 361
225 360         artist = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
226         g_assert_cmpstr (entry_get_property(artist, ARTIST_NAME), ==, "Test Artist 3");
227         entry_unref (artist, "test");
228 358
229         g_object_unref (source);
230 361         _entry_cleanup();
231 };
232
233
234 /* From musicsourceview/vertical/Removing 2 */
235 static void test_notify_3 (void) {
236         int notify_received = 0;
237         void notify (MusicSource *source, Entry *old_entry, Entry *new_entry, void *user_data) {
238                 //printf ("Notify: %s %i -> %s %i.\n", ENTRY_PF(old_entry), ENTRY_PF(new_entry));
239                 if (old_entry!=NULL && old_entry->type==ENTRY_TYPE_FILE && old_entry->id==1)
240                         notify_received ++;
241         };
242
243         MusicSource *source = source_constructor ();
244
245         music_source_begin_transaction (source);
246         test_add_song (source, 1, 1, 1, 1, 1);
247         test_add_song (source, 1, 1, 1, 2, 1);
248         test_add_song (source, 1, 1, 2, 1, 2);
249         music_source_end_transaction (source);
250
251         music_source_connect_entry_notify (source, -1, TRUE, notify, NULL);
252         music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
253         g_assert_cmpint (notify_received, ==, 1);
254
255         music_source_remove_entry (source, ENTRY_TYPE_FILE, 2);
256
257         // FIXME: assert is_empty
258
259         g_object_unref (source);
260         _entry_cleanup ();
261 };
262 358
263 451 /* noitfy_4: The source should notify if a shadowing property changes in an entry, even if that
264  *           entry wasn't checked out. */
265 372 static void test_notify_4 (void) {
266         gboolean recording_notify_received = FALSE;
267         void notify (MusicSource *source, Entry *old_entry, Entry *new_entry, void *user_data) {
268                 //printf ("Notify: %s %i -> %s %i.\n", ENTRY_PF(old_entry), ENTRY_PF(new_entry));
269                 if (old_entry!=NULL && new_entry!=NULL)
270                         recording_notify_received = TRUE;
271         };
272
273         MusicSource *source = source_constructor ();
274
275         music_source_begin_transaction (source);
276         test_add_song (source, 1, 1, 1, 0, 0);
277         music_source_end_transaction (source);
278
279         music_source_connect_entry_notify (source, ENTRY_TYPE_RECORDING, TRUE, notify, NULL);
280
281         Entry *composition_1 = music_source_checkout_entry(source, ENTRY_TYPE_COMPOSITION, 1, "test");
282         entry_set_property (composition_1, COMPOSITION_NAME, "Test Composition Foo");
283         music_source_checkin_entry (source, composition_1, "test");
284
285 451         // Recording 1 wasn't checked out, but its 'name' property should have been updated to mirror
286         // change to its composition's name.
287 372         Entry *recording_1 = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 1, "test");
288         g_assert_cmpstr (entry_get_property(recording_1, RECORDING_NAME), ==, "Test Composition Foo");
289         entry_unref (recording_1, "test");
290
291         g_assert (recording_notify_received == TRUE);
292
293         g_object_unref (source);
294         _entry_cleanup ();
295 };
296
297 415
298 void test_removal() {
299         MusicSource *source = source_constructor();
300
301         music_source_begin_transaction (source);
302         test_add_song (source, 1, 1, 1, 1, 1);
303         music_source_end_transaction (source);
304
305         music_source_remove_entry (source, ENTRY_TYPE_ARTIST, 3);
306
307         //_music_source_dump (source, FALSE);
308         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_ARTIST, NULL, NULL), ==, 2);
309         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_COMPOSITION, NULL, NULL), ==, 0);
310         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_RECORDING, NULL, NULL), ==, 0);
311         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_FILE, NULL, NULL), ==, 0);
312         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_ALBUM, NULL, NULL), ==, 0);
313         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_RELEASE, NULL, NULL), ==, 0);
314         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_TRACK, NULL, NULL), ==, 0);
315
316         g_object_unref (source);
317         _entry_cleanup();
318 };
319
320 /* Test that the entry we receive hasn't been altered. */
321 void test_removal_notify () {
322         MusicSource *source = source_constructor();
323
324         test_add_song (source, 1, 1, 1, 1, 1);
325
326 417         void notify (MusicSource *source, Entry *old_entry, Entry *new_entry, void *user_data) {
327 415                 g_assert (new_entry == NULL);   // Everything should be being removed.
328
329                 switch (old_entry->type) {
330                         case ENTRY_TYPE_FILE:
331                                 g_assert (entry_get_property(old_entry, FILE_RECORDING) != NULL);
332                                 break;
333                         case ENTRY_TYPE_RECORDING:
334                                 g_assert (entry_get_property(old_entry, RECORDING_COMPOSITION) != NULL);
335                                 g_assert (entry_get_property(old_entry, RECORDING_ARTIST) != NULL);
336                                 break;
337                         case ENTRY_TYPE_COMPOSITION:
338                                 g_assert (entry_get_property(old_entry, COMPOSITION_ARTIST) != NULL);
339                                 break;
340                         case ENTRY_TYPE_TRACK:
341                                 g_assert (entry_get_property(old_entry, TRACK_RECORDING) != NULL);
342                                 g_assert (entry_get_property(old_entry, TRACK_RELEASE) != NULL);
343                                 break;
344                         case ENTRY_TYPE_RELEASE:
345                                 g_assert (entry_get_property(old_entry, RELEASE_ALBUM) != NULL);
346                                 break;
347                         case ENTRY_TYPE_ALBUM:
348                                 g_assert (entry_get_property(old_entry, ALBUM_ARTIST) != NULL);
349                                 break;
350                 };
351         };
352
353         music_source_connect_entry_notify (source, -1, TRUE, notify, NULL);
354         music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 1);
355         
356         g_object_unref (source);
357 };
358
359 270 /* Test that entries that were checked out as part of a tree but not checked back in (because they
360  * were replaced with a different entry) are handled properly - they should be checked to see if
361  * they are still referenced anywhere and removed if not. */
362 void test_pruning_1 () {
363 269         MusicSource *source = source_constructor();
364         music_source_begin_transaction (source);
365         test_add_song (source, 1, 1, 1, 0, 0);
366         music_source_end_transaction (source);
367
368         // Now, let's try changing the artist ..
369 336         Entry *file = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test");
370 269
371         Entry *new_artist = entry_new (ENTRY_TYPE_ARTIST, 0, NULL, "test");
372         entry_set_property (new_artist, ARTIST_NAME, "Test Artist 2");
373
374         Entry *composition  = entry_get_distant_property(file, _RECORDING_COMPOSITION);
375 273         entry_take_property (composition, COMPOSITION_ARTIST, new_artist);
376 269
377         music_source_checkin_entry (source, file, "test");
378 342
379 346         //_music_source_dump (source, TRUE);
380 269         // File's original artist should have been deleted, because nobody needed it.
381 280         g_assert_cmpint (music_source_get_n_entries(source, ENTRY_TYPE_ARTIST, NULL, NULL), ==, 3);
382 269
383         g_object_unref (source);
384 273         _entry_cleanup();
385 269 };
386
387 270 /* Don't be premature in the pruning! */
388 void test_pruning_2 () {
389         MusicSource *source = source_constructor();
390         music_source_begin_transaction (source);
391         test_add_song (source, 1, 1, 1, 1, 1);
392         test_add_song (source, 1, 1, 2, 1, 1);
393         music_source_end_transaction (source);
394
395 452         // Check out the same entry twice in two seperate checkouts! (The two files have the same
396         // recording and composition).
397 270         Entry *file_1 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test");
398         g_assert (file_1!=NULL);
399
400 339         Entry *file_2 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 2, "test");
401         g_assert (file_2!=NULL);
402 270
403 452         // Set a new recording for one of the files.
404 270         Entry *old_recording   = entry_get_property (file_1, FILE_RECORDING),
405               *old_composition = entry_get_property (old_recording, RECORDING_COMPOSITION),
406               *new_recording   = entry_new (ENTRY_TYPE_RECORDING, 0, NULL, "test");
407         entry_set_property (new_recording, RECORDING_COMPOSITION, old_composition);
408
409 339         entry_take_property (file_1, FILE_RECORDING, new_recording);
410 270
411 452         // Here a poor implementation might confuse itself terribly. file 2 is still checked out and its
412         // recording is still recording 1.
413 270         music_source_checkin_entry (source, file_1, "test");
414 271
415 452         // Check file 1 hasn't been mangled already.
416         file_1 = music_source_query_entry (source, ENTRY_TYPE_FILE, 1, "test");
417         entry_check (file_1);
418         entry_unref (file_1, "test");
419
420 339         Entry *composition_2 = entry_get_distant_property(file_2, _RECORDING_COMPOSITION);
421 284
422 271         // Let's do some shadowing to make things even harder.
423 339         entry_set_property (composition_2, COMPOSITION_NAME, "Test Test");
424 452
425 339         music_source_checkin_entry (source, file_2, "test");
426 342
427 271         g_object_unref (source);
428 273         _entry_cleanup();
429 271 };
430
431 318 /* Modify a checked out entry so that the jetsam is composition 1 and artist 3 - composition
432  * 1 also points to artist 3, so this requires that when an entry is removed it's also
433  * removed from the jetsam list. */
434 271 void test_pruning_3 () {
435         MusicSource *source = source_constructor();
436         music_source_begin_transaction (source);
437 318         test_add_song (source, 1, 1, 1, 0, 0);
438 271         music_source_end_transaction (source);
439
440 318         Entry *file_entry = music_source_checkout_entry(source, ENTRY_TYPE_FILE, 1, "test");
441
442         Entry *recording_entry = entry_get_property(file_entry, FILE_RECORDING);
443         Entry *composition_entry = entry_get_property(recording_entry, RECORDING_COMPOSITION);
444         Entry *artist_entry = entry_get_property(composition_entry, COMPOSITION_ARTIST);
445
446         composition_entry = entry_new(ENTRY_TYPE_COMPOSITION, 0, NULL, "test");
447 339         entry_set_property(composition_entry, COMPOSITION_NAME, "New Test Composition");
448 318         entry_take_property(recording_entry, RECORDING_COMPOSITION, composition_entry);
449
450         artist_entry = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test");
451 339         entry_set_property (artist_entry, ARTIST_NAME, "New Test Artist");
452 318         entry_take_property (composition_entry, COMPOSITION_ARTIST, artist_entry);
453
454         music_source_checkin_entry(source, file_entry, "test");
455 270
456         g_object_unref (source);
457 273         _entry_cleanup();
458 270 };
459
460
461 185 // Test that shadowed properties are correctly returned when entries are queried.
462 453 void test_shadowing_1 () {
463 185         MusicSource *source = source_constructor();
464
465         music_source_begin_transaction (source);
466         test_add_song (source, 1, 1, 1, 1, 1);
467         test_add_song (source, 2, 2, 2, 2, 1);
468         music_source_end_transaction (source);
469
470         Entry *artist_1, *artist_2;
471 269
472 452         void assert_state(gboolean artist_two_sortname_changed, gboolean artist_names_changed,
473                           gboolean recording_2_artist_changed) {
474 185                 // Test that shadow properties are filled properly.
475 269                 artist_1 = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
476 280                 g_assert_cmpstr (entry_get_property(artist_1, ARTIST_NAME), ==,
477                                  entry_get_property(artist_1, ARTIST_SORTNAME));
478 185                 if (artist_names_changed)
479                         g_assert_cmpstr (entry_get_property(artist_1, ARTIST_NAME), ==, "Test Artist One");
480                 else g_assert_cmpstr (entry_get_property(artist_1, ARTIST_NAME), ==, "Test Artist 000001");
481 269
482 185                 // Test that shadow properties are filled properly.
483 269                 artist_2 = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 4, "test");
484 185                 if (!artist_two_sortname_changed)
485 280                         g_assert_cmpstr (entry_get_property(artist_2, ARTIST_NAME), ==,
486                                          entry_get_property(artist_2, ARTIST_SORTNAME));
487 185                 else {
488                         if (artist_names_changed)
489                                 g_assert_cmpstr (entry_get_property(artist_2, ARTIST_NAME), ==, "Test Artist Two");
490                         else
491                                 g_assert_cmpstr (entry_get_property(artist_2, ARTIST_NAME), ==, "Test Artist 000002");
492                         g_assert_cmpstr (entry_get_property(artist_2, ARTIST_SORTNAME), ==, "Two, Test Artist");
493                 };
494 269
495 185                 // And, shadow properties in other entries, which are entries themselves.
496                 Entry *recording_1 = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 1, "test"),
497                           *composition_1 = entry_get_property(recording_1, RECORDING_COMPOSITION);
498 280                 g_assert_cmpstr (entry_get_property(recording_1, RECORDING_NAME), ==,
499                                  entry_get_property(composition_1, COMPOSITION_NAME));
500                 g_assert_cmphex ((guint)entry_get_property(recording_1, RECORDING_ARTIST), ==,
501                                  (guint)entry_get_property(composition_1, COMPOSITION_ARTIST));
502 185                 entry_unref (recording_1, "test");
503
504                 Entry *recording_2 = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 2, "test"),
505 269                           *composition_2 = entry_get_property(recording_2, RECORDING_COMPOSITION);
506 185                 g_assert_cmphex ((guint)entry_get_property(composition_2, COMPOSITION_ARTIST), ==,
507                                  (guint)artist_2);
508                 if (!recording_2_artist_changed)
509                         g_assert_cmphex ((guint)entry_get_property(recording_2, RECORDING_ARTIST), ==,
510                                          (guint)artist_2);
511                 else {
512                         g_assert_cmphex ((guint)entry_get_property(recording_2, RECORDING_ARTIST), ==,
513                                          (guint)artist_1);
514                 };
515
516                 entry_unref (artist_1, "test");
517 269                 entry_unref (artist_2, "test");
518 185                 entry_unref (recording_2, "test");
519         };
520 269
521 280         assert_state (FALSE, FALSE, FALSE);
522         music_source_flush (source);        // Test that shadow properties are filled properly from db
523         assert_state (FALSE, FALSE, FALSE); // as well as cache.
524 269
525 185         // Now artist 2 has a different sort name to its name
526         artist_2 = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 4, "test");
527 339         g_assert_cmpstr (entry_get_property(artist_2, ARTIST_SORTNAME), ==, "Test Artist 000002");
528 185         entry_set_property(artist_2, ARTIST_SORTNAME, "Two, Test Artist");
529         music_source_checkin_entry(source, artist_2, "test");
530 269
531 452         //printf ("\nArtist 2 sortname changed, no longer shadowing."); fflush (stdout);
532 269         assert_state(TRUE, FALSE, FALSE);
533 185         music_source_flush (source);
534 452         //printf ("\nFlushed cache (Artist 2 sortname no longer shadowing)"); fflush (stdout);
535 185         assert_state(TRUE, FALSE, FALSE);
536 269
537 342         // Change the names of both artists. Artist 1's sortname should follow, since it's still
538 339         // shadowing, but artist 2's should stay as it is because it's no longer shadowing (it's been
539         // changed).
540 185         artist_1 = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
541         entry_set_property(artist_1, ARTIST_NAME, "Test Artist One");
542         music_source_checkin_entry(source, artist_1, "test");
543         artist_2 = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 4, "test");
544         entry_set_property(artist_2, ARTIST_NAME, "Test Artist Two");
545         music_source_checkin_entry(source, artist_2, "test");
546 269
547 452         //printf ("\nBoth artist names changed."); fflush (stdout);
548 269         assert_state(TRUE, TRUE, FALSE);
549 452         music_source_flush (source);
550         //printf ("\nFlushed cache (Both artist names changed)"); fflush (stdout);
551 185         assert_state(TRUE, TRUE, FALSE);
552 269
553 185         // Make song two be recorded by a different artist to its composer.
554         Entry *recording_2 = music_source_checkout_entry(source, ENTRY_TYPE_RECORDING, 2, "test");
555 269         artist_1 = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
556 185         entry_set_property(recording_2, RECORDING_ARTIST, artist_1);
557         entry_unref (artist_1, "test");
558         music_source_checkin_entry(source, recording_2, "test");
559 269
560         assert_state(TRUE, TRUE, TRUE);
561 185         music_source_flush (source);
562 452         assert_state(TRUE, TRUE, TRUE);
563 185
564         g_object_unref (source);
565 273         _entry_cleanup();
566 185 };
567
568 453 void test_shadowing_2 () {
569         MusicSource *source = source_constructor();
570
571         music_source_begin_transaction (source);
572         test_add_song (source, 1, 1, 1, -1, -1);
573         test_add_song (source, 1, 2, 2, -1, -1);
574         music_source_end_transaction (source);
575
576         // There should be two recordings for artist 3. recording.artist is a shadowing property.
577         // FIXME: deprecate this function & use query_relations instead
578         GSList *list = music_source_query_entry_children_ids (source, 3, ENTRY_TYPE_RECORDING,
579                                                               RECORDING_ARTIST);
580         g_assert_cmpint (g_slist_length (list), ==, 2);
581         g_slist_free (list);
582
583         g_object_unref (source);
584         _entry_cleanup();
585
586 };
587 269
588 185 void test_relations() {
589         MusicSource *source = source_constructor();
590
591         music_source_begin_transaction (source);
592         test_add_song (source, 1, 1, 1, 1, 1);
593 269         test_add_song (source, 1, 2, 2, 1, 2);
594         test_add_song (source, 1, 2, 2, 2, 1);
595 185         music_source_end_transaction (source);
596 269
597 185         // Should be one relation of track 2: recording 2.
598 269         GSList *relations = music_source_query_relations(source, ENTRY_TYPE_TRACK, 2,
599 185                                                          _TRACK_RECORDING, "test");
600         g_assert_cmpint (g_slist_length(relations), ==, 1);
601 273         entry_query_list_free (relations, "test");
602 269
603 185         // Should be two relations of recording 2: tracks 2 and 3.
604 269         relations = music_source_query_relations(source, ENTRY_TYPE_RECORDING, 2,
605 185                                                  _TRACK_RECORDING, "test");
606         g_assert_cmpint (g_slist_length(relations), ==, 2);
607 273         entry_query_list_free (relations, "test");
608 269
609 185         g_object_unref (source);
610 273         _entry_cleanup();
611 185 };
612
613 358
614
615 /* FIXME: eventually there should actually be an algorithm for choosing the best default file. I
616  *        guess highest bitrate is the key, but there should be a points system so for instance mono
617  *        versions in higher bitrates aren't preferred, unless you are listening on a mono system,
618  *        etc ... this is pretty fanciful actually. */
619 void test_recording_watch() {
620         MusicSource *source = source_constructor();
621
622         test_add_song (source, 1, 1, 1, 0, 0);
623         test_add_song (source, 1, 1, 2, 0, 0);
624
625         // Check file 1 was set as the recording's default.
626         Entry *recording = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 1, "test"),
627               *default_file = entry_get_property(recording, RECORDING_DEFAULT_FILE);
628
629         g_assert (default_file != NULL);
630         g_assert_cmpint (default_file->id, ==, 1);
631         g_assert_cmpstr (entry_get_property(default_file, FILE_PATH), ==, "Test File 000001 (000001)");
632         entry_unref (recording, "test");
633
634         // Remove file 1 and see that file 2 is now the default.
635         music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
636 452         recording = music_source_query_entry (source, ENTRY_TYPE_RECORDING, 1, "test");
637         g_assert (recording != NULL);
638
639 358         default_file = entry_get_property(recording, RECORDING_DEFAULT_FILE);
640         g_assert (default_file != NULL);
641         g_assert_cmpint (default_file->id, ==, 2);
642         g_assert_cmpstr (entry_get_property(default_file, FILE_PATH), ==,
643                          "Test File 000002 (000001)");
644         entry_unref (recording, "test");
645
646         g_object_unref(source);
647         _entry_cleanup();
648 };
649
650 269 /* When tracks are added with songs by different artists, but they appear to be the same album,
651  * the album artist should become 'Various Artists' ... */
652 445 void test_album_watch_1() {
653 269         MusicSource *source = source_constructor();
654
655         test_add_song (source, 2, 2, 2, 1, 2);  // various artists album.
656         test_add_song (source, 3, 3, 3, 1, 3);
657
658 445         // Should be a VA album at the moment.
659         Entry *album        = music_source_query_entry (source, ENTRY_TYPE_ALBUM, 1, "test"),
660               *album_artist = entry_get_property       (album, ALBUM_ARTIST);
661         g_assert_cmpint (album_artist->id, ==, ARTIST_ID_VARIOUS);
662         entry_unref (album, "test");
663
664 452         // And, there should be only one album entry.
665         g_assert_cmpint (music_source_get_n_entries (source, ENTRY_TYPE_ALBUM, NULL, NULL), ==, 1);
666
667
668         // Give track 2 the same artist name as track 1. Now artists 3 & 4 should merge, and
669         // the album name should change from 'Various Artists' back to 'Test Artist 000002'.
670 445         Entry *track     = music_source_checkout_entry (source, ENTRY_TYPE_TRACK, 2, "test"),
671               *recording = entry_get_property (track,     TRACK_RECORDING),
672               *artist    = entry_get_property (recording, RECORDING_ARTIST);
673
674         entry_set_property (artist, ARTIST_NAME, "Test Artist 000002");
675         music_source_checkin_entry (source, track, "test");
676
677 452         // Check that the artists merged, now that their names are the same.
678 451         recording = music_source_query_entry (source, ENTRY_TYPE_RECORDING, 2, "test");
679         artist    = entry_get_property (recording, RECORDING_ARTIST);
680         g_assert_cmpint (artist->id, ==, 3);
681         entry_unref (recording, "test");
682
683 452         // Check that the album has changed from VA back to being a single artist.
684 445         album        = music_source_query_entry (source, ENTRY_TYPE_ALBUM, 1, "test");
685         album_artist = entry_get_property       (album, ALBUM_ARTIST);
686         g_assert_cmpint (album_artist->id, ==, 3);
687
688         entry_unref (album, "test");
689 269
690         g_object_unref(source);
691 273         _entry_cleanup();
692 269 };
693
694 445
695 185 void music_source_tests_register(const char *root, MusicSource *(*create_func)()) {
696         source_constructor=create_func;
697 269
698 185         char path[256];
699 445         g_test_add_func(PATH_PRINTF("/%s/Checkin 1", root), test_checkin_1);
700         g_test_add_func(PATH_PRINTF("/%s/Checkin 2", root), test_checkin_2);
701         g_test_add_func(PATH_PRINTF("/%s/Checkin 3", root), test_checkin_3);
702 358         g_test_add_func(PATH_PRINTF("/%s/Notify 1", root), test_notify_1);
703         g_test_add_func(PATH_PRINTF("/%s/Notify 2", root), test_notify_2);
704 361         g_test_add_func(PATH_PRINTF("/%s/Notify 3", root), test_notify_3);
705 452         g_test_add_func(PATH_PRINTF("/%s/Notify 4", root), test_notify_4);
706 415         g_test_add_func(PATH_PRINTF("/%s/Removal", root), test_removal);
707         g_test_add_func(PATH_PRINTF("/%s/Removal Notify", root), test_removal_notify);
708 339         g_test_add_func(PATH_PRINTF("/%s/Pruning 1", root), test_pruning_1);
709 452         g_test_add_func(PATH_PRINTF("/%s/Pruning 2", root), test_pruning_2);
710 271         g_test_add_func(PATH_PRINTF("/%s/Pruning 3", root), test_pruning_3);
711 453         g_test_add_func(PATH_PRINTF("/%s/Shadowing 1", root), test_shadowing_1);
712         g_test_add_func(PATH_PRINTF("/%s/Shadowing 2", root), test_shadowing_2);
713 243         g_test_add_func(PATH_PRINTF("/%s/Relations", root), test_relations);
714 452         g_test_add_func(PATH_PRINTF("/%s/Recording Watch", root), test_recording_watch);
715         g_test_add_func(PATH_PRINTF("/%s/Album Watch 1", root), test_album_watch_1);
716 185 };

Loggerhead is a web-based interface for Bazaar branches