hythmbox-2.98/tests/test-rhythmdb-property-model.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found test-rhythmdb-property-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  This program is free software; you can redistribute it and/or modify
  4  *  it under the terms of the GNU General Public License as published by
  5  *  the Free Software Foundation; either version 2 of the License, or
  6  *  (at your option) any later version.
  7  *
  8  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  9  *  GStreamer plugins to be used and distributed together with GStreamer
 10  *  and Rhythmbox. This permission is above and beyond the permissions granted
 11  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 12  *  you may extend this exception to your version of the code, but you are not
 13  *  obligated to do so. If you do not wish to do so, delete this exception
 14  *  statement from your version.
 15  *
 16  *  This program is distributed in the hope that it will be useful,
 17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19  *  GNU General Public License for more details.
 20  *
 21  *  You should have received a copy of the GNU General Public License
 22  *  along with this program; if not, write to the Free Software
 23  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 24  *
 25  */
 26 
 27 
 28 #include "config.h"
 29 
 30 #include <check.h>
 31 #include <gtk/gtk.h>
 32 #include <locale.h>
 33 #include "test-utils.h"
 34 #include "rhythmdb-query-model.h"
 35 #include "rhythmdb-property-model.h"
 36 
 37 #include "rb-debug.h"
 38 #include "rb-file-helpers.h"
 39 #include "rb-util.h"
 40 
 41 static int
 42 _get_property_count (RhythmDBPropertyModel *model, const char *artist)
 43 {
 44 	GtkTreeIter iter;
 45 	int count;
 46 
 47 	if (rhythmdb_property_model_iter_from_string (model, artist, &iter) == FALSE) {
 48 		return 0;
 49 	}
 50 
 51 	gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
 52 			    RHYTHMDB_PROPERTY_MODEL_COLUMN_NUMBER, &count, -1);
 53 	return count;
 54 }
 55 
 56 /* tests property models attached to static query models */
 57 START_TEST (test_rhythmdb_property_model_static)
 58 {
 59 	RhythmDBQueryModel *model;
 60 	RhythmDBQueryModel *model2;
 61 	RhythmDBPropertyModel *propmodel;
 62 	RhythmDBEntry *a, *b;
 63 	GtkTreeIter iter;
 64 
 65 	start_test_case ();
 66 
 67 	/* setup */
 68 	model = rhythmdb_query_model_new_empty (db);
 69 	g_object_set (model, "show-hidden", FALSE, NULL);
 70 	propmodel = rhythmdb_property_model_new (db, RHYTHMDB_PROP_ARTIST);
 71 	g_object_set (propmodel, "query-model", model, NULL);
 72 
 73 	/* create test entries */
 74 	a = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///a.ogg");
 75 	b = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///b.ogg");
 76 	rhythmdb_commit (db);
 77 
 78 	/* set artist values */
 79 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "x");
 80 	set_entry_string (db, b, RHYTHMDB_PROP_ARTIST, "y");
 81 	rhythmdb_commit (db);
 82 
 83 	end_step ();
 84 
 85 	/* add to model */
 86 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
 87 	rhythmdb_query_model_add_entry (model, a, -1);
 88 	wait_for_signal ();
 89 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
 90 	rhythmdb_query_model_add_entry (model, b, -1);
 91 	wait_for_signal ();
 92 	fail_unless (rhythmdb_query_model_entry_to_iter (model, a, &iter));
 93 	fail_unless (rhythmdb_query_model_entry_to_iter (model, b, &iter));
 94 	/*fail_unless (_get_property_count (propmodel, _("All")) == 2);*/
 95 	fail_unless (_get_property_count (propmodel, "x") == 1);
 96 	fail_unless (_get_property_count (propmodel, "y") == 1);
 97 
 98 	end_step ();
 99 
100 	/* change one */
101 	set_waiting_signal (G_OBJECT (propmodel), "row-deleted");
102 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "y");
103 	rhythmdb_commit (db);
104 	wait_for_signal ();
105 	fail_unless (_get_property_count (propmodel, "x") == 0);
106 	fail_unless (_get_property_count (propmodel, "y") == 2);
107 
108 	end_step ();
109 
110 	/* hide it */
111 	set_waiting_signal (G_OBJECT (model), "entry-prop-changed");
112 	set_entry_hidden (db, a, TRUE);
113 	rhythmdb_commit (db);
114 	wait_for_signal ();
115 	/*fail_unless (_get_property_count (propmodel, _("All")) == 1);*/
116 	fail_unless (_get_property_count (propmodel, "x") == 0);
117 	fail_unless (_get_property_count (propmodel, "y") == 1);
118 
119 	end_step ();
120 
121 	/* change back */
122 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "x");
123 	rhythmdb_commit (db);
124 	fail_unless (_get_property_count (propmodel, "x") == 0);
125 	fail_unless (_get_property_count (propmodel, "y") == 1);
126 
127 	end_step ();
128 
129 	/* unhide */
130 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
131 	set_entry_hidden (db, a, FALSE);
132 	rhythmdb_commit (db);
133 	wait_for_signal ();
134 
135 	/*fail_unless (_get_property_count (propmodel, _("All")) == 2);*/
136 	fail_unless (_get_property_count (propmodel, "x") == 1);
137 	fail_unless (_get_property_count (propmodel, "y") == 1);
138 
139 	end_step ();
140 
141 	/* remove one */
142 	set_waiting_signal (G_OBJECT (propmodel), "pre-row-deletion");
143 	rhythmdb_query_model_remove_entry (model, a);
144 	wait_for_signal ();
145 	/*fail_unless (_get_property_count (propmodel, _("All")) == 1);*/
146 	fail_unless (_get_property_count (propmodel, "x") == 0);
147 	fail_unless (_get_property_count (propmodel, "y") == 1);
148 
149 	end_step ();
150 
151 	/* switch model */
152 	model2 = rhythmdb_query_model_new_empty (db);
153 	g_object_set (model2, "show-hidden", FALSE, NULL);
154 	rhythmdb_query_model_add_entry (model2, a, -1);
155 	rhythmdb_query_model_add_entry (model2, b, -1);
156 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
157 	g_object_set (propmodel, "query-model", model2, NULL);
158 	wait_for_signal ();
159 
160 	fail_unless (_get_property_count (propmodel, "x") == 1);
161 	fail_unless (_get_property_count (propmodel, "y") == 1);
162 
163 	end_step ();
164 
165 	/* delete an entry */
166 	set_waiting_signal (G_OBJECT (db), "entry_deleted");
167 	rhythmdb_entry_delete (db, a);
168 	rhythmdb_commit (db);
169 	wait_for_signal ();
170 	fail_unless (_get_property_count (propmodel, "x") == 0);
171 	fail_unless (_get_property_count (propmodel, "y") == 1);
172 
173 	end_step ();
174 
175 	/* and the other */
176 	set_waiting_signal (G_OBJECT (propmodel), "row-deleted");
177 	rhythmdb_entry_delete (db, b);
178 	rhythmdb_commit (db);
179 	wait_for_signal ();
180 	fail_unless (_get_property_count (propmodel, "x") == 0);
181 	fail_unless (_get_property_count (propmodel, "y") == 0);
182 
183 	end_test_case ();
184 
185 	g_object_unref (model);
186 	g_object_unref (model2);
187 	g_object_unref (propmodel);
188 }
189 END_TEST
190 
191 /* tests property models attached to query models with an actual query */
192 START_TEST (test_rhythmdb_property_model_query)
193 {
194 	RhythmDBQueryModel *model;
195 	RhythmDBQueryModel *model2;
196 	RhythmDBPropertyModel *propmodel;
197 	RhythmDBEntry *a, *b;
198 	GPtrArray *query;
199 
200 	start_test_case ();
201 
202 	/* setup */
203 	query = rhythmdb_query_parse (db,
204 				      RHYTHMDB_QUERY_PROP_EQUALS,
205 				        RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_IGNORE,
206 				      RHYTHMDB_QUERY_PROP_LIKE,
207 				        RHYTHMDB_PROP_ARTIST, "x",
208 				      RHYTHMDB_QUERY_END);
209 
210 	model = rhythmdb_query_model_new (db, query, (GCompareDataFunc)rhythmdb_query_model_location_sort_func, NULL, NULL, FALSE);
211 	rhythmdb_query_free (query);
212 
213 	query = rhythmdb_query_parse (db,
214 				      RHYTHMDB_QUERY_PROP_EQUALS,
215 				        RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_IGNORE,
216 				      RHYTHMDB_QUERY_PROP_LIKE,
217 				        RHYTHMDB_PROP_ARTIST, "y",
218 				      RHYTHMDB_QUERY_END);
219 	model2 = rhythmdb_query_model_new (db, query, (GCompareDataFunc)rhythmdb_query_model_location_sort_func, NULL, NULL, FALSE);
220 	rhythmdb_query_free (query);
221 
222 
223 	propmodel = rhythmdb_property_model_new (db, RHYTHMDB_PROP_ARTIST);
224 	g_object_set (propmodel, "query-model", model, NULL);
225 
226 	end_step ();
227 
228 	/* create test entries */
229 	set_waiting_signal (G_OBJECT (db), "entry_added");
230 	a = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///a.ogg");
231 	b = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///b.ogg");
232 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "x");
233 	set_entry_string (db, b, RHYTHMDB_PROP_ARTIST, "y");
234 	rhythmdb_commit (db);
235 	wait_for_signal ();
236 
237 	fail_unless (_get_property_count (propmodel, "x") == 1);
238 	fail_unless (_get_property_count (propmodel, "y") == 0);
239 
240 	end_step ();
241 
242 	/* change b so it matches the query */
243 	set_waiting_signal (G_OBJECT (db), "entry-changed");
244 	set_entry_string (db, b, RHYTHMDB_PROP_ARTIST, "x");
245 	rhythmdb_commit (db);
246 	wait_for_signal ();
247 	fail_unless (_get_property_count (propmodel, "x") == 2);
248 	fail_unless (_get_property_count (propmodel, "y") == 0);
249 
250 	end_step ();
251 
252 	/* change b again */
253 	set_waiting_signal (G_OBJECT (db), "entry-changed");
254 	set_entry_string (db, b, RHYTHMDB_PROP_ARTIST, "xx");
255 	rhythmdb_commit (db);
256 	wait_for_signal ();
257 	fail_unless (_get_property_count (propmodel, "x") == 1);
258 	fail_unless (_get_property_count (propmodel, "xx") == 1);
259 	fail_unless (_get_property_count (propmodel, "y") == 0);
260 
261 	end_step ();
262 
263 	/* hide a */
264 	set_waiting_signal (G_OBJECT (db), "entry-changed");
265 	set_entry_hidden (db, a, TRUE);
266 	rhythmdb_commit (db);
267 	wait_for_signal ();
268 	fail_unless (_get_property_count (propmodel, "x") == 0);
269 	fail_unless (_get_property_count (propmodel, "xx") == 1);
270 
271 	end_step ();
272 
273 	/* change a */
274 	set_waiting_signal (G_OBJECT (db), "entry-changed");
275 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "xx");
276 	rhythmdb_commit (db);
277 	wait_for_signal ();
278 	fail_unless (_get_property_count (propmodel, "x") == 0);
279 	fail_unless (_get_property_count (propmodel, "xx") == 1);
280 
281 	end_step ();
282 
283 	/* unhide a */
284 	set_waiting_signal (G_OBJECT (db), "entry-changed");
285 	set_entry_hidden (db, a, FALSE);
286 	rhythmdb_commit (db);
287 	wait_for_signal ();
288 	fail_unless (_get_property_count (propmodel, "x") == 0);
289 	fail_unless (_get_property_count (propmodel, "xx") == 2);
290 
291 	end_step ();
292 
293 	/* change a -> y */
294 	set_waiting_signal (G_OBJECT (db), "entry-changed");
295 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "y");
296 	rhythmdb_commit (db);
297 	wait_for_signal ();
298 	fail_unless (_get_property_count (propmodel, "x") == 0);
299 	fail_unless (_get_property_count (propmodel, "xx") == 1);
300 	fail_unless (_get_property_count (propmodel, "y") == 0);
301 
302 	end_step ();
303 
304 	/* switch to model2 */
305 	g_object_set (propmodel, "query-model", model2, NULL);
306 	fail_unless (_get_property_count (propmodel, "x") == 0);
307 	fail_unless (_get_property_count (propmodel, "y") == 1);
308 	fail_unless (_get_property_count (propmodel, "xx") == 0);
309 
310 	end_step ();
311 
312 	/* change a -> x */
313 	set_waiting_signal (G_OBJECT (db), "entry-changed");
314 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "x");
315 	rhythmdb_commit (db);
316 	wait_for_signal ();
317 	fail_unless (_get_property_count (propmodel, "x") == 0);
318 	fail_unless (_get_property_count (propmodel, "xx") == 0);
319 	fail_unless (_get_property_count (propmodel, "y") == 0);
320 
321 	end_step ();
322 
323 	rhythmdb_entry_delete (db, a);
324 	rhythmdb_entry_delete (db, b);
325 	rhythmdb_commit (db);
326 
327 	end_test_case ();
328 
329 	g_object_unref (model);
330 	g_object_unref (model2);
331 	g_object_unref (propmodel);
332 }
333 END_TEST
334 
335 /* tests property models attached to chained query models */
336 START_TEST (test_rhythmdb_property_model_query_chain)
337 {
338 	RhythmDBQueryModel *base_model;
339 	RhythmDBQueryModel *model;
340 	RhythmDBPropertyModel *propmodel;
341 	RhythmDBQuery *query;
342 	RhythmDBEntry *a, *b;
343 
344 	start_test_case ();
345 
346 	/* setup */
347 	query = rhythmdb_query_parse (db,
348 				      RHYTHMDB_QUERY_PROP_EQUALS,
349 				        RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_IGNORE,
350 				      RHYTHMDB_QUERY_PROP_LIKE,
351 				        RHYTHMDB_PROP_SEARCH_MATCH, "y",
352 				      RHYTHMDB_QUERY_END);
353 
354 	base_model = rhythmdb_query_model_new (db, query, (GCompareDataFunc)rhythmdb_query_model_location_sort_func, NULL, NULL, FALSE);
355 	rhythmdb_query_free (query);
356 
357 	query = rhythmdb_query_parse (db,
358 				      RHYTHMDB_QUERY_PROP_EQUALS,
359 				        RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_IGNORE,
360 				      RHYTHMDB_QUERY_PROP_EQUALS,
361 				        RHYTHMDB_PROP_TRACK_NUMBER, 1,
362 				      RHYTHMDB_QUERY_END);
363 	model = rhythmdb_query_model_new (db, query, (GCompareDataFunc)rhythmdb_query_model_location_sort_func, NULL, NULL, FALSE);
364 	rhythmdb_query_free (query);
365 
366 	rhythmdb_query_model_chain (model, base_model, TRUE);
367 
368 	propmodel = rhythmdb_property_model_new (db, RHYTHMDB_PROP_ALBUM);
369 	g_object_set (propmodel, "query-model", model, NULL);
370 
371 	/* create test entries */
372 	set_waiting_signal (G_OBJECT (db), "entry_added");
373 	a = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///a.ogg");
374 	set_entry_string (db, a, RHYTHMDB_PROP_ALBUM, "x");
375 	set_entry_ulong (db, a, RHYTHMDB_PROP_TRACK_NUMBER, 1);
376 	rhythmdb_commit (db);
377 	wait_for_signal ();
378 
379 	set_waiting_signal (G_OBJECT (db), "entry_added");
380 	b = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///b.ogg");
381 	set_entry_string (db, b, RHYTHMDB_PROP_ALBUM, "y");
382 	set_entry_ulong (db, b, RHYTHMDB_PROP_TRACK_NUMBER, 1);
383 	rhythmdb_commit (db);
384 	wait_for_signal ();
385 
386 	fail_unless (_get_property_count (propmodel, "x") == 0);
387 	fail_unless (_get_property_count (propmodel, "y") == 1);
388 
389 	end_step ();
390 
391 	/* change entry a so it matches the child query */
392 	set_waiting_signal (G_OBJECT (db), "entry-changed");
393 	set_entry_string (db, a, RHYTHMDB_PROP_ALBUM, "yy");
394 	rhythmdb_commit (db);
395 	wait_for_signal ();
396 
397 	fail_unless (_get_property_count (propmodel, "x") == 0);
398 	fail_unless (_get_property_count (propmodel, "y") == 1);
399 	fail_unless (_get_property_count (propmodel, "yy") == 1);
400 
401 	end_step ();
402 
403 	/* change entry a again */
404 	set_waiting_signal (G_OBJECT (db), "entry-changed");
405 	set_entry_string (db, a, RHYTHMDB_PROP_ALBUM, "y");
406 	rhythmdb_commit (db);
407 	wait_for_signal ();
408 
409 	fail_unless (_get_property_count (propmodel, "y") == 2);
410 	fail_unless (_get_property_count (propmodel, "yy") == 0);
411 
412 	end_step ();
413 
414 	/* change entry b again */
415 	set_waiting_signal (G_OBJECT (db), "entry-changed");
416 	set_entry_string (db, b, RHYTHMDB_PROP_ALBUM, "z");
417 	rhythmdb_commit (db);
418 	wait_for_signal ();
419 
420 	fail_unless (_get_property_count (propmodel, "y") == 1);
421 	fail_unless (_get_property_count (propmodel, "z") == 0);
422 
423 	end_step ();
424 
425 	rhythmdb_entry_delete (db, a);
426 	rhythmdb_entry_delete (db, b);
427 	rhythmdb_commit (db);
428 
429 	end_test_case ();
430 
431 	g_object_unref (model);
432 	g_object_unref (base_model);
433 	g_object_unref (propmodel);
434 }
435 END_TEST
436 
437 /* tests sort order of entries in a property model */
438 START_TEST (test_rhythmdb_property_model_sorting)
439 {
440 	RhythmDBQueryModel *model;
441 	RhythmDBPropertyModel *propmodel;
442 	RhythmDBEntry *a, *the_b, *c;
443 	GtkTreeIter iter1, iter2;
444 
445 	start_test_case ();
446 
447 	/* setup */
448 	model = rhythmdb_query_model_new_empty (db);
449 	propmodel = rhythmdb_property_model_new (db, RHYTHMDB_PROP_ARTIST);
450 	g_object_set (propmodel, "query-model", model, NULL);
451 
452 	/* create test entries */
453 	set_waiting_signal (G_OBJECT (db), "entry_added");
454 	a = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///a.ogg");
455 	set_entry_string (db, a, RHYTHMDB_PROP_ARTIST, "a");
456 	rhythmdb_commit (db);
457 	wait_for_signal ();
458 
459 	set_waiting_signal (G_OBJECT (db), "entry_added");
460 	the_b = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///the-b.ogg");
461 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST, "the b");
462 	rhythmdb_commit (db);
463 	wait_for_signal ();
464 
465 	set_waiting_signal (G_OBJECT (db), "entry_added");
466 	c = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///c.ogg");
467 	set_entry_string (db, c, RHYTHMDB_PROP_ARTIST, "c");
468 	rhythmdb_commit (db);
469 	wait_for_signal ();
470 
471 	/* add to model */
472 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
473 	rhythmdb_query_model_add_entry (model, a, -1);
474 	wait_for_signal ();
475 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
476 	rhythmdb_query_model_add_entry (model, the_b, -1);
477 	wait_for_signal ();
478 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
479 	rhythmdb_query_model_add_entry (model, c, -1);
480 	wait_for_signal ();
481 
482 	/* test a comes immediately before c */
483 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
484 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
485 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
486 
487 	/* test c comes immediately before the_b */
488 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter1);
489 	rhythmdb_property_model_iter_from_string (propmodel, "the b", &iter2);
490 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
491 
492 	end_step ();
493 
494 	/* change "the b" to sort under "b, the" */
495 	set_waiting_signal (G_OBJECT (db), "entry-changed");
496 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST_SORTNAME, "b, the");
497 	rhythmdb_commit (db);
498 	wait_for_signal ();
499 
500 	/* test a comes immediately before the_b */
501 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
502 	rhythmdb_property_model_iter_from_string (propmodel, "the b", &iter2);
503 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
504 
505 	/* test the_b comes immediately before c */
506 	rhythmdb_property_model_iter_from_string (propmodel, "the b", &iter1);
507 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
508 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
509 
510 	end_step();
511 
512 	/* change display name for b */
513 	set_waiting_signal (G_OBJECT (db), "entry-changed");
514 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST, "THE B");
515 	rhythmdb_commit (db);
516 	wait_for_signal ();
517 
518 	/* property model order shouldn't have changed */
519 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
520 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter2);
521 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
522 
523 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter1);
524 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
525 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
526 
527 	end_step();
528 
529 	/* change sortname for b */
530 	set_waiting_signal (G_OBJECT (db), "entry-changed");
531 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST_SORTNAME, "B, THE");
532 	rhythmdb_commit (db);
533 	wait_for_signal ();
534 
535 	/* property model order shouldn't have changed */
536 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
537 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter2);
538 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
539 
540 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter1);
541 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
542 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
543 
544 	end_step();
545 
546 	/* change sort order for b */
547 	set_waiting_signal (G_OBJECT (db), "entry-changed");
548 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST_SORTNAME, "zzz");
549 	rhythmdb_commit (db);
550 	wait_for_signal ();
551 
552 	/* property model order should have changed to match */
553 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
554 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
555 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
556 
557 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter1);
558 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter2);
559 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
560 
561 	end_step();
562 
563 	/* remove sort order for b */
564 	set_waiting_signal (G_OBJECT (db), "entry-changed");
565 	set_entry_string (db, the_b, RHYTHMDB_PROP_ARTIST_SORTNAME, "");
566 	rhythmdb_commit (db);
567 	wait_for_signal ();
568 
569 	/* property model order should have changed to match */
570 	rhythmdb_property_model_iter_from_string (propmodel, "a", &iter1);
571 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter2);
572 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
573 
574 	rhythmdb_property_model_iter_from_string (propmodel, "c", &iter1);
575 	rhythmdb_property_model_iter_from_string (propmodel, "THE B", &iter2);
576 	fail_unless (iter1.user_data == g_sequence_iter_prev (iter2.user_data));
577 
578 	end_step();
579 
580 
581 	rhythmdb_entry_delete (db, a);
582 	rhythmdb_entry_delete (db, the_b);
583 	rhythmdb_entry_delete (db, c);
584 	rhythmdb_commit (db);
585 
586 	end_test_case ();
587 
588 	g_object_unref (model);
589 	g_object_unref (propmodel);
590 }
591 END_TEST
592 
593 /* tests handling of empty strings */
594 START_TEST (test_rhythmdb_property_model_empty_strings)
595 {
596 	RhythmDBQueryModel *model;
597 	RhythmDBPropertyModel *propmodel;
598 	RhythmDBEntry *a, *b;
599 
600 	start_test_case ();
601 
602 	/* setup */
603 	model = rhythmdb_query_model_new_empty (db);
604 	propmodel = rhythmdb_property_model_new (db, RHYTHMDB_PROP_GENRE);
605 	g_object_set (propmodel, "query-model", model, NULL);
606 
607 	/* create test entries */
608 	set_waiting_signal (G_OBJECT (db), "entry_added");
609 	a = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///a.ogg");
610 	set_entry_string (db, a, RHYTHMDB_PROP_GENRE, "unknown");
611 	rhythmdb_commit (db);
612 	wait_for_signal ();
613 
614 	set_waiting_signal (G_OBJECT (db), "entry_added");
615 	b = rhythmdb_entry_new (db, RHYTHMDB_ENTRY_TYPE_IGNORE, "file:///b.ogg");
616 	set_entry_string (db, b, RHYTHMDB_PROP_GENRE, "something");
617 	rhythmdb_commit (db);
618 	wait_for_signal ();
619 
620 	end_step ();
621 
622 	/* add to model */
623 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
624 	rhythmdb_query_model_add_entry (model, a, -1);
625 	wait_for_signal ();
626 
627 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
628 	rhythmdb_query_model_add_entry (model, b, -1);
629 	wait_for_signal ();
630 
631 	end_step ();
632 
633 	/* set to empty string */
634 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
635 	set_entry_string (db, a, RHYTHMDB_PROP_GENRE, "");
636 	rhythmdb_commit (db);
637 	wait_for_signal ();
638 
639 	end_step ();
640 
641 	/* set to non-empty string */
642 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
643 	set_entry_string (db, a, RHYTHMDB_PROP_GENRE, "junk");
644 	rhythmdb_commit (db);
645 	wait_for_signal ();
646 
647 	end_step ();
648 
649 	/* set to empty string again */
650 	set_waiting_signal (G_OBJECT (propmodel), "row-inserted");
651 	set_entry_string (db, a, RHYTHMDB_PROP_GENRE, "");
652 	rhythmdb_commit (db);
653 	wait_for_signal ();
654 
655 	end_step ();
656 
657 	rhythmdb_entry_delete (db, a);
658 	rhythmdb_entry_delete (db, b);
659 	rhythmdb_commit (db);
660 
661 	end_test_case ();
662 	g_object_unref (model);
663 	g_object_unref (propmodel);
664 }
665 END_TEST
666 
667 static Suite *
668 rhythmdb_property_model_suite (void)
669 {
670 	Suite *s = suite_create ("rhythmdb-property-model");
671 	TCase *tc_chain = tcase_create ("rhythmdb-property-model-core");
672 	TCase *tc_bugs = tcase_create ("rhythmdb-property-model-bugs");
673 
674 	suite_add_tcase (s, tc_chain);
675 	tcase_add_checked_fixture (tc_chain, test_rhythmdb_setup, test_rhythmdb_shutdown);
676 	suite_add_tcase (s, tc_bugs);
677 	tcase_add_checked_fixture (tc_bugs, test_rhythmdb_setup, test_rhythmdb_shutdown);
678 
679 	/* test core functionality */
680 	tcase_add_test (tc_chain, test_rhythmdb_property_model_static);
681 	tcase_add_test (tc_chain, test_rhythmdb_property_model_query);
682 	tcase_add_test (tc_chain, test_rhythmdb_property_model_query_chain);
683 	tcase_add_test (tc_chain, test_rhythmdb_property_model_sorting);
684 
685 	/* tests for breakable bug fixes */
686 /*	tcase_add_test (tc_bugs, test_hidden_chain_filter);*/
687 	tcase_add_test (tc_chain, test_rhythmdb_property_model_empty_strings);
688 
689 	return s;
690 }
691 
692 int
693 main (int argc, char **argv)
694 {
695 	int ret;
696 	SRunner *sr;
697 	Suite *s;
698 
699 	/* init stuff */
700 	rb_profile_start ("rhythmdb-property-model test suite");
701 
702 	rb_threads_init ();
703 	setlocale (LC_ALL, NULL);
704 	rb_debug_init (TRUE);
705 	rb_refstring_system_init ();
706 	rb_file_helpers_init (TRUE);
707 
708 	/* setup tests */
709 	s = rhythmdb_property_model_suite ();
710 	sr = srunner_create (s);
711 	
712 	init_setup (sr, argc, argv);
713 	init_once (FALSE);
714 	
715 	srunner_run_all (sr, CK_NORMAL);
716 	ret = srunner_ntests_failed (sr);
717 	srunner_free (sr);
718 
719 	rb_file_helpers_shutdown ();
720 	rb_refstring_system_shutdown ();
721 
722 	rb_profile_end ("rhythmdb-property-model test suite");
723 	return ret;
724 }