]> Pileus Git - ~andy/gtk/blob - gtk/tests/filtermodel.c
Bug 596580 - Blank rows in entry autocompletion
[~andy/gtk] / gtk / tests / filtermodel.c
1 /* Extensive GtkTreeModelFilter tests.
2  * Copyright (C) 2009  Kristian Rietveld  <kris@gtk.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <gtk/gtk.h>
21
22
23 /* Left to do:
24  *   - Proper coverage checking to see if the unit tests cover
25  *     all possible cases.
26  *   - Verify if the ref counting is done properly for both the
27  *     normal ref_count and the zero_ref_count.  One way to test
28  *     this area is by collapsing/expanding branches on the view
29  *     that is connected to the filter model.
30  *   - Check if the iterator stamp is incremented at the correct times.
31  */
32
33
34 /*
35  * Model creation
36  */
37
38 #define LEVEL_LENGTH 5
39
40 static void
41 create_tree_store_set_values (GtkTreeStore *store,
42                               GtkTreeIter  *iter,
43                               gboolean      visible)
44 {
45   GtkTreePath *path;
46   gchar *path_string;
47
48   path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter);
49   path_string = gtk_tree_path_to_string (path);
50
51   gtk_tree_store_set (store, iter,
52                       0, path_string,
53                       1, visible,
54                       -1);
55
56   gtk_tree_path_free (path);
57   g_free (path_string);
58 }
59
60 static void
61 create_tree_store_recurse (int           depth,
62                            GtkTreeStore *store,
63                            GtkTreeIter  *parent,
64                            gboolean      visible)
65 {
66   int i;
67
68   for (i = 0; i < LEVEL_LENGTH; i++)
69     {
70       GtkTreeIter iter;
71
72       gtk_tree_store_insert (store, &iter, parent, i);
73       create_tree_store_set_values (store, &iter, visible);
74
75       if (depth > 0)
76         create_tree_store_recurse (depth - 1, store, &iter, visible);
77     }
78 }
79
80 static GtkTreeStore *
81 create_tree_store (int      depth,
82                    gboolean visible)
83 {
84   GtkTreeStore *store;
85
86   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
87
88   create_tree_store_recurse (depth, store, NULL, visible);
89
90   return store;
91 }
92
93 /*
94  * Signal monitor
95  */
96
97 typedef enum
98 {
99   ROW_INSERTED,
100   ROW_DELETED,
101   ROW_CHANGED,
102   ROW_HAS_CHILD_TOGGLED,
103   ROWS_REORDERED,
104   LAST_SIGNAL
105 }
106 SignalName;
107
108 static const char *
109 signal_name_to_string (SignalName signal)
110 {
111   switch (signal)
112     {
113       case ROW_INSERTED:
114           return "row-inserted";
115
116       case ROW_DELETED:
117           return "row-deleted";
118
119       case ROW_CHANGED:
120           return "row-changed";
121
122       case ROW_HAS_CHILD_TOGGLED:
123           return "row-has-child-toggled";
124
125       case ROWS_REORDERED:
126           return "rows-reordered";
127
128       default:
129           /* Fall through */
130           break;
131     }
132
133   return "(unknown)";
134 }
135
136 typedef struct
137 {
138   SignalName signal;
139   GtkTreePath *path;
140 }
141 Signal;
142
143
144 static Signal *
145 signal_new (SignalName signal, GtkTreePath *path)
146 {
147   Signal *s;
148
149   s = g_new0 (Signal, 1);
150   s->signal = signal;
151   s->path = gtk_tree_path_copy (path);
152
153   return s;
154 }
155
156 static void
157 signal_free (Signal *s)
158 {
159   if (s->path)
160     gtk_tree_path_free (s->path);
161
162   g_free (s);
163 }
164
165
166 typedef struct
167 {
168   GQueue *queue;
169   GtkTreeModel *client;
170   guint signal_ids[LAST_SIGNAL];
171 }
172 SignalMonitor;
173
174
175 static void
176 signal_monitor_generic_handler (SignalMonitor *m,
177                                 SignalName     signal,
178                                 GtkTreeModel  *model,
179                                 GtkTreePath   *path)
180 {
181   Signal *s;
182
183   if (g_queue_is_empty (m->queue))
184     {
185       g_error ("Signal queue empty\n");
186       g_assert_not_reached ();
187     }
188
189   if (m->client != model)
190     {
191       g_error ("Model mismatch; expected %p, got %p\n",
192                m->client, model);
193       g_assert_not_reached ();
194     }
195
196   s = g_queue_peek_tail (m->queue);
197
198 #if 0
199   /* For debugging: output signals that are coming in.  Leaks memory. */
200   g_print ("signal=%d  path=%s\n", signal, gtk_tree_path_to_string (path));
201 #endif
202
203   if (s->signal != signal
204       || gtk_tree_path_compare (s->path, path) != 0)
205     {
206       gchar *path_str, *s_path_str;
207
208       s_path_str = gtk_tree_path_to_string (s->path);
209       path_str = gtk_tree_path_to_string (path);
210
211       g_error ("Signals don't match; expected signal %s path %s, got signal %s path %s\n",
212                signal_name_to_string (s->signal), s_path_str,
213                signal_name_to_string (signal), path_str);
214
215       g_free (s_path_str);
216       g_free (path_str);
217
218       g_assert_not_reached ();
219     }
220
221   s = g_queue_pop_tail (m->queue);
222
223   signal_free (s);
224 }
225
226 static void
227 signal_monitor_row_inserted (GtkTreeModel *model,
228                              GtkTreePath  *path,
229                              GtkTreeIter  *iter,
230                              gpointer      data)
231 {
232   signal_monitor_generic_handler (data, ROW_INSERTED,
233                                   model, path);
234 }
235
236 static void
237 signal_monitor_row_deleted (GtkTreeModel *model,
238                             GtkTreePath  *path,
239                             gpointer      data)
240 {
241   signal_monitor_generic_handler (data, ROW_DELETED,
242                                   model, path);
243 }
244
245 static void
246 signal_monitor_row_changed (GtkTreeModel *model,
247                             GtkTreePath  *path,
248                             GtkTreeIter  *iter,
249                             gpointer      data)
250 {
251   signal_monitor_generic_handler (data, ROW_CHANGED,
252                                   model, path);
253 }
254
255 static void
256 signal_monitor_row_has_child_toggled (GtkTreeModel *model,
257                                       GtkTreePath  *path,
258                                       GtkTreeIter  *iter,
259                                       gpointer      data)
260 {
261   signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED,
262                                   model, path);
263 }
264
265 static void
266 signal_monitor_rows_reordered (GtkTreeModel *model,
267                                GtkTreePath  *path,
268                                GtkTreeIter  *iter,
269                                gint         *new_order,
270                                gpointer      data)
271 {
272   signal_monitor_generic_handler (data, ROWS_REORDERED,
273                                   model, path);
274 }
275
276 static SignalMonitor *
277 signal_monitor_new (GtkTreeModel *client)
278 {
279   SignalMonitor *m;
280
281   m = g_new0 (SignalMonitor, 1);
282   m->client = g_object_ref (client);
283   m->queue = g_queue_new ();
284
285   m->signal_ids[ROW_INSERTED] = g_signal_connect (client,
286                                                   "row-inserted",
287                                                   G_CALLBACK (signal_monitor_row_inserted),
288                                                   m);
289   m->signal_ids[ROW_DELETED] = g_signal_connect (client,
290                                                  "row-deleted",
291                                                  G_CALLBACK (signal_monitor_row_deleted),
292                                                  m);
293   m->signal_ids[ROW_CHANGED] = g_signal_connect (client,
294                                                  "row-changed",
295                                                  G_CALLBACK (signal_monitor_row_changed),
296                                                  m);
297   m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client,
298                                                            "row-has-child-toggled",
299                                                            G_CALLBACK (signal_monitor_row_has_child_toggled),
300                                                            m);
301   m->signal_ids[ROWS_REORDERED] = g_signal_connect (client,
302                                                     "rows-reordered",
303                                                     G_CALLBACK (signal_monitor_rows_reordered),
304                                                     m);
305
306   return m;
307 }
308
309 static void
310 signal_monitor_free (SignalMonitor *m)
311 {
312   int i;
313
314   for (i = 0; i < LAST_SIGNAL; i++)
315     g_signal_handler_disconnect (m->client, m->signal_ids[i]);
316
317   g_object_unref (m->client);
318
319   if (m->queue)
320     g_queue_free (m->queue);
321
322   g_free (m);
323 }
324
325 static void
326 signal_monitor_assert_is_empty (SignalMonitor *m)
327 {
328   g_assert (g_queue_is_empty (m->queue));
329 }
330
331 static void
332 signal_monitor_append_signal_path (SignalMonitor *m,
333                                    SignalName     signal,
334                                    GtkTreePath   *path)
335 {
336   Signal *s;
337
338   s = signal_new (signal, path);
339   g_queue_push_head (m->queue, s);
340 }
341
342 static void
343 signal_monitor_append_signal (SignalMonitor *m,
344                               SignalName     signal,
345                               const gchar   *path_string)
346 {
347   Signal *s;
348   GtkTreePath *path;
349
350   path = gtk_tree_path_new_from_string (path_string);
351
352   s = signal_new (signal, path);
353   g_queue_push_head (m->queue, s);
354
355   gtk_tree_path_free (path);
356 }
357
358 /*
359  * Fixture
360  */
361
362 typedef struct
363 {
364   GtkWidget *tree_view;
365
366   GtkTreeStore *store;
367   GtkTreeModelFilter *filter;
368
369   SignalMonitor *monitor;
370
371   guint block_signals : 1;
372 } FilterTest;
373
374
375 static void
376 filter_test_store_signal (FilterTest *fixture)
377 {
378   if (fixture->block_signals)
379     g_signal_stop_emission_by_name (fixture->store, "row-changed");
380 }
381
382
383 static void
384 filter_test_setup_generic (FilterTest    *fixture,
385                            gconstpointer  test_data,
386                            int            depth,
387                            gboolean       empty,
388                            gboolean       unfiltered)
389 {
390   const GtkTreePath *vroot = test_data;
391   GtkTreeModel *filter;
392
393   fixture->store = create_tree_store (depth, !empty);
394
395   g_signal_connect_swapped (fixture->store, "row-changed",
396                             G_CALLBACK (filter_test_store_signal), fixture);
397
398   /* Please forgive me for casting const away. */
399   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture->store),
400                                       (GtkTreePath *)vroot);
401   fixture->filter = GTK_TREE_MODEL_FILTER (filter);
402
403   if (!unfiltered)
404     gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
405
406   /* We need a tree view that's listening to get ref counting from that
407    * side.
408    */
409   fixture->tree_view = gtk_tree_view_new_with_model (filter);
410
411   fixture->monitor = signal_monitor_new (filter);
412 }
413
414 static void
415 filter_test_setup (FilterTest    *fixture,
416                    gconstpointer  test_data)
417 {
418   filter_test_setup_generic (fixture, test_data, 3, FALSE, FALSE);
419 }
420
421 static void
422 filter_test_setup_empty (FilterTest    *fixture,
423                          gconstpointer  test_data)
424 {
425   filter_test_setup_generic (fixture, test_data, 3, TRUE, FALSE);
426 }
427
428 static void
429 filter_test_setup_unfiltered (FilterTest    *fixture,
430                               gconstpointer  test_data)
431 {
432   filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE);
433 }
434
435 static void
436 filter_test_setup_empty_unfiltered (FilterTest    *fixture,
437                                     gconstpointer  test_data)
438 {
439   filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE);
440 }
441
442 static GtkTreePath *
443 strip_virtual_root (GtkTreePath *path,
444                     GtkTreePath *root_path)
445 {
446   GtkTreePath *real_path;
447
448   if (root_path)
449     {
450       int j;
451       int depth = gtk_tree_path_get_depth (path);
452       int root_depth = gtk_tree_path_get_depth (root_path);
453
454       real_path = gtk_tree_path_new ();
455
456       for (j = 0; j < depth - root_depth; j++)
457         gtk_tree_path_append_index (real_path,
458                                     gtk_tree_path_get_indices (path)[root_depth + j]);
459     }
460   else
461     real_path = gtk_tree_path_copy (path);
462
463   return real_path;
464 }
465
466 static void
467 filter_test_append_refilter_signals_recurse (FilterTest  *fixture,
468                                              GtkTreePath *store_path,
469                                              GtkTreePath *filter_path,
470                                              int          depth,
471                                              GtkTreePath *root_path)
472 {
473   int i;
474   int rows_deleted = 0;
475   GtkTreeIter iter;
476
477   gtk_tree_path_down (store_path);
478   gtk_tree_path_down (filter_path);
479
480   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
481                            &iter, store_path);
482
483   for (i = 0; i < LEVEL_LENGTH; i++)
484     {
485       gboolean visible;
486       GtkTreePath *real_path;
487
488       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter,
489                           1, &visible,
490                           -1);
491
492       if (root_path &&
493           (!gtk_tree_path_is_descendant (store_path, root_path)
494            || !gtk_tree_path_compare (store_path, root_path)))
495         {
496           if (!gtk_tree_path_compare (store_path, root_path))
497             {
498               if (depth > 1
499                   && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
500                                                     &iter))
501                 {
502                   GtkTreePath *store_copy;
503                   GtkTreePath *filter_copy;
504
505                   store_copy = gtk_tree_path_copy (store_path);
506                   filter_copy = gtk_tree_path_copy (filter_path);
507                   filter_test_append_refilter_signals_recurse (fixture,
508                                                                store_copy,
509                                                                filter_copy,
510                                                                depth - 1,
511                                                                root_path);
512                   gtk_tree_path_free (store_copy);
513                   gtk_tree_path_free (filter_copy);
514                 }
515             }
516
517           gtk_tree_path_next (store_path);
518           gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
519
520           if (visible)
521             gtk_tree_path_next (filter_path);
522
523           continue;
524         }
525
526       real_path = strip_virtual_root (filter_path, root_path);
527
528       if (visible)
529         {
530           /* This row will be inserted */
531           signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED,
532                                              real_path);
533           signal_monitor_append_signal_path (fixture->monitor,
534                                              ROW_HAS_CHILD_TOGGLED,
535                                              real_path);
536
537           if (depth > 1
538               && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
539                                                 &iter))
540             {
541               GtkTreePath *store_copy;
542               GtkTreePath *filter_copy;
543
544               store_copy = gtk_tree_path_copy (store_path);
545               filter_copy = gtk_tree_path_copy (filter_path);
546               filter_test_append_refilter_signals_recurse (fixture,
547                                                            store_copy,
548                                                            filter_copy,
549                                                            depth - 1,
550                                                            root_path);
551               gtk_tree_path_free (store_copy);
552               gtk_tree_path_free (filter_copy);
553             }
554
555           gtk_tree_path_next (filter_path);
556         }
557       else
558         {
559           /* This row will be deleted */
560           rows_deleted++;
561           signal_monitor_append_signal_path (fixture->monitor, ROW_DELETED,
562                                              real_path);
563         }
564
565       gtk_tree_path_free (real_path);
566
567       gtk_tree_path_next (store_path);
568       gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
569     }
570
571   if (rows_deleted == LEVEL_LENGTH
572       && gtk_tree_path_get_depth (filter_path) > 1)
573     {
574       GtkTreePath *real_path;
575
576       gtk_tree_path_up (store_path);
577       gtk_tree_path_up (filter_path);
578
579       /* A row-has-child-toggled will be emitted on the parent */
580       if (!root_path
581           || (root_path
582               && gtk_tree_path_is_descendant (store_path, root_path)
583               && gtk_tree_path_compare (store_path, root_path)))
584         {
585           real_path = strip_virtual_root (filter_path, root_path);
586           signal_monitor_append_signal_path (fixture->monitor,
587                                              ROW_HAS_CHILD_TOGGLED,
588                                              real_path);
589
590           gtk_tree_path_free (real_path);
591         }
592     }
593 }
594
595 static void
596 filter_test_append_refilter_signals (FilterTest *fixture,
597                                      int         depth)
598 {
599   /* A special function that walks the tree store like the
600    * model validation functions below.
601    */
602   GtkTreePath *path;
603   GtkTreePath *filter_path;
604
605   path = gtk_tree_path_new ();
606   filter_path = gtk_tree_path_new ();
607   filter_test_append_refilter_signals_recurse (fixture,
608                                                path,
609                                                filter_path,
610                                                depth,
611                                                NULL);
612   gtk_tree_path_free (path);
613   gtk_tree_path_free (filter_path);
614 }
615
616 static void
617 filter_test_append_refilter_signals_with_vroot (FilterTest  *fixture,
618                                                 int          depth,
619                                                 GtkTreePath *root_path)
620 {
621   /* A special function that walks the tree store like the
622    * model validation functions below.
623    */
624   GtkTreePath *path;
625   GtkTreePath *filter_path;
626
627   path = gtk_tree_path_new ();
628   filter_path = gtk_tree_path_new ();
629   filter_test_append_refilter_signals_recurse (fixture,
630                                                path,
631                                                filter_path,
632                                                depth,
633                                                root_path);
634   gtk_tree_path_free (path);
635   gtk_tree_path_free (filter_path);
636 }
637
638 static void
639 filter_test_enable_filter (FilterTest *fixture)
640 {
641   gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
642   gtk_tree_model_filter_refilter (fixture->filter);
643 }
644
645 static void
646 filter_test_block_signals (FilterTest *fixture)
647 {
648   fixture->block_signals = TRUE;
649 }
650
651 static void
652 filter_test_unblock_signals (FilterTest *fixture)
653 {
654   fixture->block_signals = FALSE;
655 }
656
657 static void
658 filter_test_teardown (FilterTest    *fixture,
659                       gconstpointer  test_data)
660 {
661   signal_monitor_free (fixture->monitor);
662
663   g_object_unref (fixture->filter);
664   g_object_unref (fixture->store);
665 }
666
667 /*
668  * Model structure validation
669  */
670
671 static void
672 check_filter_model_recurse (FilterTest  *fixture,
673                             GtkTreePath *store_parent_path,
674                             GtkTreePath *filter_parent_path)
675 {
676   int i;
677   GtkTreeIter store_iter;
678   GtkTreeIter filter_iter;
679   gboolean store_has_next, filter_has_next;
680
681   gtk_tree_path_down (store_parent_path);
682   gtk_tree_path_down (filter_parent_path);
683
684   store_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
685                                             &store_iter, store_parent_path);
686   filter_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->filter),
687                                              &filter_iter, filter_parent_path);
688
689   for (i = 0; i < LEVEL_LENGTH; i++)
690     {
691       gboolean visible;
692
693       g_return_if_fail (store_has_next == TRUE);
694
695       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
696                           &store_iter,
697                           1, &visible,
698                           -1);
699
700       if (visible)
701         {
702           GtkTreePath *tmp;
703           gchar *filter_str, *store_str;
704
705           g_return_if_fail (filter_has_next == TRUE);
706
707           /* Verify path */
708           tmp = gtk_tree_model_get_path (GTK_TREE_MODEL (fixture->filter),
709                                          &filter_iter);
710           g_return_if_fail (gtk_tree_path_compare (tmp, filter_parent_path) == 0);
711
712           /* Verify model content */
713           gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
714                               &store_iter,
715                               0, &store_str,
716                               -1);
717           gtk_tree_model_get (GTK_TREE_MODEL (fixture->filter),
718                               &filter_iter,
719                               0, &filter_str,
720                               -1);
721
722           g_return_if_fail (g_strcmp0 (store_str, filter_str) == 0);
723
724           g_free (store_str);
725           g_free (filter_str);
726
727           if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->filter),
728                                              &filter_iter))
729             {
730               g_return_if_fail (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), &store_iter));
731
732               check_filter_model_recurse (fixture,
733                                           gtk_tree_path_copy (store_parent_path),
734                                           tmp);
735             }
736
737           gtk_tree_path_next (filter_parent_path);
738           filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter);
739         }
740
741       gtk_tree_path_next (store_parent_path);
742       store_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &store_iter);
743     }
744
745   /* Both models should have no more content! */
746   g_return_if_fail (store_has_next == FALSE);
747   g_return_if_fail (filter_has_next == FALSE);
748
749   gtk_tree_path_free (store_parent_path);
750   gtk_tree_path_free (filter_parent_path);
751 }
752
753 static void
754 check_filter_model (FilterTest *fixture)
755 {
756   GtkTreePath *path;
757
758   if (fixture->monitor)
759     signal_monitor_assert_is_empty (fixture->monitor);
760
761   path = gtk_tree_path_new ();
762
763   check_filter_model_recurse (fixture, path, gtk_tree_path_copy (path));
764 }
765
766 static void
767 check_filter_model_with_root (FilterTest  *fixture,
768                               GtkTreePath *path)
769 {
770   if (fixture->monitor)
771     signal_monitor_assert_is_empty (fixture->monitor);
772
773   check_filter_model_recurse (fixture,
774                               gtk_tree_path_copy (path),
775                               gtk_tree_path_new ());
776 }
777
778 /* Helpers */
779
780 static void
781 check_level_length (GtkTreeModelFilter *filter,
782                     const gchar        *level,
783                     const int           length)
784 {
785   if (!level)
786     {
787       int l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL);
788       g_return_if_fail (l == length);
789     }
790   else
791     {
792       int l;
793       gboolean retrieved_iter = FALSE;
794       GtkTreeIter iter;
795
796       retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter),
797                                                             &iter, level);
798       g_return_if_fail (retrieved_iter);
799       l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter);
800       g_return_if_fail (l == length);
801     }
802 }
803
804 static void
805 set_path_visibility (FilterTest  *fixture,
806                      const gchar *path,
807                      gboolean     visible)
808 {
809   GtkTreeIter store_iter;
810
811   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
812                                        &store_iter, path);
813   gtk_tree_store_set (fixture->store, &store_iter,
814                       1, visible,
815                       -1);
816 }
817
818 #if 0
819 static void
820 insert_path_with_visibility (FilterTest  *fixture,
821                              const gchar *path_string,
822                              gboolean     visible)
823 {
824   int position;
825   GtkTreePath *path;
826   GtkTreeIter parent, iter;
827
828   path = gtk_tree_path_new_from_string (path_string);
829   position = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path)];
830   gtk_tree_path_up (path);
831
832   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &parent, path))
833     {
834       gtk_tree_store_insert (fixture->store, &iter, &parent, position);
835       create_tree_store_set_values (fixture->store, &iter, visible);
836     }
837   gtk_tree_path_free (path);
838 }
839 #endif
840
841 /*
842  * The actual tests.
843  */
844
845 static void
846 verify_test_suite (FilterTest    *fixture,
847                    gconstpointer  user_data)
848 {
849   check_filter_model (fixture);
850 }
851
852 static void
853 verify_test_suite_vroot (FilterTest    *fixture,
854                          gconstpointer  user_data)
855 {
856   check_filter_model_with_root (fixture, (GtkTreePath *)user_data);
857 }
858
859
860 static void
861 filled_hide_root_level (FilterTest    *fixture,
862                         gconstpointer  user_data)
863 {
864   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
865   set_path_visibility (fixture, "2", FALSE);
866   check_filter_model (fixture);
867   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
868
869   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
870   set_path_visibility (fixture, "0", FALSE);
871   check_filter_model (fixture);
872   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
873
874   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
875   set_path_visibility (fixture, "4", FALSE);
876   check_filter_model (fixture);
877   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
878
879
880   /* Hide remaining */
881   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
882   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
883
884   set_path_visibility (fixture, "1", FALSE);
885   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
886
887   set_path_visibility (fixture, "3", FALSE);
888   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 5);
889
890   check_filter_model (fixture);
891
892   /* Show some */
893   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
894   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
895   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
896   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
897
898   set_path_visibility (fixture, "1", TRUE);
899   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
900
901   set_path_visibility (fixture, "3", TRUE);
902   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
903
904   check_filter_model (fixture);
905 }
906
907 static void
908 filled_hide_child_levels (FilterTest    *fixture,
909                           gconstpointer  user_data)
910 {
911   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
912   set_path_visibility (fixture, "0:2", FALSE);
913   check_filter_model (fixture);
914   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
915   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
916
917   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
918   set_path_visibility (fixture, "0:4", FALSE);
919   check_filter_model (fixture);
920   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
921   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
922
923   set_path_visibility (fixture, "0:4:3", FALSE);
924   check_filter_model (fixture);
925   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
926   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
927
928   set_path_visibility (fixture, "0:4:0", FALSE);
929   set_path_visibility (fixture, "0:4:1", FALSE);
930   set_path_visibility (fixture, "0:4:2", FALSE);
931   set_path_visibility (fixture, "0:4:4", FALSE);
932   check_filter_model (fixture);
933   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
934   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
935
936   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
937   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
938   /* FIXME: Actually, the filter model should not be emitted the
939    * row-has-child-toggled signal here.  *However* an extraneous emission
940    * of this signal does not hurt and is allowed.
941    */
942   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
943   set_path_visibility (fixture, "0:4", TRUE);
944   check_filter_model (fixture);
945   check_level_length (fixture->filter, "0:3", 0);
946
947   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
948   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
949   set_path_visibility (fixture, "0:2", TRUE);
950   check_filter_model (fixture);
951   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
952   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
953   check_level_length (fixture->filter, "0:4", 0);
954
955   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
956   /* Once 0:4:0 got inserted, 0:4 became a parent */
957   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
958   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:0");
959   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
960   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:1");
961
962   set_path_visibility (fixture, "0:4:2", TRUE);
963   set_path_visibility (fixture, "0:4:4", TRUE);
964   signal_monitor_assert_is_empty (fixture->monitor);
965   check_level_length (fixture->filter, "0:4", 2);
966 }
967
968
969 static void
970 filled_vroot_hide_root_level (FilterTest    *fixture,
971                               gconstpointer  user_data)
972 {
973   GtkTreePath *path = (GtkTreePath *)user_data;
974
975   /* These changes do not affect the filter's root level */
976   set_path_visibility (fixture, "0", FALSE);
977   check_filter_model_with_root (fixture, path);
978   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
979   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
980
981   set_path_visibility (fixture, "4", FALSE);
982   check_filter_model_with_root (fixture, path);
983   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
984   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
985
986   /* Even though we set the virtual root parent node to FALSE,
987    * the virtual root contents remain.
988    */
989   set_path_visibility (fixture, "2", FALSE);
990   check_filter_model_with_root (fixture, path);
991   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
992   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
993
994   /* No change */
995   set_path_visibility (fixture, "1", FALSE);
996   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
997   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
998
999   set_path_visibility (fixture, "3", FALSE);
1000   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1001   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1002
1003   check_filter_model_with_root (fixture, path);
1004
1005   /* Show some */
1006   set_path_visibility (fixture, "2", TRUE);
1007   check_filter_model_with_root (fixture, path);
1008   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1009   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1010
1011   set_path_visibility (fixture, "1", TRUE);
1012   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1013   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1014
1015   set_path_visibility (fixture, "3", TRUE);
1016   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1017   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1018
1019   check_filter_model_with_root (fixture, path);
1020
1021   /* Now test changes in the virtual root level */
1022   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
1023   set_path_visibility (fixture, "2:2", FALSE);
1024   check_filter_model_with_root (fixture, path);
1025   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1026
1027   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "3");
1028   set_path_visibility (fixture, "2:4", FALSE);
1029   check_filter_model_with_root (fixture, path);
1030   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1031
1032   set_path_visibility (fixture, "1:4", FALSE);
1033   check_filter_model_with_root (fixture, path);
1034   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1035
1036   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "3");
1037   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "3");
1038   set_path_visibility (fixture, "2:4", TRUE);
1039   check_filter_model_with_root (fixture, path);
1040   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1041
1042   set_path_visibility (fixture, "2", FALSE);
1043   check_filter_model_with_root (fixture, path);
1044   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1045
1046   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1047   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1048   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1049   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1050   set_path_visibility (fixture, "2:0", FALSE);
1051   set_path_visibility (fixture, "2:1", FALSE);
1052   set_path_visibility (fixture, "2:2", FALSE);
1053   set_path_visibility (fixture, "2:3", FALSE);
1054   set_path_visibility (fixture, "2:4", FALSE);
1055   check_filter_model_with_root (fixture, path);
1056   check_level_length (fixture->filter, NULL, 0);
1057
1058   set_path_visibility (fixture, "2", TRUE);
1059   check_filter_model_with_root (fixture, path);
1060   check_level_length (fixture->filter, NULL, 0);
1061
1062   set_path_visibility (fixture, "1:4", FALSE);
1063   check_filter_model_with_root (fixture, path);
1064   check_level_length (fixture->filter, NULL, 0);
1065
1066   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1067   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1068   set_path_visibility (fixture, "2:4", TRUE);
1069   check_filter_model_with_root (fixture, path);
1070   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
1071
1072   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1073   set_path_visibility (fixture, "2:4", FALSE);
1074   check_filter_model_with_root (fixture, path);
1075   check_level_length (fixture->filter, NULL, 0);
1076
1077   set_path_visibility (fixture, "2", FALSE);
1078   check_filter_model_with_root (fixture, path);
1079   check_level_length (fixture->filter, NULL, 0);
1080
1081   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1082   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1083   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1084   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1085   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2");
1086   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1087   set_path_visibility (fixture, "2:0", TRUE);
1088   set_path_visibility (fixture, "2:1", TRUE);
1089   set_path_visibility (fixture, "2:2", TRUE);
1090   check_filter_model_with_root (fixture, path);
1091   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1092
1093   set_path_visibility (fixture, "2", TRUE);
1094   check_filter_model_with_root (fixture, path);
1095   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1096 }
1097
1098 static void
1099 filled_vroot_hide_child_levels (FilterTest    *fixture,
1100                                 gconstpointer  user_data)
1101 {
1102   GtkTreePath *path = (GtkTreePath *)user_data;
1103
1104   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
1105   set_path_visibility (fixture, "2:0:2", FALSE);
1106   check_filter_model_with_root (fixture, path);
1107   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1108   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1109
1110   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
1111   set_path_visibility (fixture, "2:0:4", FALSE);
1112   check_filter_model_with_root (fixture, path);
1113   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1114   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1115
1116   set_path_visibility (fixture, "2:0:4:3", FALSE);
1117   check_filter_model_with_root (fixture, path);
1118   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1119   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1120
1121   set_path_visibility (fixture, "2:0:4:0", FALSE);
1122   set_path_visibility (fixture, "2:0:4:1", FALSE);
1123   set_path_visibility (fixture, "2:0:4:2", FALSE);
1124   set_path_visibility (fixture, "2:0:4:4", FALSE);
1125   check_filter_model_with_root (fixture, path);
1126   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1127   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1128
1129   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1130   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
1131   /* FIXME: Actually, the filter model should not be emitted the
1132    * row-has-child-toggled signal here.  *However* an extraneous emission
1133    * of this signal does not hurt and is allowed.
1134    */
1135   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
1136   set_path_visibility (fixture, "2:0:4", TRUE);
1137   check_filter_model_with_root (fixture, path);
1138   check_level_length (fixture->filter, "0:3", 0);
1139
1140   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
1141   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
1142   set_path_visibility (fixture, "2:0:2", TRUE);
1143   check_filter_model_with_root (fixture, path);
1144   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1145   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1146   check_level_length (fixture->filter, "0:4", 0);
1147
1148   /* FIXME: Inconsistency!  For the non-vroot case we also receive two
1149    * row-has-child-toggled signals here.
1150    */
1151   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
1152   /* Once 0:4:0 got inserted, 0:4 became a parent */
1153   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
1154   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
1155   set_path_visibility (fixture, "2:0:4:2", TRUE);
1156   set_path_visibility (fixture, "2:0:4:4", TRUE);
1157   check_level_length (fixture->filter, "0:4", 2);
1158 }
1159
1160
1161 static void
1162 empty_show_nodes (FilterTest    *fixture,
1163                   gconstpointer  user_data)
1164 {
1165   check_filter_model (fixture);
1166   check_level_length (fixture->filter, NULL, 0);
1167
1168   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1169   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1170   set_path_visibility (fixture, "3", TRUE);
1171   check_filter_model (fixture);
1172   check_level_length (fixture->filter, NULL, 1);
1173   check_level_length (fixture->filter, "0", 0);
1174
1175   set_path_visibility (fixture, "3:2:2", TRUE);
1176   check_filter_model (fixture);
1177   check_level_length (fixture->filter, NULL, 1);
1178   check_level_length (fixture->filter, "0", 0);
1179
1180   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0");
1181   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1182   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0");
1183   set_path_visibility (fixture, "3:2", TRUE);
1184   check_filter_model (fixture);
1185   check_level_length (fixture->filter, NULL, 1);
1186   check_level_length (fixture->filter, "0", 1);
1187   check_level_length (fixture->filter, "0:0", 1);
1188   check_level_length (fixture->filter, "0:0:0", 0);
1189
1190   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1191   set_path_visibility (fixture, "3", FALSE);
1192   check_filter_model (fixture);
1193   check_level_length (fixture->filter, NULL, 0);
1194
1195   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1196   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1197   set_path_visibility (fixture, "3:2:1", TRUE);
1198   set_path_visibility (fixture, "3", TRUE);
1199   check_filter_model (fixture);
1200   check_level_length (fixture->filter, NULL, 1);
1201   check_level_length (fixture->filter, "0", 1);
1202   check_level_length (fixture->filter, "0:0", 2);
1203   check_level_length (fixture->filter, "0:0:0", 0);
1204 }
1205
1206 static void
1207 empty_show_multiple_nodes (FilterTest    *fixture,
1208                            gconstpointer  user_data)
1209 {
1210   GtkTreeIter iter;
1211   GtkTreePath *changed_path;
1212
1213   check_filter_model (fixture);
1214   check_level_length (fixture->filter, NULL, 0);
1215
1216   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1217   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1218   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1219   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1220   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "1");
1221   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1222
1223   /* We simulate a change in visible func condition with this.  The
1224    * visibility state of multiple nodes changes at once, we emit row-changed
1225    * for these nodes (and others) after that.
1226    */
1227   filter_test_block_signals (fixture);
1228   set_path_visibility (fixture, "3", TRUE);
1229   set_path_visibility (fixture, "4", TRUE);
1230   filter_test_unblock_signals (fixture);
1231
1232   changed_path = gtk_tree_path_new ();
1233   gtk_tree_path_append_index (changed_path, 2);
1234   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1235                            &iter, changed_path);
1236   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1237                               changed_path, &iter);
1238
1239   gtk_tree_path_next (changed_path);
1240   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1241   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1242                               changed_path, &iter);
1243
1244   gtk_tree_path_next (changed_path);
1245   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1246   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1247                               changed_path, &iter);
1248
1249   gtk_tree_path_free (changed_path);
1250
1251   check_filter_model (fixture);
1252   check_level_length (fixture->filter, NULL, 2);
1253   check_level_length (fixture->filter, "0", 0);
1254
1255   set_path_visibility (fixture, "3:2:2", TRUE);
1256   check_filter_model (fixture);
1257   check_level_length (fixture->filter, NULL, 2);
1258   check_level_length (fixture->filter, "0", 0);
1259
1260   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0");
1261   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1262   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0");
1263   set_path_visibility (fixture, "3:2", TRUE);
1264   check_filter_model (fixture);
1265   check_level_length (fixture->filter, NULL, 2);
1266   check_level_length (fixture->filter, "0", 1);
1267   check_level_length (fixture->filter, "0:0", 1);
1268   check_level_length (fixture->filter, "0:0:0", 0);
1269
1270   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1271   set_path_visibility (fixture, "3", FALSE);
1272   check_filter_model (fixture);
1273   check_level_length (fixture->filter, NULL, 1);
1274
1275   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1276   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1277   set_path_visibility (fixture, "3:2:1", TRUE);
1278   set_path_visibility (fixture, "3", TRUE);
1279   check_filter_model (fixture);
1280   check_level_length (fixture->filter, NULL, 2);
1281   check_level_length (fixture->filter, "0", 1);
1282   check_level_length (fixture->filter, "0:0", 2);
1283   check_level_length (fixture->filter, "0:0:0", 0);
1284 }
1285
1286 static void
1287 empty_vroot_show_nodes (FilterTest    *fixture,
1288                         gconstpointer  user_data)
1289 {
1290   GtkTreePath *path = (GtkTreePath *)user_data;
1291
1292   check_filter_model_with_root (fixture, path);
1293   check_level_length (fixture->filter, NULL, 0);
1294
1295   set_path_visibility (fixture, "2", TRUE);
1296   check_filter_model_with_root (fixture, path);
1297   check_level_length (fixture->filter, NULL, 0);
1298
1299   set_path_visibility (fixture, "2:2:2", TRUE);
1300   check_filter_model_with_root (fixture, path);
1301   check_level_length (fixture->filter, NULL, 0);
1302
1303   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1304   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1305   set_path_visibility (fixture, "2:2", TRUE);
1306   check_filter_model_with_root (fixture, path);
1307   check_level_length (fixture->filter, NULL, 1);
1308   check_level_length (fixture->filter, "0", 1);
1309   check_level_length (fixture->filter, "0:0", 0);
1310
1311   set_path_visibility (fixture, "3", TRUE);
1312   check_filter_model_with_root (fixture, path);
1313   check_level_length (fixture->filter, NULL, 1);
1314
1315   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1316   set_path_visibility (fixture, "2:2", FALSE);
1317   check_filter_model_with_root (fixture, path);
1318   check_level_length (fixture->filter, NULL, 0);
1319
1320   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1321   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1322   set_path_visibility (fixture, "2:2:1", TRUE);
1323   set_path_visibility (fixture, "2:2", TRUE);
1324   check_filter_model_with_root (fixture, path);
1325   check_level_length (fixture->filter, NULL, 1);
1326   check_level_length (fixture->filter, "0", 2);
1327   check_level_length (fixture->filter, "0:1", 0);
1328 }
1329
1330 static void
1331 empty_vroot_show_multiple_nodes (FilterTest    *fixture,
1332                                  gconstpointer  user_data)
1333 {
1334   GtkTreeIter iter;
1335   GtkTreePath *changed_path;
1336   GtkTreePath *path = (GtkTreePath *)user_data;
1337
1338   check_filter_model_with_root (fixture, path);
1339   check_level_length (fixture->filter, NULL, 0);
1340
1341   /* We simulate a change in visible func condition with this.  The
1342    * visibility state of multiple nodes changes at once, we emit row-changed
1343    * for these nodes (and others) after that.
1344    */
1345   filter_test_block_signals (fixture);
1346   set_path_visibility (fixture, "2", TRUE);
1347   set_path_visibility (fixture, "3", TRUE);
1348   filter_test_unblock_signals (fixture);
1349
1350   changed_path = gtk_tree_path_new ();
1351   gtk_tree_path_append_index (changed_path, 1);
1352   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1353                            &iter, changed_path);
1354   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1355                               changed_path, &iter);
1356
1357   gtk_tree_path_next (changed_path);
1358   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1359   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1360                               changed_path, &iter);
1361
1362   gtk_tree_path_next (changed_path);
1363   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1364   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1365                               changed_path, &iter);
1366
1367   gtk_tree_path_next (changed_path);
1368   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1369   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1370                               changed_path, &iter);
1371
1372   gtk_tree_path_free (changed_path);
1373
1374   check_filter_model_with_root (fixture, path);
1375   check_level_length (fixture->filter, NULL, 0);
1376
1377   set_path_visibility (fixture, "2:2:2", TRUE);
1378   check_filter_model_with_root (fixture, path);
1379   check_level_length (fixture->filter, NULL, 0);
1380
1381   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1382   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1383   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1384   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1385
1386   /* Again, we simulate a call to refilter */
1387   filter_test_block_signals (fixture);
1388   set_path_visibility (fixture, "2:2", TRUE);
1389   set_path_visibility (fixture, "2:3", TRUE);
1390   filter_test_unblock_signals (fixture);
1391
1392   changed_path = gtk_tree_path_new ();
1393   gtk_tree_path_append_index (changed_path, 2);
1394   gtk_tree_path_append_index (changed_path, 1);
1395   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1396                            &iter, changed_path);
1397   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1398                               changed_path, &iter);
1399
1400   gtk_tree_path_next (changed_path);
1401   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1402   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1403                               changed_path, &iter);
1404
1405   gtk_tree_path_next (changed_path);
1406   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1407   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1408                               changed_path, &iter);
1409
1410   gtk_tree_path_next (changed_path);
1411   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1412   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1413                               changed_path, &iter);
1414
1415   gtk_tree_path_free (changed_path);
1416
1417   check_filter_model_with_root (fixture, path);
1418   check_level_length (fixture->filter, NULL, 2);
1419   check_level_length (fixture->filter, "0", 1);
1420   check_level_length (fixture->filter, "0:0", 0);
1421
1422   set_path_visibility (fixture, "3", TRUE);
1423   check_filter_model_with_root (fixture, path);
1424   check_level_length (fixture->filter, NULL, 2);
1425
1426   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1427   set_path_visibility (fixture, "2:2", FALSE);
1428   check_filter_model_with_root (fixture, path);
1429   check_level_length (fixture->filter, NULL, 1);
1430
1431   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1432   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1433   set_path_visibility (fixture, "2:2:1", TRUE);
1434   set_path_visibility (fixture, "2:2", TRUE);
1435   check_filter_model_with_root (fixture, path);
1436   check_level_length (fixture->filter, NULL, 2);
1437   check_level_length (fixture->filter, "0", 2);
1438   check_level_length (fixture->filter, "0:1", 0);
1439 }
1440
1441
1442 static void
1443 unfiltered_hide_single (FilterTest    *fixture,
1444                         gconstpointer  user_data)
1445
1446 {
1447   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1448   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1449   set_path_visibility (fixture, "2", FALSE);
1450
1451   signal_monitor_assert_is_empty (fixture->monitor);
1452   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1453
1454   /* The view only shows the root level, so the filter model only has
1455    * the first two levels cached.
1456    */
1457   filter_test_append_refilter_signals (fixture, 2);
1458   filter_test_enable_filter (fixture);
1459
1460   check_filter_model (fixture);
1461   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1462 }
1463
1464 static void
1465 unfiltered_hide_single_child (FilterTest    *fixture,
1466                               gconstpointer  user_data)
1467
1468 {
1469   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1470   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1471   set_path_visibility (fixture, "2:2", FALSE);
1472
1473   signal_monitor_assert_is_empty (fixture->monitor);
1474   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1475   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1476
1477   /* The view only shows the root level, so the filter model only has
1478    * the first two levels cached.
1479    */
1480   filter_test_append_refilter_signals (fixture, 2);
1481   filter_test_enable_filter (fixture);
1482
1483   check_filter_model (fixture);
1484   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1485   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1486 }
1487
1488 static void
1489 unfiltered_hide_single_multi_level (FilterTest    *fixture,
1490                                     gconstpointer  user_data)
1491
1492 {
1493   /* This row is not shown, so its signal is not propagated */
1494   set_path_visibility (fixture, "2:2:2", FALSE);
1495
1496   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1497   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1498   set_path_visibility (fixture, "2:2", FALSE);
1499
1500   signal_monitor_assert_is_empty (fixture->monitor);
1501   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1502   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1503   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1504
1505   /* The view only shows the root level, so the filter model only has
1506    * the first two levels cached.
1507    */
1508   filter_test_append_refilter_signals (fixture, 2);
1509   filter_test_enable_filter (fixture);
1510
1511   check_filter_model (fixture);
1512   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1513   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1514
1515   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1516   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1517   set_path_visibility (fixture, "2:2", TRUE);
1518
1519   check_filter_model (fixture);
1520   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1521   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1522   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1523 }
1524
1525
1526 static void
1527 unfiltered_vroot_hide_single (FilterTest    *fixture,
1528                               gconstpointer  user_data)
1529
1530 {
1531   GtkTreePath *path = (GtkTreePath *)user_data;
1532
1533   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1534   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1535   set_path_visibility (fixture, "2:2", FALSE);
1536
1537   signal_monitor_assert_is_empty (fixture->monitor);
1538   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1539
1540   /* The view only shows the root level, so the filter model only has
1541    * the first two levels cached.  (We add an additional level to
1542    * take the virtual root into account).
1543    */
1544   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1545   filter_test_enable_filter (fixture);
1546
1547   check_filter_model_with_root (fixture, path);
1548   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1549 }
1550
1551 static void
1552 unfiltered_vroot_hide_single_child (FilterTest    *fixture,
1553                                     gconstpointer  user_data)
1554
1555 {
1556   GtkTreePath *path = (GtkTreePath *)user_data;
1557
1558   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1559   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1560   set_path_visibility (fixture, "2:2:2", FALSE);
1561
1562   signal_monitor_assert_is_empty (fixture->monitor);
1563   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1564   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1565
1566   /* The view only shows the root level, so the filter model only has
1567    * the first two levels cached.  (We add an additional level to take
1568    * the virtual root into account).
1569    */
1570   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1571   filter_test_enable_filter (fixture);
1572
1573   check_filter_model_with_root (fixture, path);
1574   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1575   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1576 }
1577
1578 static void
1579 unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
1580                                           gconstpointer  user_data)
1581
1582 {
1583   GtkTreePath *path = (GtkTreePath *)user_data;
1584
1585   /* This row is not shown, so its signal is not propagated */
1586   set_path_visibility (fixture, "2:2:2:2", FALSE);
1587
1588   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1589   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1590   set_path_visibility (fixture, "2:2:2", FALSE);
1591
1592   signal_monitor_assert_is_empty (fixture->monitor);
1593   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1594   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1595   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1596
1597   /* The view only shows the root level, so the filter model only has
1598    * the first two levels cached.
1599    */
1600   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1601   filter_test_enable_filter (fixture);
1602
1603   check_filter_model_with_root (fixture, path);
1604   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1605   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1606
1607   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1608   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1609   set_path_visibility (fixture, "2:2:2", TRUE);
1610
1611   check_filter_model_with_root (fixture, path);
1612   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1613   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1614   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1615 }
1616
1617
1618
1619 static void
1620 unfiltered_show_single (FilterTest    *fixture,
1621                         gconstpointer  user_data)
1622
1623 {
1624   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1625   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1626   set_path_visibility (fixture, "2", TRUE);
1627
1628   signal_monitor_assert_is_empty (fixture->monitor);
1629   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1630
1631   /* The view only shows the root level, so the filter model only has
1632    * the first two levels cached.
1633    */
1634   filter_test_append_refilter_signals (fixture, 2);
1635   filter_test_enable_filter (fixture);
1636
1637   check_filter_model (fixture);
1638   check_level_length (fixture->filter, NULL, 1);
1639 }
1640
1641 static void
1642 unfiltered_show_single_child (FilterTest    *fixture,
1643                               gconstpointer  user_data)
1644
1645 {
1646   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1647   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1648   set_path_visibility (fixture, "2:2", TRUE);
1649
1650   signal_monitor_assert_is_empty (fixture->monitor);
1651   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1652   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1653
1654   /* The view only shows the root level, so the filter model only has
1655    * the first two levels cached.
1656    */
1657   filter_test_append_refilter_signals (fixture, 3);
1658   filter_test_enable_filter (fixture);
1659
1660   check_filter_model (fixture);
1661   check_level_length (fixture->filter, NULL, 0);
1662
1663   /* From here we are filtered, "2" in the real model is "0" in the filter
1664    * model.
1665    */
1666   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1667   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1668   set_path_visibility (fixture, "2", TRUE);
1669   signal_monitor_assert_is_empty (fixture->monitor);
1670   check_level_length (fixture->filter, NULL, 1);
1671   check_level_length (fixture->filter, "0", 1);
1672 }
1673
1674 static void
1675 unfiltered_show_single_multi_level (FilterTest    *fixture,
1676                                     gconstpointer  user_data)
1677
1678 {
1679   /* The view is not showing this row (collapsed state), so it is not
1680    * referenced.  The signal should not go through.
1681    */
1682   set_path_visibility (fixture, "2:2:2", TRUE);
1683
1684   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1685   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1686   set_path_visibility (fixture, "2:2", TRUE);
1687
1688   signal_monitor_assert_is_empty (fixture->monitor);
1689   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1690   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1691   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1692
1693   /* The view only shows the root level, so the filter model only has
1694    * the first two levels cached.
1695    */
1696   filter_test_append_refilter_signals (fixture, 3);
1697   filter_test_enable_filter (fixture);
1698
1699   check_filter_model (fixture);
1700   check_level_length (fixture->filter, NULL, 0);
1701
1702   /* From here we are filtered, "2" in the real model is "0" in the filter
1703    * model.
1704    */
1705   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1706   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1707   set_path_visibility (fixture, "2", TRUE);
1708   check_filter_model (fixture);
1709   check_level_length (fixture->filter, NULL, 1);
1710   check_level_length (fixture->filter, "0", 1);
1711   check_level_length (fixture->filter, "0:0", 1);
1712 }
1713
1714
1715 static void
1716 unfiltered_vroot_show_single (FilterTest    *fixture,
1717                               gconstpointer  user_data)
1718
1719 {
1720   GtkTreePath *path = (GtkTreePath *)user_data;
1721
1722   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1723   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1724   set_path_visibility (fixture, "2:2", TRUE);
1725
1726   signal_monitor_assert_is_empty (fixture->monitor);
1727   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1728
1729   /* The view only shows the root level, so the filter model only has
1730    * the first two levels cached.
1731    */
1732   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1733   filter_test_enable_filter (fixture);
1734
1735   check_filter_model_with_root (fixture, path);
1736   check_level_length (fixture->filter, NULL, 1);
1737 }
1738
1739 static void
1740 unfiltered_vroot_show_single_child (FilterTest    *fixture,
1741                                     gconstpointer  user_data)
1742
1743 {
1744   GtkTreePath *path = (GtkTreePath *)user_data;
1745
1746   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1747   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1748   set_path_visibility (fixture, "2:2:2", TRUE);
1749
1750   signal_monitor_assert_is_empty (fixture->monitor);
1751   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1752   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1753
1754   /* The view only shows the root level, so the filter model only has
1755    * the first two levels cached.
1756    */
1757   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
1758   filter_test_enable_filter (fixture);
1759
1760   check_filter_model_with_root (fixture, path);
1761   check_level_length (fixture->filter, NULL, 0);
1762
1763   /* From here we are filtered, "2" in the real model is "0" in the filter
1764    * model.
1765    */
1766   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1767   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1768   set_path_visibility (fixture, "2:2", TRUE);
1769   signal_monitor_assert_is_empty (fixture->monitor);
1770   check_level_length (fixture->filter, NULL, 1);
1771   check_level_length (fixture->filter, "0", 1);
1772 }
1773
1774 static void
1775 unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
1776                                           gconstpointer  user_data)
1777
1778 {
1779   GtkTreePath *path = (GtkTreePath *)user_data;
1780
1781   /* The view is not showing this row (collapsed state), so it is not
1782    * referenced.  The signal should not go through.
1783    */
1784   set_path_visibility (fixture, "2:2:2:2", TRUE);
1785
1786   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1787   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1788   set_path_visibility (fixture, "2:2:2", TRUE);
1789
1790   signal_monitor_assert_is_empty (fixture->monitor);
1791   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1792   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1793   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1794
1795   /* The view only shows the root level, so the filter model only has
1796    * the first two levels cached.
1797    */
1798   filter_test_append_refilter_signals_with_vroot (fixture, 4, path);
1799   filter_test_enable_filter (fixture);
1800
1801   check_filter_model_with_root (fixture, path);
1802   check_level_length (fixture->filter, NULL, 0);
1803
1804   /* From here we are filtered, "2" in the real model is "0" in the filter
1805    * model.
1806    */
1807   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1808   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1809   set_path_visibility (fixture, "2:2", TRUE);
1810   check_filter_model_with_root (fixture, path);
1811   check_level_length (fixture->filter, NULL, 1);
1812   check_level_length (fixture->filter, "0", 1);
1813   check_level_length (fixture->filter, "0:0", 1);
1814 }
1815
1816
1817 static gboolean
1818 specific_path_dependent_filter_func (GtkTreeModel *model,
1819                                      GtkTreeIter  *iter,
1820                                      gpointer      data)
1821 {
1822   GtkTreePath *path;
1823
1824   path = gtk_tree_model_get_path (model, iter);
1825   if (gtk_tree_path_get_indices (path)[0] < 4)
1826     return FALSE;
1827
1828   return TRUE;
1829 }
1830
1831 static void
1832 specific_path_dependent_filter (void)
1833 {
1834   int i;
1835   GtkTreeIter iter;
1836   GtkListStore *list;
1837   GtkTreeModel *sort;
1838   GtkTreeModel *filter;
1839
1840   list = gtk_list_store_new (1, G_TYPE_INT);
1841   gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1);
1842   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
1843   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
1844   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
1845   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
1846   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
1847   gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1);
1848   gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1);
1849
1850   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list));
1851   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL);
1852   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
1853                                           specific_path_dependent_filter_func,
1854                                           NULL, NULL);
1855
1856   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0,
1857                                         GTK_SORT_DESCENDING);
1858
1859   for (i = 0; i < 4; i++)
1860     {
1861       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
1862                                          NULL, 1))
1863         gtk_list_store_remove (list, &iter);
1864
1865       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
1866                                          NULL, 2))
1867         gtk_list_store_remove (list, &iter);
1868     }
1869 }
1870
1871
1872 static gboolean
1873 specific_append_after_collapse_visible_func (GtkTreeModel *model,
1874                                              GtkTreeIter  *iter,
1875                                              gpointer      data)
1876 {
1877   gint number;
1878   gboolean hide_negative_numbers;
1879
1880   gtk_tree_model_get (model, iter, 1, &number, -1);
1881   hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers"));
1882
1883   return (number >= 0 || !hide_negative_numbers);
1884 }
1885
1886 static void
1887 specific_append_after_collapse (void)
1888 {
1889   /* This test is based on one of the test cases I found in my
1890    * old test cases directory.  I unfortunately do not have a record
1891    * from who this test case originated.  -Kris.
1892    *
1893    * General idea:
1894    * - Construct tree.
1895    * - Show tree, expand, collapse.
1896    * - Add a row.
1897    */
1898
1899   GtkTreeIter iter;
1900   GtkTreeIter child_iter;
1901   GtkTreeIter child_iter2;
1902   GtkTreePath *append_path;
1903   GtkTreeStore *store;
1904   GtkTreeModel *filter;
1905   GtkTreeModel *sort;
1906
1907   GtkWidget *window;
1908   GtkWidget *tree_view;
1909
1910   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT);
1911
1912   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
1913   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
1914                      GINT_TO_POINTER (FALSE));
1915   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
1916                                           specific_append_after_collapse_visible_func,
1917                                           filter, NULL);
1918
1919   sort = gtk_tree_model_sort_new_with_model (filter);
1920
1921   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1922   tree_view = gtk_tree_view_new_with_model (sort);
1923   gtk_container_add (GTK_CONTAINER (window), tree_view);
1924   gtk_widget_realize (tree_view);
1925
1926   while (gtk_events_pending ())
1927     gtk_main_iteration ();
1928
1929   gtk_tree_store_prepend (store, &iter, NULL);
1930   gtk_tree_store_set (store, &iter,
1931                       0, "hallo", 1, 1, -1);
1932
1933   gtk_tree_store_append (store, &child_iter, &iter);
1934   gtk_tree_store_set (store, &child_iter,
1935                       0, "toemaar", 1, 1, -1);
1936
1937   gtk_tree_store_append (store, &child_iter2, &child_iter);
1938   gtk_tree_store_set (store, &child_iter2,
1939                       0, "very deep", 1, 1, -1);
1940
1941   append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2);
1942
1943   gtk_tree_store_append (store, &child_iter, &iter);
1944   gtk_tree_store_set (store, &child_iter,
1945                       0, "sja", 1, 1, -1);
1946
1947   gtk_tree_store_append (store, &child_iter, &iter);
1948   gtk_tree_store_set (store, &child_iter,
1949                       0, "some word", 1, -1, -1);
1950
1951   /* Expand and collapse the tree */
1952   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
1953   while (gtk_events_pending ())
1954     gtk_main_iteration ();
1955
1956   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
1957   while (gtk_events_pending ())
1958     gtk_main_iteration ();
1959
1960   /* Add another it */
1961   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
1962                      GINT_TO_POINTER (TRUE));
1963
1964   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path))
1965     {
1966       gtk_tree_store_append (store, &child_iter, &iter);
1967       gtk_tree_store_set (store, &child_iter,
1968                           0, "new new new !!", 1, 1, -1);
1969     }
1970   gtk_tree_path_free (append_path);
1971
1972   /* Expand */
1973   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
1974   while (gtk_events_pending ())
1975     gtk_main_iteration ();
1976 }
1977
1978
1979 static gint
1980 specific_sort_filter_remove_node_compare_func (GtkTreeModel  *model,
1981                                                GtkTreeIter   *iter1,
1982                                                GtkTreeIter   *iter2,
1983                                                gpointer       data)
1984 {
1985   return -1;
1986 }
1987
1988 static gboolean
1989 specific_sort_filter_remove_node_visible_func (GtkTreeModel  *model,
1990                                                GtkTreeIter   *iter,
1991                                                gpointer       data)
1992 {
1993   char *item = NULL;
1994
1995   /* Do reference the model */
1996   gtk_tree_model_get (model, iter, 0, &item, -1);
1997   g_free (item);
1998
1999   return FALSE;
2000 }
2001
2002 static void
2003 specific_sort_filter_remove_node (void)
2004 {
2005   /* This test is based on one of the test cases I found in my
2006    * old test cases directory.  I unfortunately do not have a record
2007    * from who this test case originated.  -Kris.
2008    *
2009    * General idea:
2010    *  - Create tree store, sort, filter models.  The sort model has
2011    *    a default sort func that is enabled, filter model a visible func
2012    *    that defaults to returning FALSE.
2013    *  - Remove a node from the tree store.
2014    */
2015
2016   GtkTreeIter iter;
2017   GtkTreeStore *store;
2018   GtkTreeModel *filter;
2019   GtkTreeModel *sort;
2020
2021   GtkWidget *window;
2022   GtkWidget *tree_view;
2023
2024   store = gtk_tree_store_new (1, G_TYPE_STRING);
2025   gtk_tree_store_append (store, &iter, NULL);
2026   gtk_tree_store_set (store, &iter, 0, "Hello1", -1);
2027
2028   gtk_tree_store_append (store, &iter, NULL);
2029   gtk_tree_store_set (store, &iter, 0, "Hello2", -1);
2030
2031   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2032   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
2033                                            specific_sort_filter_remove_node_compare_func, NULL, NULL);
2034
2035   filter = gtk_tree_model_filter_new (sort, NULL);
2036   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2037                                           specific_sort_filter_remove_node_visible_func,
2038                                           filter, NULL);
2039
2040
2041   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2042   tree_view = gtk_tree_view_new_with_model (filter);
2043   gtk_container_add (GTK_CONTAINER (window), tree_view);
2044   gtk_widget_realize (tree_view);
2045
2046   while (gtk_events_pending ())
2047     gtk_main_iteration ();
2048
2049   /* Remove a node */
2050   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
2051   gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
2052   gtk_tree_store_remove (store, &iter);
2053
2054   while (gtk_events_pending ())
2055     gtk_main_iteration ();
2056 }
2057
2058
2059 static void
2060 specific_sort_filter_remove_root (void)
2061 {
2062   /* This test is based on one of the test cases I found in my
2063    * old test cases directory.  I unfortunately do not have a record
2064    * from who this test case originated.  -Kris.
2065    */
2066
2067   GtkTreeModel *model, *sort, *filter;
2068   GtkTreeIter root, mid, leaf;
2069   GtkTreePath *path;
2070
2071   model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT));
2072   gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL);
2073   gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root);
2074   gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid);
2075
2076   path = gtk_tree_model_get_path (model, &mid);
2077
2078   sort = gtk_tree_model_sort_new_with_model (model);
2079   filter = gtk_tree_model_filter_new (sort, path);
2080
2081   gtk_tree_store_remove (GTK_TREE_STORE (model), &root);
2082
2083   g_object_unref (filter);
2084   g_object_unref (sort);
2085   g_object_unref (model);
2086 }
2087
2088
2089 static void
2090 specific_root_mixed_visibility (void)
2091 {
2092   int i;
2093   GtkTreeModel *filter;
2094   /* A bit nasty, apologies */
2095   FilterTest fixture;
2096
2097   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2098
2099   for (i = 0; i < LEVEL_LENGTH; i++)
2100     {
2101       GtkTreeIter iter;
2102
2103       gtk_tree_store_insert (fixture.store, &iter, NULL, i);
2104       if (i % 2 == 0)
2105         create_tree_store_set_values (fixture.store, &iter, TRUE);
2106       else
2107         create_tree_store_set_values (fixture.store, &iter, FALSE);
2108     }
2109
2110   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2111   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2112   fixture.monitor = NULL;
2113
2114   gtk_tree_model_filter_set_visible_column (fixture.filter, 1);
2115
2116   /* In order to trigger the potential bug, we should not access
2117    * the filter model here (so don't call the check functions).
2118    */
2119
2120   /* Change visibility of an odd row to TRUE */
2121   set_path_visibility (&fixture, "3", TRUE);
2122   check_filter_model (&fixture);
2123   check_level_length (fixture.filter, NULL, 4);
2124 }
2125
2126
2127
2128 static gboolean
2129 specific_has_child_filter_filter_func (GtkTreeModel *model,
2130                                        GtkTreeIter  *iter,
2131                                        gpointer      data)
2132 {
2133   return gtk_tree_model_iter_has_child (model, iter);
2134 }
2135
2136 static void
2137 specific_has_child_filter (void)
2138 {
2139   GtkTreeModel *filter;
2140   GtkTreeIter iter, root;
2141   /* A bit nasty, apologies */
2142   FilterTest fixture;
2143
2144   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2145   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2146   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2147   fixture.monitor = NULL;
2148
2149   /* We will filter on parent state using a filter function.  We will
2150    * manually keep the boolean column in sync, so that we can use
2151    * check_filter_model() to check the consistency of the model.
2152    */
2153   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
2154    * to be able to check the structure here.  We keep the calls to
2155    * check_filter_model() commented out until then.
2156    */
2157   gtk_tree_model_filter_set_visible_func (fixture.filter,
2158                                           specific_has_child_filter_filter_func,
2159                                           NULL, NULL);
2160
2161   gtk_tree_store_append (fixture.store, &root, NULL);
2162   create_tree_store_set_values (fixture.store, &root, FALSE);
2163
2164   /* check_filter_model (&fixture); */
2165   check_level_length (fixture.filter, NULL, 0);
2166
2167   gtk_tree_store_append (fixture.store, &iter, &root);
2168   create_tree_store_set_values (fixture.store, &iter, TRUE);
2169
2170   /* Parent must now be visible.  Do the level length check first,
2171    * to avoid modifying the child model triggering a row-changed to
2172    * the filter model.
2173    */
2174   check_level_length (fixture.filter, NULL, 1);
2175   check_level_length (fixture.filter, "0", 0);
2176
2177   set_path_visibility (&fixture, "0", TRUE);
2178   /* check_filter_model (&fixture); */
2179
2180   gtk_tree_store_append (fixture.store, &root, NULL);
2181   check_level_length (fixture.filter, NULL, 1);
2182
2183   gtk_tree_store_append (fixture.store, &iter, &root);
2184   check_level_length (fixture.filter, NULL, 2);
2185   check_level_length (fixture.filter, "1", 0);
2186
2187   create_tree_store_set_values (fixture.store, &root, TRUE);
2188   create_tree_store_set_values (fixture.store, &iter, TRUE);
2189
2190   /* check_filter_model (&fixture); */
2191
2192   gtk_tree_store_append (fixture.store, &iter, &root);
2193   create_tree_store_set_values (fixture.store, &iter, TRUE);
2194   check_level_length (fixture.filter, NULL, 2);
2195   check_level_length (fixture.filter, "0", 0);
2196   check_level_length (fixture.filter, "1", 0);
2197
2198   /* Now remove one of the remaining child rows */
2199   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
2200                                        &iter, "0:0");
2201   gtk_tree_store_remove (fixture.store, &iter);
2202
2203   check_level_length (fixture.filter, NULL, 1);
2204   check_level_length (fixture.filter, "0", 0);
2205
2206   set_path_visibility (&fixture, "0", FALSE);
2207   /* check_filter_model (&fixture); */
2208 }
2209
2210
2211 static gboolean
2212 specific_root_has_child_filter_filter_func (GtkTreeModel *model,
2213                                             GtkTreeIter  *iter,
2214                                             gpointer      data)
2215 {
2216   int depth;
2217   GtkTreePath *path;
2218
2219   path = gtk_tree_model_get_path (model, iter);
2220   depth = gtk_tree_path_get_depth (path);
2221   gtk_tree_path_free (path);
2222
2223   if (depth > 1)
2224     return TRUE;
2225   /* else */
2226   return gtk_tree_model_iter_has_child (model, iter);
2227 }
2228
2229 static void
2230 specific_root_has_child_filter (void)
2231 {
2232   GtkTreeModel *filter;
2233   GtkTreeIter iter, root;
2234   /* A bit nasty, apologies */
2235   FilterTest fixture;
2236
2237   /* This is a variation on the above test case wherein the has-child
2238    * check for visibility only applies to root level nodes.
2239    */
2240
2241   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2242   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2243   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2244   fixture.monitor = NULL;
2245
2246   /* We will filter on parent state using a filter function.  We will
2247    * manually keep the boolean column in sync, so that we can use
2248    * check_filter_model() to check the consistency of the model.
2249    */
2250   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
2251    * to be able to check the structure here.  We keep the calls to
2252    * check_filter_model() commented out until then.
2253    */
2254   gtk_tree_model_filter_set_visible_func (fixture.filter,
2255                                           specific_root_has_child_filter_filter_func,
2256                                           NULL, NULL);
2257
2258   gtk_tree_store_append (fixture.store, &root, NULL);
2259   create_tree_store_set_values (fixture.store, &root, FALSE);
2260
2261   /* check_filter_model (&fixture); */
2262   check_level_length (fixture.filter, NULL, 0);
2263
2264   gtk_tree_store_append (fixture.store, &iter, &root);
2265   create_tree_store_set_values (fixture.store, &iter, TRUE);
2266
2267   /* Parent must now be visible.  Do the level length check first,
2268    * to avoid modifying the child model triggering a row-changed to
2269    * the filter model.
2270    */
2271   check_level_length (fixture.filter, NULL, 1);
2272   check_level_length (fixture.filter, "0", 1);
2273
2274   set_path_visibility (&fixture, "0", TRUE);
2275   /* check_filter_model (&fixture); */
2276
2277   gtk_tree_store_append (fixture.store, &root, NULL);
2278   check_level_length (fixture.filter, NULL, 1);
2279
2280   gtk_tree_store_append (fixture.store, &iter, &root);
2281   check_level_length (fixture.filter, NULL, 2);
2282   check_level_length (fixture.filter, "1", 1);
2283
2284   create_tree_store_set_values (fixture.store, &root, TRUE);
2285   create_tree_store_set_values (fixture.store, &iter, TRUE);
2286
2287   /* check_filter_model (&fixture); */
2288
2289   gtk_tree_store_append (fixture.store, &iter, &root);
2290   create_tree_store_set_values (fixture.store, &iter, TRUE);
2291   check_level_length (fixture.filter, NULL, 2);
2292   check_level_length (fixture.filter, "0", 1);
2293   check_level_length (fixture.filter, "1", 2);
2294
2295   /* Now remove one of the remaining child rows */
2296   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
2297                                        &iter, "0:0");
2298   gtk_tree_store_remove (fixture.store, &iter);
2299
2300   check_level_length (fixture.filter, NULL, 1);
2301   check_level_length (fixture.filter, "0", 2);
2302
2303   set_path_visibility (&fixture, "0", FALSE);
2304   /* check_filter_model (&fixture); */
2305 }
2306
2307
2308 static void
2309 specific_filter_add_child (void)
2310 {
2311   /* This test is based on one of the test cases I found in my
2312    * old test cases directory.  I unfortunately do not have a record
2313    * from who this test case originated.  -Kris.
2314    */
2315
2316   GtkTreeIter iter;
2317   GtkTreeIter iter_first;
2318   GtkTreeIter child;
2319   GtkTreeStore *store;
2320   GtkTreeModel *filter;
2321
2322   store = gtk_tree_store_new (1, G_TYPE_STRING);
2323
2324   gtk_tree_store_append (store, &iter_first, NULL);
2325   gtk_tree_store_set (store, &iter_first, 0, "Hello", -1);
2326
2327   gtk_tree_store_append (store, &iter, NULL);
2328   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2329
2330   gtk_tree_store_append (store, &iter, NULL);
2331   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2332
2333   gtk_tree_store_append (store, &iter, NULL);
2334   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2335
2336   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2337
2338   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
2339   gtk_tree_store_append (store, &child, &iter_first);
2340   gtk_tree_store_set (store, &child, 0, "Hello", -1);
2341 }
2342
2343 static void
2344 specific_list_store_clear (void)
2345 {
2346   int i;
2347   GtkTreeIter iter;
2348   GtkListStore *list;
2349   GtkTreeModel *filter;
2350   GtkWidget *view;
2351
2352   list = gtk_list_store_new (1, G_TYPE_INT);
2353   gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1);
2354   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
2355   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
2356   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
2357   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
2358   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
2359   gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1);
2360   gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1);
2361
2362   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL);
2363   view = gtk_tree_view_new_with_model (filter);
2364
2365   gtk_list_store_clear (list);
2366 }
2367
2368 static void
2369 specific_bug_300089 (void)
2370 {
2371   /* Test case for GNOME Bugzilla bug 300089.  Written by
2372    * Matthias Clasen.
2373    */
2374   GtkTreeModel *sort_model, *child_model;
2375   GtkTreePath *path;
2376   GtkTreeIter iter, iter2, sort_iter;
2377
2378   child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING));
2379
2380   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2381   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1);
2382   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2383   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1);
2384
2385   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter);
2386   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1);
2387   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter);
2388   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1);
2389
2390   gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL);
2391   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1);
2392
2393
2394   sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model));
2395   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
2396                                         0, GTK_SORT_ASCENDING);
2397
2398   path = gtk_tree_path_new_from_indices (1, 1, -1);
2399
2400   /* make sure a level is constructed */ 
2401   gtk_tree_model_get_iter (sort_model, &sort_iter, path);
2402
2403   /* change the "E" row in a way that causes it to change position */ 
2404   gtk_tree_model_get_iter (child_model, &iter, path);
2405   gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1);
2406 }
2407
2408
2409 static int
2410 specific_bug_301558_sort_func (GtkTreeModel *model,
2411                                GtkTreeIter  *a,
2412                                GtkTreeIter  *b,
2413                                gpointer      data)
2414 {
2415   int i, j;
2416
2417   gtk_tree_model_get (model, a, 0, &i, -1);
2418   gtk_tree_model_get (model, b, 0, &j, -1);
2419
2420   return j - i;
2421 }
2422
2423 static void
2424 specific_bug_301558 (void)
2425 {
2426   /* Test case for GNOME Bugzilla bug 301558 provided by
2427    * Markku Vire.
2428    */
2429   GtkTreeStore *tree;
2430   GtkTreeModel *filter;
2431   GtkTreeModel *sort;
2432   GtkTreeIter root, iter, iter2;
2433   GtkWidget *view;
2434   int i;
2435   gboolean add;
2436
2437   tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN);
2438   gtk_tree_store_append (tree, &iter, NULL);
2439   gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1);
2440   gtk_tree_store_append (tree, &iter2, &iter);
2441   gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1);
2442
2443   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree));
2444   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
2445                                            specific_bug_301558_sort_func,
2446                                            NULL, NULL);
2447
2448   filter = gtk_tree_model_filter_new (sort, NULL);
2449   gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1);
2450
2451   view = gtk_tree_view_new_with_model (filter);
2452
2453   while (gtk_events_pending ())
2454     gtk_main_iteration ();
2455
2456   add = TRUE;
2457
2458   for (i = 0; i < 10; i++)
2459     {
2460       if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root))
2461         g_assert_not_reached ();
2462
2463       if (add)
2464         {
2465           gtk_tree_store_append (tree, &iter, &root);
2466           gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1);
2467         }
2468       else
2469         {
2470           int n;
2471           n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root);
2472           gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter,
2473                                          &root, n - 1);
2474           gtk_tree_store_remove (tree, &iter);
2475         }
2476
2477       add = !add;
2478     }
2479 }
2480
2481
2482 static gboolean
2483 specific_bug_311955_filter_func (GtkTreeModel *model,
2484                                  GtkTreeIter  *iter,
2485                                  gpointer      data)
2486 {
2487   int value;
2488
2489   gtk_tree_model_get (model, iter, 0, &value, -1);
2490
2491   return (value != 0);
2492 }
2493
2494 static void
2495 specific_bug_311955 (void)
2496 {
2497   /* This is a test case for GNOME Bugzilla bug 311955.  It was written
2498    * by Markku Vire.
2499    */
2500   GtkTreeIter iter, child, root;
2501   GtkTreeStore *store;
2502   GtkTreeModel *sort;
2503   GtkTreeModel *filter;
2504
2505   GtkWidget *window;
2506   GtkWidget *tree_view;
2507   int i;
2508   int n;
2509
2510   store = gtk_tree_store_new (1, G_TYPE_INT);
2511
2512   gtk_tree_store_append (store, &root, NULL);
2513   gtk_tree_store_set (store, &root, 0, 33, -1);
2514
2515   gtk_tree_store_append (store, &iter, &root);
2516   gtk_tree_store_set (store, &iter, 0, 50, -1);
2517
2518   gtk_tree_store_append (store, &iter, NULL);
2519   gtk_tree_store_set (store, &iter, 0, 22, -1);
2520
2521   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2522   filter = gtk_tree_model_filter_new (sort, NULL);
2523
2524   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2525                                           specific_bug_311955_filter_func,
2526                                           NULL, NULL);
2527
2528   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2529   tree_view = gtk_tree_view_new_with_model (filter);
2530   g_object_unref (store);
2531
2532   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
2533
2534   while (gtk_events_pending ())
2535     gtk_main_iteration ();
2536
2537   /* Fill model */
2538   for (i = 0; i < 4; i++)
2539     {
2540       gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
2541
2542       gtk_tree_store_append (store, &iter, &root);
2543
2544       if (i < 3)
2545         gtk_tree_store_set (store, &iter, 0, i, -1);
2546
2547       if (i % 2 == 0)
2548         {
2549           gtk_tree_store_append (store, &child, &iter);
2550           gtk_tree_store_set (store, &child, 0, 10, -1);
2551         }
2552     }
2553
2554   while (gtk_events_pending ())
2555     gtk_main_iteration ();
2556
2557   /* Remove bottommost child from the tree. */
2558   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
2559   n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root);
2560
2561   if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2))
2562     {
2563       if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter))
2564         gtk_tree_store_remove (store, &child);
2565     }
2566   else
2567     g_assert_not_reached ();
2568 }
2569
2570 static void
2571 specific_bug_346800 (void)
2572 {
2573   /* This is a test case for GNOME Bugzilla bug 346800.  It was written
2574    * by Jonathan Matthew.
2575    */
2576
2577   GtkTreeIter node_iters[50];
2578   GtkTreeIter child_iters[50];
2579   GtkTreeModel *model;
2580   GtkTreeModelFilter *filter;
2581   GtkTreeStore *store;
2582   GType *columns;
2583   int i;
2584   int items = 50;
2585   columns = g_new (GType, 2);
2586   columns[0] = G_TYPE_STRING;
2587   columns[1] = G_TYPE_BOOLEAN;
2588   store = gtk_tree_store_newv (2, columns);
2589   model = GTK_TREE_MODEL (store);
2590
2591   filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
2592   gtk_tree_model_filter_set_visible_column (filter, 1);
2593
2594   for (i=0; i<items; i++)
2595     {
2596       /* allocate random amounts of junk, otherwise the filter model's arrays can expand without moving */
2597
2598       g_malloc (138);
2599       gtk_tree_store_append (store, &node_iters[i], NULL);
2600       gtk_tree_store_set (store, &node_iters[i],
2601                           0, "something",
2602                           1, ((i%6) == 0) ? FALSE : TRUE,
2603                           -1);
2604
2605       g_malloc (47);
2606       gtk_tree_store_append (store, &child_iters[i], &node_iters[i]);
2607       gtk_tree_store_set (store, &child_iters[i],
2608                           0, "something else",
2609                           1, FALSE,
2610                           -1);
2611       gtk_tree_model_filter_refilter (filter);
2612
2613       if (i > 6)
2614         {
2615           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-1], 1,
2616                               (i & 1) ? TRUE : FALSE, -1);
2617           gtk_tree_model_filter_refilter (filter);
2618
2619           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-2], 1,
2620                               (i & 1) ? FALSE: TRUE, -1);
2621           gtk_tree_model_filter_refilter (filter);
2622         }
2623     }
2624 }
2625
2626
2627 static void
2628 specific_bug_364946 (void)
2629 {
2630   /* This is a test case for GNOME Bugzilla bug 364946.  It was written
2631    * by Andreas Koehler.
2632    */
2633   GtkTreeStore *store;
2634   GtkTreeIter a, aa, aaa, aab, iter;
2635   GtkTreeModel *s_model;
2636
2637   store = gtk_tree_store_new (1, G_TYPE_STRING);
2638
2639   gtk_tree_store_append (store, &a, NULL);
2640   gtk_tree_store_set (store, &a, 0, "0", -1);
2641
2642   gtk_tree_store_append (store, &aa, &a);
2643   gtk_tree_store_set (store, &aa, 0, "0:0", -1);
2644
2645   gtk_tree_store_append (store, &aaa, &aa);
2646   gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1);
2647
2648   gtk_tree_store_append (store, &aab, &aa);
2649   gtk_tree_store_set (store, &aab, 0, "0:0:1", -1);
2650
2651   s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2652   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model), 0,
2653                                         GTK_SORT_ASCENDING);
2654
2655   gtk_tree_model_get_iter_from_string (s_model, &iter, "0:0:0");
2656
2657   gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1);
2658   gtk_tree_store_remove (store, &aaa);
2659   gtk_tree_store_remove (store, &aab);
2660
2661   gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (s_model));
2662 }
2663
2664
2665 static gboolean
2666 specific_bug_464173_visible_func (GtkTreeModel *model,
2667                                   GtkTreeIter  *iter,
2668                                   gpointer      data)
2669 {
2670   gboolean *visible = (gboolean *)data;
2671
2672   return *visible;
2673 }
2674
2675 static void
2676 specific_bug_464173 (void)
2677 {
2678   /* Test case for GNOME Bugzilla bug 464173, test case written
2679    * by Andreas Koehler.
2680    */
2681   GtkTreeStore *model;
2682   GtkTreeModelFilter *f_model;
2683   GtkTreeIter iter1, iter2;
2684   GtkWidget *view;
2685   gboolean visible = TRUE;
2686
2687   model = gtk_tree_store_new (1, G_TYPE_STRING);
2688   gtk_tree_store_append (model, &iter1, NULL);
2689   gtk_tree_store_set (model, &iter1, 0, "Foo", -1);
2690   gtk_tree_store_append (model, &iter2, &iter1);
2691   gtk_tree_store_set (model, &iter2, 0, "Bar", -1);
2692
2693   f_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL(model), NULL));
2694   gtk_tree_model_filter_set_visible_func (f_model,
2695                                           specific_bug_464173_visible_func,
2696                                           &visible, NULL);
2697
2698   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (f_model));
2699
2700   visible = FALSE;
2701   gtk_tree_model_filter_refilter (f_model);
2702 }
2703
2704
2705 static gboolean
2706 specific_bug_540201_filter_func (GtkTreeModel *model,
2707                                  GtkTreeIter  *iter,
2708                                  gpointer      data)
2709 {
2710   gboolean has_children;
2711
2712   has_children = gtk_tree_model_iter_has_child (model, iter);
2713
2714   return has_children;
2715 }
2716
2717 static void
2718 specific_bug_540201 (void)
2719 {
2720   /* Test case for GNOME Bugzilla bug 540201, steps provided by
2721    * Charles Day.
2722    */
2723   GtkTreeIter iter, root;
2724   GtkTreeStore *store;
2725   GtkTreeModel *filter;
2726
2727   GtkWidget *tree_view;
2728
2729   store = gtk_tree_store_new (1, G_TYPE_INT);
2730
2731   gtk_tree_store_append (store, &root, NULL);
2732   gtk_tree_store_set (store, &root, 0, 33, -1);
2733
2734   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2735   tree_view = gtk_tree_view_new_with_model (filter);
2736
2737   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2738                                           specific_bug_540201_filter_func,
2739                                           NULL, NULL);
2740
2741   gtk_tree_store_append (store, &iter, &root);
2742   gtk_tree_store_set (store, &iter, 0, 50, -1);
2743
2744   gtk_tree_store_append (store, &iter, &root);
2745   gtk_tree_store_set (store, &iter, 0, 22, -1);
2746
2747
2748   gtk_tree_store_append (store, &root, NULL);
2749   gtk_tree_store_set (store, &root, 0, 33, -1);
2750
2751   gtk_tree_store_append (store, &iter, &root);
2752   gtk_tree_store_set (store, &iter, 0, 22, -1);
2753 }
2754
2755
2756 static gboolean
2757 specific_bug_549287_visible_func (GtkTreeModel *model,
2758                                   GtkTreeIter  *iter,
2759                                   gpointer      data)
2760 {
2761   gboolean result = FALSE;
2762
2763   result = gtk_tree_model_iter_has_child (model, iter);
2764
2765   return result;
2766 }
2767
2768 static void
2769 specific_bug_549287 (void)
2770 {
2771   /* Test case for GNOME Bugzilla bug 529287, provided by Julient Puydt */
2772
2773   int i;
2774   GtkTreeStore *store;
2775   GtkTreeModel *filtered;
2776   GtkWidget *view;
2777   GtkTreeIter iter;
2778   GtkTreeIter *swap, *parent, *child;
2779
2780   store = gtk_tree_store_new (1, G_TYPE_STRING);
2781   filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2782   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered),
2783                                           specific_bug_549287_visible_func,
2784                                           NULL, NULL);
2785
2786   view = gtk_tree_view_new_with_model (filtered);
2787
2788   for (i = 0; i < 4; i++)
2789     {
2790       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
2791         {
2792           parent = gtk_tree_iter_copy (&iter);
2793           child = gtk_tree_iter_copy (&iter);
2794
2795           while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
2796                                                 child, parent, 0))
2797             {
2798
2799               swap = parent;
2800               parent = child;
2801               child = swap;
2802             }
2803
2804           gtk_tree_store_append (store, child, parent);
2805           gtk_tree_store_set (store, child,
2806                               0, "Something",
2807                               -1);
2808
2809           gtk_tree_iter_free (parent);
2810           gtk_tree_iter_free (child);
2811         }
2812       else
2813         {
2814           gtk_tree_store_append (store, &iter, NULL);
2815           gtk_tree_store_set (store, &iter,
2816                               0, "Something",
2817                               -1);
2818         }
2819
2820       /* since we inserted something, we changed the visibility conditions: */
2821       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filtered));
2822     }
2823 }
2824
2825 /* main */
2826
2827 int
2828 main (int    argc,
2829       char **argv)
2830 {
2831   gtk_test_init (&argc, &argv, NULL);
2832
2833   g_test_add ("/FilterModel/self/verify-test-suite",
2834               FilterTest, NULL,
2835               filter_test_setup,
2836               verify_test_suite,
2837               filter_test_teardown);
2838
2839   g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-1",
2840               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2841               filter_test_setup,
2842               verify_test_suite_vroot,
2843               filter_test_teardown);
2844   g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-2",
2845               FilterTest, gtk_tree_path_new_from_indices (2, 3, -1),
2846               filter_test_setup,
2847               verify_test_suite_vroot,
2848               filter_test_teardown);
2849
2850
2851   g_test_add ("/FilterModel/filled/hide-root-level",
2852               FilterTest, NULL,
2853               filter_test_setup,
2854               filled_hide_root_level,
2855               filter_test_teardown);
2856   g_test_add ("/FilterModel/filled/hide-child-levels",
2857               FilterTest, NULL,
2858               filter_test_setup,
2859               filled_hide_child_levels,
2860               filter_test_teardown);
2861
2862   g_test_add ("/FilterModel/filled/hide-root-level/vroot",
2863               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2864               filter_test_setup,
2865               filled_vroot_hide_root_level,
2866               filter_test_teardown);
2867   g_test_add ("/FilterModel/filled/hide-child-levels/vroot",
2868               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2869               filter_test_setup,
2870               filled_vroot_hide_child_levels,
2871               filter_test_teardown);
2872
2873
2874   g_test_add ("/FilterModel/empty/show-nodes",
2875               FilterTest, NULL,
2876               filter_test_setup_empty,
2877               empty_show_nodes,
2878               filter_test_teardown);
2879   g_test_add ("/FilterModel/empty/show-multiple-nodes",
2880               FilterTest, NULL,
2881               filter_test_setup_empty,
2882               empty_show_multiple_nodes,
2883               filter_test_teardown);
2884
2885   g_test_add ("/FilterModel/empty/show-nodes/vroot",
2886               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2887               filter_test_setup_empty,
2888               empty_vroot_show_nodes,
2889               filter_test_teardown);
2890   g_test_add ("/FilterModel/empty/show-multiple-nodes/vroot",
2891               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2892               filter_test_setup_empty,
2893               empty_vroot_show_multiple_nodes,
2894               filter_test_teardown);
2895
2896
2897   g_test_add ("/FilterModel/unfiltered/hide-single",
2898               FilterTest, NULL,
2899               filter_test_setup_unfiltered,
2900               unfiltered_hide_single,
2901               filter_test_teardown);
2902   g_test_add ("/FilterModel/unfiltered/hide-single-child",
2903               FilterTest, NULL,
2904               filter_test_setup_unfiltered,
2905               unfiltered_hide_single_child,
2906               filter_test_teardown);
2907   g_test_add ("/FilterModel/unfiltered/hide-single-multi-level",
2908               FilterTest, NULL,
2909               filter_test_setup_unfiltered,
2910               unfiltered_hide_single_multi_level,
2911               filter_test_teardown);
2912
2913   g_test_add ("/FilterModel/unfiltered/hide-single/vroot",
2914               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2915               filter_test_setup_unfiltered,
2916               unfiltered_vroot_hide_single,
2917               filter_test_teardown);
2918   g_test_add ("/FilterModel/unfiltered/hide-single-child/vroot",
2919               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2920               filter_test_setup_unfiltered,
2921               unfiltered_vroot_hide_single_child,
2922               filter_test_teardown);
2923   g_test_add ("/FilterModel/unfiltered/hide-single-multi-level/vroot",
2924               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2925               filter_test_setup_unfiltered,
2926               unfiltered_vroot_hide_single_multi_level,
2927               filter_test_teardown);
2928
2929
2930
2931   g_test_add ("/FilterModel/unfiltered/show-single",
2932               FilterTest, NULL,
2933               filter_test_setup_empty_unfiltered,
2934               unfiltered_show_single,
2935               filter_test_teardown);
2936   g_test_add ("/FilterModel/unfiltered/show-single-child",
2937               FilterTest, NULL,
2938               filter_test_setup_empty_unfiltered,
2939               unfiltered_show_single_child,
2940               filter_test_teardown);
2941   g_test_add ("/FilterModel/unfiltered/show-single-multi-level",
2942               FilterTest, NULL,
2943               filter_test_setup_empty_unfiltered,
2944               unfiltered_show_single_multi_level,
2945               filter_test_teardown);
2946
2947   g_test_add ("/FilterModel/unfiltered/show-single/vroot",
2948               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2949               filter_test_setup_empty_unfiltered,
2950               unfiltered_vroot_show_single,
2951               filter_test_teardown);
2952   g_test_add ("/FilterModel/unfiltered/show-single-child/vroot",
2953               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2954               filter_test_setup_empty_unfiltered,
2955               unfiltered_vroot_show_single_child,
2956               filter_test_teardown);
2957   g_test_add ("/FilterModel/unfiltered/show-single-multi-level/vroot",
2958               FilterTest, gtk_tree_path_new_from_indices (2, -1),
2959               filter_test_setup_empty_unfiltered,
2960               unfiltered_vroot_show_single_multi_level,
2961               filter_test_teardown);
2962
2963
2964   g_test_add_func ("/FilterModel/specific/path-dependent-filter",
2965                    specific_path_dependent_filter);
2966   g_test_add_func ("/FilterModel/specific/append-after-collapse",
2967                    specific_append_after_collapse);
2968   g_test_add_func ("/FilterModel/specific/sort-filter-remove-node",
2969                    specific_sort_filter_remove_node);
2970   g_test_add_func ("/FilterModel/specific/sort-filter-remove-root",
2971                    specific_sort_filter_remove_root);
2972   g_test_add_func ("/FilterModel/specific/root-mixed-visibility",
2973                    specific_root_mixed_visibility);
2974   g_test_add_func ("/FilterModel/specific/has-child-filter",
2975                    specific_has_child_filter);
2976   g_test_add_func ("/FilterModel/specific/root-has-child-filter",
2977                    specific_root_has_child_filter);
2978   g_test_add_func ("/FilterModel/specific/filter-add-child",
2979                    specific_filter_add_child);
2980   g_test_add_func ("/FilterModel/specific/list-store-clear",
2981                    specific_list_store_clear);
2982
2983   g_test_add_func ("/FilterModel/specific/bug-300089",
2984                    specific_bug_300089);
2985   g_test_add_func ("/FilterModel/specific/bug-301558",
2986                    specific_bug_301558);
2987   g_test_add_func ("/FilterModel/specific/bug-311955",
2988                    specific_bug_311955);
2989   g_test_add_func ("/FilterModel/specific/bug-346800",
2990                    specific_bug_346800);
2991   g_test_add_func ("/FilterModel/specific/bug-364946",
2992                    specific_bug_364946);
2993   g_test_add_func ("/FilterModel/specific/bug-464173",
2994                    specific_bug_464173);
2995   g_test_add_func ("/FilterModel/specific/bug-540201",
2996                    specific_bug_540201);
2997   g_test_add_func ("/FilterModel/specific/bug-549287",
2998                    specific_bug_549287);
2999
3000   return g_test_run ();
3001 }