]> Pileus Git - ~andy/gtk/blob - gtk/tests/filtermodel.c
Add more "has child" unit tests
[~andy/gtk] / gtk / tests / filtermodel.c
1 /* Extensive GtkTreeModelFilter tests.
2  * Copyright (C) 2009,2011  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   gulong 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       gchar *path_str;
186
187       path_str = gtk_tree_path_to_string (path);
188       g_error ("Signal queue empty, got signal %s path %s\n",
189                signal_name_to_string (signal), path_str);
190       g_free (path_str);
191
192       g_assert_not_reached ();
193     }
194
195   if (m->client != model)
196     {
197       g_error ("Model mismatch; expected %p, got %p\n",
198                m->client, model);
199       g_assert_not_reached ();
200     }
201
202   s = g_queue_peek_tail (m->queue);
203
204 #if 0
205   /* For debugging: output signals that are coming in.  Leaks memory. */
206   g_print ("signal=%s path=%s\n", signal_name_to_string (signal),
207            gtk_tree_path_to_string (path));
208 #endif
209
210   if (s->signal != signal
211       || gtk_tree_path_compare (s->path, path) != 0)
212     {
213       gchar *path_str, *s_path_str;
214
215       s_path_str = gtk_tree_path_to_string (s->path);
216       path_str = gtk_tree_path_to_string (path);
217
218       g_error ("Signals don't match; expected signal %s path %s, got signal %s path %s\n",
219                signal_name_to_string (s->signal), s_path_str,
220                signal_name_to_string (signal), path_str);
221
222       g_free (s_path_str);
223       g_free (path_str);
224
225       g_assert_not_reached ();
226     }
227
228   s = g_queue_pop_tail (m->queue);
229
230   signal_free (s);
231 }
232
233 static void
234 signal_monitor_row_inserted (GtkTreeModel *model,
235                              GtkTreePath  *path,
236                              GtkTreeIter  *iter,
237                              gpointer      data)
238 {
239   signal_monitor_generic_handler (data, ROW_INSERTED,
240                                   model, path);
241 }
242
243 static void
244 signal_monitor_row_deleted (GtkTreeModel *model,
245                             GtkTreePath  *path,
246                             gpointer      data)
247 {
248   signal_monitor_generic_handler (data, ROW_DELETED,
249                                   model, path);
250 }
251
252 static void
253 signal_monitor_row_changed (GtkTreeModel *model,
254                             GtkTreePath  *path,
255                             GtkTreeIter  *iter,
256                             gpointer      data)
257 {
258   signal_monitor_generic_handler (data, ROW_CHANGED,
259                                   model, path);
260 }
261
262 static void
263 signal_monitor_row_has_child_toggled (GtkTreeModel *model,
264                                       GtkTreePath  *path,
265                                       GtkTreeIter  *iter,
266                                       gpointer      data)
267 {
268   signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED,
269                                   model, path);
270 }
271
272 static void
273 signal_monitor_rows_reordered (GtkTreeModel *model,
274                                GtkTreePath  *path,
275                                GtkTreeIter  *iter,
276                                gint         *new_order,
277                                gpointer      data)
278 {
279   signal_monitor_generic_handler (data, ROWS_REORDERED,
280                                   model, path);
281 }
282
283 static SignalMonitor *
284 signal_monitor_new (GtkTreeModel *client)
285 {
286   SignalMonitor *m;
287
288   m = g_new0 (SignalMonitor, 1);
289   m->client = g_object_ref (client);
290   m->queue = g_queue_new ();
291
292   m->signal_ids[ROW_INSERTED] = g_signal_connect (client,
293                                                   "row-inserted",
294                                                   G_CALLBACK (signal_monitor_row_inserted),
295                                                   m);
296   m->signal_ids[ROW_DELETED] = g_signal_connect (client,
297                                                  "row-deleted",
298                                                  G_CALLBACK (signal_monitor_row_deleted),
299                                                  m);
300   m->signal_ids[ROW_CHANGED] = g_signal_connect (client,
301                                                  "row-changed",
302                                                  G_CALLBACK (signal_monitor_row_changed),
303                                                  m);
304   m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client,
305                                                            "row-has-child-toggled",
306                                                            G_CALLBACK (signal_monitor_row_has_child_toggled),
307                                                            m);
308   m->signal_ids[ROWS_REORDERED] = g_signal_connect (client,
309                                                     "rows-reordered",
310                                                     G_CALLBACK (signal_monitor_rows_reordered),
311                                                     m);
312
313   return m;
314 }
315
316 static void
317 signal_monitor_free (SignalMonitor *m)
318 {
319   int i;
320
321   for (i = 0; i < LAST_SIGNAL; i++)
322     g_signal_handler_disconnect (m->client, m->signal_ids[i]);
323
324   g_object_unref (m->client);
325
326   if (m->queue)
327     g_queue_free (m->queue);
328
329   g_free (m);
330 }
331
332 static void
333 signal_monitor_assert_is_empty (SignalMonitor *m)
334 {
335   g_assert (g_queue_is_empty (m->queue));
336 }
337
338 static void
339 signal_monitor_append_signal_path (SignalMonitor *m,
340                                    SignalName     signal,
341                                    GtkTreePath   *path)
342 {
343   Signal *s;
344
345   s = signal_new (signal, path);
346   g_queue_push_head (m->queue, s);
347 }
348
349 static void
350 signal_monitor_append_signal (SignalMonitor *m,
351                               SignalName     signal,
352                               const gchar   *path_string)
353 {
354   Signal *s;
355   GtkTreePath *path;
356
357   path = gtk_tree_path_new_from_string (path_string);
358
359   s = signal_new (signal, path);
360   g_queue_push_head (m->queue, s);
361
362   gtk_tree_path_free (path);
363 }
364
365 /*
366  * Fixture
367  */
368
369 typedef struct
370 {
371   GtkWidget *tree_view;
372
373   GtkTreeStore *store;
374   GtkTreeModelFilter *filter;
375
376   SignalMonitor *monitor;
377
378   guint block_signals : 1;
379 } FilterTest;
380
381
382 static void
383 filter_test_store_signal (FilterTest *fixture)
384 {
385   if (fixture->block_signals)
386     g_signal_stop_emission_by_name (fixture->store, "row-changed");
387 }
388
389
390 static void
391 filter_test_setup_generic (FilterTest    *fixture,
392                            gconstpointer  test_data,
393                            int            depth,
394                            gboolean       empty,
395                            gboolean       unfiltered)
396 {
397   const GtkTreePath *vroot = test_data;
398   GtkTreeModel *filter;
399
400   fixture->store = create_tree_store (depth, !empty);
401
402   g_signal_connect_swapped (fixture->store, "row-changed",
403                             G_CALLBACK (filter_test_store_signal), fixture);
404
405   /* Please forgive me for casting const away. */
406   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture->store),
407                                       (GtkTreePath *)vroot);
408   fixture->filter = GTK_TREE_MODEL_FILTER (filter);
409
410   if (!unfiltered)
411     gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
412
413   /* We need a tree view that's listening to get ref counting from that
414    * side.
415    */
416   fixture->tree_view = gtk_tree_view_new_with_model (filter);
417
418   fixture->monitor = signal_monitor_new (filter);
419 }
420
421 static void
422 filter_test_setup_expand_root (FilterTest *fixture)
423 {
424   int i;
425   GtkTreePath *path;
426
427   path = gtk_tree_path_new_from_indices (0, -1);
428
429   for (i = 0; i < LEVEL_LENGTH; i++)
430     {
431       gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view),
432                                 path, FALSE);
433       gtk_tree_path_next (path);
434     }
435   gtk_tree_path_free (path);
436 }
437
438 static void
439 filter_test_setup (FilterTest    *fixture,
440                    gconstpointer  test_data)
441 {
442   filter_test_setup_generic (fixture, test_data, 3, FALSE, FALSE);
443 }
444
445 static void
446 filter_test_setup_empty (FilterTest    *fixture,
447                          gconstpointer  test_data)
448 {
449   filter_test_setup_generic (fixture, test_data, 3, TRUE, FALSE);
450 }
451
452 static void
453 filter_test_setup_unfiltered (FilterTest    *fixture,
454                               gconstpointer  test_data)
455 {
456   filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE);
457 }
458
459 static void
460 filter_test_setup_unfiltered_root_expanded (FilterTest    *fixture,
461                                             gconstpointer  test_data)
462 {
463   filter_test_setup_unfiltered (fixture, test_data);
464   filter_test_setup_expand_root (fixture);
465 }
466
467 static void
468 filter_test_setup_empty_unfiltered (FilterTest    *fixture,
469                                     gconstpointer  test_data)
470 {
471   filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE);
472 }
473
474 static void
475 filter_test_setup_empty_unfiltered_root_expanded (FilterTest    *fixture,
476                                                   gconstpointer  test_data)
477 {
478   filter_test_setup_empty_unfiltered (fixture, test_data);
479   filter_test_setup_expand_root (fixture);
480 }
481
482 static GtkTreePath *
483 strip_virtual_root (GtkTreePath *path,
484                     GtkTreePath *root_path)
485 {
486   GtkTreePath *real_path;
487
488   if (root_path)
489     {
490       int j;
491       int depth = gtk_tree_path_get_depth (path);
492       int root_depth = gtk_tree_path_get_depth (root_path);
493
494       real_path = gtk_tree_path_new ();
495
496       for (j = 0; j < depth - root_depth; j++)
497         gtk_tree_path_append_index (real_path,
498                                     gtk_tree_path_get_indices (path)[root_depth + j]);
499     }
500   else
501     real_path = gtk_tree_path_copy (path);
502
503   return real_path;
504 }
505
506 static int
507 count_visible (FilterTest  *fixture,
508                GtkTreePath *store_path)
509 {
510   int i;
511   int n_visible = 0;
512   GtkTreeIter iter;
513
514   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
515                            &iter, store_path);
516
517   for (i = 0; i < LEVEL_LENGTH; i++)
518     {
519       gboolean visible;
520
521       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter,
522                           1, &visible,
523                           -1);
524
525       if (visible)
526         n_visible++;
527     }
528
529   return n_visible;
530 }
531
532 static void
533 filter_test_append_refilter_signals_recurse (FilterTest  *fixture,
534                                              GtkTreePath *store_path,
535                                              GtkTreePath *filter_path,
536                                              int          depth,
537                                              GtkTreePath *root_path)
538 {
539   int i;
540   int rows_deleted = 0;
541   GtkTreeIter iter;
542
543   gtk_tree_path_down (store_path);
544   gtk_tree_path_down (filter_path);
545
546   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
547                            &iter, store_path);
548
549   for (i = 0; i < LEVEL_LENGTH; i++)
550     {
551       gboolean visible;
552       GtkTreePath *real_path;
553
554       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter,
555                           1, &visible,
556                           -1);
557
558       if (root_path &&
559           (!gtk_tree_path_is_descendant (store_path, root_path)
560            || !gtk_tree_path_compare (store_path, root_path)))
561         {
562           if (!gtk_tree_path_compare (store_path, root_path))
563             {
564               if (depth > 1
565                   && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
566                                                     &iter))
567                 {
568                   GtkTreePath *store_copy;
569                   GtkTreePath *filter_copy;
570
571                   store_copy = gtk_tree_path_copy (store_path);
572                   filter_copy = gtk_tree_path_copy (filter_path);
573                   filter_test_append_refilter_signals_recurse (fixture,
574                                                                store_copy,
575                                                                filter_copy,
576                                                                depth - 1,
577                                                                root_path);
578                   gtk_tree_path_free (store_copy);
579                   gtk_tree_path_free (filter_copy);
580                 }
581             }
582
583           gtk_tree_path_next (store_path);
584           gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
585
586           if (visible)
587             gtk_tree_path_next (filter_path);
588
589           continue;
590         }
591
592       real_path = strip_virtual_root (filter_path, root_path);
593
594       if (visible)
595         {
596           /* This row will be inserted */
597           signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED,
598                                              real_path);
599
600           if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
601                                              &iter))
602             {
603               signal_monitor_append_signal_path (fixture->monitor,
604                                                  ROW_HAS_CHILD_TOGGLED,
605                                                  real_path);
606
607               if (depth > 1)
608                 {
609                   GtkTreePath *store_copy;
610                   GtkTreePath *filter_copy;
611
612                   store_copy = gtk_tree_path_copy (store_path);
613                   filter_copy = gtk_tree_path_copy (filter_path);
614                   filter_test_append_refilter_signals_recurse (fixture,
615                                                                store_copy,
616                                                                filter_copy,
617                                                                depth - 1,
618                                                                root_path);
619                   gtk_tree_path_free (store_copy);
620                   gtk_tree_path_free (filter_copy);
621                 }
622               else if (depth == 1)
623                 {
624                   GtkTreePath *tmp_path;
625
626                   /* If all child rows are invisible, then the last row to
627                    * become invisible will emit row-has-child-toggled on the
628                    * parent.
629                    */
630
631                   tmp_path = gtk_tree_path_copy (store_path);
632                   gtk_tree_path_append_index (tmp_path, 0);
633
634                   if (count_visible (fixture, tmp_path) == 0)
635                     signal_monitor_append_signal_path (fixture->monitor,
636                                                        ROW_HAS_CHILD_TOGGLED,
637                                                        real_path);
638
639                   gtk_tree_path_free (tmp_path);
640                 }
641             }
642
643           gtk_tree_path_next (filter_path);
644         }
645       else
646         {
647           /* This row will be deleted */
648           rows_deleted++;
649           signal_monitor_append_signal_path (fixture->monitor, ROW_DELETED,
650                                              real_path);
651         }
652
653       gtk_tree_path_free (real_path);
654
655       gtk_tree_path_next (store_path);
656       gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
657     }
658
659   if (rows_deleted == LEVEL_LENGTH
660       && gtk_tree_path_get_depth (filter_path) > 1)
661     {
662       GtkTreePath *real_path;
663
664       gtk_tree_path_up (store_path);
665       gtk_tree_path_up (filter_path);
666
667       /* A row-has-child-toggled will be emitted on the parent */
668       if (!root_path
669           || (root_path
670               && gtk_tree_path_is_descendant (store_path, root_path)
671               && gtk_tree_path_compare (store_path, root_path)))
672         {
673           real_path = strip_virtual_root (filter_path, root_path);
674           signal_monitor_append_signal_path (fixture->monitor,
675                                              ROW_HAS_CHILD_TOGGLED,
676                                              real_path);
677
678           gtk_tree_path_free (real_path);
679         }
680     }
681 }
682
683 static void
684 filter_test_append_refilter_signals (FilterTest *fixture,
685                                      int         depth)
686 {
687   /* A special function that walks the tree store like the
688    * model validation functions below.
689    */
690   GtkTreePath *path;
691   GtkTreePath *filter_path;
692
693   path = gtk_tree_path_new ();
694   filter_path = gtk_tree_path_new ();
695   filter_test_append_refilter_signals_recurse (fixture,
696                                                path,
697                                                filter_path,
698                                                depth,
699                                                NULL);
700   gtk_tree_path_free (path);
701   gtk_tree_path_free (filter_path);
702 }
703
704 static void
705 filter_test_append_refilter_signals_with_vroot (FilterTest  *fixture,
706                                                 int          depth,
707                                                 GtkTreePath *root_path)
708 {
709   /* A special function that walks the tree store like the
710    * model validation functions below.
711    */
712   GtkTreePath *path;
713   GtkTreePath *filter_path;
714
715   path = gtk_tree_path_new ();
716   filter_path = gtk_tree_path_new ();
717   filter_test_append_refilter_signals_recurse (fixture,
718                                                path,
719                                                filter_path,
720                                                depth,
721                                                root_path);
722   gtk_tree_path_free (path);
723   gtk_tree_path_free (filter_path);
724 }
725
726 static void
727 filter_test_enable_filter (FilterTest *fixture)
728 {
729   gtk_tree_model_filter_set_visible_column (fixture->filter, 1);
730   gtk_tree_model_filter_refilter (fixture->filter);
731 }
732
733 static void
734 filter_test_block_signals (FilterTest *fixture)
735 {
736   fixture->block_signals = TRUE;
737 }
738
739 static void
740 filter_test_unblock_signals (FilterTest *fixture)
741 {
742   fixture->block_signals = FALSE;
743 }
744
745 static void
746 filter_test_teardown (FilterTest    *fixture,
747                       gconstpointer  test_data)
748 {
749   signal_monitor_free (fixture->monitor);
750
751   gtk_widget_destroy (fixture->tree_view);
752
753   g_object_unref (fixture->filter);
754   g_object_unref (fixture->store);
755 }
756
757 /*
758  * Model structure validation
759  */
760
761 static void
762 check_filter_model_recurse (FilterTest  *fixture,
763                             GtkTreePath *store_parent_path,
764                             GtkTreePath *filter_parent_path)
765 {
766   int i;
767   GtkTreeIter store_iter;
768   GtkTreeIter filter_iter;
769   gboolean store_has_next, filter_has_next;
770
771   gtk_tree_path_down (store_parent_path);
772   gtk_tree_path_down (filter_parent_path);
773
774   store_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
775                                             &store_iter, store_parent_path);
776   filter_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->filter),
777                                              &filter_iter, filter_parent_path);
778
779   for (i = 0; i < LEVEL_LENGTH; i++)
780     {
781       gboolean visible;
782
783       g_return_if_fail (store_has_next == TRUE);
784
785       gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
786                           &store_iter,
787                           1, &visible,
788                           -1);
789
790       if (visible)
791         {
792           GtkTreePath *tmp;
793           gchar *filter_str, *store_str;
794
795           g_return_if_fail (filter_has_next == TRUE);
796
797           /* Verify path */
798           tmp = gtk_tree_model_get_path (GTK_TREE_MODEL (fixture->filter),
799                                          &filter_iter);
800           g_return_if_fail (gtk_tree_path_compare (tmp, filter_parent_path) == 0);
801
802           /* Verify model content */
803           gtk_tree_model_get (GTK_TREE_MODEL (fixture->store),
804                               &store_iter,
805                               0, &store_str,
806                               -1);
807           gtk_tree_model_get (GTK_TREE_MODEL (fixture->filter),
808                               &filter_iter,
809                               0, &filter_str,
810                               -1);
811
812           g_return_if_fail (g_strcmp0 (store_str, filter_str) == 0);
813
814           g_free (store_str);
815           g_free (filter_str);
816
817           if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->filter),
818                                              &filter_iter))
819             {
820               g_return_if_fail (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), &store_iter));
821
822               check_filter_model_recurse (fixture,
823                                           gtk_tree_path_copy (store_parent_path),
824                                           tmp);
825             }
826           else
827             /* Only when we do not recurse we need to free tmp */
828             gtk_tree_path_free (tmp);
829
830           gtk_tree_path_next (filter_parent_path);
831           filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter);
832         }
833
834       gtk_tree_path_next (store_parent_path);
835       store_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &store_iter);
836     }
837
838   /* Both models should have no more content! */
839   g_return_if_fail (store_has_next == FALSE);
840   g_return_if_fail (filter_has_next == FALSE);
841
842   gtk_tree_path_free (store_parent_path);
843   gtk_tree_path_free (filter_parent_path);
844 }
845
846 static void
847 check_filter_model (FilterTest *fixture)
848 {
849   GtkTreePath *path;
850
851   if (fixture->monitor)
852     signal_monitor_assert_is_empty (fixture->monitor);
853
854   path = gtk_tree_path_new ();
855
856   check_filter_model_recurse (fixture, path, gtk_tree_path_copy (path));
857 }
858
859 static void
860 check_filter_model_with_root (FilterTest  *fixture,
861                               GtkTreePath *path)
862 {
863   if (fixture->monitor)
864     signal_monitor_assert_is_empty (fixture->monitor);
865
866   check_filter_model_recurse (fixture,
867                               gtk_tree_path_copy (path),
868                               gtk_tree_path_new ());
869 }
870
871 /* Helpers */
872
873 static void
874 check_level_length (GtkTreeModelFilter *filter,
875                     const gchar        *level,
876                     const int           expected_length)
877 {
878   if (!level)
879     {
880       int model_length;
881
882       model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL);
883       g_assert_cmpint (model_length, ==, expected_length);
884     }
885   else
886     {
887       int model_length;
888       gboolean retrieved_iter = FALSE;
889       GtkTreeIter iter;
890
891       retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter),
892                                                             &iter, level);
893       g_return_if_fail (retrieved_iter);
894       model_length = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter);
895       g_assert_cmpint (model_length, ==, expected_length);
896     }
897 }
898
899 static void
900 set_path_visibility (FilterTest  *fixture,
901                      const gchar *path,
902                      gboolean     visible)
903 {
904   GtkTreeIter store_iter;
905
906   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
907                                        &store_iter, path);
908   gtk_tree_store_set (fixture->store, &store_iter,
909                       1, visible,
910                       -1);
911 }
912
913 #if 0
914 static void
915 insert_path_with_visibility (FilterTest  *fixture,
916                              const gchar *path_string,
917                              gboolean     visible)
918 {
919   int position;
920   GtkTreePath *path;
921   GtkTreeIter parent, iter;
922
923   path = gtk_tree_path_new_from_string (path_string);
924   position = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path)];
925   gtk_tree_path_up (path);
926
927   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &parent, path))
928     {
929       gtk_tree_store_insert (fixture->store, &iter, &parent, position);
930       create_tree_store_set_values (fixture->store, &iter, visible);
931     }
932   gtk_tree_path_free (path);
933 }
934 #endif
935
936 /*
937  * The actual tests.
938  */
939
940 static void
941 verify_test_suite (FilterTest    *fixture,
942                    gconstpointer  user_data)
943 {
944   check_filter_model (fixture);
945 }
946
947 static void
948 verify_test_suite_vroot (FilterTest    *fixture,
949                          gconstpointer  user_data)
950 {
951   check_filter_model_with_root (fixture, (GtkTreePath *)user_data);
952 }
953
954
955 static void
956 filled_hide_root_level (FilterTest    *fixture,
957                         gconstpointer  user_data)
958 {
959   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
960   set_path_visibility (fixture, "2", FALSE);
961   check_filter_model (fixture);
962   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
963
964   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
965   set_path_visibility (fixture, "0", FALSE);
966   check_filter_model (fixture);
967   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
968
969   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
970   set_path_visibility (fixture, "4", FALSE);
971   check_filter_model (fixture);
972   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
973
974
975   /* Hide remaining */
976   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
977   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
978
979   set_path_visibility (fixture, "1", FALSE);
980   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
981
982   set_path_visibility (fixture, "3", FALSE);
983   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 5);
984
985   check_filter_model (fixture);
986
987   /* Show some */
988   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
989   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
990   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
991   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
992
993   set_path_visibility (fixture, "1", TRUE);
994   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
995
996   set_path_visibility (fixture, "3", TRUE);
997   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3);
998
999   check_filter_model (fixture);
1000 }
1001
1002 static void
1003 filled_hide_child_levels (FilterTest    *fixture,
1004                           gconstpointer  user_data)
1005 {
1006   set_path_visibility (fixture, "0:2", FALSE);
1007   check_filter_model (fixture);
1008   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1009   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1010
1011   set_path_visibility (fixture, "0:4", FALSE);
1012   check_filter_model (fixture);
1013   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1014   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1015
1016   set_path_visibility (fixture, "0:4:3", FALSE);
1017   check_filter_model (fixture);
1018   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1019   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1020
1021   set_path_visibility (fixture, "0:4:0", FALSE);
1022   set_path_visibility (fixture, "0:4:1", FALSE);
1023   set_path_visibility (fixture, "0:4:2", FALSE);
1024   set_path_visibility (fixture, "0:4:4", FALSE);
1025   check_filter_model (fixture);
1026   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1027   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1028
1029   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1030   set_path_visibility (fixture, "0:4", TRUE);
1031   check_filter_model (fixture);
1032   check_level_length (fixture->filter, "0:3", 0);
1033
1034   set_path_visibility (fixture, "0:2", TRUE);
1035   check_filter_model (fixture);
1036   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1037   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1038   check_level_length (fixture->filter, "0:4", 0);
1039
1040   /* Once 0:4:0 got inserted, 0:4 became a parent.  Because 0:4 is
1041    * not visible, not signals are emitted.
1042    */
1043   set_path_visibility (fixture, "0:4:2", TRUE);
1044   set_path_visibility (fixture, "0:4:4", TRUE);
1045   signal_monitor_assert_is_empty (fixture->monitor);
1046   check_level_length (fixture->filter, "0:4", 2);
1047 }
1048
1049 static void
1050 filled_hide_child_levels_root_expanded (FilterTest    *fixture,
1051                                         gconstpointer  user_data)
1052 {
1053   GtkTreePath *path;
1054
1055   path = gtk_tree_path_new_from_indices (0, -1);
1056   gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
1057   gtk_tree_path_free (path);
1058
1059   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
1060   set_path_visibility (fixture, "0:2", FALSE);
1061   check_filter_model (fixture);
1062   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1063   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1064
1065   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
1066   set_path_visibility (fixture, "0:4", FALSE);
1067   check_filter_model (fixture);
1068   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1069   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1070
1071   set_path_visibility (fixture, "0:4:3", FALSE);
1072   check_filter_model (fixture);
1073   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1074   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1075
1076   set_path_visibility (fixture, "0:4:0", FALSE);
1077   set_path_visibility (fixture, "0:4:1", FALSE);
1078   set_path_visibility (fixture, "0:4:2", FALSE);
1079   set_path_visibility (fixture, "0:4:4", FALSE);
1080   check_filter_model (fixture);
1081   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1082   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1083
1084   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1085   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
1086   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
1087   set_path_visibility (fixture, "0:4", TRUE);
1088   check_filter_model (fixture);
1089   check_level_length (fixture->filter, "0:3", 0);
1090
1091   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
1092   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
1093   set_path_visibility (fixture, "0:2", TRUE);
1094   check_filter_model (fixture);
1095   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1096   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1097   check_level_length (fixture->filter, "0:4", 0);
1098
1099   /* has-child-toggled for 0:4 is required.  */
1100   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
1101   set_path_visibility (fixture, "0:4:2", TRUE);
1102   set_path_visibility (fixture, "0:4:4", TRUE);
1103   signal_monitor_assert_is_empty (fixture->monitor);
1104   check_level_length (fixture->filter, "0:4", 2);
1105 }
1106
1107
1108 static void
1109 filled_vroot_hide_root_level (FilterTest    *fixture,
1110                               gconstpointer  user_data)
1111 {
1112   GtkTreePath *path = (GtkTreePath *)user_data;
1113
1114   /* These changes do not affect the filter's root level */
1115   set_path_visibility (fixture, "0", FALSE);
1116   check_filter_model_with_root (fixture, path);
1117   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1118   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1119
1120   set_path_visibility (fixture, "4", FALSE);
1121   check_filter_model_with_root (fixture, path);
1122   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1123   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1124
1125   /* Even though we set the virtual root parent node to FALSE,
1126    * the virtual root contents remain.
1127    */
1128   set_path_visibility (fixture, "2", FALSE);
1129   check_filter_model_with_root (fixture, path);
1130   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1131   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1132
1133   /* No change */
1134   set_path_visibility (fixture, "1", FALSE);
1135   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1136   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1137
1138   set_path_visibility (fixture, "3", FALSE);
1139   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1140   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1141
1142   check_filter_model_with_root (fixture, path);
1143
1144   /* Show some */
1145   set_path_visibility (fixture, "2", TRUE);
1146   check_filter_model_with_root (fixture, path);
1147   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1148   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1149
1150   set_path_visibility (fixture, "1", TRUE);
1151   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1152   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1153
1154   set_path_visibility (fixture, "3", TRUE);
1155   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1156   check_level_length (fixture->filter, "0", LEVEL_LENGTH);
1157
1158   check_filter_model_with_root (fixture, path);
1159
1160   /* Now test changes in the virtual root level */
1161   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2");
1162   set_path_visibility (fixture, "2:2", FALSE);
1163   check_filter_model_with_root (fixture, path);
1164   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1165
1166   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "3");
1167   set_path_visibility (fixture, "2:4", FALSE);
1168   check_filter_model_with_root (fixture, path);
1169   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1170
1171   set_path_visibility (fixture, "1:4", FALSE);
1172   check_filter_model_with_root (fixture, path);
1173   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1174
1175   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "3");
1176   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "3");
1177   set_path_visibility (fixture, "2:4", TRUE);
1178   check_filter_model_with_root (fixture, path);
1179   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1180
1181   set_path_visibility (fixture, "2", FALSE);
1182   check_filter_model_with_root (fixture, path);
1183   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1184
1185   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1186   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1187   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1188   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1189   set_path_visibility (fixture, "2:0", FALSE);
1190   set_path_visibility (fixture, "2:1", FALSE);
1191   set_path_visibility (fixture, "2:2", FALSE);
1192   set_path_visibility (fixture, "2:3", FALSE);
1193   set_path_visibility (fixture, "2:4", FALSE);
1194   check_filter_model_with_root (fixture, path);
1195   check_level_length (fixture->filter, NULL, 0);
1196
1197   set_path_visibility (fixture, "2", TRUE);
1198   check_filter_model_with_root (fixture, path);
1199   check_level_length (fixture->filter, NULL, 0);
1200
1201   set_path_visibility (fixture, "1:4", FALSE);
1202   check_filter_model_with_root (fixture, path);
1203   check_level_length (fixture->filter, NULL, 0);
1204
1205   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1206   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1207   set_path_visibility (fixture, "2:4", TRUE);
1208   check_filter_model_with_root (fixture, path);
1209   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4);
1210
1211   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1212   set_path_visibility (fixture, "2:4", FALSE);
1213   check_filter_model_with_root (fixture, path);
1214   check_level_length (fixture->filter, NULL, 0);
1215
1216   set_path_visibility (fixture, "2", FALSE);
1217   check_filter_model_with_root (fixture, path);
1218   check_level_length (fixture->filter, NULL, 0);
1219
1220   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1221   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1222   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1223   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1224   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2");
1225   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1226   set_path_visibility (fixture, "2:0", TRUE);
1227   set_path_visibility (fixture, "2:1", TRUE);
1228   set_path_visibility (fixture, "2:2", TRUE);
1229   check_filter_model_with_root (fixture, path);
1230   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1231
1232   set_path_visibility (fixture, "2", TRUE);
1233   check_filter_model_with_root (fixture, path);
1234   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2);
1235 }
1236
1237 static void
1238 filled_vroot_hide_child_levels (FilterTest    *fixture,
1239                                 gconstpointer  user_data)
1240 {
1241   GtkTreePath *path = (GtkTreePath *)user_data;
1242
1243   set_path_visibility (fixture, "2:0:2", FALSE);
1244   check_filter_model_with_root (fixture, path);
1245   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1246   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1247
1248   set_path_visibility (fixture, "2:0:4", FALSE);
1249   check_filter_model_with_root (fixture, path);
1250   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1251   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1252
1253   set_path_visibility (fixture, "2:0:4:3", FALSE);
1254   check_filter_model_with_root (fixture, path);
1255   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1256   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1257
1258   set_path_visibility (fixture, "2:0:4:0", FALSE);
1259   set_path_visibility (fixture, "2:0:4:1", FALSE);
1260   set_path_visibility (fixture, "2:0:4:2", FALSE);
1261   set_path_visibility (fixture, "2:0:4:4", FALSE);
1262   check_filter_model_with_root (fixture, path);
1263   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1264   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1265
1266   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1267   set_path_visibility (fixture, "2:0:4", TRUE);
1268   check_filter_model_with_root (fixture, path);
1269   check_level_length (fixture->filter, "0:3", 0);
1270
1271   set_path_visibility (fixture, "2:0:2", TRUE);
1272   check_filter_model_with_root (fixture, path);
1273   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1274   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1275   check_level_length (fixture->filter, "0:4", 0);
1276
1277   /* Once 0:4:0 got inserted, 0:4 became a parent */
1278   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
1279   set_path_visibility (fixture, "2:0:4:2", TRUE);
1280   set_path_visibility (fixture, "2:0:4:4", TRUE);
1281   check_level_length (fixture->filter, "0:4", 2);
1282 }
1283
1284 static void
1285 filled_vroot_hide_child_levels_root_expanded (FilterTest    *fixture,
1286                                               gconstpointer  user_data)
1287 {
1288   GtkTreePath *path = (GtkTreePath *)user_data;
1289   GtkTreePath *tmp_path;
1290
1291   tmp_path = gtk_tree_path_new_from_indices (0, -1);
1292   gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), tmp_path, FALSE);
1293   gtk_tree_path_free (tmp_path);
1294
1295   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
1296   set_path_visibility (fixture, "2:0:2", FALSE);
1297   check_filter_model_with_root (fixture, path);
1298   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1299   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
1300
1301   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
1302   set_path_visibility (fixture, "2:0:4", FALSE);
1303   check_filter_model_with_root (fixture, path);
1304   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1305   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1306
1307   set_path_visibility (fixture, "2:0:4:3", FALSE);
1308   check_filter_model_with_root (fixture, path);
1309   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1310   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1311
1312   set_path_visibility (fixture, "2:0:4:0", FALSE);
1313   set_path_visibility (fixture, "2:0:4:1", FALSE);
1314   set_path_visibility (fixture, "2:0:4:2", FALSE);
1315   set_path_visibility (fixture, "2:0:4:4", FALSE);
1316   check_filter_model_with_root (fixture, path);
1317   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1318   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
1319
1320   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
1321   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
1322   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
1323   set_path_visibility (fixture, "2:0:4", TRUE);
1324   check_filter_model_with_root (fixture, path);
1325   check_level_length (fixture->filter, "0:3", 0);
1326
1327   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
1328   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
1329   set_path_visibility (fixture, "2:0:2", TRUE);
1330   check_filter_model_with_root (fixture, path);
1331   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
1332   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
1333   check_level_length (fixture->filter, "0:4", 0);
1334
1335   /* Once 0:4:0 got inserted, 0:4 became a parent */
1336   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
1337   set_path_visibility (fixture, "2:0:4:2", TRUE);
1338   set_path_visibility (fixture, "2:0:4:4", TRUE);
1339   check_level_length (fixture->filter, "0:4", 2);
1340 }
1341
1342 static void
1343 empty_show_nodes (FilterTest    *fixture,
1344                   gconstpointer  user_data)
1345 {
1346   check_filter_model (fixture);
1347   check_level_length (fixture->filter, NULL, 0);
1348
1349   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1350   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1351   set_path_visibility (fixture, "3", TRUE);
1352   check_filter_model (fixture);
1353   check_level_length (fixture->filter, NULL, 1);
1354   check_level_length (fixture->filter, "0", 0);
1355
1356   set_path_visibility (fixture, "3:2:2", TRUE);
1357   check_filter_model (fixture);
1358   check_level_length (fixture->filter, NULL, 1);
1359   check_level_length (fixture->filter, "0", 0);
1360
1361   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1362   set_path_visibility (fixture, "3:2", TRUE);
1363   check_filter_model (fixture);
1364   check_level_length (fixture->filter, NULL, 1);
1365   check_level_length (fixture->filter, "0", 1);
1366   check_level_length (fixture->filter, "0:0", 1);
1367   check_level_length (fixture->filter, "0:0:0", 0);
1368
1369   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1370   set_path_visibility (fixture, "3", FALSE);
1371   check_filter_model (fixture);
1372   check_level_length (fixture->filter, NULL, 0);
1373
1374   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1375   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1376   set_path_visibility (fixture, "3:2:1", TRUE);
1377   set_path_visibility (fixture, "3", TRUE);
1378   check_filter_model (fixture);
1379   check_level_length (fixture->filter, NULL, 1);
1380   check_level_length (fixture->filter, "0", 1);
1381   check_level_length (fixture->filter, "0:0", 2);
1382   check_level_length (fixture->filter, "0:0:0", 0);
1383 }
1384
1385 static void
1386 empty_show_multiple_nodes (FilterTest    *fixture,
1387                            gconstpointer  user_data)
1388 {
1389   GtkTreeIter iter;
1390   GtkTreePath *changed_path;
1391
1392   check_filter_model (fixture);
1393   check_level_length (fixture->filter, NULL, 0);
1394
1395   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1396   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1397   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1398   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1399
1400   /* We simulate a change in visible func condition with this.  The
1401    * visibility state of multiple nodes changes at once, we emit row-changed
1402    * for these nodes (and others) after that.
1403    */
1404   filter_test_block_signals (fixture);
1405   set_path_visibility (fixture, "3", TRUE);
1406   set_path_visibility (fixture, "4", TRUE);
1407   filter_test_unblock_signals (fixture);
1408
1409   changed_path = gtk_tree_path_new ();
1410   gtk_tree_path_append_index (changed_path, 2);
1411   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1412                            &iter, changed_path);
1413   /* Invisible node - so no signals expected */
1414   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1415                               changed_path, &iter);
1416
1417   gtk_tree_path_next (changed_path);
1418   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1419   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1420                               changed_path, &iter);
1421
1422   gtk_tree_path_next (changed_path);
1423   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1424   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1425                               changed_path, &iter);
1426
1427   gtk_tree_path_free (changed_path);
1428
1429   check_filter_model (fixture);
1430   check_level_length (fixture->filter, NULL, 2);
1431   check_level_length (fixture->filter, "0", 0);
1432
1433   set_path_visibility (fixture, "3:2:2", TRUE);
1434   check_filter_model (fixture);
1435   check_level_length (fixture->filter, NULL, 2);
1436   check_level_length (fixture->filter, "0", 0);
1437
1438   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1439   set_path_visibility (fixture, "3:2", TRUE);
1440   check_filter_model (fixture);
1441   check_level_length (fixture->filter, NULL, 2);
1442   check_level_length (fixture->filter, "0", 1);
1443   check_level_length (fixture->filter, "0:0", 1);
1444   check_level_length (fixture->filter, "0:0:0", 0);
1445
1446   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1447   set_path_visibility (fixture, "3", FALSE);
1448   check_filter_model (fixture);
1449   check_level_length (fixture->filter, NULL, 1);
1450
1451   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1452   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1453   set_path_visibility (fixture, "3:2:1", TRUE);
1454   set_path_visibility (fixture, "3", TRUE);
1455   check_filter_model (fixture);
1456   check_level_length (fixture->filter, NULL, 2);
1457   check_level_length (fixture->filter, "0", 1);
1458   check_level_length (fixture->filter, "0:0", 2);
1459   check_level_length (fixture->filter, "0:0:0", 0);
1460 }
1461
1462 static void
1463 empty_vroot_show_nodes (FilterTest    *fixture,
1464                         gconstpointer  user_data)
1465 {
1466   GtkTreePath *path = (GtkTreePath *)user_data;
1467
1468   check_filter_model_with_root (fixture, path);
1469   check_level_length (fixture->filter, NULL, 0);
1470
1471   set_path_visibility (fixture, "2", TRUE);
1472   check_filter_model_with_root (fixture, path);
1473   check_level_length (fixture->filter, NULL, 0);
1474
1475   set_path_visibility (fixture, "2:2:2", TRUE);
1476   check_filter_model_with_root (fixture, path);
1477   check_level_length (fixture->filter, NULL, 0);
1478
1479   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1480   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1481   set_path_visibility (fixture, "2:2", TRUE);
1482   check_filter_model_with_root (fixture, path);
1483   check_level_length (fixture->filter, NULL, 1);
1484   check_level_length (fixture->filter, "0", 1);
1485   check_level_length (fixture->filter, "0:0", 0);
1486
1487   set_path_visibility (fixture, "3", TRUE);
1488   check_filter_model_with_root (fixture, path);
1489   check_level_length (fixture->filter, NULL, 1);
1490
1491   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1492   set_path_visibility (fixture, "2:2", FALSE);
1493   check_filter_model_with_root (fixture, path);
1494   check_level_length (fixture->filter, NULL, 0);
1495
1496   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1497   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1498   set_path_visibility (fixture, "2:2:1", TRUE);
1499   set_path_visibility (fixture, "2:2", TRUE);
1500   check_filter_model_with_root (fixture, path);
1501   check_level_length (fixture->filter, NULL, 1);
1502   check_level_length (fixture->filter, "0", 2);
1503   check_level_length (fixture->filter, "0:1", 0);
1504 }
1505
1506 static void
1507 empty_vroot_show_multiple_nodes (FilterTest    *fixture,
1508                                  gconstpointer  user_data)
1509 {
1510   GtkTreeIter iter;
1511   GtkTreePath *changed_path;
1512   GtkTreePath *path = (GtkTreePath *)user_data;
1513
1514   check_filter_model_with_root (fixture, path);
1515   check_level_length (fixture->filter, NULL, 0);
1516
1517   /* We simulate a change in visible func condition with this.  The
1518    * visibility state of multiple nodes changes at once, we emit row-changed
1519    * for these nodes (and others) after that.
1520    */
1521   filter_test_block_signals (fixture);
1522   set_path_visibility (fixture, "2", TRUE);
1523   set_path_visibility (fixture, "3", TRUE);
1524   filter_test_unblock_signals (fixture);
1525
1526   changed_path = gtk_tree_path_new ();
1527   gtk_tree_path_append_index (changed_path, 1);
1528   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1529                            &iter, changed_path);
1530   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1531                               changed_path, &iter);
1532
1533   gtk_tree_path_next (changed_path);
1534   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1535   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1536                               changed_path, &iter);
1537
1538   gtk_tree_path_next (changed_path);
1539   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1540   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1541                               changed_path, &iter);
1542
1543   gtk_tree_path_next (changed_path);
1544   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1545   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1546                               changed_path, &iter);
1547
1548   gtk_tree_path_free (changed_path);
1549
1550   check_filter_model_with_root (fixture, path);
1551   check_level_length (fixture->filter, NULL, 0);
1552
1553   set_path_visibility (fixture, "2:2:2", TRUE);
1554   check_filter_model_with_root (fixture, path);
1555   check_level_length (fixture->filter, NULL, 0);
1556
1557   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1558   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1559   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
1560   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
1561
1562   /* Again, we simulate a call to refilter */
1563   filter_test_block_signals (fixture);
1564   set_path_visibility (fixture, "2:2", TRUE);
1565   set_path_visibility (fixture, "2:3", TRUE);
1566   filter_test_unblock_signals (fixture);
1567
1568   changed_path = gtk_tree_path_new ();
1569   gtk_tree_path_append_index (changed_path, 2);
1570   gtk_tree_path_append_index (changed_path, 1);
1571   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
1572                            &iter, changed_path);
1573   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1574                               changed_path, &iter);
1575
1576   gtk_tree_path_next (changed_path);
1577   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1578   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1579                               changed_path, &iter);
1580
1581   gtk_tree_path_next (changed_path);
1582   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1583   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1584                               changed_path, &iter);
1585
1586   gtk_tree_path_next (changed_path);
1587   gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter);
1588   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
1589                               changed_path, &iter);
1590
1591   gtk_tree_path_free (changed_path);
1592
1593   check_filter_model_with_root (fixture, path);
1594   check_level_length (fixture->filter, NULL, 2);
1595   check_level_length (fixture->filter, "0", 1);
1596   check_level_length (fixture->filter, "0:0", 0);
1597
1598   set_path_visibility (fixture, "3", TRUE);
1599   check_filter_model_with_root (fixture, path);
1600   check_level_length (fixture->filter, NULL, 2);
1601
1602   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0");
1603   set_path_visibility (fixture, "2:2", FALSE);
1604   check_filter_model_with_root (fixture, path);
1605   check_level_length (fixture->filter, NULL, 1);
1606
1607   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1608   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1609   set_path_visibility (fixture, "2:2:1", TRUE);
1610   set_path_visibility (fixture, "2:2", TRUE);
1611   check_filter_model_with_root (fixture, path);
1612   check_level_length (fixture->filter, NULL, 2);
1613   check_level_length (fixture->filter, "0", 2);
1614   check_level_length (fixture->filter, "0:1", 0);
1615 }
1616
1617
1618 static void
1619 unfiltered_hide_single (FilterTest    *fixture,
1620                         gconstpointer  user_data)
1621
1622 {
1623   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1624   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1625   set_path_visibility (fixture, "2", FALSE);
1626
1627   signal_monitor_assert_is_empty (fixture->monitor);
1628   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1629
1630   /* The view only shows the root level, so we only expect signals
1631    * for the root level.
1632    */
1633   filter_test_append_refilter_signals (fixture, 1);
1634   filter_test_enable_filter (fixture);
1635
1636   check_filter_model (fixture);
1637   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1638 }
1639
1640 static void
1641 unfiltered_hide_single_root_expanded (FilterTest    *fixture,
1642                                       gconstpointer  user_data)
1643
1644 {
1645   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1646   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1647   set_path_visibility (fixture, "2", FALSE);
1648
1649   signal_monitor_assert_is_empty (fixture->monitor);
1650   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1651
1652   filter_test_append_refilter_signals (fixture, 2);
1653   filter_test_enable_filter (fixture);
1654
1655   check_filter_model (fixture);
1656   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1657 }
1658
1659 static void
1660 unfiltered_hide_single_child (FilterTest    *fixture,
1661                               gconstpointer  user_data)
1662
1663 {
1664   /* This row is not shown, so its signal is not propagated */
1665   set_path_visibility (fixture, "2:2", FALSE);
1666
1667   signal_monitor_assert_is_empty (fixture->monitor);
1668   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1669   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1670
1671   /* The view only shows the root level, so we only expect signals
1672    * for the root level.
1673    */
1674   filter_test_append_refilter_signals (fixture, 0);
1675   filter_test_enable_filter (fixture);
1676
1677   check_filter_model (fixture);
1678   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1679   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1680 }
1681
1682 static void
1683 unfiltered_hide_single_child_root_expanded (FilterTest    *fixture,
1684                                             gconstpointer  user_data)
1685
1686 {
1687   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1688   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1689   set_path_visibility (fixture, "2:2", FALSE);
1690
1691   signal_monitor_assert_is_empty (fixture->monitor);
1692   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1693   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1694
1695   filter_test_append_refilter_signals (fixture, 2);
1696   filter_test_enable_filter (fixture);
1697
1698   check_filter_model (fixture);
1699   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1700   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1701 }
1702
1703 static void
1704 unfiltered_hide_single_multi_level (FilterTest    *fixture,
1705                                     gconstpointer  user_data)
1706
1707 {
1708   /* This row is not shown, so its signal is not propagated */
1709   set_path_visibility (fixture, "2:2:2", FALSE);
1710
1711   /* This row is not shown, so its signal is not propagated */
1712   set_path_visibility (fixture, "2:2", FALSE);
1713
1714   signal_monitor_assert_is_empty (fixture->monitor);
1715   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1716   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1717   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1718
1719   /* The view only shows the root level, so we only expect signals
1720    * for the root level.
1721    */
1722   filter_test_append_refilter_signals (fixture, 1);
1723   filter_test_enable_filter (fixture);
1724
1725   check_filter_model (fixture);
1726   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1727   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1728
1729   set_path_visibility (fixture, "2:2", TRUE);
1730
1731   check_filter_model (fixture);
1732   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1733   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1734   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1735 }
1736
1737 static void
1738 unfiltered_hide_single_multi_level_root_expanded (FilterTest    *fixture,
1739                                                   gconstpointer  user_data)
1740
1741 {
1742   /* This row is not shown, so its signal is not propagated */
1743   set_path_visibility (fixture, "2:2:2", FALSE);
1744
1745   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1746   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1747   set_path_visibility (fixture, "2:2", FALSE);
1748
1749   signal_monitor_assert_is_empty (fixture->monitor);
1750   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1751   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1752   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1753
1754   filter_test_append_refilter_signals (fixture, 2);
1755   filter_test_enable_filter (fixture);
1756
1757   check_filter_model (fixture);
1758   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1759   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1760
1761   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1762   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1763   set_path_visibility (fixture, "2:2", TRUE);
1764
1765   check_filter_model (fixture);
1766   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1767   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1768   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1769 }
1770
1771
1772
1773 static void
1774 unfiltered_vroot_hide_single (FilterTest    *fixture,
1775                               gconstpointer  user_data)
1776
1777 {
1778   GtkTreePath *path = (GtkTreePath *)user_data;
1779
1780   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1781   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1782   set_path_visibility (fixture, "2:2", FALSE);
1783
1784   signal_monitor_assert_is_empty (fixture->monitor);
1785   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1786
1787   /* The view only shows the root level, so we only expect signals
1788    * for the root level.  (Though for the depth argument, we have to
1789    * take the virtual root into account).
1790    */
1791   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
1792   filter_test_enable_filter (fixture);
1793
1794   check_filter_model_with_root (fixture, path);
1795   check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
1796 }
1797
1798 static void
1799 unfiltered_vroot_hide_single_child (FilterTest    *fixture,
1800                                     gconstpointer  user_data)
1801
1802 {
1803   GtkTreePath *path = (GtkTreePath *)user_data;
1804
1805   /* Not visible, so no signal will be received. */
1806   set_path_visibility (fixture, "2:2:2", FALSE);
1807
1808   signal_monitor_assert_is_empty (fixture->monitor);
1809   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1810   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1811
1812   /* The view only shows the root level, so we only expect signals
1813    * for the root level.  (Though for the depth argument, we have to
1814    * take the virtual root into account).
1815    */
1816   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
1817   filter_test_enable_filter (fixture);
1818
1819   check_filter_model_with_root (fixture, path);
1820   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1821   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1822 }
1823
1824 static void
1825 unfiltered_vroot_hide_single_child_root_expanded (FilterTest    *fixture,
1826                                                   gconstpointer  user_data)
1827
1828 {
1829   GtkTreePath *path = (GtkTreePath *)user_data;
1830
1831   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1832   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1833   set_path_visibility (fixture, "2:2:2", FALSE);
1834
1835   signal_monitor_assert_is_empty (fixture->monitor);
1836   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1837   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1838
1839   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1840   filter_test_enable_filter (fixture);
1841
1842   check_filter_model_with_root (fixture, path);
1843   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1844   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1845 }
1846
1847 static void
1848 unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
1849                                           gconstpointer  user_data)
1850
1851 {
1852   GtkTreePath *path = (GtkTreePath *)user_data;
1853
1854   /* This row is not shown, so its signal is not propagated */
1855   set_path_visibility (fixture, "2:2:2:2", FALSE);
1856
1857   /* Not shown, so no signal */
1858   set_path_visibility (fixture, "2:2:2", FALSE);
1859
1860   signal_monitor_assert_is_empty (fixture->monitor);
1861   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1862   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1863   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1864
1865   /* We only expect signals for the root level.  The depth is 2
1866    * because we have to take the virtual root into account.
1867    */
1868   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
1869   filter_test_enable_filter (fixture);
1870
1871   check_filter_model_with_root (fixture, path);
1872   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1873   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1874
1875   /* Not shown, so no signal */
1876   set_path_visibility (fixture, "2:2:2", TRUE);
1877
1878   check_filter_model_with_root (fixture, path);
1879   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1880   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1881   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1882 }
1883
1884 static void
1885 unfiltered_vroot_hide_single_multi_level_root_expanded (FilterTest    *fixture,
1886                                                         gconstpointer  user_data)
1887
1888 {
1889   GtkTreePath *path = (GtkTreePath *)user_data;
1890
1891   /* This row is not shown, so its signal is not propagated */
1892   set_path_visibility (fixture, "2:2:2:2", FALSE);
1893
1894   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1895   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1896   set_path_visibility (fixture, "2:2:2", FALSE);
1897
1898   signal_monitor_assert_is_empty (fixture->monitor);
1899   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1900   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1901   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
1902
1903   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
1904   filter_test_enable_filter (fixture);
1905
1906   check_filter_model_with_root (fixture, path);
1907   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1908   check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
1909
1910   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2");
1911   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1912   set_path_visibility (fixture, "2:2:2", TRUE);
1913
1914   check_filter_model_with_root (fixture, path);
1915   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1916   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1917   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
1918 }
1919
1920 static void
1921 unfiltered_show_single (FilterTest    *fixture,
1922                         gconstpointer  user_data)
1923
1924 {
1925   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
1926   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
1927   set_path_visibility (fixture, "2", TRUE);
1928
1929   signal_monitor_assert_is_empty (fixture->monitor);
1930   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1931
1932   /* We only expect signals for the root level */
1933   filter_test_append_refilter_signals (fixture, 1);
1934   filter_test_enable_filter (fixture);
1935
1936   check_filter_model (fixture);
1937   check_level_length (fixture->filter, NULL, 1);
1938 }
1939
1940 static void
1941 unfiltered_show_single_child (FilterTest    *fixture,
1942                               gconstpointer  user_data)
1943
1944 {
1945   set_path_visibility (fixture, "2:2", TRUE);
1946
1947   signal_monitor_assert_is_empty (fixture->monitor);
1948   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1949   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1950
1951   /* We only expect signals for the root level */
1952   filter_test_append_refilter_signals (fixture, 1);
1953   filter_test_enable_filter (fixture);
1954
1955   check_filter_model (fixture);
1956   check_level_length (fixture->filter, NULL, 0);
1957
1958   /* From here we are filtered, "2" in the real model is "0" in the filter
1959    * model.
1960    */
1961   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1962   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1963   set_path_visibility (fixture, "2", TRUE);
1964   signal_monitor_assert_is_empty (fixture->monitor);
1965   check_level_length (fixture->filter, NULL, 1);
1966   check_level_length (fixture->filter, "0", 1);
1967 }
1968
1969 static void
1970 unfiltered_show_single_child_root_expanded (FilterTest    *fixture,
1971                                             gconstpointer  user_data)
1972
1973 {
1974   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
1975   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
1976   set_path_visibility (fixture, "2:2", TRUE);
1977
1978   signal_monitor_assert_is_empty (fixture->monitor);
1979   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
1980   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
1981
1982   filter_test_append_refilter_signals (fixture, 2);
1983   filter_test_enable_filter (fixture);
1984
1985   check_filter_model (fixture);
1986   check_level_length (fixture->filter, NULL, 0);
1987
1988   /* From here we are filtered, "2" in the real model is "0" in the filter
1989    * model.
1990    */
1991   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
1992   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
1993   set_path_visibility (fixture, "2", TRUE);
1994   signal_monitor_assert_is_empty (fixture->monitor);
1995   check_level_length (fixture->filter, NULL, 1);
1996   check_level_length (fixture->filter, "0", 1);
1997 }
1998
1999 static void
2000 unfiltered_show_single_multi_level (FilterTest    *fixture,
2001                                     gconstpointer  user_data)
2002
2003 {
2004   /* The view is not showing these rows (collapsed state), so it is not
2005    * referenced.  The signal should not go through.
2006    */
2007   set_path_visibility (fixture, "2:2:2", TRUE);
2008   set_path_visibility (fixture, "2:2", TRUE);
2009
2010   signal_monitor_assert_is_empty (fixture->monitor);
2011   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2012   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2013   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
2014
2015   /* We only expect signals for the first level */
2016   filter_test_append_refilter_signals (fixture, 1);
2017   filter_test_enable_filter (fixture);
2018
2019   check_filter_model (fixture);
2020   check_level_length (fixture->filter, NULL, 0);
2021
2022   /* From here we are filtered, "2" in the real model is "0" in the filter
2023    * model.
2024    */
2025   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2026   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2027   set_path_visibility (fixture, "2", TRUE);
2028   check_filter_model (fixture);
2029   check_level_length (fixture->filter, NULL, 1);
2030   check_level_length (fixture->filter, "0", 1);
2031   check_level_length (fixture->filter, "0:0", 1);
2032 }
2033
2034 static void
2035 unfiltered_show_single_multi_level_root_expanded (FilterTest    *fixture,
2036                                                   gconstpointer  user_data)
2037
2038 {
2039   /* The view is not showing this row (collapsed state), so it is not
2040    * referenced.  The signal should not go through.
2041    */
2042   set_path_visibility (fixture, "2:2:2", TRUE);
2043
2044   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
2045   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
2046   set_path_visibility (fixture, "2:2", TRUE);
2047
2048   signal_monitor_assert_is_empty (fixture->monitor);
2049   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2050   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2051   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
2052
2053   filter_test_append_refilter_signals (fixture, 2);
2054   filter_test_enable_filter (fixture);
2055
2056   check_filter_model (fixture);
2057   check_level_length (fixture->filter, NULL, 0);
2058
2059   /* From here we are filtered, "2" in the real model is "0" in the filter
2060    * model.
2061    */
2062   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2063   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2064   set_path_visibility (fixture, "2", TRUE);
2065   check_filter_model (fixture);
2066   check_level_length (fixture->filter, NULL, 1);
2067   check_level_length (fixture->filter, "0", 1);
2068   check_level_length (fixture->filter, "0:0", 1);
2069 }
2070
2071 static void
2072 unfiltered_vroot_show_single (FilterTest    *fixture,
2073                               gconstpointer  user_data)
2074
2075 {
2076   GtkTreePath *path = (GtkTreePath *)user_data;
2077
2078   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
2079   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
2080   set_path_visibility (fixture, "2:2", TRUE);
2081
2082   signal_monitor_assert_is_empty (fixture->monitor);
2083   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2084
2085   /* The view only shows the root level, so the filter model only has
2086    * the first two levels cached.
2087    */
2088   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
2089   filter_test_enable_filter (fixture);
2090
2091   check_filter_model_with_root (fixture, path);
2092   check_level_length (fixture->filter, NULL, 1);
2093 }
2094
2095 static void
2096 unfiltered_vroot_show_single_child (FilterTest    *fixture,
2097                                     gconstpointer  user_data)
2098
2099 {
2100   GtkTreePath *path = (GtkTreePath *)user_data;
2101
2102   set_path_visibility (fixture, "2:2:2", TRUE);
2103
2104   signal_monitor_assert_is_empty (fixture->monitor);
2105   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2106   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2107
2108   /* The view only shows the root level, so the filter model only has
2109    * the first two levels cached.
2110    */
2111   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
2112   filter_test_enable_filter (fixture);
2113
2114   check_filter_model_with_root (fixture, path);
2115   check_level_length (fixture->filter, NULL, 0);
2116
2117   /* From here we are filtered, "2" in the real model is "0" in the filter
2118    * model.
2119    */
2120   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2121   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2122   set_path_visibility (fixture, "2:2", TRUE);
2123   signal_monitor_assert_is_empty (fixture->monitor);
2124   check_level_length (fixture->filter, NULL, 1);
2125   check_level_length (fixture->filter, "0", 1);
2126 }
2127
2128 static void
2129 unfiltered_vroot_show_single_child_root_expanded (FilterTest    *fixture,
2130                                                   gconstpointer  user_data)
2131
2132 {
2133   GtkTreePath *path = (GtkTreePath *)user_data;
2134
2135   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
2136   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
2137   set_path_visibility (fixture, "2:2:2", TRUE);
2138
2139   signal_monitor_assert_is_empty (fixture->monitor);
2140   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2141   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2142
2143   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
2144   filter_test_enable_filter (fixture);
2145
2146   check_filter_model_with_root (fixture, path);
2147   check_level_length (fixture->filter, NULL, 0);
2148
2149   /* From here we are filtered, "2" in the real model is "0" in the filter
2150    * model.
2151    */
2152   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2153   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2154   set_path_visibility (fixture, "2:2", TRUE);
2155   signal_monitor_assert_is_empty (fixture->monitor);
2156   check_level_length (fixture->filter, NULL, 1);
2157   check_level_length (fixture->filter, "0", 1);
2158 }
2159
2160
2161 static void
2162 unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
2163                                           gconstpointer  user_data)
2164
2165 {
2166   GtkTreePath *path = (GtkTreePath *)user_data;
2167
2168   /* The view is not showing this row (collapsed state), so it is not
2169    * referenced.  The signal should not go through.
2170    */
2171   set_path_visibility (fixture, "2:2:2:2", TRUE);
2172
2173   set_path_visibility (fixture, "2:2:2", TRUE);
2174
2175   signal_monitor_assert_is_empty (fixture->monitor);
2176   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2177   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2178   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
2179
2180   /* We only expect signals for the root level */
2181   filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
2182   filter_test_enable_filter (fixture);
2183
2184   check_filter_model_with_root (fixture, path);
2185   check_level_length (fixture->filter, NULL, 0);
2186
2187   /* From here we are filtered, "2" in the real model is "0" in the filter
2188    * model.
2189    */
2190   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2191   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2192   set_path_visibility (fixture, "2:2", TRUE);
2193   check_filter_model_with_root (fixture, path);
2194   check_level_length (fixture->filter, NULL, 1);
2195   check_level_length (fixture->filter, "0", 1);
2196   check_level_length (fixture->filter, "0:0", 1);
2197 }
2198
2199 static void
2200 unfiltered_vroot_show_single_multi_level_root_expanded (FilterTest    *fixture,
2201                                                         gconstpointer  user_data)
2202
2203 {
2204   GtkTreePath *path = (GtkTreePath *)user_data;
2205
2206   /* The view is not showing this row (collapsed state), so it is not
2207    * referenced.  The signal should not go through.
2208    */
2209   set_path_visibility (fixture, "2:2:2:2", TRUE);
2210
2211   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
2212   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
2213   set_path_visibility (fixture, "2:2:2", TRUE);
2214
2215   signal_monitor_assert_is_empty (fixture->monitor);
2216   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
2217   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
2218   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
2219
2220   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
2221   filter_test_enable_filter (fixture);
2222
2223   check_filter_model_with_root (fixture, path);
2224   check_level_length (fixture->filter, NULL, 0);
2225
2226   /* From here we are filtered, "2" in the real model is "0" in the filter
2227    * model.
2228    */
2229   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
2230   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
2231   set_path_visibility (fixture, "2:2", TRUE);
2232   check_filter_model_with_root (fixture, path);
2233   check_level_length (fixture->filter, NULL, 1);
2234   check_level_length (fixture->filter, "0", 1);
2235   check_level_length (fixture->filter, "0:0", 1);
2236 }
2237
2238
2239 static void
2240 insert_before (void)
2241 {
2242   GtkTreeStore *store;
2243   GtkTreeModel *filter;
2244   GtkWidget *tree_view;
2245   SignalMonitor *monitor;
2246   GtkTreeIter iter;
2247   GtkTreeIter last_iter;
2248   GtkTreePath *path;
2249
2250   /* This tests two aspects of the row-inserted handling:
2251    *   1) If the newly inserted node was already handled by building
2252    *      the root level, don't handle it a second time.
2253    *   2) Offsets of existing nodes must be updated when a new
2254    *      node is inserted.
2255    */
2256
2257   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2258   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2259   gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
2260                                             1);
2261
2262   tree_view = gtk_tree_view_new_with_model (filter);
2263   monitor = signal_monitor_new (filter);
2264
2265   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 0);
2266
2267   /* Insert 0 */
2268   path = gtk_tree_path_new_from_indices (0, -1);
2269   signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
2270   gtk_tree_path_free (path);
2271
2272   gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
2273                                      0, "Foo", 1, TRUE, -1);
2274
2275   signal_monitor_assert_is_empty (monitor);
2276   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
2277
2278   /* Insert 1 */
2279   path = gtk_tree_path_new_from_indices (1, -1);
2280   signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
2281   gtk_tree_path_free (path);
2282
2283   gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
2284                                      0, "Foo", 1, TRUE, -1);
2285   last_iter = iter;
2286
2287   signal_monitor_assert_is_empty (monitor);
2288   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
2289
2290   /* Insert on 1 again -- invisible */
2291   gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
2292                                      0, "Foo", 1, FALSE, -1);
2293
2294   signal_monitor_assert_is_empty (monitor);
2295   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
2296
2297   /* Insert on 1 again -- visible */
2298   path = gtk_tree_path_new_from_indices (1, -1);
2299   signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
2300   gtk_tree_path_free (path);
2301
2302   gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
2303                                      0, "Foo", 1, TRUE, -1);
2304
2305   signal_monitor_assert_is_empty (monitor);
2306   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
2307
2308   /* Modify the iter that should be at the last position and check the
2309    * signal we get.
2310    */
2311   path = gtk_tree_path_new_from_indices (2, -1);
2312   signal_monitor_append_signal_path (monitor, ROW_CHANGED, path);
2313   gtk_tree_path_free (path);
2314
2315   gtk_tree_store_set (store, &last_iter, 0, "Foo changed", -1);
2316
2317   signal_monitor_assert_is_empty (monitor);
2318   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
2319 }
2320
2321 static void
2322 insert_child (void)
2323 {
2324   GtkTreeStore *store;
2325   GtkTreeModel *filter;
2326   GtkWidget *tree_view;
2327   SignalMonitor *monitor;
2328   GtkTreeIter parent, iter;
2329   GtkTreePath *path;
2330
2331   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2332
2333   gtk_tree_store_insert_with_values (store, &parent, NULL, 0,
2334                                      0, "Parent", 1, TRUE, -1);
2335
2336
2337   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2338   gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
2339                                             1);
2340
2341   tree_view = gtk_tree_view_new_with_model (filter);
2342   monitor = signal_monitor_new (filter);
2343
2344   /* Insert child -- invisible */
2345   path = gtk_tree_path_new_from_indices (0, -1);
2346   signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
2347   /* The signal is received twice, once a pass through from GtkTreeStore
2348    * and one generated by GtkTreeModelFilter.  Not accurate, but cannot
2349    * hurt.
2350    */
2351   signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
2352   gtk_tree_path_free (path);
2353
2354   gtk_tree_store_insert_with_values (store, &iter, &parent, 1,
2355                                      0, "Child", 1, FALSE, -1);
2356
2357   signal_monitor_assert_is_empty (monitor);
2358   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
2359
2360   /* Insert child */
2361   path = gtk_tree_path_new_from_indices (0, 0, -1);
2362   gtk_tree_path_up (path); /* 0 */
2363   signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
2364   gtk_tree_path_free (path);
2365
2366   gtk_tree_store_insert_with_values (store, &iter, &parent, 0,
2367                                      0, "Child", 1, TRUE, -1);
2368
2369   signal_monitor_assert_is_empty (monitor);
2370   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
2371
2372   /* Insert child -- invisible */
2373   gtk_tree_store_insert_with_values (store, &iter, &parent, 1,
2374                                      0, "Child", 1, FALSE, -1);
2375
2376   signal_monitor_assert_is_empty (monitor);
2377   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
2378 }
2379
2380
2381
2382 static void
2383 remove_node (void)
2384 {
2385   GtkTreeIter iter, iter1, iter2, iter3;
2386   GtkListStore *list;
2387   GtkTreeModel *filter;
2388   GtkWidget *view G_GNUC_UNUSED;
2389
2390   list = gtk_list_store_new (1, G_TYPE_INT);
2391   gtk_list_store_insert_with_values (list, &iter1, 0, 0, 1, -1);
2392   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
2393   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
2394   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
2395   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
2396   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
2397   gtk_list_store_insert_with_values (list, &iter2, 6, 0, 7, -1);
2398   gtk_list_store_insert_with_values (list, &iter3, 7, 0, 8, -1);
2399
2400   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL);
2401   view = gtk_tree_view_new_with_model (filter);
2402
2403   gtk_list_store_remove (list, &iter1);
2404   gtk_list_store_remove (list, &iter3);
2405   gtk_list_store_remove (list, &iter2);
2406
2407   gtk_widget_destroy (view);
2408   g_object_unref (filter);
2409   g_object_unref (list);
2410 }
2411
2412 static void
2413 remove_node_vroot (void)
2414 {
2415   GtkTreeIter parent, root;
2416   GtkTreeIter iter, iter1, iter2, iter3;
2417   GtkTreeStore *tree;
2418   GtkTreeModel *filter;
2419   GtkTreePath *path;
2420   GtkWidget *view G_GNUC_UNUSED;
2421
2422   tree = gtk_tree_store_new (1, G_TYPE_INT);
2423   gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1);
2424   gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1);
2425
2426   gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1);
2427   gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1);
2428   gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1);
2429   gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1);
2430   gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1);
2431   gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1);
2432   gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1);
2433   gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1);
2434
2435   path = gtk_tree_path_new_from_indices (0, 0, -1);
2436   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path);
2437   gtk_tree_path_free (path);
2438
2439   view = gtk_tree_view_new_with_model (filter);
2440
2441   gtk_tree_store_remove (tree, &iter1);
2442   gtk_tree_store_remove (tree, &iter3);
2443   gtk_tree_store_remove (tree, &iter2);
2444
2445   gtk_widget_destroy (view);
2446   g_object_unref (filter);
2447   g_object_unref (tree);
2448 }
2449
2450 static void
2451 remove_vroot_ancestor (void)
2452 {
2453   GtkTreeIter parent, root;
2454   GtkTreeIter iter, iter1, iter2, iter3;
2455   GtkTreeStore *tree;
2456   GtkTreeModel *filter;
2457   GtkTreePath *path;
2458   GtkWidget *view G_GNUC_UNUSED;
2459
2460   tree = gtk_tree_store_new (1, G_TYPE_INT);
2461   gtk_tree_store_insert_with_values (tree, &parent, NULL, 0, 0, 0, -1);
2462   gtk_tree_store_insert_with_values (tree, &root, &parent, 0, 0, 0, -1);
2463
2464   gtk_tree_store_insert_with_values (tree, &iter1, &root, 0, 0, 1, -1);
2465   gtk_tree_store_insert_with_values (tree, &iter, &root, 1, 0, 2, -1);
2466   gtk_tree_store_insert_with_values (tree, &iter, &root, 2, 0, 3, -1);
2467   gtk_tree_store_insert_with_values (tree, &iter, &root, 3, 0, 4, -1);
2468   gtk_tree_store_insert_with_values (tree, &iter, &root, 4, 0, 5, -1);
2469   gtk_tree_store_insert_with_values (tree, &iter, &root, 5, 0, 6, -1);
2470   gtk_tree_store_insert_with_values (tree, &iter2, &root, 6, 0, 7, -1);
2471   gtk_tree_store_insert_with_values (tree, &iter3, &root, 7, 0, 8, -1);
2472
2473   path = gtk_tree_path_new_from_indices (0, 0, -1);
2474   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path);
2475   gtk_tree_path_free (path);
2476
2477   view = gtk_tree_view_new_with_model (filter);
2478
2479   gtk_tree_store_remove (tree, &parent);
2480
2481   gtk_widget_destroy (view);
2482   g_object_unref (filter);
2483   g_object_unref (tree);
2484 }
2485
2486
2487 static gboolean
2488 specific_path_dependent_filter_func (GtkTreeModel *model,
2489                                      GtkTreeIter  *iter,
2490                                      gpointer      data)
2491 {
2492   GtkTreePath *path;
2493
2494   path = gtk_tree_model_get_path (model, iter);
2495   if (gtk_tree_path_get_indices (path)[0] < 4)
2496     return FALSE;
2497
2498   return TRUE;
2499 }
2500
2501 static void
2502 specific_path_dependent_filter (void)
2503 {
2504   int i;
2505   GtkTreeIter iter;
2506   GtkListStore *list;
2507   GtkTreeModel *sort;
2508   GtkTreeModel *filter;
2509
2510   list = gtk_list_store_new (1, G_TYPE_INT);
2511   gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1);
2512   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
2513   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
2514   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
2515   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
2516   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
2517   gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1);
2518   gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1);
2519
2520   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list));
2521   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL);
2522   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2523                                           specific_path_dependent_filter_func,
2524                                           NULL, NULL);
2525
2526   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0,
2527                                         GTK_SORT_DESCENDING);
2528
2529   for (i = 0; i < 4; i++)
2530     {
2531       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
2532                                          NULL, 1))
2533         gtk_list_store_remove (list, &iter);
2534
2535       if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter,
2536                                          NULL, 2))
2537         gtk_list_store_remove (list, &iter);
2538     }
2539
2540   g_object_unref (filter);
2541   g_object_unref (sort);
2542   g_object_unref (list);
2543 }
2544
2545
2546 static gboolean
2547 specific_append_after_collapse_visible_func (GtkTreeModel *model,
2548                                              GtkTreeIter  *iter,
2549                                              gpointer      data)
2550 {
2551   gint number;
2552   gboolean hide_negative_numbers;
2553
2554   gtk_tree_model_get (model, iter, 1, &number, -1);
2555   hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers"));
2556
2557   return (number >= 0 || !hide_negative_numbers);
2558 }
2559
2560 static void
2561 specific_append_after_collapse (void)
2562 {
2563   /* This test is based on one of the test cases I found in my
2564    * old test cases directory.  I unfortunately do not have a record
2565    * from who this test case originated.  -Kris.
2566    *
2567    * General idea:
2568    * - Construct tree.
2569    * - Show tree, expand, collapse.
2570    * - Add a row.
2571    */
2572
2573   GtkTreeIter iter;
2574   GtkTreeIter child_iter;
2575   GtkTreeIter child_iter2;
2576   GtkTreePath *append_path;
2577   GtkTreeStore *store;
2578   GtkTreeModel *filter;
2579   GtkTreeModel *sort;
2580
2581   GtkWidget *window;
2582   GtkWidget *tree_view;
2583
2584   store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT);
2585
2586   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
2587   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
2588                      GINT_TO_POINTER (FALSE));
2589   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2590                                           specific_append_after_collapse_visible_func,
2591                                           filter, NULL);
2592
2593   sort = gtk_tree_model_sort_new_with_model (filter);
2594
2595   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2596   tree_view = gtk_tree_view_new_with_model (sort);
2597   gtk_container_add (GTK_CONTAINER (window), tree_view);
2598   gtk_widget_realize (tree_view);
2599
2600   while (gtk_events_pending ())
2601     gtk_main_iteration ();
2602
2603   gtk_tree_store_prepend (store, &iter, NULL);
2604   gtk_tree_store_set (store, &iter,
2605                       0, "hallo", 1, 1, -1);
2606
2607   gtk_tree_store_append (store, &child_iter, &iter);
2608   gtk_tree_store_set (store, &child_iter,
2609                       0, "toemaar", 1, 1, -1);
2610
2611   gtk_tree_store_append (store, &child_iter2, &child_iter);
2612   gtk_tree_store_set (store, &child_iter2,
2613                       0, "very deep", 1, 1, -1);
2614
2615   append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2);
2616
2617   gtk_tree_store_append (store, &child_iter, &iter);
2618   gtk_tree_store_set (store, &child_iter,
2619                       0, "sja", 1, 1, -1);
2620
2621   gtk_tree_store_append (store, &child_iter, &iter);
2622   gtk_tree_store_set (store, &child_iter,
2623                       0, "some word", 1, -1, -1);
2624
2625   /* Expand and collapse the tree */
2626   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
2627   while (gtk_events_pending ())
2628     gtk_main_iteration ();
2629
2630   gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
2631   while (gtk_events_pending ())
2632     gtk_main_iteration ();
2633
2634   /* Add another it */
2635   g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers",
2636                      GINT_TO_POINTER (TRUE));
2637
2638   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path))
2639     {
2640       gtk_tree_store_append (store, &child_iter, &iter);
2641       gtk_tree_store_set (store, &child_iter,
2642                           0, "new new new !!", 1, 1, -1);
2643     }
2644   gtk_tree_path_free (append_path);
2645
2646   /* Expand */
2647   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
2648   while (gtk_events_pending ())
2649     gtk_main_iteration ();
2650 }
2651
2652
2653 static gint
2654 specific_sort_filter_remove_node_compare_func (GtkTreeModel  *model,
2655                                                GtkTreeIter   *iter1,
2656                                                GtkTreeIter   *iter2,
2657                                                gpointer       data)
2658 {
2659   return -1;
2660 }
2661
2662 static gboolean
2663 specific_sort_filter_remove_node_visible_func (GtkTreeModel  *model,
2664                                                GtkTreeIter   *iter,
2665                                                gpointer       data)
2666 {
2667   char *item = NULL;
2668
2669   /* Do reference the model */
2670   gtk_tree_model_get (model, iter, 0, &item, -1);
2671   g_free (item);
2672
2673   return FALSE;
2674 }
2675
2676 static void
2677 specific_sort_filter_remove_node (void)
2678 {
2679   /* This test is based on one of the test cases I found in my
2680    * old test cases directory.  I unfortunately do not have a record
2681    * from who this test case originated.  -Kris.
2682    *
2683    * General idea:
2684    *  - Create tree store, sort, filter models.  The sort model has
2685    *    a default sort func that is enabled, filter model a visible func
2686    *    that defaults to returning FALSE.
2687    *  - Remove a node from the tree store.
2688    */
2689
2690   GtkTreeIter iter;
2691   GtkTreeStore *store;
2692   GtkTreeModel *filter;
2693   GtkTreeModel *sort;
2694
2695   GtkWidget *window;
2696   GtkWidget *tree_view;
2697
2698   store = gtk_tree_store_new (1, G_TYPE_STRING);
2699   gtk_tree_store_append (store, &iter, NULL);
2700   gtk_tree_store_set (store, &iter, 0, "Hello1", -1);
2701
2702   gtk_tree_store_append (store, &iter, NULL);
2703   gtk_tree_store_set (store, &iter, 0, "Hello2", -1);
2704
2705   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
2706   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
2707                                            specific_sort_filter_remove_node_compare_func, NULL, NULL);
2708
2709   filter = gtk_tree_model_filter_new (sort, NULL);
2710   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
2711                                           specific_sort_filter_remove_node_visible_func,
2712                                           filter, NULL);
2713
2714
2715   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2716   tree_view = gtk_tree_view_new_with_model (filter);
2717   gtk_container_add (GTK_CONTAINER (window), tree_view);
2718   gtk_widget_realize (tree_view);
2719
2720   while (gtk_events_pending ())
2721     gtk_main_iteration ();
2722
2723   /* Remove a node */
2724   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
2725   gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
2726   gtk_tree_store_remove (store, &iter);
2727
2728   while (gtk_events_pending ())
2729     gtk_main_iteration ();
2730 }
2731
2732
2733 static void
2734 specific_sort_filter_remove_root (void)
2735 {
2736   /* This test is based on one of the test cases I found in my
2737    * old test cases directory.  I unfortunately do not have a record
2738    * from who this test case originated.  -Kris.
2739    */
2740
2741   GtkTreeModel *model, *sort, *filter;
2742   GtkTreeIter root, mid, leaf;
2743   GtkTreePath *path;
2744
2745   model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT));
2746   gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL);
2747   gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root);
2748   gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid);
2749
2750   path = gtk_tree_model_get_path (model, &mid);
2751
2752   sort = gtk_tree_model_sort_new_with_model (model);
2753   filter = gtk_tree_model_filter_new (sort, path);
2754
2755   gtk_tree_path_free (path);
2756
2757   gtk_tree_store_remove (GTK_TREE_STORE (model), &root);
2758
2759   g_object_unref (filter);
2760   g_object_unref (sort);
2761   g_object_unref (model);
2762 }
2763
2764
2765 static void
2766 specific_root_mixed_visibility (void)
2767 {
2768   int i;
2769   GtkTreeModel *filter;
2770   /* A bit nasty, apologies */
2771   FilterTest fixture;
2772
2773   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2774
2775   for (i = 0; i < LEVEL_LENGTH; i++)
2776     {
2777       GtkTreeIter iter;
2778
2779       gtk_tree_store_insert (fixture.store, &iter, NULL, i);
2780       if (i % 2 == 0)
2781         create_tree_store_set_values (fixture.store, &iter, TRUE);
2782       else
2783         create_tree_store_set_values (fixture.store, &iter, FALSE);
2784     }
2785
2786   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2787   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2788   fixture.monitor = NULL;
2789
2790   gtk_tree_model_filter_set_visible_column (fixture.filter, 1);
2791
2792   /* In order to trigger the potential bug, we should not access
2793    * the filter model here (so don't call the check functions).
2794    */
2795
2796   /* Change visibility of an odd row to TRUE */
2797   set_path_visibility (&fixture, "3", TRUE);
2798   check_filter_model (&fixture);
2799   check_level_length (fixture.filter, NULL, 4);
2800 }
2801
2802
2803
2804 static gboolean
2805 specific_has_child_filter_filter_func (GtkTreeModel *model,
2806                                        GtkTreeIter  *iter,
2807                                        gpointer      data)
2808 {
2809   return gtk_tree_model_iter_has_child (model, iter);
2810 }
2811
2812 static void
2813 specific_has_child_filter (void)
2814 {
2815   GtkTreeModel *filter;
2816   GtkTreeIter iter, root;
2817   FilterTest fixture; /* This is not how it should be done */
2818   GtkWidget *tree_view;
2819
2820   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2821   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2822   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2823   fixture.monitor = signal_monitor_new (filter);
2824
2825   tree_view = gtk_tree_view_new_with_model (filter);
2826
2827   /* We will filter on parent state using a filter function.  We will
2828    * manually keep the boolean column in sync, so that we can use
2829    * check_filter_model() to check the consistency of the model.
2830    */
2831   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
2832    * to be able to check the structure here.  We keep the calls to
2833    * check_filter_model() commented out until then.
2834    */
2835   gtk_tree_model_filter_set_visible_func (fixture.filter,
2836                                           specific_has_child_filter_filter_func,
2837                                           NULL, NULL);
2838
2839   /* The first node will be initially invisible: no signals */
2840   gtk_tree_store_append (fixture.store, &root, NULL);
2841   create_tree_store_set_values (fixture.store, &root, FALSE);
2842
2843   /* check_filter_model (&fixture); */
2844   check_level_length (fixture.filter, NULL, 0);
2845   signal_monitor_assert_is_empty (fixture.monitor);
2846
2847   /* Insert a child node. This will cause the parent to become visible
2848    * since there is a child now.
2849    */
2850   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
2851   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
2852   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
2853
2854   gtk_tree_store_append (fixture.store, &iter, &root);
2855   create_tree_store_set_values (fixture.store, &iter, TRUE);
2856
2857   /* Parent must now be visible.  Do the level length check first,
2858    * to avoid modifying the child model triggering a row-changed to
2859    * the filter model.
2860    */
2861   check_level_length (fixture.filter, NULL, 1);
2862   check_level_length (fixture.filter, "0", 0);
2863   signal_monitor_assert_is_empty (fixture.monitor);
2864
2865   /* This should propagate row-changed */
2866   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
2867   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
2868
2869   set_path_visibility (&fixture, "0", TRUE);
2870   /* check_filter_model (&fixture); */
2871   signal_monitor_assert_is_empty (fixture.monitor);
2872
2873   /* New root node, no child, so no signal */
2874   gtk_tree_store_append (fixture.store, &root, NULL);
2875   check_level_length (fixture.filter, NULL, 1);
2876   signal_monitor_assert_is_empty (fixture.monitor);
2877
2878   /* When the child comes in, this node will become visible */
2879   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
2880   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
2881   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
2882   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
2883   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
2884
2885   gtk_tree_store_append (fixture.store, &iter, &root);
2886   check_level_length (fixture.filter, NULL, 2);
2887   check_level_length (fixture.filter, "1", 0);
2888
2889   create_tree_store_set_values (fixture.store, &root, TRUE);
2890   create_tree_store_set_values (fixture.store, &iter, TRUE);
2891
2892   /* check_filter_model (&fixture); */
2893   signal_monitor_assert_is_empty (fixture.monitor);
2894
2895   /* Add another child for 1 */
2896   gtk_tree_store_append (fixture.store, &iter, &root);
2897   create_tree_store_set_values (fixture.store, &iter, TRUE);
2898   check_level_length (fixture.filter, NULL, 2);
2899   check_level_length (fixture.filter, "0", 0);
2900   check_level_length (fixture.filter, "1", 0);
2901   signal_monitor_assert_is_empty (fixture.monitor);
2902
2903   /* Now remove one of the remaining child rows */
2904   signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
2905
2906   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
2907                                        &iter, "0:0");
2908   gtk_tree_store_remove (fixture.store, &iter);
2909
2910   check_level_length (fixture.filter, NULL, 1);
2911   check_level_length (fixture.filter, "0", 0);
2912
2913   set_path_visibility (&fixture, "0", FALSE);
2914   /* check_filter_model (&fixture); */
2915   signal_monitor_assert_is_empty (fixture.monitor);
2916 }
2917
2918
2919 static gboolean
2920 specific_root_has_child_filter_filter_func (GtkTreeModel *model,
2921                                             GtkTreeIter  *iter,
2922                                             gpointer      data)
2923 {
2924   int depth;
2925   GtkTreePath *path;
2926
2927   path = gtk_tree_model_get_path (model, iter);
2928   depth = gtk_tree_path_get_depth (path);
2929   gtk_tree_path_free (path);
2930
2931   if (depth > 1)
2932     return TRUE;
2933   /* else */
2934   return gtk_tree_model_iter_has_child (model, iter);
2935 }
2936
2937 static void
2938 specific_root_has_child_filter (void)
2939 {
2940   GtkTreeModel *filter;
2941   GtkTreeIter iter, root;
2942   FilterTest fixture; /* This is not how it should be done ... */
2943   GtkWidget *tree_view;
2944
2945   /* This is a variation on the above test case, specific has-child-filter,
2946    * herein the has-child check for visibility only applies to root level
2947    * nodes.  In this test, children are always visible because we
2948    * only filter based on the "has child" criterion.
2949    */
2950
2951   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
2952   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
2953   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
2954   fixture.monitor = signal_monitor_new (filter);
2955
2956   tree_view = gtk_tree_view_new_with_model (filter);
2957
2958   /* We will filter on parent state using a filter function.  We will
2959    * manually keep the boolean column in sync, so that we can use
2960    * check_filter_model() to check the consistency of the model.
2961    */
2962   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
2963    * to be able to check the structure here.  We keep the calls to
2964    * check_filter_model() commented out until then.
2965    */
2966   gtk_tree_model_filter_set_visible_func (fixture.filter,
2967                                           specific_root_has_child_filter_filter_func,
2968                                           NULL, NULL);
2969
2970   /* Add a first node, this will be invisible initially, so no signal
2971    * should be emitted.
2972    */
2973   gtk_tree_store_append (fixture.store, &root, NULL);
2974   create_tree_store_set_values (fixture.store, &root, FALSE);
2975
2976   signal_monitor_assert_is_empty (fixture.monitor);
2977   /* check_filter_model (&fixture); */
2978   check_level_length (fixture.filter, NULL, 0);
2979
2980   /* Add a child node.  This will cause the parent to become visible,
2981    * so we expect row-inserted signals for both.
2982    */
2983   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
2984   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
2985   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
2986
2987   gtk_tree_store_append (fixture.store, &iter, &root);
2988   signal_monitor_assert_is_empty (fixture.monitor);
2989
2990   check_level_length (fixture.filter, NULL, 1);
2991   check_level_length (fixture.filter, "0", 1);
2992
2993   /* Modify the content of iter, no signals because the parent is not
2994    * expanded.
2995    */
2996   create_tree_store_set_values (fixture.store, &iter, TRUE);
2997   signal_monitor_assert_is_empty (fixture.monitor);
2998
2999   /* Parent must now be visible.  Do the level length check first,
3000    * to avoid modifying the child model triggering a row-changed to
3001    * the filter model.
3002    */
3003   check_level_length (fixture.filter, NULL, 1);
3004   check_level_length (fixture.filter, "0", 1);
3005
3006   /* Modify path 0 */
3007   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
3008   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3009
3010   set_path_visibility (&fixture, "0", TRUE);
3011   /* check_filter_model (&fixture); */
3012
3013   signal_monitor_assert_is_empty (fixture.monitor);
3014
3015   /* Insert another node in the root level.  Initially invisible, so
3016    * not expecting any signal.
3017    */
3018   gtk_tree_store_append (fixture.store, &root, NULL);
3019   check_level_length (fixture.filter, NULL, 1);
3020
3021   signal_monitor_assert_is_empty (fixture.monitor);
3022
3023   /* Adding a child node which also makes parent at path 1 visible. */
3024   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
3025   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3026   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3027
3028   gtk_tree_store_append (fixture.store, &iter, &root);
3029   check_level_length (fixture.filter, NULL, 2);
3030   check_level_length (fixture.filter, "1", 1);
3031
3032   signal_monitor_assert_is_empty (fixture.monitor);
3033
3034   /* Check if row-changed is propagated */
3035   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
3036   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3037
3038   create_tree_store_set_values (fixture.store, &root, TRUE);
3039   create_tree_store_set_values (fixture.store, &iter, TRUE);
3040   /* check_filter_model (&fixture); */
3041   signal_monitor_assert_is_empty (fixture.monitor);
3042
3043   /* Insert another child under node 1 */
3044   gtk_tree_store_append (fixture.store, &iter, &root);
3045   create_tree_store_set_values (fixture.store, &iter, TRUE);
3046   check_level_length (fixture.filter, NULL, 2);
3047   check_level_length (fixture.filter, "0", 1);
3048   check_level_length (fixture.filter, "1", 2);
3049   signal_monitor_assert_is_empty (fixture.monitor);
3050
3051   /* Set a child node to invisible.  This should not yield any
3052    * change, because filtering is only done on whether the root
3053    * node has a child, which it still has.
3054    */
3055   set_path_visibility (&fixture, "0:0", FALSE);
3056   signal_monitor_assert_is_empty (fixture.monitor);
3057
3058   /* Now remove one of the remaining child rows */
3059   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3060   signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
3061
3062   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
3063                                        &iter, "0:0");
3064   gtk_tree_store_remove (fixture.store, &iter);
3065
3066   check_level_length (fixture.filter, NULL, 1);
3067   check_level_length (fixture.filter, "0", 2);
3068   signal_monitor_assert_is_empty (fixture.monitor);
3069
3070   /* Set visibility of 0 to FALSE, no-op for filter model since
3071    * the child 0:0 is already gone
3072    */
3073   set_path_visibility (&fixture, "0", FALSE);
3074   /* check_filter_model (&fixture); */
3075   signal_monitor_assert_is_empty (fixture.monitor);
3076 }
3077
3078 static void
3079 specific_has_child_filter_on_sort_model (void)
3080 {
3081   GtkTreeModel *filter;
3082   GtkTreeModel *sort_model;
3083   GtkTreeIter iter, root;
3084   FilterTest fixture; /* This is not how it should be done */
3085   GtkWidget *tree_view;
3086
3087   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
3088   sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store));
3089   filter = gtk_tree_model_filter_new (sort_model, NULL);
3090   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
3091   fixture.monitor = signal_monitor_new (filter);
3092
3093   tree_view = gtk_tree_view_new_with_model (filter);
3094
3095   /* We will filter on parent state using a filter function.  We will
3096    * manually keep the boolean column in sync, so that we can use
3097    * check_filter_model() to check the consistency of the model.
3098    */
3099   /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
3100    * to be able to check the structure here.  We keep the calls to
3101    * check_filter_model() commented out until then.
3102    */
3103   gtk_tree_model_filter_set_visible_func (fixture.filter,
3104                                           specific_has_child_filter_filter_func,
3105                                           NULL, NULL);
3106
3107   /* The first node will be initially invisible: no signals */
3108   gtk_tree_store_append (fixture.store, &root, NULL);
3109   create_tree_store_set_values (fixture.store, &root, FALSE);
3110
3111   /* check_filter_model (&fixture); */
3112   check_level_length (fixture.filter, NULL, 0);
3113   signal_monitor_assert_is_empty (fixture.monitor);
3114
3115   /* Insert a child node. This will cause the parent to become visible
3116    * since there is a child now.
3117    */
3118   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
3119   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3120
3121   gtk_tree_store_append (fixture.store, &iter, &root);
3122   create_tree_store_set_values (fixture.store, &iter, TRUE);
3123
3124   /* Parent must now be visible.  Do the level length check first,
3125    * to avoid modifying the child model triggering a row-changed to
3126    * the filter model.
3127    */
3128   check_level_length (fixture.filter, NULL, 1);
3129   check_level_length (fixture.filter, "0", 0);
3130   signal_monitor_assert_is_empty (fixture.monitor);
3131
3132   /* This should propagate row-changed */
3133   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
3134   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3135
3136   set_path_visibility (&fixture, "0", TRUE);
3137   /* check_filter_model (&fixture); */
3138   signal_monitor_assert_is_empty (fixture.monitor);
3139
3140   /* New root node, no child, so no signal */
3141   gtk_tree_store_append (fixture.store, &root, NULL);
3142   check_level_length (fixture.filter, NULL, 1);
3143   signal_monitor_assert_is_empty (fixture.monitor);
3144
3145   /* When the child comes in, this node will become visible */
3146   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
3147   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3148   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
3149   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3150
3151   gtk_tree_store_append (fixture.store, &iter, &root);
3152   check_level_length (fixture.filter, NULL, 2);
3153   check_level_length (fixture.filter, "1", 0);
3154
3155   create_tree_store_set_values (fixture.store, &root, TRUE);
3156   create_tree_store_set_values (fixture.store, &iter, TRUE);
3157
3158   /* check_filter_model (&fixture); */
3159   signal_monitor_assert_is_empty (fixture.monitor);
3160
3161   /* Add another child for 1 */
3162   gtk_tree_store_append (fixture.store, &iter, &root);
3163   create_tree_store_set_values (fixture.store, &iter, TRUE);
3164   check_level_length (fixture.filter, NULL, 2);
3165   check_level_length (fixture.filter, "0", 0);
3166   check_level_length (fixture.filter, "1", 0);
3167   signal_monitor_assert_is_empty (fixture.monitor);
3168
3169   /* Now remove one of the remaining child rows */
3170   signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
3171
3172   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
3173                                        &iter, "0:0");
3174   gtk_tree_store_remove (fixture.store, &iter);
3175
3176   check_level_length (fixture.filter, NULL, 1);
3177   check_level_length (fixture.filter, "0", 0);
3178
3179   set_path_visibility (&fixture, "0", FALSE);
3180   /* check_filter_model (&fixture); */
3181   signal_monitor_assert_is_empty (fixture.monitor);
3182 }
3183
3184 static gboolean
3185 specific_at_least_2_children_filter_filter_func (GtkTreeModel *model,
3186                                                  GtkTreeIter  *iter,
3187                                                  gpointer      data)
3188 {
3189   return gtk_tree_model_iter_n_children (model, iter) >= 2;
3190 }
3191
3192 static void
3193 specific_at_least_2_children_filter (void)
3194 {
3195   GtkTreeModel *filter;
3196   GtkTreeIter iter, root;
3197   FilterTest fixture; /* This is not how it should be done */
3198   GtkWidget *tree_view;
3199
3200   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
3201   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
3202   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
3203   fixture.monitor = signal_monitor_new (filter);
3204
3205   tree_view = gtk_tree_view_new_with_model (filter);
3206
3207   gtk_tree_model_filter_set_visible_func (fixture.filter,
3208                                           specific_at_least_2_children_filter_filter_func,
3209                                           NULL, NULL);
3210
3211   /* The first node will be initially invisible: no signals */
3212   gtk_tree_store_append (fixture.store, &root, NULL);
3213   create_tree_store_set_values (fixture.store, &root, FALSE);
3214
3215   /* check_filter_model (&fixture); */
3216   check_level_length (fixture.filter, NULL, 0);
3217   signal_monitor_assert_is_empty (fixture.monitor);
3218
3219   /* Insert a child node.  Nothing should happen.
3220    */
3221   gtk_tree_store_append (fixture.store, &iter, &root);
3222   create_tree_store_set_values (fixture.store, &iter, TRUE);
3223
3224   check_level_length (fixture.filter, NULL, 0);
3225   signal_monitor_assert_is_empty (fixture.monitor);
3226
3227   /* Insert a second child node.  This will cause the parent to become
3228    * visible.
3229    */
3230   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
3231   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3232
3233   gtk_tree_store_append (fixture.store, &iter, &root);
3234   create_tree_store_set_values (fixture.store, &iter, TRUE);
3235
3236   /* Parent must now be visible.  Do the level length check first,
3237    * to avoid modifying the child model triggering a row-changed to
3238    * the filter model.
3239    */
3240   check_level_length (fixture.filter, NULL, 1);
3241   check_level_length (fixture.filter, "0", 0);
3242   signal_monitor_assert_is_empty (fixture.monitor);
3243
3244   /* This should propagate row-changed */
3245   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
3246   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3247
3248   set_path_visibility (&fixture, "0", TRUE);
3249   /* check_filter_model (&fixture); */
3250   signal_monitor_assert_is_empty (fixture.monitor);
3251
3252   /* New root node, no child, so no signal */
3253   gtk_tree_store_append (fixture.store, &root, NULL);
3254   check_level_length (fixture.filter, NULL, 1);
3255   signal_monitor_assert_is_empty (fixture.monitor);
3256
3257   /* First child, no signal, no change */
3258   gtk_tree_store_append (fixture.store, &iter, &root);
3259   check_level_length (fixture.filter, NULL, 1);
3260   signal_monitor_assert_is_empty (fixture.monitor);
3261
3262   /* When the second child comes in, this node will become visible */
3263   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
3264   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3265   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
3266   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
3267
3268   gtk_tree_store_append (fixture.store, &iter, &root);
3269   check_level_length (fixture.filter, NULL, 2);
3270   check_level_length (fixture.filter, "1", 0);
3271
3272   create_tree_store_set_values (fixture.store, &root, TRUE);
3273   create_tree_store_set_values (fixture.store, &iter, TRUE);
3274
3275   /* check_filter_model (&fixture); */
3276   signal_monitor_assert_is_empty (fixture.monitor);
3277
3278   /* Add another child for 1 */
3279   gtk_tree_store_append (fixture.store, &iter, &root);
3280   create_tree_store_set_values (fixture.store, &iter, TRUE);
3281   check_level_length (fixture.filter, NULL, 2);
3282   check_level_length (fixture.filter, "0", 0);
3283   check_level_length (fixture.filter, "1", 0);
3284   signal_monitor_assert_is_empty (fixture.monitor);
3285
3286   /* Now remove one of the remaining child rows */
3287   signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
3288
3289   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
3290                                        &iter, "0:0");
3291   gtk_tree_store_remove (fixture.store, &iter);
3292
3293   check_level_length (fixture.filter, NULL, 1);
3294   check_level_length (fixture.filter, "0", 0);
3295
3296   set_path_visibility (&fixture, "0", FALSE);
3297   /* check_filter_model (&fixture); */
3298   signal_monitor_assert_is_empty (fixture.monitor);
3299 }
3300
3301 static void
3302 specific_at_least_2_children_filter_on_sort_model (void)
3303 {
3304   GtkTreeModel *filter;
3305   GtkTreeModel *sort_model;
3306   GtkTreeIter iter, root;
3307   FilterTest fixture; /* This is not how it should be done */
3308   GtkWidget *tree_view;
3309
3310   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
3311   sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store));
3312   filter = gtk_tree_model_filter_new (sort_model, NULL);
3313   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
3314   fixture.monitor = signal_monitor_new (filter);
3315
3316   tree_view = gtk_tree_view_new_with_model (filter);
3317
3318   gtk_tree_model_filter_set_visible_func (fixture.filter,
3319                                           specific_at_least_2_children_filter_filter_func,
3320                                           NULL, NULL);
3321
3322   /* The first node will be initially invisible: no signals */
3323   gtk_tree_store_append (fixture.store, &root, NULL);
3324   create_tree_store_set_values (fixture.store, &root, FALSE);
3325
3326   /* check_filter_model (&fixture); */
3327   check_level_length (fixture.filter, NULL, 0);
3328   signal_monitor_assert_is_empty (fixture.monitor);
3329
3330   /* Insert a child node.  Nothing should happen.
3331    */
3332   gtk_tree_store_append (fixture.store, &iter, &root);
3333   create_tree_store_set_values (fixture.store, &iter, TRUE);
3334
3335   check_level_length (fixture.filter, NULL, 0);
3336   signal_monitor_assert_is_empty (fixture.monitor);
3337
3338     {
3339       GtkTreePath *path = gtk_tree_path_new_from_indices (0, 0, -1);
3340       GtkTreeRowReference *ref;
3341
3342       ref = gtk_tree_row_reference_new (sort_model, path);
3343       gtk_tree_path_free (path);
3344     }
3345
3346   /* Insert a second child node.  This will cause the parent to become
3347    * visible.
3348    */
3349   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
3350   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3351
3352   gtk_tree_store_append (fixture.store, &iter, &root);
3353   create_tree_store_set_values (fixture.store, &iter, TRUE);
3354
3355   /* Parent must now be visible.  Do the level length check first,
3356    * to avoid modifying the child model triggering a row-changed to
3357    * the filter model.
3358    */
3359   check_level_length (fixture.filter, NULL, 1);
3360   check_level_length (fixture.filter, "0", 0);
3361   signal_monitor_assert_is_empty (fixture.monitor);
3362
3363   /* This should propagate row-changed */
3364   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
3365   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
3366
3367   set_path_visibility (&fixture, "0", TRUE);
3368   /* check_filter_model (&fixture); */
3369   signal_monitor_assert_is_empty (fixture.monitor);
3370
3371   /* New root node, no child, so no signal */
3372   gtk_tree_store_append (fixture.store, &root, NULL);
3373   check_level_length (fixture.filter, NULL, 1);
3374   signal_monitor_assert_is_empty (fixture.monitor);
3375 }
3376
3377
3378 static void
3379 specific_filter_add_child (void)
3380 {
3381   /* This test is based on one of the test cases I found in my
3382    * old test cases directory.  I unfortunately do not have a record
3383    * from who this test case originated.  -Kris.
3384    */
3385
3386   GtkTreeIter iter;
3387   GtkTreeIter iter_first;
3388   GtkTreeIter child;
3389   GtkTreeStore *store;
3390   GtkTreeModel *filter G_GNUC_UNUSED;
3391
3392   store = gtk_tree_store_new (1, G_TYPE_STRING);
3393
3394   gtk_tree_store_append (store, &iter_first, NULL);
3395   gtk_tree_store_set (store, &iter_first, 0, "Hello", -1);
3396
3397   gtk_tree_store_append (store, &iter, NULL);
3398   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
3399
3400   gtk_tree_store_append (store, &iter, NULL);
3401   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
3402
3403   gtk_tree_store_append (store, &iter, NULL);
3404   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
3405
3406   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
3407
3408   gtk_tree_store_set (store, &iter, 0, "Hello", -1);
3409   gtk_tree_store_append (store, &child, &iter_first);
3410   gtk_tree_store_set (store, &child, 0, "Hello", -1);
3411 }
3412
3413 static void
3414 specific_list_store_clear (void)
3415 {
3416   GtkTreeIter iter;
3417   GtkListStore *list;
3418   GtkTreeModel *filter;
3419   GtkWidget *view G_GNUC_UNUSED;
3420
3421   list = gtk_list_store_new (1, G_TYPE_INT);
3422   gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1);
3423   gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1);
3424   gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1);
3425   gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1);
3426   gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1);
3427   gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1);
3428   gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1);
3429   gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1);
3430
3431   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL);
3432   view = gtk_tree_view_new_with_model (filter);
3433
3434   gtk_list_store_clear (list);
3435 }
3436
3437 static void
3438 specific_sort_ref_leaf_and_remove_ancestor (void)
3439 {
3440   GtkTreeIter iter, child, child2, child3;
3441   GtkTreeStore *tree;
3442   GtkTreeModel *sort;
3443   GtkTreePath *path;
3444   GtkTreeRowReference *rowref;
3445   GtkWidget *view G_GNUC_UNUSED;
3446
3447   tree = gtk_tree_store_new (1, G_TYPE_INT);
3448   gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1);
3449   gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1);
3450   gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1);
3451   gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1);
3452
3453   gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1);
3454   gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1);
3455   gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1);
3456
3457   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree));
3458   view = gtk_tree_view_new_with_model (sort);
3459   gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
3460
3461   path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1);
3462   rowref = gtk_tree_row_reference_new (sort, path);
3463   gtk_tree_path_free (path);
3464
3465   path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1);
3466   rowref = gtk_tree_row_reference_new (sort, path);
3467   gtk_tree_path_free (path);
3468
3469   path = gtk_tree_path_new_from_indices (3, 0, -1);
3470   rowref = gtk_tree_row_reference_new (sort, path);
3471   gtk_tree_path_free (path);
3472
3473   path = gtk_tree_path_new_from_indices (3, -1);
3474   rowref = gtk_tree_row_reference_new (sort, path);
3475   gtk_tree_path_free (path);
3476
3477   /* Deleting a parent */
3478   path = gtk_tree_path_new_from_indices (3, 0, -1);
3479   gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path);
3480   gtk_tree_store_remove (tree, &iter);
3481   gtk_tree_path_free (path);
3482
3483   gtk_tree_row_reference_free (rowref);
3484 }
3485
3486 static void
3487 specific_ref_leaf_and_remove_ancestor (void)
3488 {
3489   GtkTreeIter iter, child, child2, child3;
3490   GtkTreeStore *tree;
3491   GtkTreeModel *filter;
3492   GtkTreePath *path;
3493   GtkTreeRowReference *rowref;
3494   GtkWidget *view G_GNUC_UNUSED;
3495
3496   tree = gtk_tree_store_new (1, G_TYPE_INT);
3497   gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1);
3498   gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1);
3499   gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1);
3500   gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1);
3501
3502   gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1);
3503   gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1);
3504   gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1);
3505
3506   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), NULL);
3507   view = gtk_tree_view_new_with_model (filter);
3508   gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
3509
3510   path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1);
3511   rowref = gtk_tree_row_reference_new (filter, path);
3512   gtk_tree_path_free (path);
3513
3514   path = gtk_tree_path_new_from_indices (3, 0, 0, 0, -1);
3515   rowref = gtk_tree_row_reference_new (filter, path);
3516   gtk_tree_path_free (path);
3517
3518   path = gtk_tree_path_new_from_indices (3, 0, -1);
3519   rowref = gtk_tree_row_reference_new (filter, path);
3520   gtk_tree_path_free (path);
3521
3522   path = gtk_tree_path_new_from_indices (3, -1);
3523   rowref = gtk_tree_row_reference_new (filter, path);
3524   gtk_tree_path_free (path);
3525
3526   /* Deleting a parent */
3527   path = gtk_tree_path_new_from_indices (3, 0, -1);
3528   gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path);
3529   gtk_tree_store_remove (tree, &iter);
3530   gtk_tree_path_free (path);
3531
3532   gtk_tree_row_reference_free (rowref);
3533 }
3534
3535 static void
3536 specific_virtual_ref_leaf_and_remove_ancestor (void)
3537 {
3538   GtkTreeIter iter, child, child2, child3;
3539   GtkTreeStore *tree;
3540   GtkTreeModel *filter;
3541   GtkTreePath *path;
3542   GtkTreeRowReference *rowref;
3543   GtkWidget *view G_GNUC_UNUSED;
3544
3545   tree = gtk_tree_store_new (1, G_TYPE_INT);
3546   gtk_tree_store_insert_with_values (tree, &iter, NULL, 0, 0, 1, -1);
3547   gtk_tree_store_insert_with_values (tree, &iter, NULL, 1, 0, 2, -1);
3548   gtk_tree_store_insert_with_values (tree, &iter, NULL, 2, 0, 3, -1);
3549   gtk_tree_store_insert_with_values (tree, &iter, NULL, 3, 0, 4, -1);
3550
3551   gtk_tree_store_insert_with_values (tree, &child, &iter, 0, 0, 50, -1);
3552   gtk_tree_store_insert_with_values (tree, &child2, &child, 0, 0, 6, -1);
3553   gtk_tree_store_insert_with_values (tree, &child3, &child2, 0, 0, 7, -1);
3554
3555   /* Set a virtual root of 3:0 */
3556   path = gtk_tree_path_new_from_indices (3, 0, -1);
3557   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree), path);
3558   gtk_tree_path_free (path);
3559
3560   view = gtk_tree_view_new_with_model (filter);
3561   gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
3562
3563   path = gtk_tree_path_new_from_indices (0, 0, -1);
3564   rowref = gtk_tree_row_reference_new (filter, path);
3565   gtk_tree_path_free (path);
3566
3567   path = gtk_tree_path_new_from_indices (0, 0, -1);
3568   rowref = gtk_tree_row_reference_new (filter, path);
3569   gtk_tree_path_free (path);
3570
3571   path = gtk_tree_path_new_from_indices (0, -1);
3572   rowref = gtk_tree_row_reference_new (filter, path);
3573   gtk_tree_path_free (path);
3574
3575   /* Deleting the virtual root */
3576   path = gtk_tree_path_new_from_indices (3, 0, -1);
3577   gtk_tree_model_get_iter (GTK_TREE_MODEL (tree), &iter, path);
3578   gtk_tree_store_remove (tree, &iter);
3579   gtk_tree_path_free (path);
3580
3581   gtk_tree_row_reference_free (rowref);
3582 }
3583
3584
3585 static int
3586 specific_bug_301558_sort_func (GtkTreeModel *model,
3587                                GtkTreeIter  *a,
3588                                GtkTreeIter  *b,
3589                                gpointer      data)
3590 {
3591   int i, j;
3592
3593   gtk_tree_model_get (model, a, 0, &i, -1);
3594   gtk_tree_model_get (model, b, 0, &j, -1);
3595
3596   return j - i;
3597 }
3598
3599 static void
3600 specific_bug_301558 (void)
3601 {
3602   /* Test case for GNOME Bugzilla bug 301558 provided by
3603    * Markku Vire.
3604    */
3605   GtkTreeStore *tree;
3606   GtkTreeModel *filter;
3607   GtkTreeModel *sort;
3608   GtkTreeIter root, iter, iter2;
3609   GtkWidget *view G_GNUC_UNUSED;
3610   int i;
3611   gboolean add;
3612
3613   g_test_bug ("301558");
3614
3615   tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN);
3616   gtk_tree_store_append (tree, &iter, NULL);
3617   gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1);
3618   gtk_tree_store_append (tree, &iter2, &iter);
3619   gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1);
3620
3621   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree));
3622   gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort),
3623                                            specific_bug_301558_sort_func,
3624                                            NULL, NULL);
3625
3626   filter = gtk_tree_model_filter_new (sort, NULL);
3627   gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1);
3628
3629   view = gtk_tree_view_new_with_model (filter);
3630
3631   while (gtk_events_pending ())
3632     gtk_main_iteration ();
3633
3634   add = TRUE;
3635
3636   for (i = 0; i < 10; i++)
3637     {
3638       if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root))
3639         g_assert_not_reached ();
3640
3641       if (add)
3642         {
3643           gtk_tree_store_append (tree, &iter, &root);
3644           gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1);
3645         }
3646       else
3647         {
3648           int n;
3649           n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root);
3650           gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter,
3651                                          &root, n - 1);
3652           gtk_tree_store_remove (tree, &iter);
3653         }
3654
3655       add = !add;
3656     }
3657 }
3658
3659
3660 static gboolean
3661 specific_bug_311955_filter_func (GtkTreeModel *model,
3662                                  GtkTreeIter  *iter,
3663                                  gpointer      data)
3664 {
3665   int value;
3666
3667   gtk_tree_model_get (model, iter, 0, &value, -1);
3668
3669   return (value != 0);
3670 }
3671
3672 static void
3673 specific_bug_311955 (void)
3674 {
3675   /* This is a test case for GNOME Bugzilla bug 311955.  It was written
3676    * by Markku Vire.
3677    */
3678   GtkTreeIter iter, child, root;
3679   GtkTreeStore *store;
3680   GtkTreeModel *sort;
3681   GtkTreeModel *filter;
3682
3683   GtkWidget *window G_GNUC_UNUSED;
3684   GtkWidget *tree_view;
3685   int i;
3686   int n;
3687   GtkTreePath *path;
3688
3689   g_test_bug ("311955");
3690
3691   store = gtk_tree_store_new (1, G_TYPE_INT);
3692
3693   gtk_tree_store_append (store, &root, NULL);
3694   gtk_tree_store_set (store, &root, 0, 33, -1);
3695
3696   gtk_tree_store_append (store, &iter, &root);
3697   gtk_tree_store_set (store, &iter, 0, 50, -1);
3698
3699   gtk_tree_store_append (store, &iter, NULL);
3700   gtk_tree_store_set (store, &iter, 0, 22, -1);
3701
3702   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
3703   filter = gtk_tree_model_filter_new (sort, NULL);
3704
3705   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
3706                                           specific_bug_311955_filter_func,
3707                                           NULL, NULL);
3708
3709   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
3710   tree_view = gtk_tree_view_new_with_model (filter);
3711   g_object_unref (store);
3712
3713   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
3714
3715   while (gtk_events_pending ())
3716     gtk_main_iteration ();
3717
3718   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
3719   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1);
3720
3721   /* Fill model */
3722   for (i = 0; i < 4; i++)
3723     {
3724       gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
3725
3726       gtk_tree_store_append (store, &iter, &root);
3727
3728       if (i < 3)
3729         gtk_tree_store_set (store, &iter, 0, i, -1);
3730
3731       if (i % 2 == 0)
3732         {
3733           gtk_tree_store_append (store, &child, &iter);
3734           gtk_tree_store_set (store, &child, 0, 10, -1);
3735         }
3736     }
3737
3738   while (gtk_events_pending ())
3739     gtk_main_iteration ();
3740
3741   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
3742   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 1);
3743
3744   /* Remove bottommost child from the tree. */
3745   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
3746   n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root);
3747
3748   if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2))
3749     {
3750       if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter))
3751         gtk_tree_store_remove (store, &child);
3752     }
3753   else
3754     g_assert_not_reached ();
3755
3756   path = gtk_tree_path_new_from_indices (0, 2, -1);
3757   gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
3758   gtk_tree_path_free (path);
3759
3760   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
3761   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0);
3762 }
3763
3764 static void
3765 specific_bug_311955_clean (void)
3766 {
3767   /* Cleaned up version of the test case for GNOME Bugzilla bug 311955,
3768    * which is easier to understand.
3769    */
3770   GtkTreeIter iter, child, grandchild;
3771   GtkTreeStore *store;
3772   GtkTreeModel *sort;
3773   GtkTreeModel *filter;
3774
3775   GtkWidget *tree_view;
3776   GtkTreePath *path;
3777
3778   store = gtk_tree_store_new (1, G_TYPE_INT);
3779
3780   gtk_tree_store_append (store, &iter, NULL);
3781   gtk_tree_store_set (store, &iter, 0, 1, -1);
3782
3783   gtk_tree_store_append (store, &child, &iter);
3784   gtk_tree_store_set (store, &child, 0, 1, -1);
3785
3786   sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
3787   filter = gtk_tree_model_filter_new (sort, NULL);
3788
3789   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
3790                                           specific_bug_311955_filter_func,
3791                                           NULL, NULL);
3792
3793   tree_view = gtk_tree_view_new_with_model (filter);
3794   g_object_unref (store);
3795
3796   gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
3797
3798   while (gtk_events_pending ())
3799     gtk_main_iteration ();
3800
3801   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
3802   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1);
3803
3804   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
3805
3806   gtk_tree_store_append (store, &child, &iter);
3807   gtk_tree_store_set (store, &child, 0, 0, -1);
3808
3809   gtk_tree_store_append (store, &child, &iter);
3810   gtk_tree_store_set (store, &child, 0, 1, -1);
3811
3812   gtk_tree_store_append (store, &child, &iter);
3813   gtk_tree_store_set (store, &child, 0, 1, -1);
3814
3815   gtk_tree_store_append (store, &grandchild, &child);
3816   gtk_tree_store_set (store, &grandchild, 0, 1, -1);
3817
3818   gtk_tree_store_append (store, &child, &iter);
3819   /* Don't set a value: assume 0 */
3820
3821   /* Remove leaf node, check trigger row-has-child-toggled */
3822   path = gtk_tree_path_new_from_indices (0, 3, 0, -1);
3823   gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
3824   gtk_tree_path_free (path);
3825   gtk_tree_store_remove (store, &iter);
3826
3827   path = gtk_tree_path_new_from_indices (0, 2, -1);
3828   gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
3829   gtk_tree_path_free (path);
3830
3831   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
3832   check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0);
3833
3834   gtk_widget_destroy (tree_view);
3835 }
3836
3837 static void
3838 specific_bug_346800 (void)
3839 {
3840   /* This is a test case for GNOME Bugzilla bug 346800.  It was written
3841    * by Jonathan Matthew.
3842    */
3843
3844   GtkTreeIter node_iters[50];
3845   GtkTreeIter child_iters[50];
3846   GtkTreeModel *model;
3847   GtkTreeModelFilter *filter;
3848   GtkTreeStore *store;
3849   GType *columns;
3850   int i;
3851   int items = 50;
3852   columns = g_new (GType, 2);
3853   columns[0] = G_TYPE_STRING;
3854   columns[1] = G_TYPE_BOOLEAN;
3855   store = gtk_tree_store_newv (2, columns);
3856   model = GTK_TREE_MODEL (store);
3857
3858   g_test_bug ("346800");
3859
3860   filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL));
3861   gtk_tree_model_filter_set_visible_column (filter, 1);
3862
3863   for (i=0; i<items; i++)
3864     {
3865       /* allocate random amounts of junk, otherwise the filter model's arrays can expand without moving */
3866
3867       g_malloc (138);
3868       gtk_tree_store_append (store, &node_iters[i], NULL);
3869       gtk_tree_store_set (store, &node_iters[i],
3870                           0, "something",
3871                           1, ((i%6) == 0) ? FALSE : TRUE,
3872                           -1);
3873
3874       g_malloc (47);
3875       gtk_tree_store_append (store, &child_iters[i], &node_iters[i]);
3876       gtk_tree_store_set (store, &child_iters[i],
3877                           0, "something else",
3878                           1, FALSE,
3879                           -1);
3880       gtk_tree_model_filter_refilter (filter);
3881
3882       if (i > 6)
3883         {
3884           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-1], 1,
3885                               (i & 1) ? TRUE : FALSE, -1);
3886           gtk_tree_model_filter_refilter (filter);
3887
3888           gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-2], 1,
3889                               (i & 1) ? FALSE: TRUE, -1);
3890           gtk_tree_model_filter_refilter (filter);
3891         }
3892     }
3893 }
3894
3895 static gboolean
3896 specific_bug_464173_visible_func (GtkTreeModel *model,
3897                                   GtkTreeIter  *iter,
3898                                   gpointer      data)
3899 {
3900   gboolean *visible = (gboolean *)data;
3901
3902   return *visible;
3903 }
3904
3905 static void
3906 specific_bug_464173 (void)
3907 {
3908   /* Test case for GNOME Bugzilla bug 464173, test case written
3909    * by Andreas Koehler.
3910    */
3911   GtkTreeStore *model;
3912   GtkTreeModelFilter *f_model;
3913   GtkTreeIter iter1, iter2;
3914   GtkWidget *view G_GNUC_UNUSED;
3915   gboolean visible = TRUE;
3916
3917   g_test_bug ("464173");
3918
3919   model = gtk_tree_store_new (1, G_TYPE_STRING);
3920   gtk_tree_store_append (model, &iter1, NULL);
3921   gtk_tree_store_set (model, &iter1, 0, "Foo", -1);
3922   gtk_tree_store_append (model, &iter2, &iter1);
3923   gtk_tree_store_set (model, &iter2, 0, "Bar", -1);
3924
3925   f_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL(model), NULL));
3926   gtk_tree_model_filter_set_visible_func (f_model,
3927                                           specific_bug_464173_visible_func,
3928                                           &visible, NULL);
3929
3930   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (f_model));
3931
3932   visible = FALSE;
3933   gtk_tree_model_filter_refilter (f_model);
3934 }
3935
3936
3937 static gboolean
3938 specific_bug_540201_filter_func (GtkTreeModel *model,
3939                                  GtkTreeIter  *iter,
3940                                  gpointer      data)
3941 {
3942   gboolean has_children;
3943
3944   has_children = gtk_tree_model_iter_has_child (model, iter);
3945
3946   return has_children;
3947 }
3948
3949 static void
3950 specific_bug_540201 (void)
3951 {
3952   /* Test case for GNOME Bugzilla bug 540201, steps provided by
3953    * Charles Day.
3954    */
3955   GtkTreeIter iter, root;
3956   GtkTreeStore *store;
3957   GtkTreeModel *filter;
3958
3959   GtkWidget *tree_view G_GNUC_UNUSED;
3960
3961   g_test_bug ("540201");
3962
3963   store = gtk_tree_store_new (1, G_TYPE_INT);
3964
3965   gtk_tree_store_append (store, &root, NULL);
3966   gtk_tree_store_set (store, &root, 0, 33, -1);
3967
3968   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
3969   tree_view = gtk_tree_view_new_with_model (filter);
3970
3971   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
3972                                           specific_bug_540201_filter_func,
3973                                           NULL, NULL);
3974
3975   gtk_tree_store_append (store, &iter, &root);
3976   gtk_tree_store_set (store, &iter, 0, 50, -1);
3977
3978   gtk_tree_store_append (store, &iter, &root);
3979   gtk_tree_store_set (store, &iter, 0, 22, -1);
3980
3981
3982   gtk_tree_store_append (store, &root, NULL);
3983   gtk_tree_store_set (store, &root, 0, 33, -1);
3984
3985   gtk_tree_store_append (store, &iter, &root);
3986   gtk_tree_store_set (store, &iter, 0, 22, -1);
3987 }
3988
3989
3990 static gboolean
3991 specific_bug_549287_visible_func (GtkTreeModel *model,
3992                                   GtkTreeIter  *iter,
3993                                   gpointer      data)
3994 {
3995   gboolean result = FALSE;
3996
3997   result = gtk_tree_model_iter_has_child (model, iter);
3998
3999   return result;
4000 }
4001
4002 static void
4003 specific_bug_549287 (void)
4004 {
4005   /* Test case for GNOME Bugzilla bug 529287, provided by Julient Puydt */
4006
4007   int i;
4008   GtkTreeStore *store;
4009   GtkTreeModel *filtered;
4010   GtkWidget *view G_GNUC_UNUSED;
4011   GtkTreeIter iter;
4012   GtkTreeIter *swap, *parent, *child;
4013
4014   g_test_bug ("529287");
4015
4016   store = gtk_tree_store_new (1, G_TYPE_STRING);
4017   filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
4018   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered),
4019                                           specific_bug_549287_visible_func,
4020                                           NULL, NULL);
4021
4022   view = gtk_tree_view_new_with_model (filtered);
4023
4024   for (i = 0; i < 4; i++)
4025     {
4026       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
4027         {
4028           parent = gtk_tree_iter_copy (&iter);
4029           child = gtk_tree_iter_copy (&iter);
4030
4031           while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
4032                                                 child, parent, 0))
4033             {
4034
4035               swap = parent;
4036               parent = child;
4037               child = swap;
4038             }
4039
4040           gtk_tree_store_append (store, child, parent);
4041           gtk_tree_store_set (store, child,
4042                               0, "Something",
4043                               -1);
4044
4045           gtk_tree_iter_free (parent);
4046           gtk_tree_iter_free (child);
4047         }
4048       else
4049         {
4050           gtk_tree_store_append (store, &iter, NULL);
4051           gtk_tree_store_set (store, &iter,
4052                               0, "Something",
4053                               -1);
4054         }
4055
4056       /* since we inserted something, we changed the visibility conditions: */
4057       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filtered));
4058     }
4059 }
4060
4061 static gboolean
4062 specific_bug_621076_visible_func (GtkTreeModel *model,
4063                                   GtkTreeIter  *iter,
4064                                   gpointer      data)
4065 {
4066   gboolean visible = FALSE;
4067   gchar *str = NULL;
4068
4069   gtk_tree_model_get (model, iter, 0, &str, -1);
4070   if (str != NULL && g_str_has_prefix (str, "visible"))
4071     {
4072       visible = TRUE;
4073     }
4074   else
4075     {
4076       GtkTreeIter child_iter;
4077       gboolean valid;
4078
4079       /* Recursively check if we have a visible child */
4080       for (valid = gtk_tree_model_iter_children (model, &child_iter, iter);
4081            valid; valid = gtk_tree_model_iter_next (model, &child_iter))
4082         {
4083           if (specific_bug_621076_visible_func (model, &child_iter, data))
4084             {
4085               visible = TRUE;
4086               break;
4087             }
4088         }
4089     }
4090
4091   if (str)
4092     g_free (str);
4093
4094   return visible;
4095 }
4096
4097 static void
4098 specific_bug_621076 (void)
4099 {
4100   /* Test case for GNOME Bugzilla bug 621076, provided by Xavier Claessens */
4101
4102   /* This test case differs from has-child-filter and root-has-child-filter
4103    * in that the visible function both filters on content and model
4104    * structure.  Also, it is recursive.
4105    */
4106
4107   GtkTreeStore *store;
4108   GtkTreeModel *filter;
4109   GtkWidget *view;
4110   GtkTreeIter group_iter;
4111   GtkTreeIter item_iter;
4112   SignalMonitor *monitor;
4113
4114   g_test_bug ("621076");
4115
4116   store = gtk_tree_store_new (1, G_TYPE_STRING);
4117   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
4118   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
4119                                           specific_bug_621076_visible_func,
4120                                           NULL, NULL);
4121
4122   view = gtk_tree_view_new_with_model (filter);
4123   g_object_ref_sink (view);
4124
4125   monitor = signal_monitor_new (filter);
4126
4127   signal_monitor_append_signal (monitor, ROW_INSERTED, "0");
4128   gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1,
4129                                      0, "visible-group-0",
4130                                      -1);
4131   signal_monitor_assert_is_empty (monitor);
4132
4133   /* visible-group-0 is not expanded, so ROW_INSERTED should not be emitted
4134    * for its children. However, ROW_HAS_CHILD_TOGGLED should be emitted on
4135    * visible-group-0 to tell the view that row can be expanded. */
4136   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "0");
4137   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "0");
4138   group_iter = item_iter;
4139   gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1,
4140                                      0, "visible-0:0",
4141                                      -1);
4142   signal_monitor_assert_is_empty (monitor);
4143
4144   signal_monitor_append_signal (monitor, ROW_INSERTED, "1");
4145   gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1,
4146                                      0, "visible-group-1",
4147                                      -1);
4148   signal_monitor_assert_is_empty (monitor);
4149
4150   /* We are adding an hidden item inside visible-group-1, so
4151    * ROW_HAS_CHILD_TOGGLED should not be emitted.  It is emitted though,
4152    * because the signal originating at TreeStore will be propagated,
4153    * as well a generated signal because the state of the parent *could*
4154    * change by a change in the model.
4155    */
4156   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1");
4157   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1");
4158   group_iter = item_iter;
4159   gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1,
4160                                      0, "group-1:0",
4161                                      -1);
4162   signal_monitor_assert_is_empty (monitor);
4163
4164   /* This group is invisible and its parent too. Nothing should be emitted */
4165   group_iter = item_iter;
4166   gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1,
4167                                      0, "group-1:0:0",
4168                                      -1);
4169   signal_monitor_assert_is_empty (monitor);
4170
4171   /* Adding a visible item in this group hierarchy will make all nodes
4172    * in this path visible.  The first level should simply tell the view
4173    * that it now has a child, and the view will load the tree if needed
4174    * (depends on the expanded state).
4175    */
4176   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1");
4177   group_iter = item_iter;
4178   gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1,
4179                                      0, "visible-1:0:0:0",
4180                                      -1);
4181   signal_monitor_assert_is_empty (monitor);
4182
4183   check_level_length (GTK_TREE_MODEL_FILTER (filter), "1", 1);
4184
4185   gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1,
4186                                      0, "group-2",
4187                                      -1);
4188   signal_monitor_assert_is_empty (monitor);
4189
4190   /* Parent is invisible, and adding this invisible item won't change that,
4191    * so no signal should be emitted. */
4192   group_iter = item_iter;
4193   gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1,
4194                                      0, "invisible-2:0",
4195                                      -1);
4196   signal_monitor_assert_is_empty (monitor);
4197
4198   /* This makes group-2 visible, so it gets inserted and tells it has
4199    * children.
4200    */
4201   signal_monitor_append_signal (monitor, ROW_INSERTED, "2");
4202   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2");
4203   gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1,
4204                                      0, "visible-2:1",
4205                                      -1);
4206   signal_monitor_assert_is_empty (monitor);
4207
4208   /* group-2 is already visible, so this time it is a normal insertion */
4209   gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1,
4210                                      0, "visible-2:2",
4211                                      -1);
4212   signal_monitor_assert_is_empty (monitor);
4213
4214
4215   gtk_tree_store_insert_with_values (store, &item_iter, NULL, -1,
4216                                      0, "group-3",
4217                                      -1);
4218   signal_monitor_assert_is_empty (monitor);
4219
4220   /* Parent is invisible, and adding this invisible item won't change that,
4221    * so no signal should be emitted. */
4222   group_iter = item_iter;
4223   gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1,
4224                                      0, "invisible-3:0",
4225                                      -1);
4226   signal_monitor_assert_is_empty (monitor);
4227
4228   gtk_tree_store_insert_with_values (store, &item_iter, &group_iter, -1,
4229                                      0, "invisible-3:1",
4230                                      -1);
4231   signal_monitor_assert_is_empty (monitor);
4232
4233   /* This will make group 3 visible. */
4234   signal_monitor_append_signal (monitor, ROW_INSERTED, "3");
4235   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "3");
4236   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "3");
4237   gtk_tree_store_set (store, &item_iter, 0, "visible-3:1", -1);
4238   signal_monitor_assert_is_empty (monitor);
4239
4240   /* Make sure all groups are expanded, so the filter has the tree cached */
4241   gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
4242   while (gtk_events_pending ())
4243     gtk_main_iteration ();
4244
4245   /* Should only yield a row-changed */
4246   signal_monitor_append_signal (monitor, ROW_CHANGED, "3:0");
4247   gtk_tree_store_set (store, &item_iter, 0, "visible-3:1", -1);
4248   signal_monitor_assert_is_empty (monitor);
4249
4250   /* Now remove/hide some items. If a group loses its last item, the group
4251    * should be deleted instead of the item.
4252    */
4253
4254   signal_monitor_append_signal (monitor, ROW_DELETED, "2:1");
4255   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "2:2");
4256   gtk_tree_store_remove (store, &item_iter);
4257   signal_monitor_assert_is_empty (monitor);
4258
4259   signal_monitor_append_signal (monitor, ROW_DELETED, "2:0");
4260   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2");
4261   signal_monitor_append_signal (monitor, ROW_DELETED, "2");
4262   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "2:1");
4263   gtk_tree_store_set (store, &item_iter, 0, "invisible-2:1", -1);
4264   signal_monitor_assert_is_empty (monitor);
4265
4266   signal_monitor_append_signal (monitor, ROW_DELETED, "1:0:0:0");
4267   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1:0:0");
4268   signal_monitor_append_signal (monitor, ROW_DELETED, "1:0");
4269   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "1");
4270   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter, "1:0:0:0");
4271   gtk_tree_store_remove (store, &item_iter);
4272   signal_monitor_assert_is_empty (monitor);
4273
4274   /* Hide a group using row-changed instead of row-deleted */
4275   /* Caution: group 2 is gone, so offsets of the signals have moved. */
4276   signal_monitor_append_signal (monitor, ROW_DELETED, "2:0");
4277   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "2");
4278   signal_monitor_append_signal (monitor, ROW_DELETED, "2");
4279   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &item_iter,
4280                                        "3:1");
4281   gtk_tree_store_set (store, &item_iter, 0, "invisible-3:1", -1);
4282   signal_monitor_assert_is_empty (monitor);
4283
4284 #if 0
4285   {
4286     GtkWidget *window;
4287     GtkTreeViewColumn *col;
4288
4289     gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
4290
4291     col = gtk_tree_view_column_new_with_attributes ("foo",
4292         gtk_cell_renderer_text_new (),
4293         "text", 0, NULL);
4294     gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
4295
4296     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4297     g_signal_connect (window, "delete-event",
4298         G_CALLBACK (gtk_widget_destroy), NULL);
4299     g_signal_connect (window, "destroy",
4300         G_CALLBACK (gtk_main_quit), NULL);
4301
4302     gtk_container_add (GTK_CONTAINER (window), view);
4303
4304     gtk_widget_show (view);
4305     gtk_widget_show (window);
4306
4307     gtk_main ();
4308   }
4309 #endif
4310
4311   /* Cleanup */
4312   signal_monitor_free (monitor);
4313   g_object_unref (view);
4314   g_object_unref (store);
4315   g_object_unref (filter);
4316 }
4317
4318 /* main */
4319
4320 void
4321 register_filter_model_tests (void)
4322 {
4323   g_test_add ("/TreeModelFilter/self/verify-test-suite",
4324               FilterTest, NULL,
4325               filter_test_setup,
4326               verify_test_suite,
4327               filter_test_teardown);
4328
4329   g_test_add ("/TreeModelFilter/self/verify-test-suite/vroot/depth-1",
4330               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4331               filter_test_setup,
4332               verify_test_suite_vroot,
4333               filter_test_teardown);
4334   g_test_add ("/TreeModelFilter/self/verify-test-suite/vroot/depth-2",
4335               FilterTest, gtk_tree_path_new_from_indices (2, 3, -1),
4336               filter_test_setup,
4337               verify_test_suite_vroot,
4338               filter_test_teardown);
4339
4340
4341   g_test_add ("/TreeModelFilter/filled/hide-root-level",
4342               FilterTest, NULL,
4343               filter_test_setup,
4344               filled_hide_root_level,
4345               filter_test_teardown);
4346   g_test_add ("/TreeModelFilter/filled/hide-child-levels",
4347               FilterTest, NULL,
4348               filter_test_setup,
4349               filled_hide_child_levels,
4350               filter_test_teardown);
4351   g_test_add ("/TreeModelFilter/filled/hide-child-levels/root-expanded",
4352               FilterTest, NULL,
4353               filter_test_setup,
4354               filled_hide_child_levels_root_expanded,
4355               filter_test_teardown);
4356
4357   g_test_add ("/TreeModelFilter/filled/hide-root-level/vroot",
4358               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4359               filter_test_setup,
4360               filled_vroot_hide_root_level,
4361               filter_test_teardown);
4362   g_test_add ("/TreeModelFilter/filled/hide-child-levels/vroot",
4363               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4364               filter_test_setup,
4365               filled_vroot_hide_child_levels,
4366               filter_test_teardown);
4367   g_test_add ("/TreeModelFilter/filled/hide-child-levels/vroot-root-expanded",
4368               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4369               filter_test_setup,
4370               filled_vroot_hide_child_levels_root_expanded,
4371               filter_test_teardown);
4372
4373
4374   g_test_add ("/TreeModelFilter/empty/show-nodes",
4375               FilterTest, NULL,
4376               filter_test_setup_empty,
4377               empty_show_nodes,
4378               filter_test_teardown);
4379   g_test_add ("/TreeModelFilter/empty/show-multiple-nodes",
4380               FilterTest, NULL,
4381               filter_test_setup_empty,
4382               empty_show_multiple_nodes,
4383               filter_test_teardown);
4384
4385   g_test_add ("/TreeModelFilter/empty/show-nodes/vroot",
4386               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4387               filter_test_setup_empty,
4388               empty_vroot_show_nodes,
4389               filter_test_teardown);
4390   g_test_add ("/TreeModelFilter/empty/show-multiple-nodes/vroot",
4391               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4392               filter_test_setup_empty,
4393               empty_vroot_show_multiple_nodes,
4394               filter_test_teardown);
4395
4396
4397   g_test_add ("/TreeModelFilter/unfiltered/hide-single",
4398               FilterTest, NULL,
4399               filter_test_setup_unfiltered,
4400               unfiltered_hide_single,
4401               filter_test_teardown);
4402   g_test_add ("/TreeModelFilter/unfiltered/hide-single/root-expanded",
4403               FilterTest, NULL,
4404               filter_test_setup_unfiltered_root_expanded,
4405               unfiltered_hide_single_root_expanded,
4406               filter_test_teardown);
4407   g_test_add ("/TreeModelFilter/unfiltered/hide-single-child",
4408               FilterTest, NULL,
4409               filter_test_setup_unfiltered,
4410               unfiltered_hide_single_child,
4411               filter_test_teardown);
4412   g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/root-expanded",
4413               FilterTest, NULL,
4414               filter_test_setup_unfiltered_root_expanded,
4415               unfiltered_hide_single_child_root_expanded,
4416               filter_test_teardown);
4417   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level",
4418               FilterTest, NULL,
4419               filter_test_setup_unfiltered,
4420               unfiltered_hide_single_multi_level,
4421               filter_test_teardown);
4422   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/root-expanded",
4423               FilterTest, NULL,
4424               filter_test_setup_unfiltered_root_expanded,
4425               unfiltered_hide_single_multi_level_root_expanded,
4426               filter_test_teardown);
4427
4428   g_test_add ("/TreeModelFilter/unfiltered/hide-single/vroot",
4429               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4430               filter_test_setup_unfiltered,
4431               unfiltered_vroot_hide_single,
4432               filter_test_teardown);
4433   g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/vroot",
4434               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4435               filter_test_setup_unfiltered,
4436               unfiltered_vroot_hide_single_child,
4437               filter_test_teardown);
4438   g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/vroot/root-expanded",
4439               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4440               filter_test_setup_unfiltered_root_expanded,
4441               unfiltered_vroot_hide_single_child_root_expanded,
4442               filter_test_teardown);
4443   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/vroot",
4444               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4445               filter_test_setup_unfiltered,
4446               unfiltered_vroot_hide_single_multi_level,
4447               filter_test_teardown);
4448   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/vroot/root-expanded",
4449               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4450               filter_test_setup_unfiltered_root_expanded,
4451               unfiltered_vroot_hide_single_multi_level_root_expanded,
4452               filter_test_teardown);
4453
4454
4455
4456   g_test_add ("/TreeModelFilter/unfiltered/show-single",
4457               FilterTest, NULL,
4458               filter_test_setup_empty_unfiltered,
4459               unfiltered_show_single,
4460               filter_test_teardown);
4461   g_test_add ("/TreeModelFilter/unfiltered/show-single-child",
4462               FilterTest, NULL,
4463               filter_test_setup_empty_unfiltered,
4464               unfiltered_show_single_child,
4465               filter_test_teardown);
4466   g_test_add ("/TreeModelFilter/unfiltered/show-single-child/root-expanded",
4467               FilterTest, NULL,
4468               filter_test_setup_empty_unfiltered_root_expanded,
4469               unfiltered_show_single_child_root_expanded,
4470               filter_test_teardown);
4471   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level",
4472               FilterTest, NULL,
4473               filter_test_setup_empty_unfiltered,
4474               unfiltered_show_single_multi_level,
4475               filter_test_teardown);
4476   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/root-expanded",
4477               FilterTest, NULL,
4478               filter_test_setup_empty_unfiltered_root_expanded,
4479               unfiltered_show_single_multi_level_root_expanded,
4480               filter_test_teardown);
4481
4482   g_test_add ("/TreeModelFilter/unfiltered/show-single/vroot",
4483               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4484               filter_test_setup_empty_unfiltered,
4485               unfiltered_vroot_show_single,
4486               filter_test_teardown);
4487   g_test_add ("/TreeModelFilter/unfiltered/show-single-child/vroot",
4488               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4489               filter_test_setup_empty_unfiltered,
4490               unfiltered_vroot_show_single_child,
4491               filter_test_teardown);
4492   g_test_add ("/TreeModelFilter/unfiltered/show-single-child/vroot/root-expanded",
4493               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4494               filter_test_setup_empty_unfiltered_root_expanded,
4495               unfiltered_vroot_show_single_child_root_expanded,
4496               filter_test_teardown);
4497   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/vroot",
4498               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4499               filter_test_setup_empty_unfiltered,
4500               unfiltered_vroot_show_single_multi_level,
4501               filter_test_teardown);
4502   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/vroot/root-expanded",
4503               FilterTest, gtk_tree_path_new_from_indices (2, -1),
4504               filter_test_setup_empty_unfiltered_root_expanded,
4505               unfiltered_vroot_show_single_multi_level_root_expanded,
4506               filter_test_teardown);
4507
4508   /* Inserts in child models after creation of filter model */
4509   g_test_add_func ("/TreeModelFilter/insert/before",
4510                    insert_before);
4511   g_test_add_func ("/TreeModelFilter/insert/child",
4512                    insert_child);
4513
4514   /* Removals from child model after creating of filter model */
4515   g_test_add_func ("/TreeModelFilter/remove/node",
4516                    remove_node);
4517   g_test_add_func ("/TreeModelFilter/remove/node-vroot",
4518                    remove_node_vroot);
4519   g_test_add_func ("/TreeModelFilter/remove/vroot-ancestor",
4520                    remove_vroot_ancestor);
4521
4522
4523   g_test_add_func ("/TreeModelFilter/specific/path-dependent-filter",
4524                    specific_path_dependent_filter);
4525   g_test_add_func ("/TreeModelFilter/specific/append-after-collapse",
4526                    specific_append_after_collapse);
4527   g_test_add_func ("/TreeModelFilter/specific/sort-filter-remove-node",
4528                    specific_sort_filter_remove_node);
4529   g_test_add_func ("/TreeModelFilter/specific/sort-filter-remove-root",
4530                    specific_sort_filter_remove_root);
4531   g_test_add_func ("/TreeModelFilter/specific/root-mixed-visibility",
4532                    specific_root_mixed_visibility);
4533   g_test_add_func ("/TreeModelFilter/specific/has-child-filter",
4534                    specific_has_child_filter);
4535   g_test_add_func ("/TreeModelFilter/specific/has-child-filter-on-sort-model",
4536                    specific_has_child_filter_on_sort_model);
4537   g_test_add_func ("/TreeModelFilter/specific/at-least-2-children-filter",
4538                    specific_at_least_2_children_filter);
4539   g_test_add_func ("/TreeModelFilter/specific/at-least-2-children-filter-on-sort-model",
4540                    specific_at_least_2_children_filter_on_sort_model);
4541   g_test_add_func ("/TreeModelFilter/specific/root-has-child-filter",
4542                    specific_root_has_child_filter);
4543   g_test_add_func ("/TreeModelFilter/specific/filter-add-child",
4544                    specific_filter_add_child);
4545   g_test_add_func ("/TreeModelFilter/specific/list-store-clear",
4546                    specific_list_store_clear);
4547   g_test_add_func ("/TreeModelFilter/specific/sort-ref-leaf-and-remove-ancestor",
4548                    specific_sort_ref_leaf_and_remove_ancestor);
4549   g_test_add_func ("/TreeModelFilter/specific/ref-leaf-and-remove-ancestor",
4550                    specific_ref_leaf_and_remove_ancestor);
4551   g_test_add_func ("/TreeModelFilter/specific/virtual-ref-leaf-and-remove-ancestor",
4552                    specific_virtual_ref_leaf_and_remove_ancestor);
4553
4554   g_test_add_func ("/TreeModelFilter/specific/bug-301558",
4555                    specific_bug_301558);
4556   g_test_add_func ("/TreeModelFilter/specific/bug-311955",
4557                    specific_bug_311955);
4558   g_test_add_func ("/TreeModelFilter/specific/bug-311955-clean",
4559                    specific_bug_311955_clean);
4560   g_test_add_func ("/TreeModelFilter/specific/bug-346800",
4561                    specific_bug_346800);
4562   g_test_add_func ("/TreeModelFilter/specific/bug-464173",
4563                    specific_bug_464173);
4564   g_test_add_func ("/TreeModelFilter/specific/bug-540201",
4565                    specific_bug_540201);
4566   g_test_add_func ("/TreeModelFilter/specific/bug-549287",
4567                    specific_bug_549287);
4568   g_test_add_func ("/TreeModelFilter/specific/bug-621076",
4569                    specific_bug_621076);
4570 }