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