| 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 | 261 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | 185 | */ |
| 17 | 261 | |
| 18 | /* Test the vertical performance of a musicsourceview - that it | |
| 19 | 185 | * reads pages in the correct order etc. Prototyped in musicsourceview-tests.h. |
| 20 | */ | |
| 21 | ||
| 22 | #include <stdlib.h> | |
| 23 | #include <glib.h> | |
| 24 | #include <gtk/gtk.h> | |
| 25 | #include "misc.h" | |
| 26 | #include "conftool.h" | |
| 27 | #include "entry.h" | |
| 28 | #include "musicsource.h" | |
| 29 | #include "musicsourceimporting.h" | |
| 30 | #include "musicsourceview.h" | |
| 31 | #include "test-utils.h" | |
| 32 | #include "musicsourceview-tests.h" | |
| 33 | ||
| 34 | // For testing its internals | |
| 35 | #include "musicsourceview-private.h" | |
| 36 | 361 | #include "genericview-private.h" |
| 37 | 185 | |
| 38 | MusicSource *(*source_constructor)(); | |
| 39 | ||
| 40 | ||
| 41 | 406 | /*************************************************************************************************** |
| 42 | * Utility functions | |
| 43 | */ | |
| 44 | ||
| 45 | /* Dynamic tests can run with a GtkTreeView attached to test whether it stays accurate or has some | |
| 46 | * internal brain hemmhorage from receiving the wrong signals. */ | |
| 47 | ||
| 48 | static GtkTreeView *dynamic_view_test_setup (MusicSource *source, const char *config_string) { | |
| 49 | if (source==NULL) | |
| 50 | source = source_constructor(); | |
| 51 | ||
| 52 | ViewConfig *config = view_config_string_parse(config_string); | |
| 53 | MusicSourceView *music_source_view = music_source_create_view(source, config); | |
| 54 | ||
| 55 | 419 | GtkWidget *tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(music_source_view)); |
| 56 | 412 | g_object_ref_sink (tree_view); |
| 57 | 406 | |
| 58 | 442 | _music_source_view_check_tree (music_source_view, NULL); |
| 59 | ||
| 60 | 420 | return GTK_TREE_VIEW(tree_view); |
| 61 | 406 | }; |
| 62 | ||
| 63 | static void dynamic_view_test_teardown (GtkTreeView *tree_view) { | |
| 64 | MusicSourceView *music_source_view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 65 | g_object_unref_many (tree_view, music_source_view, | |
| 66 | music_source_view_get_source(music_source_view), NULL); | |
| 67 | _entry_cleanup (); | |
| 68 | }; | |
| 69 | ||
| 70 | 185 | |
| 71 | ////////////////////////////////////////////// | |
| 72 | // Basic vertical tests. | |
| 73 | // | |
| 74 | ||
| 75 | static void test_flat_with_files(int count) { | |
| 76 | 215 | MusicSource *source = source_constructor(); |
| 77 | 261 | |
| 78 | 185 | // Add count files. |
| 79 | // | |
| 80 | music_source_begin_transaction(source); | |
| 81 | if (count>MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*2) | |
| 82 | printf("\nAdding entries: 0/%i", count); | |
| 83 | 261 | |
| 84 | 185 | for (int i=0;i<count;i++) { |
| 85 | 286 | test_add_song (source, i, i, i, 0, 0); |
| 86 | 185 | if (count>MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*2 && i&15) |
| 87 | printf("\rAdding entries: %i/%i", i, count); | |
| 88 | }; | |
| 89 | music_source_end_transaction(source); | |
| 90 | if (count>MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*2) | |
| 91 | printf("\rRunning %i entries test.. ", count); | |
| 92 | 261 | |
| 93 | 185 | // Create view. |
| 94 | 215 | ViewConfig *config = view_config_string_parse("file[path]"); |
| 95 | 185 | MusicSourceView *view=music_source_create_view(source, config); |
| 96 | _music_source_view_check_tree(view, NULL); | |
| 97 | ||
| 98 | GtkTreeIter iter; | |
| 99 | gboolean result; | |
| 100 | 261 | |
| 101 | 185 | //print_view(view, 5); |
| 102 | 261 | |
| 103 | 185 | // Test we have correct number of children (the estimate should be correct here because |
| 104 | // this is a simple view. | |
| 105 | int n_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 106 | //_music_source_view_dump_node(view, NULL); fflush(stdout); | |
| 107 | //printf("n %i c %i\n", n_children, count); fflush(stdout); | |
| 108 | g_assert_cmpint(n_children, ==, count); | |
| 109 | 261 | |
| 110 | 185 | result=gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &iter, NULL, count); |
| 111 | g_assert_cmpint(result, ==, FALSE); | |
| 112 | 261 | |
| 113 | 185 | result=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); |
| 114 | if (count==0) { | |
| 115 | 261 | g_assert_cmpint(result, ==, FALSE); |
| 116 | 185 | } else { |
| 117 | g_assert_cmpint(result, ==, TRUE); | |
| 118 | 261 | |
| 119 | 185 | // Iterate through the fellow. |
| 120 | // | |
| 121 | // FIXME: how can we test the iter goes into freefall in the right place? Should we, | |
| 122 | // or should we just check the function executes with reasonable speed ? Once we have | |
| 123 | // a testing framework which can track execution speed regressions such a test would | |
| 124 | // be unneccessary I guess. | |
| 125 | 261 | for (int i=0; i<count; i++) { |
| 126 | 221 | result = gtk_tree_model_iter_has_child(GTK_TREE_MODEL(view), &iter); |
| 127 | 185 | g_assert_cmpint(result, ==, FALSE); |
| 128 | 261 | |
| 129 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 130 | 185 | if (i==count-1) |
| 131 | g_assert_cmpint(result, ==, FALSE); | |
| 132 | else | |
| 133 | 261 | g_assert_cmpint(result, ==, TRUE); |
| 134 | 185 | }; |
| 135 | 261 | |
| 136 | 185 | result=gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); |
| 137 | g_assert_cmpint(result, ==, FALSE); | |
| 138 | 261 | |
| 139 | ||
| 140 | 185 | // Pick some entries at random and check it's all good. |
| 141 | // | |
| 142 | int random_rows=count; | |
| 143 | 261 | if (random_rows>10) |
| 144 | 185 | random_rows=MAX(5, count/10); |
| 145 | for (int i=0;i<random_rows;i++) { | |
| 146 | int n=g_test_rand_int_range(0, count); | |
| 147 | result=gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &iter, NULL, n); | |
| 148 | g_assert_cmpint(result, ==, TRUE); | |
| 149 | 261 | |
| 150 | 185 | GValue value={0}; |
| 151 | 286 | gtk_tree_model_get_value(GTK_TREE_MODEL(view), &iter, COLUMN_FILE_PATH, &value); |
| 152 | char *str=g_strdup_printf(TEST_FILE_FORMAT, n, n); | |
| 153 | 185 | g_assert_cmpstr(g_value_get_string(&value), ==, str); |
| 154 | g_free(str); | |
| 155 | }; | |
| 156 | 261 | }; |
| 157 | ||
| 158 | 185 | _music_source_view_check_tree(view, NULL); |
| 159 | g_object_unref(view); | |
| 160 | g_object_unref(source); | |
| 161 | 261 | |
| 162 | 185 | if (count>MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*2) |
| 163 | printf("done\n"); | |
| 164 | 273 | _entry_cleanup (); |
| 165 | 185 | }; |
| 166 | ||
| 167 | ||
| 168 | 261 | static void test_flat() { |
| 169 | 185 | test_flat_with_files(0); |
| 170 | test_flat_with_files(1); | |
| 171 | test_flat_with_files(2); | |
| 172 | test_flat_with_files(3); | |
| 173 | test_flat_with_files(MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/2); | |
| 174 | test_flat_with_files(MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/2+MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE); | |
| 175 | 261 | |
| 176 | 185 | if (g_test_slow()) |
| 177 | test_flat_with_files(MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*100); | |
| 178 | }; | |
| 179 | ||
| 180 | ////////////////////////////////////////////////////// | |
| 181 | // | |
| 182 | ||
| 183 | 259 | #define ADD_RECORDINGS(_n_rec, _n_file) { \ |
| 184 | for (int i=0; i<(_n_rec); i++) \ | |
| 185 | test_add_track_and_recording (source, i*2 + offs, (_n_file)); \ | |
| 186 | offs += (_n_rec)*2; total_files += (_n_rec)*(_n_file); } | |
| 187 | ||
| 188 | 206 | int populate_for_chaining_overestimate(MusicSource *source) { |
| 189 | 259 | music_source_begin_transaction (source); |
| 190 | int offs = 0, total_files = 0; | |
| 191 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10)+1, (MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10)+1); | |
| 192 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1), 1); | |
| 193 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10)+1, (MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10)+1); | |
| 194 | 261 | music_source_end_transaction (source); |
| 195 | 259 | return total_files; |
| 196 | 185 | }; |
| 197 | ||
| 198 | 209 | int populate_for_chaining_underestimate(MusicSource *source) { |
| 199 | 185 | music_source_begin_transaction(source); |
| 200 | 261 | int offs = 0, total_files = 0; |
| 201 | 259 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1), 1); |
| 202 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/5)+1, (MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10)+1); | |
| 203 | ADD_RECORDINGS ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1), 1); | |
| 204 | 261 | music_source_end_transaction (source); |
| 205 | 259 | return total_files; |
| 206 | 185 | }; |
| 207 | ||
| 208 | int populate_for_chaining_4(MusicSource *source) { | |
| 209 | // Create just one recording, and then a whole bunch of tracks which point to it. | |
| 210 | // This gives a situation where two nodes have the same leaf entry, but differ by their | |
| 211 | // intermediate ids! | |
| 212 | // | |
| 213 | music_source_begin_transaction(source); | |
| 214 | 286 | Entry *artist = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test"); |
| 215 | entry_set_property (artist, ARTIST_NAME, "Test Artist"); | |
| 216 | 261 | |
| 217 | 185 | Entry *composition = entry_new (ENTRY_TYPE_COMPOSITION, 0, NULL, "test"); |
| 218 | 286 | entry_set_property (composition, COMPOSITION_ARTIST, artist); |
| 219 | 185 | entry_set_property (composition, COMPOSITION_NAME, "test composition"); |
| 220 | 261 | |
| 221 | 276 | Entry *recording = entry_new(ENTRY_TYPE_RECORDING, 0, NULL, "test"); |
| 222 | 185 | entry_take_property (recording, RECORDING_COMPOSITION, composition); |
| 223 | 276 | entry_set_property (recording, RECORDING_NAME, "test recording"); |
| 224 | int file_id = test_add_file(source, recording, 0, 0); | |
| 225 | entry_unref (recording, "test"); | |
| 226 | 261 | |
| 227 | 276 | Entry *file = music_source_query_entry(source, ENTRY_TYPE_FILE, file_id, "test"); |
| 228 | 185 | recording=entry_get_property(file, FILE_RECORDING); |
| 229 | 261 | |
| 230 | 286 | Entry *album = entry_new(ENTRY_TYPE_ALBUM, 0, NULL, "test"); |
| 231 | entry_set_property (album, ALBUM_NAME, "Test Album"); | |
| 232 | entry_set_property (album, ALBUM_ARTIST, artist); | |
| 233 | ||
| 234 | Entry *release = entry_new(ENTRY_TYPE_RELEASE, 0, NULL, "test"); | |
| 235 | entry_take_property (release, RELEASE_ALBUM, album); | |
| 236 | ||
| 237 | 259 | for (int i=0; i<(MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1)*3; i++) { |
| 238 | 185 | Entry *track=entry_new(ENTRY_TYPE_TRACK, 0, NULL, "test"); |
| 239 | 286 | entry_set_property(track, TRACK_NUMBER, GINT_TO_POINTER(i+1)); |
| 240 | 185 | entry_set_property(track, TRACK_RECORDING, recording); |
| 241 | 286 | entry_set_property(track, TRACK_RELEASE, release); |
| 242 | 185 | music_source_add_entry(source, track); |
| 243 | }; | |
| 244 | 261 | |
| 245 | 286 | entry_unref (file, "test"); |
| 246 | entry_unref (artist, "test"); | |
| 247 | entry_unref (release, "test"); | |
| 248 | 261 | music_source_end_transaction(source); |
| 249 | 185 | return (MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1)*3; |
| 250 | }; | |
| 251 | ||
| 252 | static GtkTreeRowReference **chaining_create_refs(MusicSourceView *view, const int e_children) { | |
| 253 | // Set up some row references - they are tested later. | |
| 254 | 259 | GtkTreeRowReference **row_references = g_new(GtkTreeRowReference *, 3); |
| 255 | 261 | |
| 256 | 185 | GtkTreePath *path = gtk_tree_path_new_first(); |
| 257 | row_references[0] = gtk_tree_row_reference_new(GTK_TREE_MODEL(view), path); | |
| 258 | gtk_tree_path_free (path); | |
| 259 | 261 | |
| 260 | 259 | path = gtk_tree_path_new_from_indices(e_children-1, -1); |
| 261 | row_references[1] = gtk_tree_row_reference_new(GTK_TREE_MODEL(view), path); | |
| 262 | 261 | gtk_tree_path_free (path); |
| 263 | 259 | |
| 264 | path = gtk_tree_path_new_from_indices(MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1, -1); | |
| 265 | row_references[2] = gtk_tree_row_reference_new(GTK_TREE_MODEL(view), path); | |
| 266 | gtk_tree_path_free (path); | |
| 267 | 261 | |
| 268 | 185 | return row_references; |
| 269 | }; | |
| 270 | ||
| 271 | static void chaining_check_refs(MusicSourceView *view, GtkTreeRowReference **row_references, const int n_children) { | |
| 272 | // Check the row references. | |
| 273 | GtkTreePath *path = gtk_tree_row_reference_get_path(row_references[0]); | |
| 274 | int *indices = gtk_tree_path_get_indices(path); | |
| 275 | g_assert_cmpint (indices[0], ==, 0); | |
| 276 | 261 | |
| 277 | 185 | path=gtk_tree_row_reference_get_path(row_references[1]); |
| 278 | indices=gtk_tree_path_get_indices(path); | |
| 279 | g_assert_cmpint(indices[0], ==, n_children-1); | |
| 280 | 261 | |
| 281 | 185 | path=gtk_tree_row_reference_get_path(row_references[2]); |
| 282 | indices=gtk_tree_path_get_indices(path); | |
| 283 | g_assert_cmpint(indices[0], ==, MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE+1); | |
| 284 | ||
| 285 | gtk_tree_row_reference_free(row_references[0]); | |
| 286 | gtk_tree_row_reference_free(row_references[1]); | |
| 287 | gtk_tree_row_reference_free(row_references[2]); | |
| 288 | g_free(row_references); | |
| 289 | }; | |
| 290 | ||
| 291 | 203 | // Chaining one - a static source which appears bigger than it is. |
| 292 | 206 | static void test_chaining_overestimate_static () { |
| 293 | 185 | // Ignore "more than one connection" view config message. |
| 294 | 261 | g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, log_ignore, NULL); |
| 295 | 185 | |
| 296 | MusicSource *source = source_constructor(); | |
| 297 | 206 | int count = populate_for_chaining_overestimate(source); |
| 298 | 185 | |
| 299 | // Create view. | |
| 300 | 215 | ViewConfig *config = view_config_string_parse("track[number]:recording[name]:file[path]"); |
| 301 | 185 | MusicSourceView *view = music_source_create_view(source, config); |
| 302 | _music_source_view_check_tree (view, NULL); | |
| 303 | 261 | |
| 304 | 185 | // Estimate should be exact or way over |
| 305 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 306 | 261 | g_assert_cmpint ((int)e_children, >=, count); |
| 307 | 185 | |
| 308 | 259 | //_music_source_view_dump_node (view, NULL); |
| 309 | ||
| 310 | //printf ("Creating refs...\n"); fflush (stdout); | |
| 311 | 185 | GtkTreeRowReference **row_references = chaining_create_refs(view, e_children); |
| 312 | ||
| 313 | // Let's query the row in the middle! If this doesn't break things, nothing will. This | |
| 314 | // causes the surplus between middle and last pages to be discovered and removed. | |
| 315 | GtkTreeIter iter; | |
| 316 | 259 | //printf ("Querying child %i.\n", e_children/2); fflush (stdout); |
| 317 | 261 | gboolean result = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &iter, NULL, |
| 318 | 259 | e_children / 2); |
| 319 | 185 | g_assert (result==TRUE); |
| 320 | ||
| 321 | 261 | // Now iterate over the surplus between the first and middle pages. |
| 322 | 185 | result = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &iter, NULL, MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/2); |
| 323 | for (int i=0; i<MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE-1; i++) { | |
| 324 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 325 | g_assert (result==TRUE); | |
| 326 | g_assert (iter.user_data2!=NULL); // Should not go into freefall within one page's worth of rows. | |
| 327 | 261 | |
| 328 | 259 | //ViewPage *page = (ViewPage *)iter.user_data2; |
| 329 | //const int page_i = GPOINTER_TO_INT(iter.user_data3); | |
| 330 | //ViewNode *node = g_array_index(page->nodes, ViewNode *, page_i); | |
| 331 | //printf ("%i: [%i] %i\n", i, page_i+page->start_e, node->id); fflush (stdout); | |
| 332 | 185 | }; |
| 333 | 261 | |
| 334 | 185 | // Go through the whole view, checking it's okay. |
| 335 | 261 | _music_source_view_check_tree (view, NULL); |
| 336 | 185 | //_music_source_view_dump_node (view, NULL); |
| 337 | int n_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 338 | //printf("n %i, c %i\n", n_children, count);fflush(stdout); | |
| 339 | 259 | g_assert_cmpint (n_children, ==, count); |
| 340 | 185 | |
| 341 | result = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); | |
| 342 | int i=0; | |
| 343 | while (result!=FALSE) { | |
| 344 | GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(view), &iter); | |
| 345 | int id = music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path); | |
| 346 | //printf("i %i, id %i\n", i, id);fflush(stdout); | |
| 347 | i++; g_assert_cmpint (i, ==, id); | |
| 348 | gtk_tree_path_free (path); | |
| 349 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 350 | }; | |
| 351 | ||
| 352 | 261 | chaining_check_refs (view, row_references, n_children); |
| 353 | 185 | _music_source_view_check_tree (view, NULL); |
| 354 | 261 | |
| 355 | 185 | g_object_unref (view); |
| 356 | 261 | g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, g_log_default_handler, NULL); |
| 357 | g_object_unref (source); | |
| 358 | 273 | _entry_cleanup (); |
| 359 | 185 | }; |
| 360 | ||
| 361 | 203 | |
| 362 | 206 | // Chaining two - adding to a source which appears bigger than it is. |
| 363 | 350 | /*static void test_chaining_overestimate_adding () { |
| 364 | 206 | // Ignore "more than one connection" view config message. |
| 365 | 261 | g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, log_ignore, NULL); |
| 366 | 206 | |
| 367 | MusicSource *source = source_constructor(); | |
| 368 | int file_count = populate_for_chaining_overestimate(source); | |
| 369 | ||
| 370 | // Create view. | |
| 371 | 215 | ViewConfig *config = view_config_string_parse("track[number]:recording[name]:file[path]"); |
| 372 | 206 | MusicSourceView *view = music_source_create_view(source, config); |
| 373 | 261 | _music_source_view_check_tree (view, NULL); |
| 374 | 206 | |
| 375 | // Estimate should be exact or way over | |
| 376 | 208 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL), |
| 377 | e_children_original = e_children; | |
| 378 | 261 | g_assert_cmpint (e_children, >=, file_count); |
| 379 | ||
| 380 | 208 | GtkTreeRowReference **row_references = chaining_create_refs(view, e_children); |
| 381 | 206 | |
| 382 | // Add an entry half way down the last page. | |
| 383 | int recording_count = music_source_get_n_entries(source, ENTRY_TYPE_RECORDING, NULL, NULL); | |
| 384 | 261 | |
| 385 | 206 | int recording_n = recording_count - ((MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE/10) / 2); |
| 386 | if (!(recording_n & 1)) recording_n++; | |
| 387 | test_add_track_and_recording (source, recording_n, 10); | |
| 388 | 261 | _music_source_view_check_tree (view, NULL); |
| 389 | 208 | |
| 390 | // The estimate will probably have shrunk, because while looking for where to insert the | |
| 391 | // previous entries more pages will have been queried. | |
| 392 | // | |
| 393 | e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 394 | 261 | g_assert_cmpint (e_children, >=, file_count+10); |
| 395 | g_assert_cmpint (e_children, <=, e_children_original+10); | |
| 396 | 208 | |
| 397 | // Add an entry half way down the imaginary page. | |
| 398 | recording_n = recording_count / 2; | |
| 399 | 261 | if (!(recording_n & 1)) recording_n++; |
| 400 | 208 | test_add_track_and_recording (source, recording_n, 1); |
| 401 | 261 | _music_source_view_check_tree (view, NULL); |
| 402 | 208 | |
| 403 | // We still probably don't have the right number of children. | |
| 404 | e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 405 | 261 | g_assert_cmpint (e_children, >=, file_count+11); |
| 406 | g_assert_cmpint (e_children, <=, e_children_original+11); | |
| 407 | ||
| 408 | 208 | _music_source_view_dump_node (view, NULL); |
| 409 | 261 | |
| 410 | // Now iterate over the surplus between the first and middle pages. | |
| 411 | 208 | GtkTreeIter iter; |
| 412 | 261 | gboolean result = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &iter, NULL, |
| 413 | 208 | MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE / 2); |
| 414 | 206 | for (int i=0; i<MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE-1; i++) { |
| 415 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 416 | g_assert (result==TRUE); | |
| 417 | 208 | g_assert (iter.user_data2!=NULL); // Shouldn't freefall within one page's worth of rows. |
| 418 | 261 | }; |
| 419 | 206 | _music_source_view_check_tree (view, NULL); |
| 420 | 261 | |
| 421 | // Due to the way locate_entry works (binary search) all of the surplus should now have been | |
| 422 | 208 | // queried fully. Yay! |
| 423 | 261 | |
| 424 | int n_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 425 | g_assert_cmpint(n_children, ==, (file_count + 11)); | |
| 426 | ||
| 427 | chaining_check_refs (view, row_references, n_children); | |
| 428 | ||
| 429 | 206 | g_object_unref (view); |
| 430 | 261 | g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, g_log_default_handler, NULL); |
| 431 | g_object_unref (source); | |
| 432 | 350 | };*/ |
| 433 | 206 | |
| 434 | ||
| 435 | 185 | // Chaining three - a source which appears smaller than it is, and testing the border between the bottom of |
| 436 | // one page and the next. | |
| 437 | 209 | // This is three instead of two because I still feel there should be more overestimation tests. |
| 438 | 185 | // I just can't work out what they should be at the moment. .. |
| 439 | 209 | static void test_chaining_underestimate_static () { |
| 440 | 185 | // Ignore "more than one connection" view config message. |
| 441 | 261 | g_log_set_handler(NULL, G_LOG_LEVEL_MESSAGE, log_ignore, NULL); |
| 442 | 185 | |
| 443 | MusicSource *source = source_constructor(); | |
| 444 | 209 | int count = populate_for_chaining_underestimate(source); |
| 445 | 261 | |
| 446 | 185 | // Create view. |
| 447 | 215 | ViewConfig *config=view_config_string_parse("track[number]:recording[name]:file[path]"); |
| 448 | 185 | MusicSourceView *view=music_source_create_view(source, config); |
| 449 | _music_source_view_check_tree(view, NULL); | |
| 450 | ||
| 451 | // Estimate should be exact or way under | |
| 452 | int e_children=gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 453 | 261 | g_assert_cmpint((int)e_children, <=, count); |
| 454 | 185 | |
| 455 | GtkTreeRowReference **row_references=chaining_create_refs(view, e_children); | |
| 456 | 261 | |
| 457 | 185 | // Iterate through it all, to try and break it. |
| 458 | GtkTreeIter iter; | |
| 459 | gboolean result=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); | |
| 460 | int i=0; | |
| 461 | while(result!=FALSE) { | |
| 462 | GtkTreePath *path=gtk_tree_model_get_path(GTK_TREE_MODEL(view), &iter); | |
| 463 | int id=music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path); | |
| 464 | i++; g_assert_cmpint(i, ==, id); | |
| 465 | gtk_tree_path_free(path); | |
| 466 | result=gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 467 | }; | |
| 468 | 261 | |
| 469 | 185 | int n_children=gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 470 | 261 | g_assert_cmpint((int)n_children, ==, count); |
| 471 | 185 | |
| 472 | chaining_check_refs(view, row_references, n_children); | |
| 473 | 261 | |
| 474 | 185 | _music_source_view_check_tree(view, NULL); |
| 475 | g_object_unref(view); | |
| 476 | g_log_set_handler(NULL, G_LOG_LEVEL_MESSAGE, g_log_default_handler, NULL); | |
| 477 | 261 | g_object_unref(source); |
| 478 | 185 | }; |
| 479 | ||
| 480 | 217 | /* This tests intermediate id handling, but only the specific case of intermediate ids on |
| 481 | * chained sources. */ | |
| 482 | 185 | static void test_chaining_4() { |
| 483 | // Ignore "more than one connection" view config message. | |
| 484 | 261 | g_log_set_handler(NULL, G_LOG_LEVEL_MESSAGE, log_ignore, NULL); |
| 485 | 185 | |
| 486 | MusicSource *source = source_constructor(); | |
| 487 | int count=populate_for_chaining_4(source); | |
| 488 | ||
| 489 | 261 | //music_source_dump(source); |
| 490 | 185 | // Create view. |
| 491 | 215 | ViewConfig *config=view_config_string_parse("track[number]:recording[name]:file[path]"); |
| 492 | 185 | MusicSourceView *view=music_source_create_view(source, config); |
| 493 | _music_source_view_check_tree(view, NULL); | |
| 494 | 261 | |
| 495 | 185 | // Estimate should be exact or way under |
| 496 | int e_children=gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); | |
| 497 | 261 | g_assert_cmpint((int)e_children, <=, count); |
| 498 | 185 | //printf("e %i\n", e_children); fflush(stdout); |
| 499 | ||
| 500 | // Iterate through it all, to try and break it. | |
| 501 | GtkTreeIter iter; | |
| 502 | gboolean result=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); | |
| 503 | int i=0; | |
| 504 | while(result!=FALSE) { | |
| 505 | //printf("iter: page %x, n %i\n", iter.user_data2, iter.user_data3);fflush(stdout); | |
| 506 | //_music_source_view_dump_node(view, iter.user_data); | |
| 507 | GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(view), &iter); | |
| 508 | //printf("path %s\n", gtk_tree_path_to_string(path)); | |
| 509 | //int id = music_source_view_get_id_at_path(view, ENTRY_TYPE_TRACK, path); | |
| 510 | //printf("i: %i, id: %i\n", i, id);fflush(stdout); | |
| 511 | i++; //g_assert_cmpint(i, ==, id); | |
| 512 | gtk_tree_path_free (path); | |
| 513 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 514 | }; | |
| 515 | 261 | |
| 516 | 185 | _music_source_view_check_tree(view, NULL); |
| 517 | g_object_unref(view); | |
| 518 | g_log_set_handler(NULL, G_LOG_LEVEL_MESSAGE, g_log_default_handler, NULL); | |
| 519 | 261 | g_object_unref(source); |
| 520 | 185 | }; |
| 521 | ||
| 522 | 259 | |
| 523 | 261 | /* This tests for a couple of weird bugs that appeared in Libraryview. */ |
| 524 | 259 | static void test_chaining_5 () { |
| 525 | MusicSource *source = source_constructor (); | |
| 526 | 261 | |
| 527 | 259 | /* Every odd file id is orphaned - remember that when i==0, file 1 etc. is added. */ |
| 528 | music_source_begin_transaction (source); | |
| 529 | for (int i=0; i<256; i++) { | |
| 530 | 261 | test_add_song (source, i/16, i, i, i&1? i/16: 0, i&1? i&15: 0); |
| 531 | 259 | }; |
| 532 | music_source_end_transaction (source); | |
| 533 | 261 | |
| 534 | 259 | ViewConfig *config = view_config_string_parse( |
| 535 | "artist[name]:(album[name]:release[date]>track[number]:" | |
| 536 | "recording[name]:file[path])/(recording[name]>file[path])"); | |
| 537 | MusicSourceView *view = music_source_create_view (source, config); | |
| 538 | 261 | |
| 539 | // This can break when e_children is calculated wrongly. | |
| 540 | 259 | GtkTreeIter iter; |
| 541 | gboolean result = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(view), &iter, NULL, 16*9); | |
| 542 | g_assert (result==TRUE); | |
| 543 | 261 | |
| 544 | //_music_source_view_dump_node (view, NULL); | |
| 545 | ||
| 546 | // Sometimes the node is just populated wrong. | |
| 547 | result = gtk_tree_model_iter_children (GTK_TREE_MODEL(view), &iter, NULL); | |
| 548 | int i = 0; | |
| 549 | while (result) { | |
| 550 | const int correct_artist_id = i<16? 3: 4+(i-16)/9, | |
| 551 | real_artist_id = music_source_view_get_id_at_iter(view, ENTRY_TYPE_ARTIST, &iter); | |
| 552 | ||
| 553 | if (correct_artist_id!=real_artist_id) { | |
| 554 | print_view (view, NULL); g_message ("At row %i: ", i); | |
| 555 | g_assert_cmpint (correct_artist_id, ==, real_artist_id); | |
| 556 | }; | |
| 557 | ||
| 558 | result = gtk_tree_model_iter_next (GTK_TREE_MODEL(view), &iter); | |
| 559 | i++; | |
| 560 | }; | |
| 561 | ||
| 562 | _music_source_view_check_tree (view, NULL); | |
| 563 | ||
| 564 | 259 | g_object_unref_many (view, source, NULL); |
| 565 | }; | |
| 566 | ||
| 567 | 185 | // Test that recording:file[bitrate] doesn't group the files by recording. |
| 568 | 222 | static void test_sorting_1() { |
| 569 | 185 | MusicSource *source = source_constructor(); |
| 570 | music_source_begin_transaction(source); | |
| 571 | 261 | |
| 572 | Entry *recording = NULL, | |
| 573 | 286 | *artist = entry_new(ENTRY_TYPE_ARTIST, 0, NULL, "test"), |
| 574 | *composition = entry_new(ENTRY_TYPE_COMPOSITION, 0, NULL, "test"); | |
| 575 | entry_set_property (artist, ARTIST_NAME, "test artist"); | |
| 576 | 185 | entry_set_property (composition, COMPOSITION_NAME, "test composition"); |
| 577 | 286 | entry_take_property (composition, COMPOSITION_ARTIST, artist); |
| 578 | 185 | for (int i=0;i<4;i++) { |
| 579 | if ((i%2)==0) { | |
| 580 | recording = entry_new(ENTRY_TYPE_RECORDING, 0, NULL, "test"); | |
| 581 | entry_set_property (recording, RECORDING_COMPOSITION, composition); | |
| 582 | 273 | entry_take_property (recording, RECORDING_NAME, g_strdup_printf("Recording %i", i%2)); |
| 583 | 261 | }; |
| 584 | 276 | Entry *file = test_make_file (source, NULL, 0, i); |
| 585 | entry_set_property (file, FILE_RECORDING, recording); | |
| 586 | 185 | switch (i) { |
| 587 | 276 | case 0: entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(64)); break; |
| 588 | case 1: entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(320)); break; | |
| 589 | case 2: entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(192)); break; | |
| 590 | case 3: entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(256)); break; | |
| 591 | 185 | }; |
| 592 | 276 | music_source_add_entry (source, file); |
| 593 | 185 | if (i%2==1) |
| 594 | 276 | entry_unref (recording, "test"); |
| 595 | 185 | }; |
| 596 | entry_unref (composition, "test"); | |
| 597 | music_source_end_transaction(source); | |
| 598 | ||
| 599 | 276 | ViewConfig *config = view_config_string_parse("recording:file[path]"); |
| 600 | MusicSourceView *view = music_source_create_view(source, config); | |
| 601 | _music_source_view_check_tree (view, NULL); | |
| 602 | 261 | |
| 603 | 185 | // Should be four files |
| 604 | 276 | int n_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 605 | 185 | //printf("n %i\n", n_children);fflush(stdout); |
| 606 | 276 | g_assert_cmpint (n_children, ==, 4); |
| 607 | 261 | |
| 608 | 185 | GtkTreeIter iter; |
| 609 | gboolean result = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); | |
| 610 | // FIXME: why not have enums | |
| 611 | int path_column = -1; | |
| 612 | 276 | for (int i=0; i<MUSIC_SOURCE_VIEW_COLUMN_COUNT; i++) |
| 613 | 185 | if (column[i].apid == _FILE_PATH) { |
| 614 | 276 | path_column = i; |
| 615 | 185 | break; |
| 616 | }; | |
| 617 | 276 | g_assert (path_column>=0 && path_column<MUSIC_SOURCE_VIEW_COLUMN_COUNT); |
| 618 | 261 | |
| 619 | 185 | GtkTreePath *path=gtk_tree_path_new_first(); |
| 620 | for (int i=0;i<4;i++) { | |
| 621 | int id = music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path); | |
| 622 | g_assert_cmpint(id, ==, i+1); | |
| 623 | gtk_tree_path_next(path); | |
| 624 | }; | |
| 625 | gtk_tree_path_free(path); | |
| 626 | 261 | _music_source_view_check_tree(view, NULL); |
| 627 | g_object_unref(view); | |
| 628 | 185 | |
| 629 | config=view_config_string_parse("recording:file[bitrate]"); | |
| 630 | 261 | view=music_source_create_view(source, config); |
| 631 | 185 | _music_source_view_check_tree(view, NULL); |
| 632 | 261 | |
| 633 | 185 | result = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter); |
| 634 | // FIXME: why not have enums | |
| 635 | int bitrate_column = -1; | |
| 636 | for (int i=0;i<MUSIC_SOURCE_VIEW_COLUMN_COUNT;i++) | |
| 637 | if (column[i].apid == _FILE_BITRATE) { | |
| 638 | bitrate_column=i; | |
| 639 | break; | |
| 640 | }; | |
| 641 | g_assert(bitrate_column>=0 && bitrate_column<MUSIC_SOURCE_VIEW_COLUMN_COUNT); | |
| 642 | 261 | |
| 643 | 185 | for (int i=0;i<4;i++) { |
| 644 | g_assert(result == TRUE); | |
| 645 | char *bitrate_string; | |
| 646 | gtk_tree_model_get(GTK_TREE_MODEL(view), &iter, bitrate_column, &bitrate_string, -1); | |
| 647 | int bitrate = atoi(bitrate_string); | |
| 648 | switch (i) { | |
| 649 | case 0: g_assert_cmpint(bitrate, ==, 64); break; | |
| 650 | case 1: g_assert_cmpint(bitrate, ==, 192); break; | |
| 651 | case 2: g_assert_cmpint(bitrate, ==, 256); break; | |
| 652 | case 3: g_assert_cmpint(bitrate, ==, 320); break; | |
| 653 | }; | |
| 654 | g_free(bitrate_string); | |
| 655 | result = gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter); | |
| 656 | }; | |
| 657 | 261 | _music_source_view_check_tree(view, NULL); |
| 658 | g_object_unref(view); | |
| 659 | ||
| 660 | 185 | g_object_unref(source); |
| 661 | 273 | _entry_cleanup (); |
| 662 | 185 | } |
| 663 | ||
| 664 | 221 | /* Test sort grouping on various configs. */ |
| 665 | 222 | static void test_sorting_2() { |
| 666 | 185 | MusicSource *source = source_constructor(); |
| 667 | music_source_begin_transaction(source); | |
| 668 | // artist, composition, file, album, track | |
| 669 | 286 | test_add_song(source, 0, 0, 1, 1, 1); |
| 670 | test_add_song(source, 0, 0, 2, 1, 1); | |
| 671 | test_add_song(source, 0, 1, 0, 1, 2); | |
| 672 | test_add_song(source, 0, 1, 3, 1, 2); | |
| 673 | test_add_song(source, 1, 2, 5, 2, 3); | |
| 674 | test_add_song(source, 1, 2, 6, 2, 3); | |
| 675 | test_add_song(source, 1, 3, 4, 2, 4); | |
| 676 | test_add_song(source, 1, 3, 7, 2, 4); | |
| 677 | 185 | music_source_end_transaction(source); |
| 678 | 261 | |
| 679 | MusicSourceView *view; | |
| 680 | 453 | /* const char *reference_1[8][5] = { |
| 681 | 185 | {"Test Artist 000000", "Test Composition 000001", "Test File 000000 (000001)", NULL, NULL}, |
| 682 | {"Test Artist 000000", "Test Composition 000000", "Test File 000001 (000000)", NULL, NULL}, | |
| 683 | {"Test Artist 000000", "Test Composition 000000", "Test File 000002 (000000)", NULL, NULL}, | |
| 684 | {"Test Artist 000000", "Test Composition 000001", "Test File 000003 (000001)", NULL, NULL}, | |
| 685 | {"Test Artist 000001", "Test Composition 000003", "Test File 000004 (000003)", NULL, NULL}, | |
| 686 | {"Test Artist 000001", "Test Composition 000002", "Test File 000005 (000002)", NULL, NULL}, | |
| 687 | {"Test Artist 000001", "Test Composition 000002", "Test File 000006 (000002)", NULL, NULL}, | |
| 688 | {"Test Artist 000001", "Test Composition 000003", "Test File 000007 (000003)", NULL, NULL}}; | |
| 689 | 261 | |
| 690 | 185 | const char *config_1 = "artist[name]:recording:file[path]"; |
| 691 | 261 | view = music_source_create_view(source, view_config_string_parse(config_1)); |
| 692 | //printf ("Config: %s.\n", view_config_to_string(view_config_string_parse(config_1), TRUE)); fflush (stdout); | |
| 693 | assert_view (view, NULL, 8, reference_1); | |
| 694 | 185 | g_object_unref (view); |
| 695 | 261 | |
| 696 | 185 | |
| 697 | const char *reference_2[8][5] = { | |
| 698 | 286 | {"Test Artist 000000", "Test Composition 000001", "Test File 000000 (000001)", "Test Album 000001", "2"}, |
| 699 | {"Test Artist 000000", "Test Composition 000000", "Test File 000001 (000000)", "Test Album 000001", "1"}, | |
| 700 | {"Test Artist 000000", "Test Composition 000000", "Test File 000002 (000000)", "Test Album 000001", "1"}, | |
| 701 | {"Test Artist 000000", "Test Composition 000001", "Test File 000003 (000001)", "Test Album 000001", "2"}, | |
| 702 | {"Test Artist 000001", "Test Composition 000003", "Test File 000004 (000003)", "Test Album 000002", "4"}, | |
| 703 | {"Test Artist 000001", "Test Composition 000002", "Test File 000005 (000002)", "Test Album 000002", "3"}, | |
| 704 | {"Test Artist 000001", "Test Composition 000002", "Test File 000006 (000002)", "Test Album 000002", "3"}, | |
| 705 | {"Test Artist 000001", "Test Composition 000003", "Test File 000007 (000003)", "Test Album 000002", "4"}}; | |
| 706 | 261 | |
| 707 | 185 | const char *config_2 = "artist[name]:album:release:track:recording:file[path]"; |
| 708 | 261 | view = music_source_create_view(source, view_config_string_parse(config_2)); |
| 709 | assert_view (view, NULL, 8, reference_2); | |
| 710 | 185 | g_object_unref (view); |
| 711 | 453 | */ |
| 712 | ||
| 713 | // 3 - Test for sort grouping being broken. Each pair of files is in a sort group defined by | |
| 714 | // its track, in this case. | |
| 715 | 185 | const char *reference_3[8][5] = { |
| 716 | 286 | {"Test Artist 000000", "Test Composition 000000", "Test File 000001 (000000)", "Test Album 000001", "1"}, |
| 717 | {"Test Artist 000000", "Test Composition 000000", "Test File 000002 (000000)", "Test Album 000001", "1"}, | |
| 718 | {"Test Artist 000000", "Test Composition 000001", "Test File 000000 (000001)", "Test Album 000001", "2"}, | |
| 719 | {"Test Artist 000000", "Test Composition 000001", "Test File 000003 (000001)", "Test Album 000001", "2"}, | |
| 720 | {"Test Artist 000001", "Test Composition 000002", "Test File 000005 (000002)", "Test Album 000002", "3"}, | |
| 721 | {"Test Artist 000001", "Test Composition 000002", "Test File 000006 (000002)", "Test Album 000002", "3"}, | |
| 722 | {"Test Artist 000001", "Test Composition 000003", "Test File 000004 (000003)", "Test Album 000002", "4"}, | |
| 723 | {"Test Artist 000001", "Test Composition 000003", "Test File 000007 (000003)", "Test Album 000002", "4"}}; | |
| 724 | 261 | |
| 725 | 453 | const char *config_3 = "artist[name]:album:release[date]:track[number]:recording:file[path]"; |
| 726 | 261 | view = music_source_create_view(source, view_config_string_parse(config_3)); |
| 727 | assert_view (view, NULL, 8, reference_3); | |
| 728 | 185 | g_object_unref (view); |
| 729 | ||
| 730 | g_object_unref(source); | |
| 731 | 273 | _entry_cleanup (); |
| 732 | 185 | } |
| 733 | ||
| 734 | 235 | |
| 735 | 185 | //////////////////////////////////////////////////// |
| 736 | // Location | |
| 737 | // | |
| 738 | 225 | // More tests for location happen in preset tests - there's no point duplicating the tests here. |
| 739 | // Bear in mind these tests don't cover a big portion of the location functionality. | |
| 740 | // | |
| 741 | 185 | |
| 742 | // Source should have 'recording_count' consecutive recordings. | |
| 743 | // | |
| 744 | 226 | static void test_location_with_source (MusicSource *source, const char *config_string, |
| 745 | 261 | int file_count, int file_depth) { |
| 746 | 226 | ViewConfig *config = view_config_string_parse (config_string); |
| 747 | 185 | MusicSourceView *view = music_source_create_view (source, config); |
| 748 | 261 | //print_view (view, NULL); |
| 749 | ||
| 750 | 185 | // On first iteration, find location of last file. This tests estimated sources. |
| 751 | 261 | GtkTreePath *path = music_source_view_get_path_for_id(view, ENTRY_TYPE_FILE, file_count); |
| 752 | 226 | //printf ("Path: %s.\n", gtk_tree_path_to_string (path)); fflush (stdout); |
| 753 | 185 | int n_children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(view), NULL); |
| 754 | 225 | //_music_source_view_dump_node (view, NULL); |
| 755 | 185 | g_assert (path!=NULL); |
| 756 | 226 | g_assert_cmpint (gtk_tree_path_get_depth(path), ==, file_depth); |
| 757 | if (file_depth==1) | |
| 758 | g_assert_cmpint (gtk_tree_path_get_indices(path)[0], ==, n_children-1); | |
| 759 | 185 | gtk_tree_path_free (path); |
| 760 | 261 | |
| 761 | 276 | // Test the iterative version (this is for execution in idles etc., because |
| 762 | 261 | // get_path_for_id can be pretty slow.) |
| 763 | 185 | // |
| 764 | 276 | const int test_count = MAX(file_count/10, 20); |
| 765 | for (int i=0; i<test_count*2; i++) { | |
| 766 | 185 | // On first iteration, find location of last file. This tests estimated sources. |
| 767 | int file_id = g_test_rand_int_range (1, file_count+1); | |
| 768 | //if (!music_source_is_valid_id (source, ENTRY_TYPE_RECORDING, recording_id)) | |
| 769 | // continue; | |
| 770 | 261 | |
| 771 | 185 | MusicSourceViewSearch *state = music_source_view_get_path_for_id_begin |
| 772 | 261 | (view, ENTRY_TYPE_FILE, file_id); |
| 773 | 185 | while (!music_source_view_get_path_for_id_step(view, state)) { |
| 774 | if (g_test_rand_int()&4) { | |
| 775 | // Sometimes, let's just give up halfway through! | |
| 776 | music_source_view_get_path_for_id_end (view, state); | |
| 777 | state = NULL; | |
| 778 | break; | |
| 779 | }; | |
| 780 | }; | |
| 781 | 261 | |
| 782 | 185 | if (state!=NULL) { |
| 783 | GtkTreePath *path = music_source_view_get_path_for_id_end (view, state); | |
| 784 | 261 | |
| 785 | 225 | //_music_source_view_dump_node (view, NULL); |
| 786 | 185 | g_assert (path!=NULL); |
| 787 | 226 | g_assert_cmpint (gtk_tree_path_get_depth(path), ==, file_depth); |
| 788 | 261 | |
| 789 | 185 | g_assert_cmpint (music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path), ==, |
| 790 | file_id); | |
| 791 | 226 | //g_assert_cmpint (gtk_tree_path_get_indices(path)[file_depth-1], ==, file_id-1); |
| 792 | 261 | |
| 793 | 185 | gtk_tree_path_free (path); |
| 794 | }; | |
| 795 | 261 | }; |
| 796 | 237 | |
| 797 | for (int i=0; i<test_count; i++) { | |
| 798 | // On first iteration, find location of last file. This tests estimated sources. | |
| 799 | int file_id = g_test_rand_int_range (1, file_count+1); | |
| 800 | //if (!music_source_is_valid_id (source, ENTRY_TYPE_RECORDING, recording_id)) | |
| 801 | // continue; | |
| 802 | 261 | |
| 803 | path = music_source_view_get_path_for_id (view, ENTRY_TYPE_FILE, file_id); | |
| 804 | 237 | //printf ("Got path: %s.\n", gtk_tree_path_to_string(path)); fflush (stdout); |
| 805 | 261 | |
| 806 | 237 | //_music_source_view_dump_node (view, NULL); |
| 807 | g_assert (path!=NULL); | |
| 808 | g_assert_cmpint (gtk_tree_path_get_depth(path), ==, file_depth); | |
| 809 | 261 | |
| 810 | 237 | g_assert_cmpint (music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path), ==, |
| 811 | file_id); | |
| 812 | //g_assert_cmpint (gtk_tree_path_get_indices(path)[file_depth-1], ==, file_id-1); | |
| 813 | 261 | |
| 814 | 237 | gtk_tree_path_free (path); |
| 815 | }; | |
| 816 | 261 | |
| 817 | 185 | g_object_unref (view); |
| 818 | }; | |
| 819 | ||
| 820 | ||
| 821 | 261 | static void test_location_1() { |
| 822 | int file_count; | |
| 823 | MusicSource *source = source_constructor(); | |
| 824 | 185 | test_add_n_songs (source, 10, TRUE); |
| 825 | 226 | test_location_with_source (source, "track[number]:recording[name]:file[path]", 10, 1); |
| 826 | 185 | g_object_unref (source); |
| 827 | 276 | _entry_cleanup (); |
| 828 | 261 | |
| 829 | 185 | source = source_constructor(); |
| 830 | 261 | test_add_n_songs (source, MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4, TRUE); |
| 831 | test_location_with_source (source, "track[number]:recording[name]:file[path]", | |
| 832 | 226 | MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4, 1); |
| 833 | 185 | g_object_unref (source); |
| 834 | 276 | _entry_cleanup (); |
| 835 | 261 | |
| 836 | 185 | //printf ("Chaining 1\n"); |
| 837 | source = source_constructor(); | |
| 838 | 261 | file_count = populate_for_chaining_overestimate(source); |
| 839 | 226 | test_location_with_source (source, "track[number]:recording[name]:file[path]", file_count, 1); |
| 840 | 185 | g_object_unref (source); |
| 841 | 276 | _entry_cleanup (); |
| 842 | 261 | |
| 843 | 185 | //printf ("\nChaining 3\n"); |
| 844 | source = source_constructor(); | |
| 845 | 209 | file_count = populate_for_chaining_underestimate(source); |
| 846 | 226 | test_location_with_source (source, "track[number]:recording[name]:file[path]", file_count, 1); |
| 847 | 185 | g_object_unref (source); |
| 848 | 273 | _entry_cleanup (); |
| 849 | 185 | // FIXME: chaining 4 doesn't make much sense at the moment, because we can't choose which |
| 850 | // intermediate entries to use when picking a node. However when this code does exist it | |
| 851 | 261 | // will be an excellent test! |
| 852 | 185 | //printf ("\nChaining 4\n"); |
| 853 | /*source = source_constructor(); | |
| 854 | file_count = populate_for_chaining_4 (source); | |
| 855 | test_location_with_source (source, file_count); | |
| 856 | g_object_unref (source);*/ | |
| 857 | 261 | |
| 858 | 185 | // More tests on location are performed in the preset tests: |
| 859 | // - testing artist:album:blah blah:files, with orphans. | |
| 860 | // - testing a branching view, with orphans not in the root. | |
| 861 | }; | |
| 862 | ||
| 863 | static void test_location_2() { | |
| 864 | // Slightly weird corner case - this requires brute force searching, because the property | |
| 865 | // 'file' is sorted by is the same for each file. | |
| 866 | MusicSource *source = source_constructor (); | |
| 867 | 261 | |
| 868 | 185 | music_source_begin_transaction (source); |
| 869 | test_add_song (source, 1, 1, 1, 0, 0); | |
| 870 | test_add_song (source, 1, 1, 2, 0, 0); | |
| 871 | test_add_song (source, 1, 1, 3, 0, 0); | |
| 872 | music_source_end_transaction (source); | |
| 873 | ||
| 874 | 215 | ViewConfig *config = view_config_string_parse ("composition[name]:recording:file[bitrate]"); |
| 875 | 185 | MusicSourceView *view = music_source_create_view(source, config); |
| 876 | 261 | |
| 877 | GtkTreePath *path; | |
| 878 | 185 | path = music_source_view_get_path_for_id (view, ENTRY_TYPE_FILE, 2); |
| 879 | g_assert_cmpint (gtk_tree_path_get_depth(path), ==, 1); | |
| 880 | g_assert_cmpint (gtk_tree_path_get_indices(path)[0], ==, 1); | |
| 881 | gtk_tree_path_free (path); | |
| 882 | 261 | |
| 883 | 227 | MusicSourceViewSearch *search = music_source_view_get_path_for_id_begin(view, ENTRY_TYPE_FILE, 2); |
| 884 | 185 | while (!music_source_view_get_path_for_id_step(view, search)); |
| 885 | path = music_source_view_get_path_for_id_end(view, search); | |
| 886 | g_assert_cmpint (gtk_tree_path_get_depth(path), ==, 1); | |
| 887 | g_assert_cmpint (gtk_tree_path_get_indices(path)[0], ==, 1); | |
| 888 | 261 | gtk_tree_path_free (path); |
| 889 | ||
| 890 | 185 | g_object_unref (view); |
| 891 | g_object_unref (source); | |
| 892 | 281 | _entry_cleanup (); |
| 893 | 185 | }; |
| 894 | ||
| 895 | 226 | /* This test has 3 levels! */ |
| 896 | static void test_location_3() { | |
| 897 | MusicSource *source = source_constructor (); | |
| 898 | 261 | test_add_n_songs (source, MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4, TRUE); |
| 899 | 226 | test_location_with_source (source, "artist[name]>composition:recording[name]>" |
| 900 | "file[bitrate]", MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4, 3); | |
| 901 | g_object_unref (source); | |
| 902 | 281 | _entry_cleanup (); |
| 903 | 226 | }; |
| 904 | ||
| 905 | 227 | /* Test orphans somehow! */ |
| 906 | static void test_location_4() { | |
| 907 | MusicSource *source = source_constructor (); | |
| 908 | 261 | |
| 909 | 228 | /* Every odd file id is orphaned - remember that when i==0, file 1 etc. is added. */ |
| 910 | 259 | music_source_begin_transaction (source); |
| 911 | for (int i=0; i<MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4; i++) { | |
| 912 | 261 | test_add_song (source, i/16, i, i, i&1? i/16: 0, i&1? i&15: 0); |
| 913 | 259 | }; |
| 914 | music_source_end_transaction (source); | |
| 915 | 261 | |
| 916 | 227 | test_location_with_source (source, "artist[name]:(album[name]:release[date]>track[number]:" |
| 917 | "recording[name]:file[path])/(recording[name]>file[path])", | |
| 918 | 228 | MUSIC_SOURCE_VIEW_MIN_PAGE_SIZE*4, 2); |
| 919 | 261 | |
| 920 | 227 | g_object_unref (source); |
| 921 | 281 | _entry_cleanup (); |
| 922 | 227 | }; |
| 923 | 185 | |
| 924 | ||
| 925 | 259 | |
| 926 | 185 | ////////////////////////////////////////////////// |
| 927 | // Removal test cases | |
| 928 | // | |
| 929 | ||
| 930 | 441 | /* removal 1: Test removal of a specific entry. |
| 931 | * | |
| 932 | * This is more of a challenge than it appears, because the files are sorted by bitrate and they | |
| 933 | * have identical bitrates, so the removal function has to use brute force to find which to | |
| 934 | * remove. */ | |
| 935 | static void test_removal_1 (void) { | |
| 936 | 185 | MusicSource *source = source_constructor (); |
| 937 | 261 | |
| 938 | 185 | music_source_begin_transaction (source); |
| 939 | test_add_song (source, 1, 1, 1, 0, 0); | |
| 940 | test_add_song (source, 1, 1, 2, 0, 0); | |
| 941 | test_add_song (source, 1, 1, 3, 0, 0); | |
| 942 | music_source_end_transaction (source); | |
| 943 | ||
| 944 | 454 | const char *config_string = "composition[name]:recording:file[path]"; |
| 945 | 412 | GtkTreeView *tree_view = dynamic_view_test_setup(source, config_string); |
| 946 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 947 | 261 | |
| 948 | const char *reference_1[3][5] = { | |
| 949 | 185 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, |
| 950 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", NULL, NULL}, | |
| 951 | {"Test Artist 000001", "Test Composition 000001", "Test File 000003 (000001)", NULL, NULL}}; | |
| 952 | assert_view (view, NULL, 3, reference_1); | |
| 953 | //_music_source_view_dump_node (view, NULL); | |
| 954 | 261 | |
| 955 | 185 | music_source_remove_entry (source, ENTRY_TYPE_FILE, 2); |
| 956 | 261 | |
| 957 | 224 | /* This will give a "file 2 was NULL :(" error if the entry gets removed but the view doesn't |
| 958 | * remove the row. */ | |
| 959 | 261 | const char *reference_2[2][5] = { |
| 960 | 185 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, |
| 961 | {"Test Artist 000001", "Test Composition 000001", "Test File 000003 (000001)", NULL, NULL}}; | |
| 962 | assert_view (view, NULL, 2, reference_2); | |
| 963 | ||
| 964 | music_source_remove_entry (source, ENTRY_TYPE_FILE, 3); | |
| 965 | 261 | music_source_remove_entry (source, ENTRY_TYPE_FILE, 1); |
| 966 | ||
| 967 | 185 | assert_view (view, NULL, 0, NULL); |
| 968 | 261 | |
| 969 | 412 | dynamic_view_test_teardown (tree_view); |
| 970 | 185 | }; |
| 971 | 261 | |
| 972 | 454 | /* removal 2: Remove an entry that appears in several rows under the same node. |
| 973 | * | |
| 974 | * Note that part of this test relies on arbitrary result order; it's not easy to rewrite the | |
| 975 | * test to not depending on this. If you're using genericview with a source other than | |
| 976 | * genericsource, maybe comment it out or rewrite it. */ | |
| 977 | 441 | static void test_removal_2 (void) { |
| 978 | 185 | MusicSource *source = source_constructor (); |
| 979 | 261 | |
| 980 | 435 | // This is a slightly complex case. We add: |
| 981 | // One artist, who has two albums. Album 1 has two tracks and album 2 has one track. | |
| 982 | // One recording, which has two files. Every album track is this recording, and ... | |
| 983 | // it is sorted by file path. | |
| 984 | // | |
| 985 | // So the view is of six files, two for each track. | |
| 986 | 361 | music_source_begin_transaction (source); |
| 987 | test_add_song (source, 1, 1, 1, 1, 1); | |
| 988 | test_add_song (source, 1, 1, 1, 2, 1); | |
| 989 | test_add_song (source, 1, 1, 2, 1, 2); | |
| 990 | music_source_end_transaction (source); | |
| 991 | ||
| 992 | // Config with a m:1 join to make things awkward. | |
| 993 | 412 | const char *config_string = "artist[name]:album:release:track:recording:file[path]"; |
| 994 | GtkTreeView *tree_view = dynamic_view_test_setup(source, config_string); | |
| 995 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 996 | 361 | |
| 997 | const char *reference_1[6][5] = { | |
| 998 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"}, | |
| 999 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "2"}, | |
| 1000 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000002", "1"}, | |
| 1001 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000001", "1"}, | |
| 1002 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000001", "2"}, | |
| 1003 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000002", "1"}}; | |
| 1004 | assert_view (view, NULL, 6, reference_1); | |
| 1005 | ||
| 1006 | music_source_remove_entry (source, ENTRY_TYPE_FILE, 1); | |
| 1007 | ||
| 1008 | 390 | // These are actually valid in any order, because it's only sorted by file path and the file |
| 1009 | // paths are identical. | |
| 1010 | 361 | const char *reference_2[3][5] = { |
| 1011 | 402 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000001", "1"}, |
| 1012 | 397 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000002", "1"}, |
| 1013 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", "Test Album 000001", "2"}}; | |
| 1014 | 361 | assert_view (view, NULL, 3, reference_2); |
| 1015 | 412 | |
| 1016 | dynamic_view_test_teardown (tree_view); | |
| 1017 | }; | |
| 1018 | ||
| 1019 | 441 | /* removal 3: Two entries of one row */ |
| 1020 | static void test_removal_3 (void) { | |
| 1021 | 412 | MusicSource *source = source_constructor (); |
| 1022 | test_add_song (source, 1, 1, 1, 1, 1); | |
| 1023 | ||
| 1024 | const char *config_string = "artist[name]:album:release[date]:track:recording[name]:file[path]"; | |
| 1025 | GtkTreeView *tree_view = dynamic_view_test_setup(source, config_string); | |
| 1026 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1027 | ||
| 1028 | const char *reference_1[1][5] = { | |
| 1029 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"}}; | |
| 1030 | ||
| 1031 | assert_view (view, NULL, 1, reference_1); | |
| 1032 | ||
| 1033 | music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 1); | |
| 1034 | ||
| 1035 | g_assert_cmpint (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL), ==, 0); | |
| 1036 | ||
| 1037 | dynamic_view_test_teardown (tree_view); | |
| 1038 | }; | |
| 1039 | ||
| 1040 | 441 | /* removal 4: A multi-level config. */ |
| 1041 | static void test_removal_4 (void) { | |
| 1042 | MusicSource *source = source_constructor (); | |
| 1043 | test_add_song (source, TEST_UNKNOWN_ARTIST, 1, 1, 0, 0); | |
| 1044 | ||
| 1045 | const char *config_string = "artist[name] > recording[name]:file[path]"; | |
| 1046 | GtkTreeView *tree_view = dynamic_view_test_setup(source, config_string); | |
| 1047 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1048 | ||
| 1049 | const char *reference_root[1][5] = { | |
| 1050 | {"Unknown Artist", NULL, NULL, NULL, NULL}}; | |
| 1051 | const char *reference_0[1][5] = { | |
| 1052 | {"Unknown Artist", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}}; | |
| 1053 | ||
| 1054 | GtkTreeIter iter; gtk_tree_model_iter_children (GTK_TREE_MODEL(view), &iter, NULL); | |
| 1055 | assert_view (view, NULL, 1, reference_root); | |
| 1056 | assert_view (view, &iter, 1, reference_0); | |
| 1057 | ||
| 1058 | music_source_remove_entry (source, ENTRY_TYPE_FILE, 1); | |
| 1059 | ||
| 1060 | // Now, the 'Unknown Artist' entry should have gone too. | |
| 1061 | g_assert_cmpint (gtk_tree_model_iter_n_children (GTK_TREE_MODEL(view), NULL), ==, 0); | |
| 1062 | ||
| 1063 | dynamic_view_test_teardown (tree_view); | |
| 1064 | } | |
| 1065 | 361 | |
| 1066 | 354 | /*************************************************************************************************** |
| 1067 | * Changing test cases | |
| 1068 | * (needs to be before insertion test cases because adding entries causes -changed to be emitted | |
| 1069 | * a lot anyway). | |
| 1070 | */ | |
| 1071 | ||
| 1072 | 364 | // Modify an entry and check that row-changed is emitted, and that it gets resorted when needed |
| 1073 | 354 | static void test_changing_1() { |
| 1074 | 378 | // FIXME: minimise the number of times its emitted .. |
| 1075 | 402 | gboolean row_1_row_changed_was_emitted; |
| 1076 | 396 | void row_changed (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, |
| 1077 | GtkTreePath *correct_path) { | |
| 1078 | if (gtk_tree_path_compare(path, correct_path)==0) | |
| 1079 | row_1_row_changed_was_emitted = TRUE; | |
| 1080 | }; | |
| 1081 | 402 | |
| 1082 | 396 | gboolean rows_reordered_was_emitted; const int rows_reordered_template[3] = { 1, 0, 2 }; |
| 1083 | void rows_reordered (GtkTreeModel *model, GtkTreePath *parent_path, GtkTreeIter *parent_iter, | |
| 1084 | gint *new_order) { | |
| 1085 | g_assert_cmpintarray(new_order, rows_reordered_template, 3); | |
| 1086 | rows_reordered_was_emitted = TRUE; | |
| 1087 | }; | |
| 1088 | 402 | |
| 1089 | 396 | MusicSource *source = source_constructor(); |
| 1090 | test_add_song (source, 1, 1, 1, 0, 0); | |
| 1091 | test_add_song (source, 2, 2, 2, 0, 0); | |
| 1092 | test_add_song (source, 1, 3, 3, 0, 0); | |
| 1093 | ||
| 1094 | 406 | GtkTreeView *tree_view = dynamic_view_test_setup(source, "artist[name]:recording[name]"); |
| 1095 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1096 | ||
| 1097 | 396 | const char *reference_1[3][5] = { |
| 1098 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, | |
| 1099 | {"Test Artist 000001", "Test Composition 000003", "Test File 000003 (000003)", NULL, NULL}, | |
| 1100 | {"Test Artist 000002", "Test Composition 000002", "Test File 000002 (000002)", NULL, NULL}, | |
| 1101 | }; | |
| 1102 | ||
| 1103 | assert_view (view, NULL, 3, reference_1); | |
| 1104 | ||
| 1105 | row_1_row_changed_was_emitted = FALSE; rows_reordered_was_emitted = FALSE; | |
| 1106 | 402 | g_signal_connect (view, "row-changed", G_CALLBACK(row_changed), |
| 1107 | 396 | gtk_tree_path_new_from_string("1")); |
| 1108 | 402 | g_signal_connect (view, "rows-reordered", G_CALLBACK(rows_reordered), NULL); |
| 1109 | 396 | |
| 1110 | Entry *file_3 = music_source_checkout_entry(source, ENTRY_TYPE_FILE, 3, "test"); | |
| 1111 | entry_set_property (file_3, FILE_PATH, "Test File Y2"); | |
| 1112 | music_source_checkin_entry (source, file_3, "test"); | |
| 1113 | ||
| 1114 | g_assert (row_1_row_changed_was_emitted); | |
| 1115 | 397 | //g_assert (!rows_reordered_was_emitted); |
| 1116 | 396 | row_1_row_changed_was_emitted = FALSE; |
| 1117 | ||
| 1118 | Entry *composition_1 = music_source_checkout_entry(source, ENTRY_TYPE_COMPOSITION, 1, "test"); | |
| 1119 | entry_set_property (composition_1, COMPOSITION_NAME, "Test Composition 4"); | |
| 1120 | music_source_checkin_entry (source, composition_1, "test"); | |
| 1121 | ||
| 1122 | const char *reference_2[3][5] = { | |
| 1123 | {"Test Artist 000001", "Test Composition 000003", "Test File Y2", NULL, NULL}, | |
| 1124 | {"Test Artist 000001", "Test Composition 4", "Test File 000001 (000001)", NULL, NULL}, | |
| 1125 | {"Test Artist 000002", "Test Composition 000002", "Test File 000002 (000002)", NULL, NULL}, | |
| 1126 | }; | |
| 1127 | ||
| 1128 | assert_view (view, NULL, 3, reference_2); | |
| 1129 | g_assert (row_1_row_changed_was_emitted); | |
| 1130 | g_assert (rows_reordered_was_emitted); | |
| 1131 | ||
| 1132 | 406 | dynamic_view_test_teardown (tree_view); |
| 1133 | 396 | }; |
| 1134 | ||
| 1135 | 431 | /* Changing 2: resorting |
| 1136 | * | |
| 1137 | * Tests that rows are reordered property when an entry changes. (If the sort order is messed up | |
| 1138 | * at any point, excluding while a view notify handler is processing, then everything breaks) */ | |
| 1139 | 396 | static void test_changing_2 () { |
| 1140 | 431 | MusicSource *source = source_constructor(); |
| 1141 | ViewConfig *config = view_config_string_parse ("artist[name]:recording[name]:file[bitrate]"); | |
| 1142 | MusicSourceView *view = music_source_create_view (source, config); | |
| 1143 | ||
| 1144 | // Read something from the node - otherwise, the view will assume it's invisible. | |
| 1145 | gtk_tree_model_iter_n_children (GTK_TREE_MODEL(view), NULL); | |
| 1146 | ||
| 1147 | for (int i=0; i<6; i++) | |
| 1148 | test_add_song (source, TEST_UNKNOWN_ARTIST, TEST_UNKNOWN_TITLE, i, 0, 0); | |
| 1149 | ||
| 1150 | // Update the artist and composition entries. This is a part of what happens during tag reading. | |
| 1151 | // The node now needs resorting. | |
| 1152 | // | |
| 1153 | Entry *file = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 4, "test"), | |
| 1154 | *recording = entry_get_property (file, FILE_RECORDING), | |
| 1155 | 438 | *composition = entry_new (ENTRY_TYPE_COMPOSITION, 0, NULL, "test"), |
| 1156 | *artist = entry_new (ENTRY_TYPE_ARTIST, 0, NULL, "test"); | |
| 1157 | 431 | entry_set_property (artist, ARTIST_NAME, "Inspecter 7"); |
| 1158 | entry_take_property (composition, COMPOSITION_ARTIST, artist); | |
| 1159 | entry_set_property (composition, COMPOSITION_NAME, "Sharky 17"); | |
| 1160 | entry_take_property (recording, RECORDING_COMPOSITION, composition); | |
| 1161 | music_source_checkin_entry (source, file, "test"); | |
| 1162 | ||
| 1163 | const char *reference[][5] = { | |
| 1164 | {"Inspecter 7", "Sharky 17", "Test File 000003 (-00001)", NULL, NULL}, | |
| 1165 | {"Unknown Artist", "Unknown Title", "Test File 000001 (-00001)", NULL, NULL}, | |
| 1166 | {"Unknown Artist", "Unknown Title", "Test File 000002 (-00001)", NULL, NULL}, | |
| 1167 | {"Unknown Artist", "Unknown Title", "Test File 000004 (-00001)", NULL, NULL}, | |
| 1168 | {"Unknown Artist", "Unknown Title", "Test File 000005 (-00001)", NULL, NULL}, | |
| 1169 | {"Unknown Artist", "Unknown Title", "Test File 000000 (-00001)", NULL, NULL}}; | |
| 1170 | assert_view (view, NULL, 6, reference); | |
| 1171 | ||
| 1172 | g_object_unref (view); | |
| 1173 | g_object_unref (source); | |
| 1174 | } | |
| 1175 | ||
| 1176 | 438 | /* Changing 3: resorting 2 |
| 1177 | * | |
| 1178 | * Tests a common case of change propagation, where a recording and a file both change. There is a | |
| 1179 | * danger of the view getting confused and not finding either during change propagation. */ | |
| 1180 | 431 | static void test_changing_3 () { |
| 1181 | 438 | MusicSource *source = source_constructor(); |
| 1182 | ViewConfig *config = view_config_string_parse ("artist[name]:recording[name]:file[bitrate]"); | |
| 1183 | MusicSourceView *view = music_source_create_view (source, config); | |
| 1184 | ||
| 1185 | // Read something from the node - otherwise, the view will assume it's invisible. | |
| 1186 | gtk_tree_model_iter_n_children (GTK_TREE_MODEL(view), NULL); | |
| 1187 | ||
| 1188 | for (int i=0; i<6; i++) | |
| 1189 | test_add_song (source, TEST_UNKNOWN_ARTIST, TEST_UNKNOWN_TITLE, i, 0, 0); | |
| 1190 | ||
| 1191 | // Update the artist and composition entries, as before. | |
| 1192 | { | |
| 1193 | Entry *file = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test"), | |
| 1194 | *recording = entry_get_property (file, FILE_RECORDING), | |
| 1195 | *composition = entry_new (ENTRY_TYPE_COMPOSITION, 0, NULL, "test"), | |
| 1196 | *artist = entry_new (ENTRY_TYPE_ARTIST, 0, NULL, "test"); | |
| 1197 | entry_set_property (artist, ARTIST_NAME, "Fat Freddy's Drop"); | |
| 1198 | entry_take_property (composition, COMPOSITION_ARTIST, artist); | |
| 1199 | entry_set_property (composition, COMPOSITION_NAME, "Big BW"); | |
| 1200 | entry_take_property (recording, RECORDING_COMPOSITION, composition); | |
| 1201 | entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(512000)); | |
| 1202 | music_source_checkin_entry (source, file, "test"); | |
| 1203 | } | |
| 1204 | ||
| 1205 | // Now, let's do this a second time, out of sequence to try to break the binary search. | |
| 1206 | { | |
| 1207 | Entry *file = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 3, "test"), | |
| 1208 | *recording = entry_get_property (file, FILE_RECORDING), | |
| 1209 | *composition = entry_new (ENTRY_TYPE_COMPOSITION, 0, NULL, "test"), | |
| 1210 | *artist = entry_new (ENTRY_TYPE_ARTIST, 0, NULL, "test"); | |
| 1211 | entry_set_property (artist, ARTIST_NAME, "Fat Freddy's Drop"); | |
| 1212 | entry_take_property (composition, COMPOSITION_ARTIST, artist); | |
| 1213 | entry_set_property (composition, COMPOSITION_NAME, "Shiverman"); | |
| 1214 | entry_take_property (recording, RECORDING_COMPOSITION, composition); | |
| 1215 | entry_set_property (file, FILE_BITRATE, GINT_TO_POINTER(512000)); | |
| 1216 | music_source_checkin_entry (source, file, "test"); | |
| 1217 | } | |
| 1218 | ||
| 1219 | const char *reference[][5] = { | |
| 1220 | {"Fat Freddy's Drop", "Big BW", "Test File 000000 (-00001)", NULL, NULL}, | |
| 1221 | {"Fat Freddy's Drop", "Shiverman", "Test File 000002 (-00001)", NULL, NULL}, | |
| 1222 | {"Unknown Artist", "Unknown Title", "Test File 000001 (-00001)", NULL, NULL}, | |
| 1223 | {"Unknown Artist", "Unknown Title", "Test File 000003 (-00001)", NULL, NULL}, | |
| 1224 | {"Unknown Artist", "Unknown Title", "Test File 000004 (-00001)", NULL, NULL}, | |
| 1225 | {"Unknown Artist", "Unknown Title", "Test File 000005 (-00001)", NULL, NULL}}; | |
| 1226 | assert_view (view, NULL, 6, reference); | |
| 1227 | ||
| 1228 | g_object_unref (view); | |
| 1229 | g_object_unref (source); | |
| 1230 | } | |
| 1231 | ||
| 1232 | /* Changing 4: Tests rows-reordered emission more thoroughly. */ | |
| 1233 | static void test_changing_4 () { | |
| 1234 | 419 | gboolean rows_reordered_was_emitted; const int *rows_reordered_template; |
| 1235 | 396 | void rows_reordered (GtkTreeModel *model, GtkTreePath *parent_path, GtkTreeIter *parent_iter, |
| 1236 | gint *new_order) { | |
| 1237 | g_assert_cmpintarray(new_order, rows_reordered_template, 10); | |
| 1238 | rows_reordered_was_emitted = TRUE; | |
| 1239 | }; | |
| 1240 | 402 | |
| 1241 | 396 | MusicSource *source = source_constructor(); |
| 1242 | ViewConfig *config = view_config_string_parse("artist[name]:recording[name]"); | |
| 1243 | ||
| 1244 | for (int i=1; i<11; i++) | |
| 1245 | test_add_song (source, i%5, i, i, 0, 0); | |
| 1246 | ||
| 1247 | MusicSourceView *view = music_source_create_view(source, config); | |
| 1248 | const char *reference_1[10][5] = { | |
| 1249 | {"Test Artist 000000", "Test Composition 000005", "Test File 000005 (000005)", NULL, NULL}, | |
| 1250 | {"Test Artist 000000", "Test Composition 000010", "Test File 000010 (000010)", NULL, NULL}, | |
| 1251 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, | |
| 1252 | {"Test Artist 000001", "Test Composition 000006", "Test File 000006 (000006)", NULL, NULL}, | |
| 1253 | {"Test Artist 000002", "Test Composition 000002", "Test File 000002 (000002)", NULL, NULL}, | |
| 1254 | {"Test Artist 000002", "Test Composition 000007", "Test File 000007 (000007)", NULL, NULL}, | |
| 1255 | {"Test Artist 000003", "Test Composition 000003", "Test File 000003 (000003)", NULL, NULL}, | |
| 1256 | {"Test Artist 000003", "Test Composition 000008", "Test File 000008 (000008)", NULL, NULL}, | |
| 1257 | {"Test Artist 000004", "Test Composition 000004", "Test File 000004 (000004)", NULL, NULL}, | |
| 1258 | {"Test Artist 000004", "Test Composition 000009", "Test File 000009 (000009)", NULL, NULL} | |
| 1259 | }; | |
| 1260 | ||
| 1261 | assert_view (view, NULL, 10, reference_1); | |
| 1262 | ||
| 1263 | 402 | const int rows_reordered_template_1[10] = { 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 }; |
| 1264 | 396 | rows_reordered_was_emitted = FALSE; |
| 1265 | 402 | rows_reordered_template = rows_reordered_template_1; |
| 1266 | ||
| 1267 | 396 | g_signal_connect (view, "rows-reordered", G_CALLBACK(rows_reordered), NULL); |
| 1268 | 402 | |
| 1269 | 396 | Entry *artist_0 = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 7, "test"); |
| 1270 | entry_set_property (artist_0, ARTIST_NAME, "Test Artist 000005"); | |
| 1271 | music_source_checkin_entry (source, artist_0, "test"); | |
| 1272 | ||
| 1273 | g_assert (rows_reordered_was_emitted); | |
| 1274 | ||
| 1275 | g_object_unref_many (view, source, NULL); _entry_cleanup (); | |
| 1276 | }; | |
| 1277 | 378 | |
| 1278 | 442 | /* Changing 5 (Folding): checks Unknown Artist is hidden when its last child is gone. */ |
| 1279 | 438 | static void test_changing_5_folding (void) { |
| 1280 | 442 | const char *config_string = "artist[name]>recording:file[path]"; |
| 1281 | GtkTreeView *tree_view = dynamic_view_test_setup(NULL, config_string); | |
| 1282 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1283 | MusicSource *source = music_source_view_get_source(view); | |
| 1284 | ||
| 1285 | test_add_song (source, TEST_UNKNOWN_ARTIST, 1, 1, 0, 0); | |
| 1286 | ||
| 1287 | const char *reference_1_root[1][5] = { | |
| 1288 | {"Unknown Artist", NULL, NULL, NULL, NULL} }; | |
| 1289 | assert_view (view, NULL, 1, reference_1_root); | |
| 1290 | ||
| 1291 | Entry *recording = music_source_checkout_entry (source, ENTRY_TYPE_RECORDING, 1, "test"), | |
| 1292 | *composition = entry_get_property (recording, RECORDING_COMPOSITION), | |
| 1293 | *artist = entry_new (ENTRY_TYPE_ARTIST, 0, NULL, "test"); | |
| 1294 | entry_set_property (artist, ARTIST_NAME, "Groove Armada"); | |
| 1295 | entry_take_property (composition, COMPOSITION_ARTIST, artist); | |
| 1296 | music_source_checkin_entry (source, recording, "test"); | |
| 1297 | ||
| 1298 | // Unknown Artist should now have been hidden. | |
| 1299 | const char *reference_2_root[1][5] = { | |
| 1300 | {"Groove Armada", NULL, NULL, NULL, NULL} }; | |
| 1301 | assert_view (view, NULL, 1, reference_2_root); | |
| 1302 | ||
| 1303 | dynamic_view_test_teardown (tree_view); | |
| 1304 | }; | |
| 1305 | ||
| 1306 | /* Changing 6 (Folding): checks emission of has-child-toggled. */ | |
| 1307 | static void test_changing_6_folding (void) { | |
| 1308 | 419 | gboolean has_child_toggled_emitted = FALSE; |
| 1309 | void has_child_toggled (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, | |
| 1310 | gpointer user_data) { | |
| 1311 | 420 | g_assert_cmpstr (gtk_tree_path_to_string(path), ==, "0"); |
| 1312 | 419 | has_child_toggled_emitted = TRUE; |
| 1313 | }; | |
| 1314 | ||
| 1315 | const char *config_string = "artist[name]:album:release[date] > track:recording[name]"; | |
| 1316 | GtkTreeView *tree_view = dynamic_view_test_setup(NULL, config_string); | |
| 1317 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1318 | MusicSource *source = music_source_view_get_source(view); | |
| 1319 | ||
| 1320 | g_signal_connect (view, "row-has-child-toggled", G_CALLBACK(has_child_toggled), NULL); | |
| 1321 | ||
| 1322 | 422 | // Add first song - we should get has-child-toggled for its track. This is needed for |
| 1323 | // GtkTreeView, so that it knows to give the new artist:album:release row a > expander. | |
| 1324 | 419 | test_add_song (source, 1, 1, 1, 1, 2); |
| 1325 | g_assert (has_child_toggled_emitted); has_child_toggled_emitted = FALSE; | |
| 1326 | ||
| 1327 | 422 | // Add a second song on the same album, so the same root row. Now we should be signalled! |
| 1328 | 419 | test_add_song (source, 1, 2, 2, 1, 1); |
| 1329 | g_assert (!has_child_toggled_emitted); | |
| 1330 | ||
| 1331 | const char *reference_1_root[1][5] = { | |
| 1332 | {"Test Artist 000001", NULL, NULL, "Test Album 000001", NULL} }; | |
| 1333 | ||
| 1334 | const char *reference_1_0[2][5] = { | |
| 1335 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "2"}, | |
| 1336 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000001", "1"} }; | |
| 1337 | ||
| 1338 | assert_view (view, NULL, 1, reference_1_root); | |
| 1339 | ||
| 1340 | GtkTreeIter iter; gtk_tree_model_iter_children(GTK_TREE_MODEL(view), &iter, NULL); | |
| 1341 | assert_view (view, &iter, 2, reference_1_0); | |
| 1342 | ||
| 1343 | music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 1); | |
| 1344 | g_assert (!has_child_toggled_emitted); | |
| 1345 | ||
| 1346 | 422 | // We don't want has-child-toggled emitting - the whole row is about to get deleted, because |
| 1347 | // Calliope doesn't display empty rows ... so there is no point. | |
| 1348 | 419 | music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 2); |
| 1349 | 422 | g_assert (!has_child_toggled_emitted); |
| 1350 | 419 | |
| 1351 | g_assert_cmpint (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL), ==, 0); | |
| 1352 | ||
| 1353 | dynamic_view_test_teardown (tree_view); | |
| 1354 | }; | |
| 1355 | ||
| 1356 | 442 | /* Changing 7 (Folding): checks emission of has-child-toggled on row changes. */ |
| 1357 | static void test_changing_7_folding (void) { | |
| 1358 | 420 | gboolean has_child_toggled_emitted = FALSE; |
| 1359 | void has_child_toggled (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, | |
| 1360 | gpointer user_data) { | |
| 1361 | g_assert_cmpstr (gtk_tree_path_to_string(path), ==, "0"); | |
| 1362 | has_child_toggled_emitted = TRUE; | |
| 1363 | }; | |
| 1364 | ||
| 1365 | GtkTreeIter iter; | |
| 1366 | const char *config_string = "artist[name]:(album:release[date]>track[number]:recording:file[bitrate])" | |
| 1367 | " /(recording[name]:file[bitrate])"; | |
| 1368 | GtkTreeView *tree_view = dynamic_view_test_setup(NULL, config_string); | |
| 1369 | MusicSourceView *view = MUSIC_SOURCE_VIEW(gtk_tree_view_get_model(tree_view)); | |
| 1370 | MusicSource *source = music_source_view_get_source(view); | |
| 1371 | ||
| 1372 | g_signal_connect (view, "row-has-child-toggled", G_CALLBACK(has_child_toggled), NULL); | |
| 1373 | ||
| 1374 | test_add_song (source, 1, 1, 1, 0, 0); | |
| 1375 | ||
| 1376 | const char *reference_1_root[1][5] = { | |
| 1377 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL} }; | |
| 1378 | ||
| 1379 | assert_view (view, NULL, 1, reference_1_root); | |
| 1380 | g_assert (!has_child_toggled_emitted); | |
| 1381 | ||
| 1382 | Entry *album = entry_new(ENTRY_TYPE_ALBUM, 0, NULL, "test"); | |
| 1383 | entry_set_property (album, ALBUM_NAME, "Test Album 000001"); | |
| 1384 | entry_take_property (album, ALBUM_ARTIST, | |
| 1385 | music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test")); | |
| 1386 | ||
| 1387 | Entry *release = entry_new(ENTRY_TYPE_RELEASE, 0, NULL, "test"); | |
| 1388 | entry_take_property (release, RELEASE_ALBUM, album); | |
| 1389 | ||
| 1390 | Entry *track = entry_new(ENTRY_TYPE_TRACK, 0, NULL, "test"); | |
| 1391 | entry_set_property (track, TRACK_NUMBER, GINT_TO_POINTER(1)); | |
| 1392 | entry_take_property (track, TRACK_RELEASE, release); | |
| 1393 | entry_take_property (track, TRACK_RECORDING, | |
| 1394 | music_source_query_entry(source, ENTRY_TYPE_RECORDING, 1, "test")); | |
| 1395 | ||
| 1396 | music_source_add_entry (source, track); | |
| 1397 | ||
| 1398 | const char *reference_2_root[1][5] = { | |
| 1399 | {"Test Artist 000001", NULL, NULL, NULL, NULL} }; | |
| 1400 | ||
| 1401 | const char *reference_2_0[2][5] = { | |
| 1402 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"} }; | |
| 1403 | ||
| 1404 | assert_view (view, NULL, 1, reference_2_root); | |
| 1405 | gtk_tree_model_iter_children(GTK_TREE_MODEL(view), &iter, NULL); | |
| 1406 | assert_view (view, &iter, 1, reference_2_0); | |
| 1407 | ||
| 1408 | g_assert (has_child_toggled_emitted); | |
| 1409 | ||
| 1410 | /*music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 1); | |
| 1411 | g_assert (!has_child_toggled_emitted); | |
| 1412 | ||
| 1413 | music_source_remove_entry (source, ENTRY_TYPE_RECORDING, 2); | |
| 1414 | g_assert (has_child_toggled_emitted); has_child_toggled_emitted = FALSE;*/ | |
| 1415 | ||
| 1416 | //g_assert_cmpint (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL), ==, 0); | |
| 1417 | ||
| 1418 | dynamic_view_test_teardown (tree_view); | |
| 1419 | }; | |
| 1420 | ||
| 1421 | 419 | |
| 1422 | 361 | /* FIXME: more test cases: |
| 1423 | 374 | * 1. changed signal on a m:1 join entry |
| 1424 | 364 | * 2. tougher tests that rows-reordered is always emitted correctly |
| 1425 | * 3. two tracks on different albums of same recording | |
| 1426 | * 4. a file one recording moving to another | |
| 1427 | 365 | * 5. changing an entry which is sort grouped. |
| 1428 | 364 | * 6. an entry not being an orphan any more !! |
| 1429 | 361 | */ |
| 1430 | 354 | |
| 1431 | ||
| 1432 | 185 | ////////////////////////////////////////////////// |
| 1433 | // Insertion test cases | |
| 1434 | // | |
| 1435 | 261 | // Note that these differ from what happens in real musicsources in one way - a normal musicsource will |
| 1436 | 185 | // wrap an add in begin_transaction and end_transaction. For example, when a recording is added and |
| 1437 | 261 | // then a track, the tests here add the recording as an orphan and then remove it again once the track is |
| 1438 | // added. In practice, because entry-added::recording will almost never be triggered before the track is | |
| 1439 | 185 | // also added to the source, this doesn't happen. The tests don't reproduce this because they are making the |
| 1440 | // sourceview work harder - if it passes these tests it will almost certainly work with the real sources. | |
| 1441 | // | |
| 1442 | ||
| 1443 | // Simplest insertion test case | |
| 1444 | // | |
| 1445 | static void test_inserting_1(void) { | |
| 1446 | MusicSource *source = source_constructor(); | |
| 1447 | 215 | ViewConfig *config = view_config_string_parse("file[path]"); |
| 1448 | 185 | MusicSourceView *view = music_source_create_view(source, config); |
| 1449 | 261 | |
| 1450 | 185 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1451 | g_assert_cmpint(e_children, ==, 0); | |
| 1452 | 261 | |
| 1453 | 286 | test_add_song(source, 3, 3, 3, 0, 0); |
| 1454 | 185 | GtkTreePath *path = gtk_tree_path_new_first(); |
| 1455 | GtkTreeRowReference *row_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(view), path); | |
| 1456 | gtk_tree_path_free(path); | |
| 1457 | 261 | |
| 1458 | 286 | test_add_song(source, 1, 1, 1, 0, 0); |
| 1459 | test_add_song(source, 4, 4, 4, 0, 0); | |
| 1460 | test_add_song(source, 2, 2, 2, 0, 0); | |
| 1461 | test_add_song(source, 5, 5, 5, 0, 0); | |
| 1462 | 185 | _music_source_view_check_tree(view, NULL); |
| 1463 | 261 | |
| 1464 | 185 | path = gtk_tree_row_reference_get_path(row_ref); |
| 1465 | int id = music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path); | |
| 1466 | g_assert_cmpint(id, ==, 1); | |
| 1467 | gtk_tree_row_reference_free(row_ref); | |
| 1468 | 261 | |
| 1469 | 185 | const char *reference[5][5] = { |
| 1470 | 286 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, |
| 1471 | {"Test Artist 000002", "Test Composition 000002", "Test File 000002 (000002)", NULL, NULL}, | |
| 1472 | {"Test Artist 000003", "Test Composition 000003", "Test File 000003 (000003)", NULL, NULL}, | |
| 1473 | {"Test Artist 000004", "Test Composition 000004", "Test File 000004 (000004)", NULL, NULL}, | |
| 1474 | {"Test Artist 000005", "Test Composition 000005", "Test File 000005 (000005)", NULL, NULL}}; | |
| 1475 | 261 | assert_view(view, NULL, 5, reference); |
| 1476 | ||
| 1477 | 185 | g_object_unref_many(view, source, NULL); |
| 1478 | 281 | _entry_cleanup (); |
| 1479 | 185 | }; |
| 1480 | ||
| 1481 | 226 | // Multiple levels |
| 1482 | // | |
| 1483 | static void test_inserting_2(void) { | |
| 1484 | MusicSource *source = source_constructor(); | |
| 1485 | 261 | ViewConfig *config = view_config_string_parse |
| 1486 | 226 | ("recording[name]>file[path]"); |
| 1487 | MusicSourceView *view = music_source_create_view(source, config); | |
| 1488 | 261 | |
| 1489 | 226 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1490 | g_assert_cmpint (e_children, ==, 0); | |
| 1491 | ||
| 1492 | test_add_song (source, 1, 1, 1, 0, 0); | |
| 1493 | test_add_song (source, 1, 1, 2, 0, 0); | |
| 1494 | 261 | |
| 1495 | 226 | const char *reference_root_a[1][5] = { |
| 1496 | {"Test Artist 000001", "Test Composition 000001", NULL, NULL, NULL}}; | |
| 1497 | ||
| 1498 | const char *reference_recording_1_a[2][5] = { | |
| 1499 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, | |
| 1500 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", NULL, NULL}}; | |
| 1501 | ||
| 1502 | GtkTreeIter recording_iter; gboolean result; | |
| 1503 | assert_view (view, NULL, 1, reference_root_a); | |
| 1504 | 261 | |
| 1505 | 226 | result = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(view), &recording_iter); |
| 1506 | g_assert(result); | |
| 1507 | assert_view (view, &recording_iter, 2, reference_recording_1_a); | |
| 1508 | 261 | |
| 1509 | 226 | // Now add some more. This is important because entrysadded takes the easy way out and |
| 1510 | 261 | // does nothing if the node isn't yet queried - so now it is queried the code takes a |
| 1511 | 226 | // different path. |
| 1512 | test_add_song (source, 1, 2, 3, 0, 0); | |
| 1513 | test_add_song (source, 1, 1, 4, 0, 0); | |
| 1514 | 261 | |
| 1515 | 226 | const char *reference_root_b[2][5] = { |
| 1516 | {"Test Artist 000001", "Test Composition 000001", NULL, NULL, NULL}, | |
| 1517 | {"Test Artist 000001", "Test Composition 000002", NULL, NULL, NULL}}; | |
| 1518 | ||
| 1519 | const char *reference_recording_1_b[3][5] = { | |
| 1520 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", NULL, NULL}, | |
| 1521 | {"Test Artist 000001", "Test Composition 000001", "Test File 000002 (000001)", NULL, NULL}, | |
| 1522 | {"Test Artist 000001", "Test Composition 000001", "Test File 000004 (000001)", NULL, NULL}}; | |
| 1523 | 261 | |
| 1524 | 226 | assert_view (view, NULL, 2, reference_root_b); |
| 1525 | 261 | |
| 1526 | 226 | result = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(view), &recording_iter); |
| 1527 | g_assert(result); | |
| 1528 | assert_view (view, &recording_iter, 3, reference_recording_1_b); | |
| 1529 | ||
| 1530 | g_object_unref_many (view, source, NULL); | |
| 1531 | 281 | _entry_cleanup (); |
| 1532 | 226 | }; |
| 1533 | ||
| 1534 | 185 | // Insertion with sort groups & many:1 joins. |
| 1535 | // | |
| 1536 | 226 | static void test_inserting_3(void) { |
| 1537 | // This setting makes the test easier to debug, but potentially less thorough. | |
| 1538 | 185 | // |
| 1539 | 261 | //#define SIMPLE |
| 1540 | ||
| 1541 | 185 | MusicSource *source = source_constructor(); |
| 1542 | 233 | ViewConfig *config = view_config_string_parse("(album[name]:release:track:recording:file[path])" |
| 1543 | "/ (artist[name]:recording:file[path])"); | |
| 1544 | 185 | MusicSourceView *view = music_source_create_view(source, config); |
| 1545 | 261 | |
| 1546 | 185 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1547 | g_assert_cmpint(e_children, ==, 0); | |
| 1548 | 261 | |
| 1549 | 185 | music_source_begin_transaction(source); // Being in a transaction changes things subtly, |
| 1550 | test_add_song(source, 1, 1, 1, 1, 1); // because now the file-added signal will come | |
| 1551 | music_source_end_transaction(source); // after the track entry is added. | |
| 1552 | ||
| 1553 | GtkTreePath *path = gtk_tree_path_new_first(); | |
| 1554 | GtkTreeRowReference *row_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(view), path); | |
| 1555 | gtk_tree_path_free(path); | |
| 1556 | ||
| 1557 | // Add two tracks of same recording | |
| 1558 | 261 | test_add_song (source, 1, 2, 2, 1, 2); |
| 1559 | test_add_song (source, 1, 2, 2, 2, 1); | |
| 1560 | 185 | |
| 1561 | // Add files to existing recording | |
| 1562 | Entry *recording = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 2, "test"); | |
| 1563 | 281 | g_assert (recording!=NULL); |
| 1564 | 185 | |
| 1565 | test_add_file(source, recording, 2, 3); | |
| 1566 | 261 | |
| 1567 | 185 | #ifndef SIMPLE |
| 1568 | test_add_file(source, recording, 2, 4); | |
| 1569 | ||
| 1570 | // How about another track of the same recording now! | |
| 1571 | 261 | test_add_song(source, 1, 2, 2, 3, 1); |
| 1572 | ||
| 1573 | 185 | // Some more songs |
| 1574 | 261 | test_add_song(source, 1, 3, 5, 2, 2); |
| 1575 | test_add_song(source, 1, 4, 6, 2, 3); | |
| 1576 | 185 | #endif |
| 1577 | 281 | entry_unref (recording, "test"); |
| 1578 | 185 | |
| 1579 | _music_source_view_check_tree(view, NULL); | |
| 1580 | #ifdef SIMPLE | |
| 1581 | const int n_children = 5; | |
| 1582 | const char *reference[5][5] = { | |
| 1583 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"}, | |
| 1584 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000001", "2"}, | |
| 1585 | {"Test Artist 000001", "Test Composition 000002", "Test File 000003 (000002)", "Test Album 000001", "2"}, | |
| 1586 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000002", "1"}, | |
| 1587 | {"Test Artist 000001", "Test Composition 000002", "Test File 000003 (000002)", "Test Album 000002", "1"}}; | |
| 1588 | #else | |
| 1589 | const int n_children = 12; | |
| 1590 | const char *reference[12][5] = { | |
| 1591 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"}, | |
| 1592 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000001", "2"}, | |
| 1593 | {"Test Artist 000001", "Test Composition 000002", "Test File 000003 (000002)", "Test Album 000001", "2"}, | |
| 1594 | {"Test Artist 000001", "Test Composition 000002", "Test File 000004 (000002)", "Test Album 000001", "2"}, | |
| 1595 | 261 | |
| 1596 | 185 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000002", "1"}, |
| 1597 | {"Test Artist 000001", "Test Composition 000002", "Test File 000003 (000002)", "Test Album 000002", "1"}, | |
| 1598 | {"Test Artist 000001", "Test Composition 000002", "Test File 000004 (000002)", "Test Album 000002", "1"}, | |
| 1599 | {"Test Artist 000001", "Test Composition 000003", "Test File 000005 (000003)", "Test Album 000002", "2"}, | |
| 1600 | {"Test Artist 000001", "Test Composition 000004", "Test File 000006 (000004)", "Test Album 000002", "3"}, | |
| 1601 | 261 | |
| 1602 | 185 | {"Test Artist 000001", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000003", "1"}, |
| 1603 | {"Test Artist 000001", "Test Composition 000002", "Test File 000003 (000002)", "Test Album 000003", "1"}, | |
| 1604 | {"Test Artist 000001", "Test Composition 000002", "Test File 000004 (000002)", "Test Album 000003", "1"}}; | |
| 1605 | 261 | #endif |
| 1606 | ||
| 1607 | assert_view(view, NULL, n_children, reference); | |
| 1608 | ||
| 1609 | 185 | path = gtk_tree_row_reference_get_path(row_ref); |
| 1610 | int id = music_source_view_get_id_at_path(view, ENTRY_TYPE_FILE, path); | |
| 1611 | g_assert_cmpint(id, ==, 1); | |
| 1612 | gtk_tree_row_reference_free(row_ref); | |
| 1613 | 261 | |
| 1614 | 185 | g_object_unref_many(view, source, NULL); |
| 1615 | 281 | _entry_cleanup (); |
| 1616 | 185 | }; |
| 1617 | ||
| 1618 | // Orphans! | |
| 1619 | // | |
| 1620 | static void test_inserting_4(void) { | |
| 1621 | MusicSource *source = source_constructor(); | |
| 1622 | 234 | ViewConfig *config = view_config_string_parse |
| 1623 | (" artist[name] > (album:release[date]>track:recording:file[path])" | |
| 1624 | " / (recording:file[path])"); | |
| 1625 | 185 | MusicSourceView *view = music_source_create_view(source, config); |
| 1626 | 261 | |
| 1627 | 185 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1628 | 269 | g_assert_cmpint (e_children, ==, 0); |
| 1629 | 185 | |
| 1630 | 261 | test_add_song (source, 1, 1, 1, 1, 1); |
| 1631 | test_add_song (source, 2, 2, 2, 2, 1); | |
| 1632 | ||
| 1633 | 185 | // An orphan :( |
| 1634 | 261 | //printf ("\n\nAdding orphan ..\n"); fflush (stdout); |
| 1635 | 185 | test_add_song (source, 2, 3, 3, -1, -1); |
| 1636 | 261 | |
| 1637 | 269 | const char *reference_root[2][5] = { |
| 1638 | 185 | {"Test Artist 000001", NULL, NULL, NULL, NULL}, |
| 1639 | 269 | {"Test Artist 000002", NULL, NULL, NULL, NULL}}; |
| 1640 | 185 | |
| 1641 | const char *reference_artist_1[1][5] = { | |
| 1642 | {"Test Artist 000001", NULL, NULL, "Test Album 000001", NULL}}; | |
| 1643 | 261 | |
| 1644 | 185 | const char *reference_album_1[1][5] = { |
| 1645 | {"Test Artist 000001", "Test Composition 000001", "Test File 000001 (000001)", "Test Album 000001", "1"}}; | |
| 1646 | ||
| 1647 | const char *reference_artist_2[2][5] = { | |
| 1648 | {"Test Artist 000002", NULL, NULL, "Test Album 000002", NULL}, | |
| 1649 | {"Test Artist 000002", "Test Composition 000003", "Test File 000003 (000003)", NULL, NULL}}; | |
| 1650 | ||
| 1651 | const char *reference_album_2[1][5] = { | |
| 1652 | {"Test Artist 000002", "Test Composition 000002", "Test File 000002 (000002)", "Test Album 000002", "1"}}; | |
| 1653 | 261 | |
| 1654 | ||
| 1655 | 185 | GtkTreeIter artist_iter, album_iter; gboolean result; |
| 1656 | 269 | assert_view (view, NULL, 2, reference_root); |
| 1657 | 261 | |
| 1658 | 185 | result = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(view), &artist_iter); g_assert(result); |
| 1659 | assert_view (view, &artist_iter, 1, reference_artist_1); | |
| 1660 | 261 | |
| 1661 | 185 | result = gtk_tree_model_iter_children (GTK_TREE_MODEL(view), &album_iter, &artist_iter); g_assert(result); |
| 1662 | assert_view (view, &album_iter, 1, reference_album_1); | |
| 1663 | 261 | |
| 1664 | 185 | result = gtk_tree_model_iter_next (GTK_TREE_MODEL(view), &artist_iter); g_assert(result); |
| 1665 | assert_view (view, &artist_iter, 2, reference_artist_2); | |
| 1666 | 261 | |
| 1667 | 185 | result = gtk_tree_model_iter_children (GTK_TREE_MODEL(view), &album_iter, &artist_iter); g_assert(result); |
| 1668 | assert_view (view, &album_iter, 1, reference_album_2); | |
| 1669 | 261 | |
| 1670 | 185 | // FIXME: add some more after nodes exist !!! |
| 1671 | ||
| 1672 | g_object_unref_many (view, source, NULL); | |
| 1673 | 281 | _entry_cleanup (); |
| 1674 | 185 | }; |
| 1675 | ||
| 1676 | ||
| 1677 | 261 | // Orphans 2. |
| 1678 | 209 | // |
| 1679 | 261 | // This config gives an orphan config of artist[name]:recording:file[bitrate] .. so files can't be |
| 1680 | 390 | // found using the binary search, because they are all the same bitrate. |
| 1681 | // FIXME: test that slightly more ... | |
| 1682 | 209 | // |
| 1683 | static void test_inserting_5 (void) { | |
| 1684 | MusicSource *source = source_constructor(); | |
| 1685 | 261 | ViewConfig *config = view_config_string_parse |
| 1686 | 235 | ("artist[name]:(album:release[date] > " |
| 1687 | 266 | "track[number]:recording[many-to-one-join]:file[bitrate])" |
| 1688 | 235 | "/ (recording:file[bitrate])"); |
| 1689 | 209 | MusicSourceView *view = music_source_create_view(source, config); |
| 1690 | ||
| 1691 | 261 | // Read the node so the files are actually added - otherwise entry-added will see the nodes |
| 1692 | 235 | // haven't been read and leave them as such. |
| 1693 | gtk_tree_model_iter_n_children (GTK_TREE_MODEL(view), NULL); | |
| 1694 | ||
| 1695 | 261 | // Because we add in a strange order, remember entry id's don't match up to the numbers given |
| 1696 | 235 | // here. |
| 1697 | 213 | test_add_song (source, 2, 2, 2, 2, 1); |
| 1698 | test_add_song (source, 1, 3, 3, -1, -1); | |
| 1699 | test_add_song (source, 2, 4, 4, -1, -1); | |
| 1700 | 261 | test_add_song (source, 1, 1, 1, 1, 1); |
| 1701 | 235 | test_add_song (source, 3, 5, 5, 3, 1); |
| 1702 | 261 | |
| 1703 | 235 | const char *reference[5][5] = { |
| 1704 | {"Test Artist 000001", NULL, NULL, "Test Album 000001", NULL}, | |
| 1705 | {"Test Artist 000001", "Test Composition 000003", "Test File 000003 (000003)", NULL, NULL}, | |
| 1706 | {"Test Artist 000002", NULL, NULL, "Test Album 000002", NULL}, | |
| 1707 | {"Test Artist 000002", "Test Composition 000004", "Test File 000004 (000004)", NULL, NULL}, | |
| 1708 | 412 | {"Test Artist 000003", NULL, NULL, "Test Album 000003", NULL} }; |
| 1709 | 261 | |
| 1710 | assert_view (view, NULL, 5, reference); | |
| 1711 | ||
| 1712 | 235 | // Test one of the track nodes. |
| 1713 | 261 | GtkTreeIter release_iter, track_iter; |
| 1714 | 235 | gboolean result = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(view), &release_iter, NULL, 2); |
| 1715 | g_assert_cmpint (result, ==, TRUE); | |
| 1716 | 261 | g_assert_cmpint (((ViewNode *)release_iter.user_data)->depth, ==, 0); |
| 1717 | 235 | g_assert_cmpint (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), &release_iter), ==, 1); |
| 1718 | 261 | |
| 1719 | 235 | result = gtk_tree_model_iter_children(GTK_TREE_MODEL(view), &track_iter, &release_iter); |
| 1720 | g_assert_cmpint (result, ==, TRUE); | |
| 1721 | 261 | |
| 1722 | 390 | // Test for get_path_for_id and get_id_at_path inconsistencies. |
| 1723 | 235 | for (int i=1; i<=5; i++) { |
| 1724 | 209 | int recording_id = i; |
| 1725 | 261 | GtkTreePath *path = music_source_view_get_path_for_id(view, ENTRY_TYPE_RECORDING, |
| 1726 | 209 | recording_id); |
| 1727 | 261 | //printf ("Got path %s.\n", gtk_tree_path_to_string(path)); fflush (stdout); |
| 1728 | 209 | int id_at_path = music_source_view_get_id_at_path(view, ENTRY_TYPE_RECORDING, |
| 1729 | path); | |
| 1730 | g_assert_cmpint (id_at_path, ==, recording_id); | |
| 1731 | 261 | gtk_tree_path_free (path); |
| 1732 | 209 | }; |
| 1733 | ||
| 1734 | 336 | g_object_unref_many (view, source, NULL); _entry_cleanup (); |
| 1735 | }; | |
| 1736 | ||
| 1737 | 412 | |
| 1738 | 209 | |
| 1739 | 185 | typedef struct { |
| 1740 | MusicSource *source; | |
| 1741 | int recording_count; | |
| 1742 | } PresetFixture; | |
| 1743 | ||
| 1744 | static void preset_static_setup(PresetFixture *fixture, const void *test_data) { | |
| 1745 | fixture->source=source_constructor(); | |
| 1746 | music_source_begin_transaction(fixture->source); | |
| 1747 | // say 250 recs and 100 orphans | |
| 1748 | int artist_n=1; | |
| 1749 | int album_n=1; | |
| 1750 | int track_n=1, offs=0, i; | |
| 1751 | for (i=1;i<=250;i++) { | |
| 1752 | test_add_song(fixture->source, artist_n, i, i, album_n, track_n); | |
| 1753 | if (i&16) artist_n++; | |
| 1754 | if (i&16) { | |
| 1755 | album_n++; | |
| 1756 | track_n=1; | |
| 1757 | 261 | } else track_n++; |
| 1758 | 185 | }; |
| 1759 | offs=i; | |
| 1760 | for (int i=1;i<=100;i++) { | |
| 1761 | test_add_song(fixture->source, artist_n, i+offs, i+offs, -1, -1); | |
| 1762 | if (i&16) artist_n++; | |
| 1763 | }; | |
| 1764 | fixture->recording_count = 350; | |
| 1765 | music_source_end_transaction(fixture->source); | |
| 1766 | }; | |
| 1767 | ||
| 1768 | static void preset_static_teardown(PresetFixture *fixture, const void *test_data) { | |
| 1769 | g_object_unref(fixture->source); | |
| 1770 | 273 | _entry_cleanup (); |
| 1771 | 185 | }; |
| 1772 | ||
| 1773 | ||
| 1774 | 261 | static void test_preset_static(PresetFixture *fixture, const void *test_data) { |
| 1775 | 259 | const int preset_n = GPOINTER_TO_INT(test_data); |
| 1776 | 185 | int exec_count=20; |
| 1777 | if (!g_test_slow() && !g_test_perf()) | |
| 1778 | exec_count=1; | |
| 1779 | 261 | |
| 1780 | 185 | double secs=0.0; |
| 1781 | //music_source_dump (fixture->source); | |
| 1782 | for (int i=0;i<exec_count;i++) { | |
| 1783 | 261 | ViewConfig *config = view_config_string_parse(view_config_preset[preset_n][1]); |
| 1784 | MusicSourceView *view = music_source_create_view(fixture->source, config); | |
| 1785 | 185 | |
| 1786 | g_test_timer_start(); | |
| 1787 | 261 | |
| 1788 | //print_view (view, 4); | |
| 1789 | ||
| 1790 | 185 | // Find some of these fellows. |
| 1791 | 262 | for (int i=0; i<70; i++) { |
| 1792 | 185 | int recording_id = g_test_rand_int_range (1, fixture->recording_count+1); |
| 1793 | 262 | GtkTreePath *path = NULL; |
| 1794 | ||
| 1795 | if (i & 1) { | |
| 1796 | MusicSourceViewSearch *search = music_source_view_get_path_for_id_begin | |
| 1797 | (view, ENTRY_TYPE_RECORDING, recording_id); | |
| 1798 | while (!music_source_view_get_path_for_id_step(view, search)); | |
| 1799 | path = music_source_view_get_path_for_id_end (view, search); | |
| 1800 | g_warn_if_fail (path!=NULL); | |
| 1801 | } else { | |
| 1802 | path = music_source_view_get_path_for_id (view, ENTRY_TYPE_RECORDING, | |
| 1803 | recording_id); | |
| 1804 | g_warn_if_fail (path!=NULL); | |
| 1805 | }; | |
| 1806 | ||
| 1807 | 185 | g_assert_cmpint (music_source_view_get_id_at_path (view, ENTRY_TYPE_RECORDING, |
| 1808 | path), ==, recording_id); | |
| 1809 | 261 | gtk_tree_path_free (path); |
| 1810 | 185 | }; |
| 1811 | 261 | |
| 1812 | 185 | GtkTreeIter iter; |
| 1813 | int i=0; | |
| 1814 | if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter)) { | |
| 1815 | i++; | |
| 1816 | while (gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter)) | |
| 1817 | 261 | i++; |
| 1818 | 185 | } |
| 1819 | 261 | |
| 1820 | 185 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1821 | //printf("e_children: %i, counted %i\n", e_children, i); fflush(stdout); | |
| 1822 | g_assert_cmpint (e_children, ==, i); | |
| 1823 | 261 | |
| 1824 | 185 | secs+=g_test_timer_elapsed(); |
| 1825 | 261 | |
| 1826 | 185 | g_object_unref(view); |
| 1827 | }; | |
| 1828 | ||
| 1829 | //secs/=(double)exec_count; | |
| 1830 | if (g_test_perf() || g_test_slow()) | |
| 1831 | g_test_minimized_result(secs, "%f", secs); | |
| 1832 | }; | |
| 1833 | ||
| 1834 | ||
| 1835 | 261 | static void test_preset_dynamic(const void *data) { |
| 1836 | 185 | int preset_n = GPOINTER_TO_INT(data); |
| 1837 | 209 | int exec_count = 20; |
| 1838 | 185 | if (!g_test_slow() && !g_test_perf()) |
| 1839 | 209 | exec_count = 1; |
| 1840 | 261 | |
| 1841 | 209 | double secs = 0.0; |
| 1842 | for (int i=0; i<exec_count; i++) { | |
| 1843 | 185 | MusicSource *source = source_constructor(); |
| 1844 | 261 | ViewConfig *config = view_config_string_parse(view_config_preset[preset_n][1]); |
| 1845 | MusicSourceView *view = music_source_create_view(source, config); | |
| 1846 | 185 | |
| 1847 | 209 | g_test_timer_start (); |
| 1848 | 261 | |
| 1849 | 185 | // FIXME: should this or should it not be done inside a transaction? Which is a better |
| 1850 | // test? | |
| 1851 | music_source_begin_transaction (source); | |
| 1852 | int flag[351] = {0}; | |
| 1853 | for (int i=0; i<350; i++) { | |
| 1854 | 421 | int n; do n = g_test_rand_int_range (1, 350+1); while (flag[n]); |
| 1855 | 261 | |
| 1856 | 185 | int artist_n = n / 16, |
| 1857 | 209 | album_n = n > 250? -1: (n / 16) + 1, |
| 1858 | track_n = n > 250? -1: (n % 16) + 1; | |
| 1859 | 261 | |
| 1860 | 185 | test_add_song (source, artist_n, n, n, album_n, track_n); |
| 1861 | flag[n] = TRUE; | |
| 1862 | }; | |
| 1863 | music_source_end_transaction (source); | |
| 1864 | 261 | |
| 1865 | 209 | // Find some of these fellows. static does this randomly so that the chaining is tested |
| 1866 | 185 | // a bit more thoroughly, but here it makes more sense to do a linear test. |
| 1867 | for (int i=1; i<=350; i++) { | |
| 1868 | int recording_id = i; | |
| 1869 | 261 | GtkTreePath *path = music_source_view_get_path_for_id(view, ENTRY_TYPE_RECORDING, |
| 1870 | 209 | recording_id); |
| 1871 | int id_at_path = music_source_view_get_id_at_path(view, ENTRY_TYPE_RECORDING, | |
| 1872 | path); | |
| 1873 | 261 | //printf ("rec %i: Got path: %s, id at path %i\n", recording_id, |
| 1874 | 209 | // gtk_tree_path_to_string(path), id_at_path); fflush(stdout); |
| 1875 | 185 | g_assert_cmpint (id_at_path, ==, recording_id); |
| 1876 | 261 | gtk_tree_path_free (path); |
| 1877 | 185 | }; |
| 1878 | 261 | |
| 1879 | 185 | GtkTreeIter iter; |
| 1880 | int i=0; | |
| 1881 | if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(view), &iter)) { | |
| 1882 | i++; | |
| 1883 | while (gtk_tree_model_iter_next(GTK_TREE_MODEL(view), &iter)) | |
| 1884 | 261 | i++; |
| 1885 | 185 | } |
| 1886 | 261 | |
| 1887 | 185 | // FIXME: also test removing! |
| 1888 | 261 | |
| 1889 | 185 | int e_children = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(view), NULL); |
| 1890 | g_assert_cmpint (e_children, ==, i); | |
| 1891 | 261 | |
| 1892 | 209 | secs += g_test_timer_elapsed(); |
| 1893 | 261 | |
| 1894 | 185 | g_object_unref (view); |
| 1895 | g_object_unref (source); | |
| 1896 | }; | |
| 1897 | ||
| 1898 | //secs/=(double)exec_count; | |
| 1899 | if (g_test_perf() || g_test_slow()) | |
| 1900 | g_test_minimized_result(secs, "%f", secs); | |
| 1901 | }; | |
| 1902 | ||
| 1903 | void vertical_tests_register(const char *root, MusicSource *(*create_func)()) { | |
| 1904 | source_constructor=create_func; | |
| 1905 | 261 | |
| 1906 | char path[256]; | |
| 1907 | 438 | g_test_add_func (PATH_PRINTF("/%s/vertical/Flat", root), test_flat); |
| 1908 | 261 | |
| 1909 | 412 | g_test_add_func (PATH_PRINTF("/%s/vertical/Chaining/overestimate/Static", root), |
| 1910 | 206 | G_CALLBACK(test_chaining_overestimate_static)); |
| 1911 | 261 | //g_test_add_func (PATH_PRINTF("/%s/vertical/Chaining/overestimate/Adding", root), |
| 1912 | 221 | // G_CALLBACK(test_chaining_overestimate_adding)); |
| 1913 | 261 | g_test_add_func (PATH_PRINTF("/%s/vertical/Chaining/underestimate/Static", root), |
| 1914 | 209 | G_CALLBACK(test_chaining_underestimate_static)); |
| 1915 | 237 | g_test_add_func (PATH_PRINTF("/%s/vertical/Chaining 4", root), G_CALLBACK(test_chaining_4)); |
| 1916 | 259 | g_test_add_func (PATH_PRINTF("/%s/vertical/Chaining 5", root), G_CALLBACK(test_chaining_5)); |
| 1917 | 261 | |
| 1918 | 237 | g_test_add_func (PATH_PRINTF("/%s/vertical/Sorting 1", root), test_sorting_1); |
| 1919 | 390 | g_test_add_func (PATH_PRINTF("/%s/vertical/Sorting 2", root), test_sorting_2); |
| 1920 | g_test_add_func (PATH_PRINTF("/%s/vertical/Location 1", root), test_location_1); | |
| 1921 | 231 | g_test_add_func (PATH_PRINTF("/%s/vertical/Location 2", root), test_location_2); |
| 1922 | g_test_add_func (PATH_PRINTF("/%s/vertical/Location 3", root), test_location_3); | |
| 1923 | 418 | g_test_add_func (PATH_PRINTF("/%s/vertical/Location 4", root), test_location_4); |
| 1924 | 261 | |
| 1925 | 441 | g_test_add_func (PATH_PRINTF("/%s/vertical/Removal 1", root), test_removal_1); |
| 1926 | g_test_add_func (PATH_PRINTF("/%s/vertical/Removal 2", root), test_removal_2); | |
| 1927 | g_test_add_func (PATH_PRINTF("/%s/vertical/Removal 3", root), test_removal_3); | |
| 1928 | g_test_add_func (PATH_PRINTF("/%s/vertical/Removal 4", root), test_removal_4); | |
| 1929 | 354 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 1", root), test_changing_1); |
| 1930 | 396 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 2", root), test_changing_2); |
| 1931 | 431 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 3", root), test_changing_3); |
| 1932 | 438 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 4", root), test_changing_4); |
| 1933 | 431 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 5 (Folding)", root), |
| 1934 | test_changing_5_folding); | |
| 1935 | 438 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 6 (Folding)", root), |
| 1936 | test_changing_6_folding); | |
| 1937 | 442 | g_test_add_func (PATH_PRINTF("/%s/vertical/Changing 7 (Folding)", root), |
| 1938 | test_changing_7_folding); | |
| 1939 | 226 | g_test_add_func (PATH_PRINTF("/%s/vertical/Inserting 1", root), test_inserting_1); |
| 1940 | 185 | g_test_add_func (PATH_PRINTF("/%s/vertical/Inserting 2", root), test_inserting_2); |
| 1941 | g_test_add_func (PATH_PRINTF("/%s/vertical/Inserting 3", root), test_inserting_3); | |
| 1942 | 226 | g_test_add_func (PATH_PRINTF("/%s/vertical/Inserting 4", root), test_inserting_4); |
| 1943 | 418 | g_test_add_func (PATH_PRINTF("/%s/vertical/Inserting 5", root), test_inserting_5); |
| 1944 | 261 | |
| 1945 | 412 | for (int i=0; i<VIEW_CONFIG_PRESETS; i++) { |
| 1946 | 261 | g_test_add (PATH_PRINTF("/%s/vertical/Preset %i Static: %s", root, i, |
| 1947 | view_config_preset[i][0]), | |
| 1948 | PresetFixture, GINT_TO_POINTER(i), preset_static_setup, | |
| 1949 | 185 | test_preset_static, preset_static_teardown); |
| 1950 | 261 | g_test_add_data_func (PATH_PRINTF("/%s/vertical/Preset %i Dynamic: %s", root, i, |
| 1951 | view_config_preset[i][0]), | |
| 1952 | 209 | GINT_TO_POINTER(i), test_preset_dynamic); |
| 1953 | 412 | }; |
| 1954 | 185 | }; |
| 1955 | ||
| 1956 |
Loggerhead is a web-based interface for Bazaar branches