]> Pileus Git - ~andy/gtk/blobdiff - gtk/tests/filtermodel.c
Change FSF Address
[~andy/gtk] / gtk / tests / filtermodel.c
index 048a3074535adc108133d0d90bbba8fa714b8d59..507ceefa305aa785314ff5b51a929236f39cc238 100644 (file)
@@ -1,5 +1,5 @@
 /* Extensive GtkTreeModelFilter tests.
- * Copyright (C) 2009  Kristian Rietveld  <kris@gtk.org>
+ * Copyright (C) 2009,2011  Kristian Rietveld  <kris@gtk.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <gtk/gtk.h>
+#include <string.h>
 
+#include "treemodel.h"
+#include "gtktreemodelrefcount.h"
 
 /* Left to do:
  *   - Proper coverage checking to see if the unit tests cover
  *     all possible cases.
- *   - Verify if the ref counting is done properly for both the
- *     normal ref_count and the zero_ref_count.  One way to test
- *     this area is by collapsing/expanding branches on the view
- *     that is connected to the filter model.
  *   - Check if the iterator stamp is incremented at the correct times.
+ *
+ * For more thorough testing:
+ *   - Test with randomized models.
+ *   - Extensively test a filter model wrapping a sort model,
+ *     or a sort model wrapping a filter model by:
+ *       # Checking structure.
+ *       # Checking for correct signals emissions.
+ *       # Checking correct reference counting.
+ *       # Tests should be done with the sort and filter model
+ *         in various filtering and sorting states.
  */
 
 
@@ -90,278 +97,6 @@ create_tree_store (int      depth,
   return store;
 }
 
-/*
- * Signal monitor
- */
-
-typedef enum
-{
-  ROW_INSERTED,
-  ROW_DELETED,
-  ROW_CHANGED,
-  ROW_HAS_CHILD_TOGGLED,
-  ROWS_REORDERED,
-  LAST_SIGNAL
-}
-SignalName;
-
-static const char *
-signal_name_to_string (SignalName signal)
-{
-  switch (signal)
-    {
-      case ROW_INSERTED:
-          return "row-inserted";
-
-      case ROW_DELETED:
-          return "row-deleted";
-
-      case ROW_CHANGED:
-          return "row-changed";
-
-      case ROW_HAS_CHILD_TOGGLED:
-          return "row-has-child-toggled";
-
-      case ROWS_REORDERED:
-          return "rows-reordered";
-
-      default:
-          /* Fall through */
-          break;
-    }
-
-  return "(unknown)";
-}
-
-typedef struct
-{
-  SignalName signal;
-  GtkTreePath *path;
-}
-Signal;
-
-
-static Signal *
-signal_new (SignalName signal, GtkTreePath *path)
-{
-  Signal *s;
-
-  s = g_new0 (Signal, 1);
-  s->signal = signal;
-  s->path = gtk_tree_path_copy (path);
-
-  return s;
-}
-
-static void
-signal_free (Signal *s)
-{
-  if (s->path)
-    gtk_tree_path_free (s->path);
-
-  g_free (s);
-}
-
-
-typedef struct
-{
-  GQueue *queue;
-  GtkTreeModel *client;
-  gulong signal_ids[LAST_SIGNAL];
-}
-SignalMonitor;
-
-
-static void
-signal_monitor_generic_handler (SignalMonitor *m,
-                                SignalName     signal,
-                                GtkTreeModel  *model,
-                                GtkTreePath   *path)
-{
-  Signal *s;
-
-  if (g_queue_is_empty (m->queue))
-    {
-      gchar *path_str;
-
-      path_str = gtk_tree_path_to_string (path);
-      g_error ("Signal queue empty, got signal %s path %s\n",
-               signal_name_to_string (signal), path_str);
-      g_free (path_str);
-
-      g_assert_not_reached ();
-    }
-
-  if (m->client != model)
-    {
-      g_error ("Model mismatch; expected %p, got %p\n",
-               m->client, model);
-      g_assert_not_reached ();
-    }
-
-  s = g_queue_peek_tail (m->queue);
-
-#if 0
-  /* For debugging: output signals that are coming in.  Leaks memory. */
-  g_print ("signal=%s path=%s\n", signal_name_to_string (signal),
-           gtk_tree_path_to_string (path));
-#endif
-
-  if (s->signal != signal
-      || gtk_tree_path_compare (s->path, path) != 0)
-    {
-      gchar *path_str, *s_path_str;
-
-      s_path_str = gtk_tree_path_to_string (s->path);
-      path_str = gtk_tree_path_to_string (path);
-
-      g_error ("Signals don't match; expected signal %s path %s, got signal %s path %s\n",
-               signal_name_to_string (s->signal), s_path_str,
-               signal_name_to_string (signal), path_str);
-
-      g_free (s_path_str);
-      g_free (path_str);
-
-      g_assert_not_reached ();
-    }
-
-  s = g_queue_pop_tail (m->queue);
-
-  signal_free (s);
-}
-
-static void
-signal_monitor_row_inserted (GtkTreeModel *model,
-                             GtkTreePath  *path,
-                             GtkTreeIter  *iter,
-                             gpointer      data)
-{
-  signal_monitor_generic_handler (data, ROW_INSERTED,
-                                  model, path);
-}
-
-static void
-signal_monitor_row_deleted (GtkTreeModel *model,
-                            GtkTreePath  *path,
-                            gpointer      data)
-{
-  signal_monitor_generic_handler (data, ROW_DELETED,
-                                  model, path);
-}
-
-static void
-signal_monitor_row_changed (GtkTreeModel *model,
-                            GtkTreePath  *path,
-                            GtkTreeIter  *iter,
-                            gpointer      data)
-{
-  signal_monitor_generic_handler (data, ROW_CHANGED,
-                                  model, path);
-}
-
-static void
-signal_monitor_row_has_child_toggled (GtkTreeModel *model,
-                                      GtkTreePath  *path,
-                                      GtkTreeIter  *iter,
-                                      gpointer      data)
-{
-  signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED,
-                                  model, path);
-}
-
-static void
-signal_monitor_rows_reordered (GtkTreeModel *model,
-                               GtkTreePath  *path,
-                               GtkTreeIter  *iter,
-                               gint         *new_order,
-                               gpointer      data)
-{
-  signal_monitor_generic_handler (data, ROWS_REORDERED,
-                                  model, path);
-}
-
-static SignalMonitor *
-signal_monitor_new (GtkTreeModel *client)
-{
-  SignalMonitor *m;
-
-  m = g_new0 (SignalMonitor, 1);
-  m->client = g_object_ref (client);
-  m->queue = g_queue_new ();
-
-  m->signal_ids[ROW_INSERTED] = g_signal_connect (client,
-                                                  "row-inserted",
-                                                  G_CALLBACK (signal_monitor_row_inserted),
-                                                  m);
-  m->signal_ids[ROW_DELETED] = g_signal_connect (client,
-                                                 "row-deleted",
-                                                 G_CALLBACK (signal_monitor_row_deleted),
-                                                 m);
-  m->signal_ids[ROW_CHANGED] = g_signal_connect (client,
-                                                 "row-changed",
-                                                 G_CALLBACK (signal_monitor_row_changed),
-                                                 m);
-  m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client,
-                                                           "row-has-child-toggled",
-                                                           G_CALLBACK (signal_monitor_row_has_child_toggled),
-                                                           m);
-  m->signal_ids[ROWS_REORDERED] = g_signal_connect (client,
-                                                    "rows-reordered",
-                                                    G_CALLBACK (signal_monitor_rows_reordered),
-                                                    m);
-
-  return m;
-}
-
-static void
-signal_monitor_free (SignalMonitor *m)
-{
-  int i;
-
-  for (i = 0; i < LAST_SIGNAL; i++)
-    g_signal_handler_disconnect (m->client, m->signal_ids[i]);
-
-  g_object_unref (m->client);
-
-  if (m->queue)
-    g_queue_free (m->queue);
-
-  g_free (m);
-}
-
-static void
-signal_monitor_assert_is_empty (SignalMonitor *m)
-{
-  g_assert (g_queue_is_empty (m->queue));
-}
-
-static void
-signal_monitor_append_signal_path (SignalMonitor *m,
-                                   SignalName     signal,
-                                   GtkTreePath   *path)
-{
-  Signal *s;
-
-  s = signal_new (signal, path);
-  g_queue_push_head (m->queue, s);
-}
-
-static void
-signal_monitor_append_signal (SignalMonitor *m,
-                              SignalName     signal,
-                              const gchar   *path_string)
-{
-  Signal *s;
-  GtkTreePath *path;
-
-  path = gtk_tree_path_new_from_string (path_string);
-
-  s = signal_new (signal, path);
-  g_queue_push_head (m->queue, s);
-
-  gtk_tree_path_free (path);
-}
-
 /*
  * Fixture
  */
@@ -418,6 +153,23 @@ filter_test_setup_generic (FilterTest    *fixture,
   fixture->monitor = signal_monitor_new (filter);
 }
 
+static void
+filter_test_setup_expand_root (FilterTest *fixture)
+{
+  int i;
+  GtkTreePath *path;
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+
+  for (i = 0; i < LEVEL_LENGTH; i++)
+    {
+      gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view),
+                                path, FALSE);
+      gtk_tree_path_next (path);
+    }
+  gtk_tree_path_free (path);
+}
+
 static void
 filter_test_setup (FilterTest    *fixture,
                    gconstpointer  test_data)
@@ -439,6 +191,14 @@ filter_test_setup_unfiltered (FilterTest    *fixture,
   filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE);
 }
 
+static void
+filter_test_setup_unfiltered_root_expanded (FilterTest    *fixture,
+                                            gconstpointer  test_data)
+{
+  filter_test_setup_unfiltered (fixture, test_data);
+  filter_test_setup_expand_root (fixture);
+}
+
 static void
 filter_test_setup_empty_unfiltered (FilterTest    *fixture,
                                     gconstpointer  test_data)
@@ -446,6 +206,14 @@ filter_test_setup_empty_unfiltered (FilterTest    *fixture,
   filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE);
 }
 
+static void
+filter_test_setup_empty_unfiltered_root_expanded (FilterTest    *fixture,
+                                                  gconstpointer  test_data)
+{
+  filter_test_setup_empty_unfiltered (fixture, test_data);
+  filter_test_setup_expand_root (fixture);
+}
+
 static GtkTreePath *
 strip_virtual_root (GtkTreePath *path,
                     GtkTreePath *root_path)
@@ -470,6 +238,32 @@ strip_virtual_root (GtkTreePath *path,
   return real_path;
 }
 
+static int
+count_visible (FilterTest  *fixture,
+               GtkTreePath *store_path)
+{
+  int i;
+  int n_visible = 0;
+  GtkTreeIter iter;
+
+  gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
+                           &iter, store_path);
+
+  for (i = 0; i < LEVEL_LENGTH; i++)
+    {
+      gboolean visible;
+
+      gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter,
+                          1, &visible,
+                          -1);
+
+      if (visible)
+        n_visible++;
+    }
+
+  return n_visible;
+}
+
 static void
 filter_test_append_refilter_signals_recurse (FilterTest  *fixture,
                                              GtkTreePath *store_path,
@@ -537,26 +331,48 @@ filter_test_append_refilter_signals_recurse (FilterTest  *fixture,
           /* This row will be inserted */
           signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED,
                                              real_path);
-          signal_monitor_append_signal_path (fixture->monitor,
-                                             ROW_HAS_CHILD_TOGGLED,
-                                             real_path);
 
-          if (depth > 1
-              && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
-                                                &iter))
+          if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store),
+                                             &iter))
             {
-              GtkTreePath *store_copy;
-              GtkTreePath *filter_copy;
-
-              store_copy = gtk_tree_path_copy (store_path);
-              filter_copy = gtk_tree_path_copy (filter_path);
-              filter_test_append_refilter_signals_recurse (fixture,
-                                                           store_copy,
-                                                           filter_copy,
-                                                           depth - 1,
-                                                           root_path);
-              gtk_tree_path_free (store_copy);
-              gtk_tree_path_free (filter_copy);
+              signal_monitor_append_signal_path (fixture->monitor,
+                                                 ROW_HAS_CHILD_TOGGLED,
+                                                 real_path);
+
+              if (depth > 1)
+                {
+                  GtkTreePath *store_copy;
+                  GtkTreePath *filter_copy;
+
+                  store_copy = gtk_tree_path_copy (store_path);
+                  filter_copy = gtk_tree_path_copy (filter_path);
+                  filter_test_append_refilter_signals_recurse (fixture,
+                                                               store_copy,
+                                                               filter_copy,
+                                                               depth - 1,
+                                                               root_path);
+                  gtk_tree_path_free (store_copy);
+                  gtk_tree_path_free (filter_copy);
+                }
+              else if (depth == 1)
+                {
+                  GtkTreePath *tmp_path;
+
+                  /* If all child rows are invisible, then the last row to
+                   * become invisible will emit row-has-child-toggled on the
+                   * parent.
+                   */
+
+                  tmp_path = gtk_tree_path_copy (store_path);
+                  gtk_tree_path_append_index (tmp_path, 0);
+
+                  if (count_visible (fixture, tmp_path) == 0)
+                    signal_monitor_append_signal_path (fixture->monitor,
+                                                       ROW_HAS_CHILD_TOGGLED,
+                                                       real_path);
+
+                  gtk_tree_path_free (tmp_path);
+                }
             }
 
           gtk_tree_path_next (filter_path);
@@ -922,6 +738,59 @@ static void
 filled_hide_child_levels (FilterTest    *fixture,
                           gconstpointer  user_data)
 {
+  set_path_visibility (fixture, "0:2", FALSE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
+
+  set_path_visibility (fixture, "0:4", FALSE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  set_path_visibility (fixture, "0:4:3", FALSE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  set_path_visibility (fixture, "0:4:0", FALSE);
+  set_path_visibility (fixture, "0:4:1", FALSE);
+  set_path_visibility (fixture, "0:4:2", FALSE);
+  set_path_visibility (fixture, "0:4:4", FALSE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
+  set_path_visibility (fixture, "0:4", TRUE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, "0:3", 0);
+
+  set_path_visibility (fixture, "0:2", TRUE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0:4", 0);
+
+  /* Once 0:4:0 got inserted, 0:4 became a parent.  Because 0:4 is
+   * not visible, not signals are emitted.
+   */
+  set_path_visibility (fixture, "0:4:2", TRUE);
+  set_path_visibility (fixture, "0:4:4", TRUE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, "0:4", 2);
+}
+
+static void
+filled_hide_child_levels_root_expanded (FilterTest    *fixture,
+                                        gconstpointer  user_data)
+{
+  GtkTreePath *path;
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
   signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
   set_path_visibility (fixture, "0:2", FALSE);
   check_filter_model (fixture);
@@ -949,10 +818,6 @@ filled_hide_child_levels (FilterTest    *fixture,
 
   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
-  /* FIXME: Actually, the filter model should not be emitted the
-   * row-has-child-toggled signal here.  *However* an extraneous emission
-   * of this signal does not hurt and is allowed.
-   */
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
   set_path_visibility (fixture, "0:4", TRUE);
   check_filter_model (fixture);
@@ -966,13 +831,8 @@ filled_hide_child_levels (FilterTest    *fixture,
   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
   check_level_length (fixture->filter, "0:4", 0);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
-  /* Once 0:4:0 got inserted, 0:4 became a parent */
+  /* has-child-toggled for 0:4 is required.  */
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:0");
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:1");
-
   set_path_visibility (fixture, "0:4:2", TRUE);
   set_path_visibility (fixture, "0:4:4", TRUE);
   signal_monitor_assert_is_empty (fixture->monitor);
@@ -1115,13 +975,11 @@ filled_vroot_hide_child_levels (FilterTest    *fixture,
 {
   GtkTreePath *path = (GtkTreePath *)user_data;
 
-  signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
   set_path_visibility (fixture, "2:0:2", FALSE);
   check_filter_model_with_root (fixture, path);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
   set_path_visibility (fixture, "2:0:4", FALSE);
   check_filter_model_with_root (fixture, path);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
@@ -1141,47 +999,94 @@ filled_vroot_hide_child_levels (FilterTest    *fixture,
   check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
 
   /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
-  /* FIXME: Actually, the filter model should not be emitted the
-   * row-has-child-toggled signal here.  *However* an extraneous emission
-   * of this signal does not hurt and is allowed.
-   */
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
   set_path_visibility (fixture, "2:0:4", TRUE);
   check_filter_model_with_root (fixture, path);
   check_level_length (fixture->filter, "0:3", 0);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
   set_path_visibility (fixture, "2:0:2", TRUE);
   check_filter_model_with_root (fixture, path);
   check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
   check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
   check_level_length (fixture->filter, "0:4", 0);
 
-  /* FIXME: Inconsistency!  For the non-vroot case we also receive two
-   * row-has-child-toggled signals here.
+  /* Once 0:4:0 got inserted, 0:4 became a parent. However, 0:4 is not
+   * visible, so no signal should be emitted.
    */
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0");
-  /* Once 0:4:0 got inserted, 0:4 became a parent */
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1");
   set_path_visibility (fixture, "2:0:4:2", TRUE);
   set_path_visibility (fixture, "2:0:4:4", TRUE);
   check_level_length (fixture->filter, "0:4", 2);
+  signal_monitor_assert_is_empty (fixture->monitor);
 }
 
-
 static void
-empty_show_nodes (FilterTest    *fixture,
-                  gconstpointer  user_data)
+filled_vroot_hide_child_levels_root_expanded (FilterTest    *fixture,
+                                              gconstpointer  user_data)
 {
-  check_filter_model (fixture);
-  check_level_length (fixture->filter, NULL, 0);
+  GtkTreePath *path = (GtkTreePath *)user_data;
+  GtkTreePath *tmp_path;
 
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
-  set_path_visibility (fixture, "3", TRUE);
+  tmp_path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), tmp_path, FALSE);
+  gtk_tree_path_free (tmp_path);
+
+  signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2");
+  set_path_visibility (fixture, "2:0:2", FALSE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1);
+
+  signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3");
+  set_path_visibility (fixture, "2:0:4", FALSE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  set_path_visibility (fixture, "2:0:4:3", FALSE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  set_path_visibility (fixture, "2:0:4:0", FALSE);
+  set_path_visibility (fixture, "2:0:4:1", FALSE);
+  set_path_visibility (fixture, "2:0:4:2", FALSE);
+  set_path_visibility (fixture, "2:0:4:4", FALSE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2);
+
+  /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3");
+  set_path_visibility (fixture, "2:0:4", TRUE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, "0:3", 0);
+
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2");
+  set_path_visibility (fixture, "2:0:2", TRUE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, "0:2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0:3", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "0:4", 0);
+
+  /* Once 0:4:0 got inserted, 0:4 became a parent */
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4");
+  set_path_visibility (fixture, "2:0:4:2", TRUE);
+  set_path_visibility (fixture, "2:0:4:4", TRUE);
+  check_level_length (fixture->filter, "0:4", 2);
+  signal_monitor_assert_is_empty (fixture->monitor);
+}
+
+static void
+empty_show_nodes (FilterTest    *fixture,
+                  gconstpointer  user_data)
+{
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, 0);
+
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "3", TRUE);
   check_filter_model (fixture);
   check_level_length (fixture->filter, NULL, 1);
   check_level_length (fixture->filter, "0", 0);
@@ -1191,9 +1096,7 @@ empty_show_nodes (FilterTest    *fixture,
   check_level_length (fixture->filter, NULL, 1);
   check_level_length (fixture->filter, "0", 0);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0");
   set_path_visibility (fixture, "3:2", TRUE);
   check_filter_model (fixture);
   check_level_length (fixture->filter, NULL, 1);
@@ -1231,8 +1134,6 @@ empty_show_multiple_nodes (FilterTest    *fixture,
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
-  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "1");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1");
 
   /* We simulate a change in visible func condition with this.  The
    * visibility state of multiple nodes changes at once, we emit row-changed
@@ -1247,6 +1148,7 @@ empty_show_multiple_nodes (FilterTest    *fixture,
   gtk_tree_path_append_index (changed_path, 2);
   gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store),
                            &iter, changed_path);
+  /* Invisible node - so no signals expected */
   gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store),
                               changed_path, &iter);
 
@@ -1271,9 +1173,7 @@ empty_show_multiple_nodes (FilterTest    *fixture,
   check_level_length (fixture->filter, NULL, 2);
   check_level_length (fixture->filter, "0", 0);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0");
   set_path_visibility (fixture, "3:2", TRUE);
   check_filter_model (fixture);
   check_level_length (fixture->filter, NULL, 2);
@@ -1465,9 +1365,28 @@ unfiltered_hide_single (FilterTest    *fixture,
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
+  /* The view only shows the root level, so we only expect signals
+   * for the root level.
    */
+  filter_test_append_refilter_signals (fixture, 1);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1);
+}
+
+static void
+unfiltered_hide_single_root_expanded (FilterTest    *fixture,
+                                      gconstpointer  user_data)
+
+{
+  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
+  set_path_visibility (fixture, "2", FALSE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+
   filter_test_append_refilter_signals (fixture, 2);
   filter_test_enable_filter (fixture);
 
@@ -1479,6 +1398,29 @@ static void
 unfiltered_hide_single_child (FilterTest    *fixture,
                               gconstpointer  user_data)
 
+{
+  /* This row is not shown, so its signal is not propagated */
+  set_path_visibility (fixture, "2:2", FALSE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+
+  /* The view only shows the root level, so we only expect signals
+   * for the root level.
+   */
+  filter_test_append_refilter_signals (fixture, 0);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
+}
+
+static void
+unfiltered_hide_single_child_root_expanded (FilterTest    *fixture,
+                                            gconstpointer  user_data)
+
 {
   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
@@ -1488,9 +1430,6 @@ unfiltered_hide_single_child (FilterTest    *fixture,
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
   filter_test_append_refilter_signals (fixture, 2);
   filter_test_enable_filter (fixture);
 
@@ -1503,6 +1442,40 @@ static void
 unfiltered_hide_single_multi_level (FilterTest    *fixture,
                                     gconstpointer  user_data)
 
+{
+  /* This row is not shown, so its signal is not propagated */
+  set_path_visibility (fixture, "2:2:2", FALSE);
+
+  /* This row is not shown, so its signal is not propagated */
+  set_path_visibility (fixture, "2:2", FALSE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
+
+  /* The view only shows the root level, so we only expect signals
+   * for the root level.
+   */
+  filter_test_append_refilter_signals (fixture, 1);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
+
+  set_path_visibility (fixture, "2:2", TRUE);
+
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
+}
+
+static void
+unfiltered_hide_single_multi_level_root_expanded (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
+
 {
   /* This row is not shown, so its signal is not propagated */
   set_path_visibility (fixture, "2:2:2", FALSE);
@@ -1516,9 +1489,6 @@ unfiltered_hide_single_multi_level (FilterTest    *fixture,
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
   filter_test_append_refilter_signals (fixture, 2);
   filter_test_enable_filter (fixture);
 
@@ -1537,6 +1507,7 @@ unfiltered_hide_single_multi_level (FilterTest    *fixture,
 }
 
 
+
 static void
 unfiltered_vroot_hide_single (FilterTest    *fixture,
                               gconstpointer  user_data)
@@ -1551,11 +1522,11 @@ unfiltered_vroot_hide_single (FilterTest    *fixture,
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.  (We add an additional level to
+  /* The view only shows the root level, so we only expect signals
+   * for the root level.  (Though for the depth argument, we have to
    * take the virtual root into account).
    */
-  filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
+  filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
   filter_test_enable_filter (fixture);
 
   check_filter_model_with_root (fixture, path);
@@ -1566,6 +1537,32 @@ static void
 unfiltered_vroot_hide_single_child (FilterTest    *fixture,
                                     gconstpointer  user_data)
 
+{
+  GtkTreePath *path = (GtkTreePath *)user_data;
+
+  /* Not visible, so no signal will be received. */
+  set_path_visibility (fixture, "2:2:2", FALSE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+
+  /* The view only shows the root level, so we only expect signals
+   * for the root level.  (Though for the depth argument, we have to
+   * take the virtual root into account).
+   */
+  filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
+}
+
+static void
+unfiltered_vroot_hide_single_child_root_expanded (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
+
 {
   GtkTreePath *path = (GtkTreePath *)user_data;
 
@@ -1577,10 +1574,6 @@ unfiltered_vroot_hide_single_child (FilterTest    *fixture,
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.  (We add an additional level to take
-   * the virtual root into account).
-   */
   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
   filter_test_enable_filter (fixture);
 
@@ -1593,6 +1586,43 @@ static void
 unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
                                           gconstpointer  user_data)
 
+{
+  GtkTreePath *path = (GtkTreePath *)user_data;
+
+  /* This row is not shown, so its signal is not propagated */
+  set_path_visibility (fixture, "2:2:2:2", FALSE);
+
+  /* Not shown, so no signal */
+  set_path_visibility (fixture, "2:2:2", FALSE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
+
+  /* We only expect signals for the root level.  The depth is 2
+   * because we have to take the virtual root into account.
+   */
+  filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1);
+
+  /* Not shown, so no signal */
+  set_path_visibility (fixture, "2:2:2", TRUE);
+
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
+}
+
+static void
+unfiltered_vroot_hide_single_multi_level_root_expanded (FilterTest    *fixture,
+                                                        gconstpointer  user_data)
+
 {
   GtkTreePath *path = (GtkTreePath *)user_data;
 
@@ -1608,9 +1638,6 @@ unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
   filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
   filter_test_enable_filter (fixture);
 
@@ -1628,8 +1655,6 @@ unfiltered_vroot_hide_single_multi_level (FilterTest    *fixture,
   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1);
 }
 
-
-
 static void
 unfiltered_show_single (FilterTest    *fixture,
                         gconstpointer  user_data)
@@ -1642,10 +1667,8 @@ unfiltered_show_single (FilterTest    *fixture,
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
-  filter_test_append_refilter_signals (fixture, 2);
+  /* We only expect signals for the root level */
+  filter_test_append_refilter_signals (fixture, 1);
   filter_test_enable_filter (fixture);
 
   check_filter_model (fixture);
@@ -1657,18 +1680,14 @@ unfiltered_show_single_child (FilterTest    *fixture,
                               gconstpointer  user_data)
 
 {
-  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
   set_path_visibility (fixture, "2:2", TRUE);
 
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
-  filter_test_append_refilter_signals (fixture, 3);
+  /* We only expect signals for the root level */
+  filter_test_append_refilter_signals (fixture, 1);
   filter_test_enable_filter (fixture);
 
   check_filter_model (fixture);
@@ -1686,15 +1705,10 @@ unfiltered_show_single_child (FilterTest    *fixture,
 }
 
 static void
-unfiltered_show_single_multi_level (FilterTest    *fixture,
-                                    gconstpointer  user_data)
+unfiltered_show_single_child_root_expanded (FilterTest    *fixture,
+                                            gconstpointer  user_data)
 
 {
-  /* The view is not showing this row (collapsed state), so it is not
-   * referenced.  The signal should not go through.
-   */
-  set_path_visibility (fixture, "2:2:2", TRUE);
-
   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
   set_path_visibility (fixture, "2:2", TRUE);
@@ -1702,12 +1716,8 @@ unfiltered_show_single_multi_level (FilterTest    *fixture,
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
-  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
-  filter_test_append_refilter_signals (fixture, 3);
+  filter_test_append_refilter_signals (fixture, 2);
   filter_test_enable_filter (fixture);
 
   check_filter_model (fixture);
@@ -1719,46 +1729,114 @@ unfiltered_show_single_multi_level (FilterTest    *fixture,
   signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
   set_path_visibility (fixture, "2", TRUE);
-  check_filter_model (fixture);
+  signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, 1);
   check_level_length (fixture->filter, "0", 1);
-  check_level_length (fixture->filter, "0:0", 1);
 }
 
-
 static void
-unfiltered_vroot_show_single (FilterTest    *fixture,
-                              gconstpointer  user_data)
+unfiltered_show_single_multi_level (FilterTest    *fixture,
+                                    gconstpointer  user_data)
 
 {
-  GtkTreePath *path = (GtkTreePath *)user_data;
-
-  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
+  /* The view is not showing these rows (collapsed state), so it is not
+   * referenced.  The signal should not go through.
+   */
+  set_path_visibility (fixture, "2:2:2", TRUE);
   set_path_visibility (fixture, "2:2", TRUE);
 
   signal_monitor_assert_is_empty (fixture->monitor);
   check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
-  filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
+  /* We only expect signals for the first level */
+  filter_test_append_refilter_signals (fixture, 1);
   filter_test_enable_filter (fixture);
 
-  check_filter_model_with_root (fixture, path);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, 0);
+
+  /* From here we are filtered, "2" in the real model is "0" in the filter
+   * model.
+   */
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "2", TRUE);
+  check_filter_model (fixture);
   check_level_length (fixture->filter, NULL, 1);
+  check_level_length (fixture->filter, "0", 1);
+  check_level_length (fixture->filter, "0:0", 1);
 }
 
 static void
-unfiltered_vroot_show_single_child (FilterTest    *fixture,
-                                    gconstpointer  user_data)
+unfiltered_show_single_multi_level_root_expanded (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
 
 {
-  GtkTreePath *path = (GtkTreePath *)user_data;
+  /* The view is not showing this row (collapsed state), so it is not
+   * referenced.  The signal should not go through.
+   */
+  set_path_visibility (fixture, "2:2:2", TRUE);
 
   signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
   signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
+  set_path_visibility (fixture, "2:2", TRUE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
+
+  filter_test_append_refilter_signals (fixture, 2);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, 0);
+
+  /* From here we are filtered, "2" in the real model is "0" in the filter
+   * model.
+   */
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "2", TRUE);
+  check_filter_model (fixture);
+  check_level_length (fixture->filter, NULL, 1);
+  check_level_length (fixture->filter, "0", 1);
+  check_level_length (fixture->filter, "0:0", 1);
+}
+
+static void
+unfiltered_vroot_show_single (FilterTest    *fixture,
+                              gconstpointer  user_data)
+
+{
+  GtkTreePath *path = (GtkTreePath *)user_data;
+
+  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2");
+  set_path_visibility (fixture, "2:2", TRUE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+
+  /* The view only shows the root level, so the filter model only has
+   * the first two levels cached.
+   */
+  filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, 1);
+}
+
+static void
+unfiltered_vroot_show_single_child (FilterTest    *fixture,
+                                    gconstpointer  user_data)
+
+{
+  GtkTreePath *path = (GtkTreePath *)user_data;
+
   set_path_visibility (fixture, "2:2:2", TRUE);
 
   signal_monitor_assert_is_empty (fixture->monitor);
@@ -1785,6 +1863,39 @@ unfiltered_vroot_show_single_child (FilterTest    *fixture,
   check_level_length (fixture->filter, "0", 1);
 }
 
+static void
+unfiltered_vroot_show_single_child_root_expanded (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
+
+{
+  GtkTreePath *path = (GtkTreePath *)user_data;
+
+  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
+  set_path_visibility (fixture, "2:2:2", TRUE);
+
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+
+  filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
+  filter_test_enable_filter (fixture);
+
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, 0);
+
+  /* From here we are filtered, "2" in the real model is "0" in the filter
+   * model.
+   */
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "2:2", TRUE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, 1);
+  check_level_length (fixture->filter, "0", 1);
+}
+
+
 static void
 unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
                                           gconstpointer  user_data)
@@ -1797,8 +1908,6 @@ unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
    */
   set_path_visibility (fixture, "2:2:2:2", TRUE);
 
-  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
-  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
   set_path_visibility (fixture, "2:2:2", TRUE);
 
   signal_monitor_assert_is_empty (fixture->monitor);
@@ -1806,10 +1915,8 @@ unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
   check_level_length (fixture->filter, "2", LEVEL_LENGTH);
   check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* The view only shows the root level, so the filter model only has
-   * the first two levels cached.
-   */
-  filter_test_append_refilter_signals_with_vroot (fixture, 4, path);
+  /* We only expect signals for the root level */
+  filter_test_append_refilter_signals_with_vroot (fixture, 2, path);
   filter_test_enable_filter (fixture);
 
   check_filter_model_with_root (fixture, path);
@@ -1827,134 +1934,540 @@ unfiltered_vroot_show_single_multi_level (FilterTest    *fixture,
   check_level_length (fixture->filter, "0:0", 1);
 }
 
-
 static void
-insert_before (void)
+unfiltered_vroot_show_single_multi_level_root_expanded (FilterTest    *fixture,
+                                                        gconstpointer  user_data)
+
 {
-  GtkTreeStore *store;
-  GtkTreeModel *filter;
-  GtkWidget *tree_view;
-  SignalMonitor *monitor;
-  GtkTreeIter iter;
-  GtkTreeIter last_iter;
-  GtkTreePath *path;
+  GtkTreePath *path = (GtkTreePath *)user_data;
 
-  /* This tests two aspects of the row-inserted handling:
-   *   1) If the newly inserted node was already handled by building
-   *      the root level, don't handle it a second time.
-   *   2) Offsets of existing nodes must be updated when a new
-   *      node is inserted.
+  /* The view is not showing this row (collapsed state), so it is not
+   * referenced.  The signal should not go through.
    */
+  set_path_visibility (fixture, "2:2:2:2", TRUE);
 
-  store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
-  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
-  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
-                                            1);
-
-  tree_view = gtk_tree_view_new_with_model (filter);
-  monitor = signal_monitor_new (filter);
+  signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2");
+  set_path_visibility (fixture, "2:2:2", TRUE);
 
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 0);
+  signal_monitor_assert_is_empty (fixture->monitor);
+  check_level_length (fixture->filter, NULL, LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2", LEVEL_LENGTH);
+  check_level_length (fixture->filter, "2:2", LEVEL_LENGTH);
 
-  /* Insert 0 */
-  path = gtk_tree_path_new_from_indices (0, -1);
-  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
-  gtk_tree_path_free (path);
+  filter_test_append_refilter_signals_with_vroot (fixture, 3, path);
+  filter_test_enable_filter (fixture);
 
-  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
-                                     0, "Foo", 1, TRUE, -1);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, 0);
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+  /* From here we are filtered, "2" in the real model is "0" in the filter
+   * model.
+   */
+  signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "2:2", TRUE);
+  check_filter_model_with_root (fixture, path);
+  check_level_length (fixture->filter, NULL, 1);
+  check_level_length (fixture->filter, "0", 1);
+  check_level_length (fixture->filter, "0:0", 1);
+}
 
-  /* Insert 1 */
-  path = gtk_tree_path_new_from_indices (1, -1);
-  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
-  gtk_tree_path_free (path);
+static void
+unfiltered_rows_reordered_root_level (FilterTest    *fixture,
+                                      gconstpointer  user_data)
+{
+  int order0[] = { 1, 2, 3, 4, 0 };
+  int order1[] = { 0, 2, 1, 3, 4 };
+  int order2[] = { 4, 0, 1, 2, 3 };
+  GtkTreeIter iter0, iter1, iter2, iter3, iter4;
+  GtkTreePath *path;
 
-  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
-                                     0, "Foo", 1, TRUE, -1);
-  last_iter = iter;
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter0, "0");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter2, "2");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "4");
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
+  path = gtk_tree_path_new ();
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 5);
+  gtk_tree_store_move_after (fixture->store, &iter0, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  /* Insert on 1 again -- invisible */
-  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
-                                     0, "Foo", 1, FALSE, -1);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 5);
+  gtk_tree_store_move_after (fixture->store, &iter2, &iter3);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 5);
+  gtk_tree_store_move_before (fixture->store, &iter0, &iter1);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  /* Insert on 1 again -- visible */
-  path = gtk_tree_path_new_from_indices (1, -1);
-  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
   gtk_tree_path_free (path);
+}
 
-  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
-                                     0, "Foo", 1, TRUE, -1);
+static void
+unfiltered_rows_reordered_child_level (FilterTest    *fixture,
+                                       gconstpointer  user_data)
+{
+  int order0[] = { 1, 2, 3, 4, 0 };
+  int order1[] = { 0, 2, 1, 3, 4 };
+  int order2[] = { 4, 0, 1, 2, 3 };
+  GtkTreeIter iter0, iter1, iter2, iter3, iter4;
+  GtkTreePath *path;
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
+  /* Expand row 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
 
-  /* Modify the iter that should be at the last position and check the
-   * signal we get.
-   */
-  path = gtk_tree_path_new_from_indices (2, -1);
-  signal_monitor_append_signal_path (monitor, ROW_CHANGED, path);
-  gtk_tree_path_free (path);
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter0, "0:0");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "0:1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter2, "0:2");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "0:3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "0:4");
 
-  gtk_tree_store_set (store, &last_iter, 0, "Foo changed", -1);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 5);
+  gtk_tree_store_move_after (fixture->store, &iter0, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 5);
+  gtk_tree_store_move_after (fixture->store, &iter2, &iter3);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 5);
+  gtk_tree_store_move_before (fixture->store, &iter0, &iter1);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_path_free (path);
 }
 
 static void
-insert_child (void)
+filtered_rows_reordered_root_level_first_hidden (FilterTest    *fixture,
+                                                 gconstpointer  user_data)
 {
-  GtkTreeStore *store;
-  GtkTreeModel *filter;
-  GtkWidget *tree_view;
-  SignalMonitor *monitor;
-  GtkTreeIter parent, iter;
+  int order0[] = { 1, 2, 3, 0 };
+  int order1[] = { 0, 2, 1, 3 };
+  int order2[] = { 3, 0, 1, 2 };
+  GtkTreeIter iter1, iter2, iter3, iter4;
   GtkTreePath *path;
 
-  store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  /* Hide middle path */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0");
+  set_path_visibility (fixture, "0", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  gtk_tree_store_insert_with_values (store, &parent, NULL, 0,
-                                     0, "Parent", 1, TRUE, -1);
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter2, "2");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "4");
 
+  path = gtk_tree_path_new ();
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 4);
+  gtk_tree_store_move_after (fixture->store, &iter1, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
-  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
-                                            1);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 4);
+  gtk_tree_store_move_after (fixture->store, &iter3, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  tree_view = gtk_tree_view_new_with_model (filter);
-  monitor = signal_monitor_new (filter);
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 4);
+  gtk_tree_store_move_before (fixture->store, &iter1, &iter2);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  /* Insert child -- invisible */
-  path = gtk_tree_path_new_from_indices (0, -1);
-  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
-  /* The signal is received twice, once a pass through from GtkTreeStore
-   * and one generated by GtkTreeModelFilter.  Not accurate, but cannot
-   * hurt.
-   */
-  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
   gtk_tree_path_free (path);
+}
 
-  gtk_tree_store_insert_with_values (store, &iter, &parent, 1,
-                                     0, "Child", 1, FALSE, -1);
+static void
+filtered_rows_reordered_root_level_middle_hidden (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
+{
+  int order0[] = { 1, 2, 3, 0 };
+  int order1[] = { 0, 2, 1, 3 };
+  int order2[] = { 3, 0, 1, 2 };
+  GtkTreeIter iter0, iter1, iter3, iter4;
+  GtkTreePath *path;
 
-  signal_monitor_assert_is_empty (monitor);
-  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+  /* Hide middle path */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "2");
+  set_path_visibility (fixture, "2", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
 
-  /* Insert child */
-  path = gtk_tree_path_new_from_indices (0, 0, -1);
-  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
-  gtk_tree_path_up (path); /* 0 */
-  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
-  gtk_tree_path_free (path);
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter0, "0");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "4");
+
+  path = gtk_tree_path_new ();
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 4);
+  gtk_tree_store_move_after (fixture->store, &iter0, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 4);
+  gtk_tree_store_move_after (fixture->store, &iter3, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 4);
+  gtk_tree_store_move_before (fixture->store, &iter0, &iter1);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_path_free (path);
+}
+
+static void
+filtered_rows_reordered_child_level_first_hidden (FilterTest    *fixture,
+                                                  gconstpointer  user_data)
+{
+  int order0[] = { 1, 2, 3, 0 };
+  int order1[] = { 0, 2, 1, 3 };
+  int order2[] = { 3, 0, 1, 2 };
+  GtkTreeIter iter1, iter2, iter3, iter4;
+  GtkTreePath *path;
+
+  /* Expand row 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, TRUE);
+
+  /* Hide middle path */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:0");
+  set_path_visibility (fixture, "0:0", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "0:1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter2, "0:2");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "0:3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "0:4");
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 4);
+  gtk_tree_store_move_after (fixture->store, &iter1, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 4);
+  gtk_tree_store_move_after (fixture->store, &iter3, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 4);
+  gtk_tree_store_move_before (fixture->store, &iter1, &iter2);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_path_free (path);
+}
+
+static void
+filtered_rows_reordered_child_level_middle_hidden (FilterTest    *fixture,
+                                                   gconstpointer  user_data)
+{
+  int order0[] = { 1, 2, 3, 0 };
+  int order1[] = { 0, 2, 1, 3 };
+  int order2[] = { 3, 0, 1, 2 };
+  GtkTreeIter iter0, iter1, iter3, iter4;
+  GtkTreePath *path;
+
+  /* Expand row 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
+
+  /* Hide middle path */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:2");
+  set_path_visibility (fixture, "0:2", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter0, "0:0");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "0:1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter3, "0:3");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "0:4");
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 4);
+  gtk_tree_store_move_after (fixture->store, &iter0, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order1, 4);
+  gtk_tree_store_move_after (fixture->store, &iter3, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order2, 4);
+  gtk_tree_store_move_before (fixture->store, &iter0, &iter1);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_path_free (path);
+}
+
+static void
+filtered_rows_reordered_child_level_4_hidden (FilterTest    *fixture,
+                                              gconstpointer  user_data)
+{
+  int order0[] = { 0 };
+  GtkTreeIter iter1, iter4;
+  GtkTreePath *path;
+
+  /* Expand row 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
+
+  /* Hide last 4 paths */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:4");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:3");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:2");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:0");
+  set_path_visibility (fixture, "0:4", FALSE);
+  set_path_visibility (fixture, "0:3", FALSE);
+  set_path_visibility (fixture, "0:2", FALSE);
+  set_path_visibility (fixture, "0:0", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "0:1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "0:4");
+
+  signal_monitor_append_signal_reordered (fixture->monitor,
+                                          ROWS_REORDERED,
+                                          path, order0, 1);
+  gtk_tree_store_move_after (fixture->store, &iter1, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_path_free (path);
+}
+
+static void
+filtered_rows_reordered_child_level_all_hidden (FilterTest    *fixture,
+                                                gconstpointer  user_data)
+{
+  GtkTreeIter iter1, iter4;
+  GtkTreePath *path;
+
+  /* Expand row 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (fixture->tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
+  /* Hide last 4 paths */
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:4");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:3");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:2");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:1");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_DELETED, "0:0");
+  signal_monitor_append_signal (fixture->monitor,
+                                ROW_HAS_CHILD_TOGGLED, "0");
+  set_path_visibility (fixture, "0:4", FALSE);
+  set_path_visibility (fixture, "0:3", FALSE);
+  set_path_visibility (fixture, "0:2", FALSE);
+  set_path_visibility (fixture, "0:1", FALSE);
+  set_path_visibility (fixture, "0:0", FALSE);
+  signal_monitor_assert_is_empty (fixture->monitor);
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter1, "0:1");
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store),
+                                       &iter4, "0:4");
+
+  gtk_tree_store_move_after (fixture->store, &iter1, &iter4);
+  signal_monitor_assert_is_empty (fixture->monitor);
+}
+
+static void
+insert_before (void)
+{
+  GtkTreeStore *store;
+  GtkTreeModel *filter;
+  GtkWidget *tree_view;
+  SignalMonitor *monitor;
+  GtkTreeIter iter;
+  GtkTreeIter last_iter;
+  GtkTreePath *path;
+
+  /* This tests two aspects of the row-inserted handling:
+   *   1) If the newly inserted node was already handled by building
+   *      the root level, don't handle it a second time.
+   *   2) Offsets of existing nodes must be updated when a new
+   *      node is inserted.
+   */
+
+  store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
+                                            1);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+  monitor = signal_monitor_new (filter);
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 0);
+
+  /* Insert 0 */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+                                     0, "Foo", 1, TRUE, -1);
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+
+  /* Insert 1 */
+  path = gtk_tree_path_new_from_indices (1, -1);
+  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
+                                     0, "Foo", 1, TRUE, -1);
+  last_iter = iter;
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
+
+  /* Insert on 1 again -- invisible */
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
+                                     0, "Foo", 1, FALSE, -1);
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
+
+  /* Insert on 1 again -- visible */
+  path = gtk_tree_path_new_from_indices (1, -1);
+  signal_monitor_append_signal_path (monitor, ROW_INSERTED, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 1,
+                                     0, "Foo", 1, TRUE, -1);
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
+
+  /* Modify the iter that should be at the last position and check the
+   * signal we get.
+   */
+  path = gtk_tree_path_new_from_indices (2, -1);
+  signal_monitor_append_signal_path (monitor, ROW_CHANGED, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_store_set (store, &last_iter, 0, "Foo changed", -1);
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 3);
+
+  g_object_unref (filter);
+  g_object_unref (store);
+  gtk_widget_destroy (tree_view);
+}
+
+static void
+insert_child (void)
+{
+  GtkTreeStore *store;
+  GtkTreeModel *filter;
+  GtkWidget *tree_view;
+  SignalMonitor *monitor;
+  GtkTreeIter parent, iter;
+  GtkTreePath *path;
+
+  store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+  gtk_tree_store_insert_with_values (store, &parent, NULL, 0,
+                                     0, "Parent", 1, TRUE, -1);
+
+
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
+                                            1);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+  monitor = signal_monitor_new (filter);
+
+  /* Insert child -- invisible */
+  path = gtk_tree_path_new_from_indices (0, -1);
+  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
+  /* The signal is received twice, once a pass through from GtkTreeStore
+   * and one generated by GtkTreeModelFilter.  Not accurate, but cannot
+   * hurt.
+   */
+  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
+  gtk_tree_path_free (path);
+
+  gtk_tree_store_insert_with_values (store, &iter, &parent, 1,
+                                     0, "Child", 1, FALSE, -1);
+
+  signal_monitor_assert_is_empty (monitor);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+
+  /* Insert child */
+  path = gtk_tree_path_new_from_indices (0, 0, -1);
+  gtk_tree_path_up (path); /* 0 */
+  signal_monitor_append_signal_path (monitor, ROW_HAS_CHILD_TOGGLED, path);
+  gtk_tree_path_free (path);
 
   gtk_tree_store_insert_with_values (store, &iter, &parent, 0,
                                      0, "Child", 1, TRUE, -1);
@@ -1968,6 +2481,10 @@ insert_child (void)
 
   signal_monitor_assert_is_empty (monitor);
   check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+
+  g_object_unref (filter);
+  g_object_unref (store);
+  gtk_widget_destroy (tree_view);
 }
 
 
@@ -2076,6 +2593,1992 @@ remove_vroot_ancestor (void)
   g_object_unref (tree);
 }
 
+static void
+ref_count_single_level (void)
+{
+  GtkTreeIter iter[5];
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter[0], NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter[1], NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter[2], NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter[3], NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter[4], NULL);
+
+  assert_root_level_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &iter[0], 2);
+  assert_node_ref_count (ref_model, &iter[1], 1);
+  assert_node_ref_count (ref_model, &iter[2], 1);
+  assert_node_ref_count (ref_model, &iter[3], 1);
+  assert_node_ref_count (ref_model, &iter[4], 1);
+
+  gtk_widget_destroy (tree_view);
+
+  assert_node_ref_count (ref_model, &iter[0], 1);
+  assert_node_ref_count (ref_model, &iter[1], 0);
+  assert_node_ref_count (ref_model, &iter[2], 0);
+  assert_node_ref_count (ref_model, &iter[3], 0);
+  assert_node_ref_count (ref_model, &iter[4], 0);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &iter[0], 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_two_levels (void)
+{
+  GtkTreeIter parent1, parent2, iter, iter_first;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_first, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent2);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  /* This is quite confusing:
+   *  - node 0 has a ref count of 2 because it is referenced as the
+   *    first node in a level and by the tree view.
+   *  - node 1 has a ref count of 2 because it is referenced by its
+   *    child level and by the tree view.
+   */
+  assert_root_level_referenced (ref_model, 2);
+  assert_node_ref_count (ref_model, &iter_first, 1);
+  assert_node_ref_count (ref_model, &iter, 0);
+
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_first, 2);
+  assert_node_ref_count (ref_model, &iter, 1);
+
+  gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
+
+  /* The child level is not destroyed because its parent is visible */
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_first, 1);
+  assert_node_ref_count (ref_model, &iter, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_first, 1);
+  assert_node_ref_count (ref_model, &iter, 0);
+
+  gtk_widget_destroy (tree_view);
+
+  assert_root_level_referenced (ref_model, 1);
+  assert_node_ref_count (ref_model, &iter_first, 1);
+  assert_node_ref_count (ref_model, &iter, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  /* The root level and first level remain cached, only the references on the
+   * first nodes of these levels are kept.
+   */
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &iter_first, 1);
+  assert_node_ref_count (ref_model, &iter, 0);
+
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_three_levels (void)
+{
+  GtkTreeIter grandparent1, grandparent2, parent1, parent2;
+  GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   *   + parent1
+   *     + iter_parent1
+   *   + parent2
+   *     + iter_parent2_first
+   *     + iter_parent2
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  /* This is quite confusing:
+   *  - node 0 has a ref count of 2 because it is referenced as the
+   *    first node in a level and by the tree view.
+   *  - node 1 has a ref count of 2 because it is referenced by its
+   *    child level and by the tree view.
+   */
+  assert_root_level_referenced (ref_model, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_level_unreferenced (ref_model, &parent1);
+  assert_level_unreferenced (ref_model, &parent2);
+
+  path = gtk_tree_path_new_from_indices (1, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 2);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 2);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 0);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_path_append_index (path, 1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 2);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_path_up (path);
+  gtk_tree_view_collapse_row (GTK_TREE_VIEW (tree_view), path);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 0);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_widget_destroy (tree_view);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  /* The root level and first level remain cached, only the references on the
+   * first nodes of these levels are kept.  Grandparent2 is the parent
+   * of the first level with parent1, so grandparent2 keeps a reference
+   * as well.
+   */
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 0);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_delete_row (void)
+{
+  GtkTreeIter grandparent1, grandparent2, parent1, parent2;
+  GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   *   + parent1
+   *     + iter_parent1
+   *   + parent2
+   *     + iter_parent2_first
+   *     + iter_parent2
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_root_level_referenced (ref_model, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_level_unreferenced (ref_model, &parent1);
+  assert_level_unreferenced (ref_model, &parent2);
+
+  path = gtk_tree_path_new_from_indices (1, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 2);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 2);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &iter_parent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 2);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 2);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent2, 3);
+  assert_level_referenced (ref_model, 2, &parent2);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_filter_row_length_1 (void)
+{
+  GtkTreeIter level1_1;
+  GtkTreeIter level2_1;
+  GtkTreeIter level3_1;
+  GtkTreeIter level4_1;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+
+  /* + level1_1
+   *   + level2_1
+   *     + level3_1
+   *       + level4_1
+   *
+   * Node level1_1 is expanded.  This makes that levels 1 and 2 are
+   * visible.  Level 3 is cached because its parent is visible.  Level 4
+   * is not cached.
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 3);
+  assert_node_ref_count (ref_model, &level3_1, 1);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 3);
+  assert_node_ref_count (ref_model, &level3_1, 1);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  /* level3_1 has a visible parent, so the node is kept in the cache. */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 3);
+  assert_node_ref_count (ref_model, &level3_1, 1);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  /* level2_1 has a visible parent, so is kept in the cache.  However,
+   * the external reference should be released.
+   */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_filter_row_length_1_remove_in_root_level (void)
+{
+  GtkTreeIter level1_1;
+  GtkTreeIter level2_1;
+  GtkTreeIter level3_1;
+  GtkTreeIter level4_1;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+
+  /* + level1_1
+   *   + level2_1
+   *     + level3_1
+   *       + level4_1
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 3);
+  assert_node_ref_count (ref_model, &level3_1, 3);
+  assert_node_ref_count (ref_model, &level4_1, 2);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 0);
+  assert_node_ref_count (ref_model, &level2_1, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_filter_row_length_1_remove_in_child_level (void)
+{
+  GtkTreeIter level1_1;
+  GtkTreeIter level2_1;
+  GtkTreeIter level3_1;
+  GtkTreeIter level4_1;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+
+  /* + level1_1
+   *   + level2_1
+   *     + level3_1
+   *       + level4_1
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 3);
+  assert_node_ref_count (ref_model, &level3_1, 3);
+  assert_node_ref_count (ref_model, &level4_1, 2);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 3);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 0);
+  assert_node_ref_count (ref_model, &level2_1, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_filter_row_length_gt_1 (void)
+{
+  GtkTreeIter level1_1, level1_2;
+  GtkTreeIter level2_1, level2_2;
+  GtkTreeIter level3_1, level3_2;
+  GtkTreeIter level4_1, level4_2;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+
+  /* + level1_1
+   * + level1_2
+   *   + level2_1
+   *   + level2_2
+   *     + level3_1
+   *     + level3_2
+   *       + level4_1
+   *       + level4_2
+   *
+   * Node level1_2 is expanded.  This makes that levels 1 and 2 are
+   * visible.  Level 3 is cached because its parent is visible.  Level 4
+   * is not cached.
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_2, &level1_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_2, &level2_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_2, &level3_2);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_2, 0, TRUE, -1);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  path = gtk_tree_path_new_from_indices (1, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 2);
+  assert_node_ref_count (ref_model, &level3_1, 1);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 2);
+  assert_node_ref_count (ref_model, &level3_1, 1);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 2);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 1);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 0);
+  assert_node_ref_count (ref_model, &level2_1, 0);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &level1_1, 1);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_filter_row_length_gt_1_visible_children (void)
+{
+  GtkTreeIter level1_1, level1_2;
+  GtkTreeIter level2_1, level2_2;
+  GtkTreeIter level3_1, level3_2;
+  GtkTreeIter level4_1, level4_2;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+
+  /* + level1_1
+   * + level1_2
+   *   + level2_1
+   *   + level2_2
+   *     + level3_1
+   *     + level3_2
+   *       + level4_1
+   *       + level4_2
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level1_2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_1, &level1_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level2_2, &level1_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_1, &level2_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level3_2, &level2_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_1, &level3_2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &level4_2, &level3_2);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level1_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level3_2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level4_2, 0, TRUE, -1);
+
+  assert_entire_model_unreferenced (ref_model);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  path = gtk_tree_path_new_from_indices (1, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, TRUE);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 2);
+  assert_node_ref_count (ref_model, &level3_1, 2);
+  assert_node_ref_count (ref_model, &level3_2, 2);
+  assert_node_ref_count (ref_model, &level4_1, 2);
+  assert_node_ref_count (ref_model, &level4_2, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &level2_2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &level1_1, 2);
+  assert_node_ref_count (ref_model, &level1_2, 2);
+  assert_node_ref_count (ref_model, &level2_1, 2);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  gtk_widget_destroy (tree_view);
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  assert_node_ref_count (ref_model, &level1_1, 1);
+  assert_node_ref_count (ref_model, &level1_2, 1);
+  assert_node_ref_count (ref_model, &level2_1, 1);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  g_object_unref (filter_model);
+
+  assert_node_ref_count (ref_model, &level1_1, 0);
+  assert_node_ref_count (ref_model, &level1_2, 0);
+  assert_node_ref_count (ref_model, &level2_1, 0);
+  assert_node_ref_count (ref_model, &level2_2, 0);
+  assert_node_ref_count (ref_model, &level3_1, 0);
+  assert_node_ref_count (ref_model, &level3_2, 0);
+  assert_node_ref_count (ref_model, &level4_1, 0);
+  assert_node_ref_count (ref_model, &level4_2, 0);
+
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_cleanup (void)
+{
+  GtkTreeIter grandparent1, grandparent2, parent1, parent2;
+  GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   *   + parent1
+   *     + iter_parent1
+   *   + parent2
+   *     + iter_parent2_first
+   *     + iter_parent2
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 2);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 2);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_widget_destroy (tree_view);
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &parent1, 2);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  /* The root level and first level remain cached, only the references on the
+   * first nodes of these levels are kept.  Grandparent2 is the parent
+   * of the first level with parent1, so grandparent2 keeps a reference
+   * as well.
+   */
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 0);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_row_ref (void)
+{
+  GtkTreeIter grandparent1, grandparent2, parent1, parent2;
+  GtkTreeIter iter_parent1, iter_parent2, iter_parent2_first;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GtkTreePath *path;
+  GtkTreeRowReference *row_ref;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   *   + parent1
+   *     + iter_parent1
+   *   + parent2
+   *     + iter_parent2
+   *     + iter_parent2
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent1, &parent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2_first, &parent2);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &iter_parent2, &parent2);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  path = gtk_tree_path_new_from_indices (1, 1, 1, -1);
+  row_ref = gtk_tree_row_reference_new (filter_model, path);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_tree_row_reference_free (row_ref);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 0);
+
+  path = gtk_tree_path_new_from_indices (1, 1, 1, -1);
+  row_ref = gtk_tree_row_reference_new (filter_model, path);
+  gtk_tree_path_free (path);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 2);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+  assert_node_ref_count (ref_model, &iter_parent2_first, 1);
+  assert_node_ref_count (ref_model, &iter_parent2, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+
+  gtk_tree_row_reference_free (row_ref);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &iter_parent1, 0);
+
+  gtk_widget_destroy (tree_view);
+
+  gtk_tree_model_filter_clear_cache (GTK_TREE_MODEL_FILTER (filter_model));
+
+  /* The root level and first level remain cached, only the references on the
+   * first nodes of these levels are kept.  Grandparent2 is the parent
+   * of the first level with parent1, so grandparent2 keeps a reference
+   * as well.
+   */
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &parent1, 1);
+
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_insert (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3;
+  GtkTreeIter new_node;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL);
+
+  assert_node_ref_count (ref_model, &new_node, 2);
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_remove (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent1);
+
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2);
+
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_remove_filtered (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   * + grandparent4
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, NULL);
+
+  /* Filter first node */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 2);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent4);
+
+  /* Check level length to get root level cached again */
+  check_level_length (GTK_TREE_MODEL_FILTER (filter_model), NULL, 0);
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter_model), NULL, 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_reordered (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_after (GTK_TREE_STORE (model),
+                             &grandparent1, &grandparent3);
+
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_reordered_filtered (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+
+  /* Test with 1 node filtered */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  /* Move the invisible node grandparent1 */
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_after (GTK_TREE_STORE (model),
+                             &grandparent1, &grandparent3);
+
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+
+  /* Move the invisible node grandparent1 */
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_before (GTK_TREE_STORE (model),
+                              &grandparent1, &grandparent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  /* Now swap grandparent2 and grandparent3, first reference must transfer */
+  /* gtk_tree_store_swap() will emit rows-reordered */
+  gtk_tree_store_swap (GTK_TREE_STORE (model),
+                       &grandparent2, &grandparent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+
+  /* Swap back */
+  gtk_tree_store_swap (GTK_TREE_STORE (model),
+                       &grandparent2, &grandparent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+
+  /* Test with two nodes filtered */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_before (GTK_TREE_STORE (model),
+                             &grandparent3, &grandparent1);
+
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_root_level_filter (void)
+{
+  GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4;
+  GtkTreeIter new_node;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + grandparent1
+   * + grandparent2
+   * + grandparent3
+   * + grandparent4
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, NULL);
+
+  /* Filter first node */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 2);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 2);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 2);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node);
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, NULL);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &new_node, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &new_node, 2);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 2);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_insert (void)
+{
+  GtkTreeIter grandparent1;
+  GtkTreeIter parent1, parent2, parent3;
+  GtkTreeIter new_node;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   *   + parent1
+   *   + parent2
+   *   + parent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &grandparent1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &new_node, 1);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_remove (void)
+{
+  GtkTreeIter grandparent1;
+  GtkTreeIter parent1, parent2, parent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   *   + parent1
+   *   + parent2
+   *   + parent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent3, 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_remove_filtered (void)
+{
+  GtkTreeIter grandparent1;
+  GtkTreeIter parent1, parent2, parent3, parent4;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + grandparent1
+   *   + parent1
+   *   + parent2
+   *   + parent3
+   *   + parent4
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent4, &grandparent1);
+
+  /* Filter first node */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent3, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent4, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+  assert_node_ref_count (ref_model, &parent4, 0);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent3, 1);
+  assert_node_ref_count (ref_model, &parent4, 0);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent4, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent4);
+
+  /* Check level length to get level cached again */
+  check_level_length (GTK_TREE_MODEL_FILTER (filter_model), "0", 0);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter_model), "0", 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_reordered (void)
+{
+  GtkTreeIter grandparent1;
+  GtkTreeIter parent1, parent2, parent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  /* + grandparent1
+   *   + parent1
+   *   + parent2
+   *   + parent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_after (GTK_TREE_STORE (model),
+                             &parent1, &parent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+  assert_node_ref_count (ref_model, &parent1, 0);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_reordered_filtered (void)
+{
+  GtkTreeIter grandparent1;
+  GtkTreeIter parent1, parent2, parent3;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + grandparent1
+   *   + parent1
+   *   + parent2
+   *   + parent3
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent1, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent2, &grandparent1);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &parent3, &grandparent1);
+
+  /* Test with 1 node filtered (parent1) */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent3, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  /* Move invisible node parent 1 */
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_after (GTK_TREE_STORE (model),
+                             &parent1, &parent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+  assert_node_ref_count (ref_model, &parent1, 0);
+
+  /* Move invisible node parent 1 */
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_before (GTK_TREE_STORE (model),
+                              &parent1, &parent2);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  /* Now swap parent2 and parent2, first reference must transfer */
+  /* gtk_tree_store_swap() will emit rows-reordered */
+  gtk_tree_store_swap (GTK_TREE_STORE (model),
+                       &parent2, &parent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent3, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+
+  /* Swap back */
+  gtk_tree_store_swap (GTK_TREE_STORE (model),
+                       &parent2, &parent3);
+
+  assert_node_ref_count (ref_model, &grandparent1, 3);
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 1);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &parent1, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 0);
+
+  /* Test with two nodes filtered */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &parent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &parent1, 0);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent3, 1);
+
+  /* gtk_tree_store_move() will emit rows-reordered */
+  gtk_tree_store_move_before (GTK_TREE_STORE (model),
+                             &parent3, &parent1);
+
+  assert_node_ref_count (ref_model, &parent3, 1);
+  assert_node_ref_count (ref_model, &parent2, 0);
+  assert_node_ref_count (ref_model, &parent1, 0);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static void
+ref_count_transfer_child_level_filter (void)
+{
+  GtkTreeIter root;
+  GtkTreeIter grandparent1, grandparent2, grandparent3, grandparent4;
+  GtkTreeIter new_node;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  /* + root
+   *    + grandparent1
+   *    + grandparent2
+   *    + grandparent3
+   *    + grandparent4
+   */
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent1, &root);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent2, &root);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent3, &root);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &grandparent4, &root);
+
+  /* Filter first node */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &root, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 1);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent3, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &root);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 1);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent1, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node);
+  gtk_tree_store_prepend (GTK_TREE_STORE (model), &new_node, &root);
+
+  assert_node_ref_count (ref_model, &new_node, 0);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 1);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &new_node, 0, TRUE, -1);
+
+  assert_node_ref_count (ref_model, &new_node, 1);
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 0);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent2, 0, TRUE, -1);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &new_node);
+
+  assert_node_ref_count (ref_model, &grandparent1, 0);
+  assert_node_ref_count (ref_model, &grandparent2, 1);
+  assert_node_ref_count (ref_model, &grandparent3, 0);
+  assert_node_ref_count (ref_model, &grandparent4, 0);
+
+  gtk_tree_store_set (GTK_TREE_STORE (model), &grandparent4, 0, TRUE, -1);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &grandparent2);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
 
 static gboolean
 specific_path_dependent_filter_func (GtkTreeModel *model,
@@ -2347,71 +4850,347 @@ specific_sort_filter_remove_root (void)
 
   gtk_tree_path_free (path);
 
-  gtk_tree_store_remove (GTK_TREE_STORE (model), &root);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &root);
+
+  g_object_unref (filter);
+  g_object_unref (sort);
+  g_object_unref (model);
+}
+
+
+static void
+specific_root_mixed_visibility (void)
+{
+  int i;
+  GtkTreeModel *filter;
+  /* A bit nasty, apologies */
+  FilterTest fixture;
+
+  fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+  for (i = 0; i < LEVEL_LENGTH; i++)
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_store_insert (fixture.store, &iter, NULL, i);
+      if (i % 2 == 0)
+        create_tree_store_set_values (fixture.store, &iter, TRUE);
+      else
+        create_tree_store_set_values (fixture.store, &iter, FALSE);
+    }
+
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
+  fixture.filter = GTK_TREE_MODEL_FILTER (filter);
+  fixture.monitor = NULL;
+
+  gtk_tree_model_filter_set_visible_column (fixture.filter, 1);
+
+  /* In order to trigger the potential bug, we should not access
+   * the filter model here (so don't call the check functions).
+   */
+
+  /* Change visibility of an odd row to TRUE */
+  set_path_visibility (&fixture, "3", TRUE);
+  check_filter_model (&fixture);
+  check_level_length (fixture.filter, NULL, 4);
+}
+
+
+
+static gboolean
+specific_has_child_filter_filter_func (GtkTreeModel *model,
+                                       GtkTreeIter  *iter,
+                                       gpointer      data)
+{
+  return gtk_tree_model_iter_has_child (model, iter);
+}
+
+static void
+specific_has_child_filter (void)
+{
+  GtkTreeModel *filter;
+  GtkTreeIter iter, root;
+  FilterTest fixture; /* This is not how it should be done */
+  GtkWidget *tree_view;
+
+  fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
+  fixture.filter = GTK_TREE_MODEL_FILTER (filter);
+  fixture.monitor = signal_monitor_new (filter);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+
+  /* We will filter on parent state using a filter function.  We will
+   * manually keep the boolean column in sync, so that we can use
+   * check_filter_model() to check the consistency of the model.
+   */
+  /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
+   * to be able to check the structure here.  We keep the calls to
+   * check_filter_model() commented out until then.
+   */
+  gtk_tree_model_filter_set_visible_func (fixture.filter,
+                                          specific_has_child_filter_filter_func,
+                                          NULL, NULL);
+
+  /* The first node will be initially invisible: no signals */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  create_tree_store_set_values (fixture.store, &root, FALSE);
+
+  /* check_filter_model (&fixture); */
+  check_level_length (fixture.filter, NULL, 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* Insert a child node. This will cause the parent to become visible
+   * since there is a child now.
+   */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+
+  /* Parent must now be visible.  Do the level length check first,
+   * to avoid modifying the child model triggering a row-changed to
+   * the filter model.
+   */
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* This should propagate row-changed */
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  set_path_visibility (&fixture, "0", TRUE);
+  /* check_filter_model (&fixture); */
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* New root node, no child, so no signal */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  check_level_length (fixture.filter, NULL, 1);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* When the child comes in, this node will become visible */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
+
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  check_level_length (fixture.filter, NULL, 2);
+  check_level_length (fixture.filter, "1", 0);
+
+  create_tree_store_set_values (fixture.store, &root, TRUE);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+
+  /* check_filter_model (&fixture); */
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* Add another child for 1 */
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+  check_level_length (fixture.filter, NULL, 2);
+  check_level_length (fixture.filter, "0", 0);
+  check_level_length (fixture.filter, "1", 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* Now remove one of the remaining child rows */
+  signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
+                                       &iter, "0:0");
+  gtk_tree_store_remove (fixture.store, &iter);
+
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 0);
+
+  set_path_visibility (&fixture, "0", FALSE);
+  /* check_filter_model (&fixture); */
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  g_object_unref (fixture.filter);
+  g_object_unref (fixture.store);
+  gtk_widget_destroy (tree_view);
+}
+
+
+static gboolean
+specific_root_has_child_filter_filter_func (GtkTreeModel *model,
+                                            GtkTreeIter  *iter,
+                                            gpointer      data)
+{
+  int depth;
+  GtkTreePath *path;
+
+  path = gtk_tree_model_get_path (model, iter);
+  depth = gtk_tree_path_get_depth (path);
+  gtk_tree_path_free (path);
+
+  if (depth > 1)
+    return TRUE;
+  /* else */
+  return gtk_tree_model_iter_has_child (model, iter);
+}
+
+static void
+specific_root_has_child_filter (void)
+{
+  GtkTreeModel *filter;
+  GtkTreeIter iter, root;
+  FilterTest fixture; /* This is not how it should be done ... */
+  GtkWidget *tree_view;
+
+  /* This is a variation on the above test case, specific has-child-filter,
+   * herein the has-child check for visibility only applies to root level
+   * nodes.  In this test, children are always visible because we
+   * only filter based on the "has child" criterion.
+   */
+
+  fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
+  fixture.filter = GTK_TREE_MODEL_FILTER (filter);
+  fixture.monitor = signal_monitor_new (filter);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+
+  /* We will filter on parent state using a filter function.  We will
+   * manually keep the boolean column in sync, so that we can use
+   * check_filter_model() to check the consistency of the model.
+   */
+  /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
+   * to be able to check the structure here.  We keep the calls to
+   * check_filter_model() commented out until then.
+   */
+  gtk_tree_model_filter_set_visible_func (fixture.filter,
+                                          specific_root_has_child_filter_filter_func,
+                                          NULL, NULL);
+
+  /* Add a first node, this will be invisible initially, so no signal
+   * should be emitted.
+   */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  create_tree_store_set_values (fixture.store, &root, FALSE);
+
+  signal_monitor_assert_is_empty (fixture.monitor);
+  /* check_filter_model (&fixture); */
+  check_level_length (fixture.filter, NULL, 0);
+
+  /* Add a child node.  This will cause the parent to become visible,
+   * so we expect row-inserted signals for both.
+   */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 1);
+
+  /* Modify the content of iter, no signals because the parent is not
+   * expanded.
+   */
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* Parent must now be visible.  Do the level length check first,
+   * to avoid modifying the child model triggering a row-changed to
+   * the filter model.
+   */
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 1);
+
+  /* Modify path 0 */
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  set_path_visibility (&fixture, "0", TRUE);
+  /* check_filter_model (&fixture); */
+
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  g_object_unref (filter);
-  g_object_unref (sort);
-  g_object_unref (model);
-}
+  /* Insert another node in the root level.  Initially invisible, so
+   * not expecting any signal.
+   */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  check_level_length (fixture.filter, NULL, 1);
 
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-static void
-specific_root_mixed_visibility (void)
-{
-  int i;
-  GtkTreeModel *filter;
-  /* A bit nasty, apologies */
-  FilterTest fixture;
+  /* Adding a child node which also makes parent at path 1 visible. */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
 
-  fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  check_level_length (fixture.filter, NULL, 2);
+  check_level_length (fixture.filter, "1", 1);
 
-  for (i = 0; i < LEVEL_LENGTH; i++)
-    {
-      GtkTreeIter iter;
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-      gtk_tree_store_insert (fixture.store, &iter, NULL, i);
-      if (i % 2 == 0)
-        create_tree_store_set_values (fixture.store, &iter, TRUE);
-      else
-        create_tree_store_set_values (fixture.store, &iter, FALSE);
-    }
+  /* Check if row-changed is propagated */
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
 
-  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
-  fixture.filter = GTK_TREE_MODEL_FILTER (filter);
-  fixture.monitor = NULL;
+  create_tree_store_set_values (fixture.store, &root, TRUE);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+  /* check_filter_model (&fixture); */
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  gtk_tree_model_filter_set_visible_column (fixture.filter, 1);
+  /* Insert another child under node 1 */
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+  check_level_length (fixture.filter, NULL, 2);
+  check_level_length (fixture.filter, "0", 1);
+  check_level_length (fixture.filter, "1", 2);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* In order to trigger the potential bug, we should not access
-   * the filter model here (so don't call the check functions).
+  /* Set a child node to invisible.  This should not yield any
+   * change, because filtering is only done on whether the root
+   * node has a child, which it still has.
    */
+  set_path_visibility (&fixture, "0:0", FALSE);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Change visibility of an odd row to TRUE */
-  set_path_visibility (&fixture, "3", TRUE);
-  check_filter_model (&fixture);
-  check_level_length (fixture.filter, NULL, 4);
-}
+  /* Now remove one of the remaining child rows */
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
 
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
+                                       &iter, "0:0");
+  gtk_tree_store_remove (fixture.store, &iter);
 
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 2);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-static gboolean
-specific_has_child_filter_filter_func (GtkTreeModel *model,
-                                       GtkTreeIter  *iter,
-                                       gpointer      data)
-{
-  return gtk_tree_model_iter_has_child (model, iter);
+  /* Set visibility of 0 to FALSE, no-op for filter model since
+   * the child 0:0 is already gone
+   */
+  set_path_visibility (&fixture, "0", FALSE);
+  /* check_filter_model (&fixture); */
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  g_object_unref (fixture.filter);
+  g_object_unref (fixture.store);
+  gtk_widget_destroy (tree_view);
 }
 
 static void
-specific_has_child_filter (void)
+specific_has_child_filter_on_sort_model (void)
 {
   GtkTreeModel *filter;
+  GtkTreeModel *sort_model;
   GtkTreeIter iter, root;
   FilterTest fixture; /* This is not how it should be done */
   GtkWidget *tree_view;
 
   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
-  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
+  sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store));
+  filter = gtk_tree_model_filter_new (sort_model, NULL);
   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
   fixture.monitor = signal_monitor_new (filter);
 
@@ -2442,7 +5221,6 @@ specific_has_child_filter (void)
    */
   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
 
   gtk_tree_store_append (fixture.store, &iter, &root);
   create_tree_store_set_values (fixture.store, &iter, TRUE);
@@ -2471,7 +5249,6 @@ specific_has_child_filter (void)
   /* When the child comes in, this node will become visible */
   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
 
@@ -2506,41 +5283,28 @@ specific_has_child_filter (void)
   set_path_visibility (&fixture, "0", FALSE);
   /* check_filter_model (&fixture); */
   signal_monitor_assert_is_empty (fixture.monitor);
-}
 
+  g_object_unref (fixture.filter);
+  g_object_unref (fixture.store);
+  gtk_widget_destroy (tree_view);
+}
 
 static gboolean
-specific_root_has_child_filter_filter_func (GtkTreeModel *model,
-                                            GtkTreeIter  *iter,
-                                            gpointer      data)
+specific_at_least_2_children_filter_filter_func (GtkTreeModel *model,
+                                                 GtkTreeIter  *iter,
+                                                 gpointer      data)
 {
-  int depth;
-  GtkTreePath *path;
-
-  path = gtk_tree_model_get_path (model, iter);
-  depth = gtk_tree_path_get_depth (path);
-  gtk_tree_path_free (path);
-
-  if (depth > 1)
-    return TRUE;
-  /* else */
-  return gtk_tree_model_iter_has_child (model, iter);
+  return gtk_tree_model_iter_n_children (model, iter) >= 2;
 }
 
 static void
-specific_root_has_child_filter (void)
+specific_at_least_2_children_filter (void)
 {
   GtkTreeModel *filter;
   GtkTreeIter iter, root;
-  FilterTest fixture; /* This is not how it should be done ... */
+  FilterTest fixture; /* This is not how it should be done */
   GtkWidget *tree_view;
 
-  /* This is a variation on the above test case, specific has-child-filter,
-   * herein the has-child check for visibility only applies to root level
-   * nodes.  In this test, children are always visible because we
-   * only filter based on the "has child" criterion.
-   */
-
   fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
   filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL);
   fixture.filter = GTK_TREE_MODEL_FILTER (filter);
@@ -2548,116 +5312,86 @@ specific_root_has_child_filter (void)
 
   tree_view = gtk_tree_view_new_with_model (filter);
 
-  /* We will filter on parent state using a filter function.  We will
-   * manually keep the boolean column in sync, so that we can use
-   * check_filter_model() to check the consistency of the model.
-   */
-  /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH
-   * to be able to check the structure here.  We keep the calls to
-   * check_filter_model() commented out until then.
-   */
   gtk_tree_model_filter_set_visible_func (fixture.filter,
-                                          specific_root_has_child_filter_filter_func,
+                                          specific_at_least_2_children_filter_filter_func,
                                           NULL, NULL);
 
-  /* Add a first node, this will be invisible initially, so no signal
-   * should be emitted.
-   */
+  /* The first node will be initially invisible: no signals */
   gtk_tree_store_append (fixture.store, &root, NULL);
   create_tree_store_set_values (fixture.store, &root, FALSE);
 
-  signal_monitor_assert_is_empty (fixture.monitor);
   /* check_filter_model (&fixture); */
   check_level_length (fixture.filter, NULL, 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Add a child node.  This will cause the parent to become visible,
-   * so we expect row-inserted signals for both.
+  /* Insert a child node.  Nothing should happen.
    */
-  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
-
   gtk_tree_store_append (fixture.store, &iter, &root);
-  signal_monitor_assert_is_empty (fixture.monitor);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
 
-  check_level_length (fixture.filter, NULL, 1);
-  check_level_length (fixture.filter, "0", 1);
+  check_level_length (fixture.filter, NULL, 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Modify the content of iter, yields row-changed signals */
-  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0:0");
+  /* Insert a second child node.  This will cause the parent to become
+   * visible.
+   */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
 
+  gtk_tree_store_append (fixture.store, &iter, &root);
   create_tree_store_set_values (fixture.store, &iter, TRUE);
-  signal_monitor_assert_is_empty (fixture.monitor);
 
   /* Parent must now be visible.  Do the level length check first,
    * to avoid modifying the child model triggering a row-changed to
    * the filter model.
    */
   check_level_length (fixture.filter, NULL, 1);
-  check_level_length (fixture.filter, "0", 1);
+  check_level_length (fixture.filter, "0", 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Modify path 0 */
+  /* This should propagate row-changed */
   signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
 
   set_path_visibility (&fixture, "0", TRUE);
   /* check_filter_model (&fixture); */
-
   signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Insert another node in the root level.  Initially invisible, so
-   * not expecting any signal.
-   */
+  /* New root node, no child, so no signal */
   gtk_tree_store_append (fixture.store, &root, NULL);
   check_level_length (fixture.filter, NULL, 1);
+  signal_monitor_assert_is_empty (fixture.monitor);
 
+  /* First child, no signal, no change */
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  check_level_length (fixture.filter, NULL, 1);
   signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Adding a child node which also makes parent at path 1 visible. */
+  /* When the second child comes in, this node will become visible */
   signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
   signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
 
   gtk_tree_store_append (fixture.store, &iter, &root);
   check_level_length (fixture.filter, NULL, 2);
-  check_level_length (fixture.filter, "1", 1);
-
-  signal_monitor_assert_is_empty (fixture.monitor);
-
-  /* Check if row-changed is propagated */
-  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1");
-  /* is row-has-child-toggled really necessary? */
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "1");
-  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1:0");
+  check_level_length (fixture.filter, "1", 0);
 
   create_tree_store_set_values (fixture.store, &root, TRUE);
   create_tree_store_set_values (fixture.store, &iter, TRUE);
+
   /* check_filter_model (&fixture); */
   signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Insert another child under node 1 */
-  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "1:1");
-  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "1:1");
-
+  /* Add another child for 1 */
   gtk_tree_store_append (fixture.store, &iter, &root);
   create_tree_store_set_values (fixture.store, &iter, TRUE);
   check_level_length (fixture.filter, NULL, 2);
-  check_level_length (fixture.filter, "0", 1);
-  check_level_length (fixture.filter, "1", 2);
-  signal_monitor_assert_is_empty (fixture.monitor);
-
-  /* Set a child node to invisible.  This should not yield any
-   * change, because filtering is only done on whether the root
-   * node has a child, which it still has.
-   */
-  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0:0");
-
-  set_path_visibility (&fixture, "0:0", FALSE);
+  check_level_length (fixture.filter, "0", 0);
+  check_level_length (fixture.filter, "1", 0);
   signal_monitor_assert_is_empty (fixture.monitor);
 
   /* Now remove one of the remaining child rows */
-  signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0:0");
-  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
   signal_monitor_append_signal (fixture.monitor, ROW_DELETED, "0");
 
   gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store),
@@ -2665,15 +5399,96 @@ specific_root_has_child_filter (void)
   gtk_tree_store_remove (fixture.store, &iter);
 
   check_level_length (fixture.filter, NULL, 1);
-  check_level_length (fixture.filter, "0", 2);
+  check_level_length (fixture.filter, "0", 0);
+
+  set_path_visibility (&fixture, "0", FALSE);
+  /* check_filter_model (&fixture); */
   signal_monitor_assert_is_empty (fixture.monitor);
 
-  /* Set visibility of 0 to FALSE, no-op for filter model since
-   * the child 0:0 is already gone
+  g_object_unref (fixture.filter);
+  g_object_unref (fixture.store);
+  gtk_widget_destroy (tree_view);
+}
+
+static void
+specific_at_least_2_children_filter_on_sort_model (void)
+{
+  GtkTreeRowReference *ref;
+  GtkTreeModel *filter;
+  GtkTreeModel *sort_model;
+  GtkTreeIter iter, root;
+  FilterTest fixture; /* This is not how it should be done */
+  GtkWidget *tree_view;
+
+  fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+  sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (fixture.store));
+  filter = gtk_tree_model_filter_new (sort_model, NULL);
+  fixture.filter = GTK_TREE_MODEL_FILTER (filter);
+  fixture.monitor = signal_monitor_new (filter);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+
+  gtk_tree_model_filter_set_visible_func (fixture.filter,
+                                          specific_at_least_2_children_filter_filter_func,
+                                          NULL, NULL);
+
+  /* The first node will be initially invisible: no signals */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  create_tree_store_set_values (fixture.store, &root, FALSE);
+
+  /* check_filter_model (&fixture); */
+  check_level_length (fixture.filter, NULL, 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* Insert a child node.  Nothing should happen.
    */
-  set_path_visibility (&fixture, "0", FALSE);
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+
+  check_level_length (fixture.filter, NULL, 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+    {
+      GtkTreePath *path = gtk_tree_path_new_from_indices (0, 0, -1);
+
+      ref = gtk_tree_row_reference_new (sort_model, path);
+      gtk_tree_path_free (path);
+    }
+
+  /* Insert a second child node.  This will cause the parent to become
+   * visible.
+   */
+  signal_monitor_append_signal (fixture.monitor, ROW_INSERTED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  gtk_tree_store_append (fixture.store, &iter, &root);
+  create_tree_store_set_values (fixture.store, &iter, TRUE);
+
+  /* Parent must now be visible.  Do the level length check first,
+   * to avoid modifying the child model triggering a row-changed to
+   * the filter model.
+   */
+  check_level_length (fixture.filter, NULL, 1);
+  check_level_length (fixture.filter, "0", 0);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* This should propagate row-changed */
+  signal_monitor_append_signal (fixture.monitor, ROW_CHANGED, "0");
+  signal_monitor_append_signal (fixture.monitor, ROW_HAS_CHILD_TOGGLED, "0");
+
+  set_path_visibility (&fixture, "0", TRUE);
   /* check_filter_model (&fixture); */
   signal_monitor_assert_is_empty (fixture.monitor);
+
+  /* New root node, no child, so no signal */
+  gtk_tree_store_append (fixture.store, &root, NULL);
+  check_level_length (fixture.filter, NULL, 1);
+  signal_monitor_assert_is_empty (fixture.monitor);
+
+  gtk_tree_row_reference_free (ref);
+  g_object_unref (fixture.filter);
+  g_object_unref (fixture.store);
+  gtk_widget_destroy (tree_view);
 }
 
 
@@ -2986,6 +5801,7 @@ specific_bug_311955 (void)
   GtkWidget *tree_view;
   int i;
   int n;
+  GtkTreePath *path;
 
   g_test_bug ("311955");
 
@@ -3016,6 +5832,9 @@ specific_bug_311955 (void)
   while (gtk_events_pending ())
     gtk_main_iteration ();
 
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 2);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1);
+
   /* Fill model */
   for (i = 0; i < 4; i++)
     {
@@ -3036,6 +5855,9 @@ specific_bug_311955 (void)
   while (gtk_events_pending ())
     gtk_main_iteration ();
 
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 1);
+
   /* Remove bottommost child from the tree. */
   gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root);
   n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root);
@@ -3047,6 +5869,86 @@ specific_bug_311955 (void)
     }
   else
     g_assert_not_reached ();
+
+  path = gtk_tree_path_new_from_indices (0, 2, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0);
+}
+
+static void
+specific_bug_311955_clean (void)
+{
+  /* Cleaned up version of the test case for GNOME Bugzilla bug 311955,
+   * which is easier to understand.
+   */
+  GtkTreeIter iter, child, grandchild;
+  GtkTreeStore *store;
+  GtkTreeModel *sort;
+  GtkTreeModel *filter;
+
+  GtkWidget *tree_view;
+  GtkTreePath *path;
+
+  store = gtk_tree_store_new (1, G_TYPE_INT);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 0, 1, -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 0, 1, -1);
+
+  sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
+  filter = gtk_tree_model_filter_new (sort, NULL);
+
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                          specific_bug_311955_filter_func,
+                                          NULL, NULL);
+
+  tree_view = gtk_tree_view_new_with_model (filter);
+  g_object_unref (store);
+
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), NULL, 1);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 1);
+
+  gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 0, 0, -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 0, 1, -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 0, 1, -1);
+
+  gtk_tree_store_append (store, &grandchild, &child);
+  gtk_tree_store_set (store, &grandchild, 0, 1, -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  /* Don't set a value: assume 0 */
+
+  /* Remove leaf node, check trigger row-has-child-toggled */
+  path = gtk_tree_path_new_from_indices (0, 3, 0, -1);
+  gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
+  gtk_tree_path_free (path);
+  gtk_tree_store_remove (store, &iter);
+
+  path = gtk_tree_path_new_from_indices (0, 2, -1);
+  gtk_tree_view_expand_row (GTK_TREE_VIEW (tree_view), path, FALSE);
+  gtk_tree_path_free (path);
+
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0", 3);
+  check_level_length (GTK_TREE_MODEL_FILTER (filter), "0:2", 0);
+
+  gtk_widget_destroy (tree_view);
 }
 
 static void
@@ -3421,7 +6323,6 @@ specific_bug_621076 (void)
   signal_monitor_assert_is_empty (monitor);
 
   /* group-2 is already visible, so this time it is a normal insertion */
-  signal_monitor_append_signal (monitor, ROW_INSERTED, "2:1");
   gtk_tree_store_insert_with_values (store, NULL, &group_iter, -1,
                                      0, "visible-2:2",
                                      -1);
@@ -3449,7 +6350,6 @@ specific_bug_621076 (void)
   /* This will make group 3 visible. */
   signal_monitor_append_signal (monitor, ROW_INSERTED, "3");
   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "3");
-  signal_monitor_append_signal (monitor, ROW_INSERTED, "3:0");
   signal_monitor_append_signal (monitor, ROW_HAS_CHILD_TOGGLED, "3");
   gtk_tree_store_set (store, &item_iter, 0, "visible-3:1", -1);
   signal_monitor_assert_is_empty (monitor);
@@ -3532,6 +6432,330 @@ specific_bug_621076 (void)
   g_object_unref (filter);
 }
 
+static void
+specific_bug_657353_related (void)
+{
+  GtkTreeIter node1, node2, node3, node4;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeModel *filter_model;
+  GtkWidget *tree_view;
+  GType column_types[] = { G_TYPE_BOOLEAN };
+
+  /* gtk_tree_model_filter_rows_reordered() used to have a problem to
+   * not properly transfer the first ref count when the first node in
+   * the level does not have elt->offset == 0.  This test checks for
+   * that.  This bug could cause the faulty condition
+   *   elt->ext_ref_count > elt->ref_count
+   * to raise.
+   */
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  gtk_tree_store_set_column_types (GTK_TREE_STORE (model), 1,
+                                   column_types);
+
+  gtk_tree_store_append (GTK_TREE_STORE (model), &node1, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &node2, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &node3, NULL);
+  gtk_tree_store_append (GTK_TREE_STORE (model), &node4, NULL);
+
+  /* Hide the first node */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &node1, 0, FALSE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &node2, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &node3, 0, TRUE, -1);
+  gtk_tree_store_set (GTK_TREE_STORE (model), &node4, 0, TRUE, -1);
+
+  filter_model = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), 0);
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  assert_node_ref_count (ref_model, &node1, 0);
+  assert_node_ref_count (ref_model, &node2, 2);
+  assert_node_ref_count (ref_model, &node3, 1);
+  assert_node_ref_count (ref_model, &node4, 1);
+
+  /* Swap nodes 2 and 3 */
+
+  /* gtk_tree_store_swap() will emit rows-reordered */
+  gtk_tree_store_swap (GTK_TREE_STORE (model),
+                       &node2, &node3);
+
+  assert_node_ref_count (ref_model, &node1, 0);
+  assert_node_ref_count (ref_model, &node3, 2);
+  assert_node_ref_count (ref_model, &node2, 1);
+  assert_node_ref_count (ref_model, &node4, 1);
+
+  /* Hide node 3 */
+  gtk_tree_store_set (GTK_TREE_STORE (model), &node3, 0, FALSE, -1);
+
+  assert_node_ref_count (ref_model, &node1, 0);
+  assert_node_ref_count (ref_model, &node3, 0);
+  assert_node_ref_count (ref_model, &node2, 2);
+  assert_node_ref_count (ref_model, &node4, 1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (ref_model);
+}
+
+static gboolean
+specific_bug_657353_visible_func (GtkTreeModel *model,
+                                  GtkTreeIter  *iter,
+                                  gpointer      data)
+{
+  gchar *str;
+  gboolean ret = FALSE;
+
+  gtk_tree_model_get (model, iter, 0, &str, -1);
+  ret = strstr (str, "hidden") ? FALSE : TRUE;
+  g_free (str);
+
+  return ret;
+}
+
+static void
+specific_bug_657353 (void)
+{
+  GtkListStore *store;
+  GtkTreeModel *sort_model;
+  GtkTreeModel *filter_model;
+  GtkTreeIter iter, iter_a, iter_b, iter_c;
+  GtkWidget *tree_view;
+
+  /* This is a very carefully crafted test case that is triggering the
+   * situation described in bug 657353.
+   *
+   *   GtkListStore acts like EphyCompletionModel
+   *   GtkTreeModelSort acts like the sort model added in
+   *                      ephy_location_entry_set_completion.
+   *   GtkTreeModelFilter acts like the filter model in
+   *                      GtkEntryCompletion.
+   */
+
+  /* Set up a model that's wrapped in a GtkTreeModelSort.  The first item
+   * will be hidden.
+   */
+  store = gtk_list_store_new (1, G_TYPE_STRING);
+  gtk_list_store_insert_with_values (store, &iter_b, 0, 0, "BBB hidden", -1);
+  gtk_list_store_insert_with_values (store, &iter, 1, 0, "EEE", -1);
+  gtk_list_store_insert_with_values (store, &iter, 2, 0, "DDD", -1);
+  gtk_list_store_insert_with_values (store, &iter_c, 3, 0, "CCC", -1);
+
+  sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store));
+
+  filter_model = gtk_tree_model_filter_new (sort_model, NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
+                                          specific_bug_657353_visible_func,
+                                          filter_model, NULL);
+
+  tree_view = gtk_tree_view_new_with_model (filter_model);
+
+  /* This triggers emission of rows-reordered.  The elt with offset == 0
+   * is hidden, which used to cause misbehavior.  (The first reference should
+   * have moved to CCC, which did not happen).
+   */
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model),
+                                        0, GTK_SORT_ASCENDING);
+
+  /* By inserting another item that will appear at the first position, a
+   * reference transfer is done from CCC (which failed to get this reference
+   * earlier) to AAA.  At this point, the rule
+   * elt->ref_count >= elt->ext_ref_count is broken for CCC.
+   */
+  gtk_list_store_insert_with_values (store, &iter_a, 6, 0, "AAA", -1);
+
+  /* When we hide CCC, the references cannot be correctly released, because
+   * CCC failed to get a reference during rows-reordered.  The faulty condition
+   * only manifests itself here with MODEL_FILTER_DEBUG disabled (as is usual
+   * in production).
+   */
+  gtk_list_store_set (store, &iter_c, 0, "CCC hidden", -1);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter_model);
+  g_object_unref (sort_model);
+  g_object_unref (store);
+}
+
+static void
+specific_bug_658696 (void)
+{
+  GtkTreeStore *store;
+  GtkTreeModel *filter;
+  GtkTreePath *vroot;
+  GtkTreeIter iter;
+
+  store = create_tree_store (4, TRUE);
+
+  vroot = gtk_tree_path_new_from_indices (0, 0, -1);
+  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), vroot);
+  gtk_tree_path_free (vroot);
+
+  /* This used to cause a crash in gtk_tree_model_filter_check_ancestors() */
+  gtk_tree_store_append (store, &iter, NULL);
+
+  g_object_unref (store);
+  g_object_unref (filter);
+}
+
+static gboolean
+specific_bug_659022_visible_func (GtkTreeModel *model,
+                                  GtkTreeIter  *iter,
+                                  gpointer      data)
+{
+  GtkTreeIter tmp;
+
+  if (!gtk_tree_model_iter_parent (model, &tmp, iter))
+    {
+      if (gtk_tree_model_iter_n_children (model, iter) >= 2)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+specific_bug_659022_row_changed_emission (void)
+{
+  GtkTreeModel *filter;
+  GtkTreeModel *model;
+  GtkTreeIter parent, child, child2;
+  GtkTreePath *path;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+
+  filter = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                          specific_bug_659022_visible_func,
+                                          NULL, NULL);
+
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (filter));
+
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &parent, NULL, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child, &parent, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child2, &parent, 0);
+
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter));
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &child2);
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter));
+
+  path = gtk_tree_model_get_path (model, &child);
+  gtk_tree_model_row_changed (model, path, &child);
+  gtk_tree_path_free (path);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter);
+  g_object_unref (model);
+}
+
+static void
+specific_bug_659022_row_deleted_node_invisible (void)
+{
+  GtkTreeModel *filter;
+  GtkTreeModel *model;
+  GtkTreeIter parent, child;
+  GtkTreeIter parent2, child2, child3;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+
+  filter = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                          specific_bug_659022_visible_func,
+                                          NULL, NULL);
+
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (filter));
+
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &parent, NULL, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child, &parent, 0);
+
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &parent2, NULL, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child2, &parent2, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child3, &parent2, 0);
+
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter));
+
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter);
+  g_object_unref (model);
+}
+
+static void
+specific_bug_659022_row_deleted_free_level (void)
+{
+  GtkTreeModel *filter;
+  GtkTreeModel *model;
+  GtkTreeModelRefCount *ref_model;
+  GtkTreeIter parent, child;
+  GtkTreeIter parent2, child2, child3;
+  GtkWidget *tree_view;
+
+  model = gtk_tree_model_ref_count_new ();
+  ref_model = GTK_TREE_MODEL_REF_COUNT (model);
+
+  filter = gtk_tree_model_filter_new (model, NULL);
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
+                                          specific_bug_659022_visible_func,
+                                          NULL, NULL);
+
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (filter));
+
+  /* Carefully construct a model */
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &parent, NULL, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child, &parent, 0);
+
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &parent2, NULL, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child2, &parent2, 0);
+  gtk_tree_store_insert (GTK_TREE_STORE (model), &child3, &parent2, 0);
+
+  /* Only parent2 is visible, child3 holds first ref count for that level
+   * (Note that above, both child2 as child3 are inserted at position 0).
+   */
+  assert_node_ref_count (ref_model, &parent, 0);
+  assert_node_ref_count (ref_model, &child, 0);
+  assert_node_ref_count (ref_model, &parent2, 3);
+  assert_node_ref_count (ref_model, &child3, 1);
+  assert_node_ref_count (ref_model, &child2, 0);
+
+  /* Make sure child level is cached */
+  gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
+
+  assert_node_ref_count (ref_model, &parent, 0);
+  assert_node_ref_count (ref_model, &child, 0);
+  assert_node_ref_count (ref_model, &parent2, 3);
+  assert_node_ref_count (ref_model, &child3, 2);
+  assert_node_ref_count (ref_model, &child2, 1);
+
+  gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view));
+
+  assert_node_ref_count (ref_model, &parent, 0);
+  assert_node_ref_count (ref_model, &child, 0);
+  assert_node_ref_count (ref_model, &parent2, 3);
+  assert_node_ref_count (ref_model, &child3, 1);
+  assert_node_ref_count (ref_model, &child2, 0);
+
+  /* Remove node with longer child level first */
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent2);
+  gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
+
+  gtk_widget_destroy (tree_view);
+  g_object_unref (filter);
+  g_object_unref (model);
+}
+
 /* main */
 
 void
@@ -3565,6 +6789,11 @@ register_filter_model_tests (void)
               filter_test_setup,
               filled_hide_child_levels,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filled/hide-child-levels/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup,
+              filled_hide_child_levels_root_expanded,
+              filter_test_teardown);
 
   g_test_add ("/TreeModelFilter/filled/hide-root-level/vroot",
               FilterTest, gtk_tree_path_new_from_indices (2, -1),
@@ -3576,6 +6805,11 @@ register_filter_model_tests (void)
               filter_test_setup,
               filled_vroot_hide_child_levels,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filled/hide-child-levels/vroot-root-expanded",
+              FilterTest, gtk_tree_path_new_from_indices (2, -1),
+              filter_test_setup,
+              filled_vroot_hide_child_levels_root_expanded,
+              filter_test_teardown);
 
 
   g_test_add ("/TreeModelFilter/empty/show-nodes",
@@ -3606,16 +6840,31 @@ register_filter_model_tests (void)
               filter_test_setup_unfiltered,
               unfiltered_hide_single,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/hide-single/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup_unfiltered_root_expanded,
+              unfiltered_hide_single_root_expanded,
+              filter_test_teardown);
   g_test_add ("/TreeModelFilter/unfiltered/hide-single-child",
               FilterTest, NULL,
               filter_test_setup_unfiltered,
               unfiltered_hide_single_child,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup_unfiltered_root_expanded,
+              unfiltered_hide_single_child_root_expanded,
+              filter_test_teardown);
   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level",
               FilterTest, NULL,
               filter_test_setup_unfiltered,
               unfiltered_hide_single_multi_level,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup_unfiltered_root_expanded,
+              unfiltered_hide_single_multi_level_root_expanded,
+              filter_test_teardown);
 
   g_test_add ("/TreeModelFilter/unfiltered/hide-single/vroot",
               FilterTest, gtk_tree_path_new_from_indices (2, -1),
@@ -3627,11 +6876,21 @@ register_filter_model_tests (void)
               filter_test_setup_unfiltered,
               unfiltered_vroot_hide_single_child,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/hide-single-child/vroot/root-expanded",
+              FilterTest, gtk_tree_path_new_from_indices (2, -1),
+              filter_test_setup_unfiltered_root_expanded,
+              unfiltered_vroot_hide_single_child_root_expanded,
+              filter_test_teardown);
   g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/vroot",
               FilterTest, gtk_tree_path_new_from_indices (2, -1),
               filter_test_setup_unfiltered,
               unfiltered_vroot_hide_single_multi_level,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/hide-single-multi-level/vroot/root-expanded",
+              FilterTest, gtk_tree_path_new_from_indices (2, -1),
+              filter_test_setup_unfiltered_root_expanded,
+              unfiltered_vroot_hide_single_multi_level_root_expanded,
+              filter_test_teardown);
 
 
 
@@ -3645,11 +6904,21 @@ register_filter_model_tests (void)
               filter_test_setup_empty_unfiltered,
               unfiltered_show_single_child,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/show-single-child/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup_empty_unfiltered_root_expanded,
+              unfiltered_show_single_child_root_expanded,
+              filter_test_teardown);
   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level",
               FilterTest, NULL,
               filter_test_setup_empty_unfiltered,
               unfiltered_show_single_multi_level,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/root-expanded",
+              FilterTest, NULL,
+              filter_test_setup_empty_unfiltered_root_expanded,
+              unfiltered_show_single_multi_level_root_expanded,
+              filter_test_teardown);
 
   g_test_add ("/TreeModelFilter/unfiltered/show-single/vroot",
               FilterTest, gtk_tree_path_new_from_indices (2, -1),
@@ -3661,11 +6930,64 @@ register_filter_model_tests (void)
               filter_test_setup_empty_unfiltered,
               unfiltered_vroot_show_single_child,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/show-single-child/vroot/root-expanded",
+              FilterTest, gtk_tree_path_new_from_indices (2, -1),
+              filter_test_setup_empty_unfiltered_root_expanded,
+              unfiltered_vroot_show_single_child_root_expanded,
+              filter_test_teardown);
   g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/vroot",
               FilterTest, gtk_tree_path_new_from_indices (2, -1),
               filter_test_setup_empty_unfiltered,
               unfiltered_vroot_show_single_multi_level,
               filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/show-single-multi-level/vroot/root-expanded",
+              FilterTest, gtk_tree_path_new_from_indices (2, -1),
+              filter_test_setup_empty_unfiltered_root_expanded,
+              unfiltered_vroot_show_single_multi_level_root_expanded,
+              filter_test_teardown);
+
+
+  g_test_add ("/TreeModelFilter/unfiltered/rows-reordered/root-level",
+              FilterTest, NULL,
+              filter_test_setup_unfiltered,
+              unfiltered_rows_reordered_root_level,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/unfiltered/rows-reordered/child-level",
+              FilterTest, NULL,
+              filter_test_setup_unfiltered,
+              unfiltered_rows_reordered_child_level,
+              filter_test_teardown);
+
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/root-level/first-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_root_level_first_hidden,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/root-level/middle-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_root_level_middle_hidden,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/child-level/first-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_child_level_first_hidden,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/child-level/middle-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_child_level_middle_hidden,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/child-level/4-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_child_level_4_hidden,
+              filter_test_teardown);
+  g_test_add ("/TreeModelFilter/filtered/rows-reordered/child-level/all-hidden",
+              FilterTest, NULL,
+              filter_test_setup,
+              filtered_rows_reordered_child_level_all_hidden,
+              filter_test_teardown);
 
   /* Inserts in child models after creation of filter model */
   g_test_add_func ("/TreeModelFilter/insert/before",
@@ -3681,6 +7003,58 @@ register_filter_model_tests (void)
   g_test_add_func ("/TreeModelFilter/remove/vroot-ancestor",
                    remove_vroot_ancestor);
 
+  /* Reference counting */
+  g_test_add_func ("/TreeModelFilter/ref-count/single-level",
+                   ref_count_single_level);
+  g_test_add_func ("/TreeModelFilter/ref-count/two-levels",
+                   ref_count_two_levels);
+  g_test_add_func ("/TreeModelFilter/ref-count/three-levels",
+                   ref_count_three_levels);
+  g_test_add_func ("/TreeModelFilter/ref-count/delete-row",
+                   ref_count_delete_row);
+  g_test_add_func ("/TreeModelFilter/ref-count/filter-row/length-1",
+                   ref_count_filter_row_length_1);
+  g_test_add_func ("/TreeModelFilter/ref-count/filter-row/length-1-remove-in-root-level",
+                   ref_count_filter_row_length_1_remove_in_root_level);
+  g_test_add_func ("/TreeModelFilter/ref-count/filter-row/length-1-remove-in-child-level",
+                   ref_count_filter_row_length_1_remove_in_child_level);
+  g_test_add_func ("/TreeModelFilter/ref-count/filter-row/length-gt-1",
+                   ref_count_filter_row_length_gt_1);
+  g_test_add_func ("/TreeModelFilter/ref-count/filter-row/length-gt-1-visible-children",
+                   ref_count_filter_row_length_gt_1_visible_children);
+  g_test_add_func ("/TreeModelFilter/ref-count/cleanup",
+                   ref_count_cleanup);
+  g_test_add_func ("/TreeModelFilter/ref-count/row-ref",
+                   ref_count_row_ref);
+
+  /* Reference counting, transfer of first reference on
+   * first node in level.  This is a GtkTreeModelFilter-specific
+   * feature.
+   */
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/insert",
+                   ref_count_transfer_root_level_insert);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/remove",
+                   ref_count_transfer_root_level_remove);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/remove/filtered",
+                   ref_count_transfer_root_level_remove_filtered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/reordered",
+                   ref_count_transfer_root_level_reordered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/reordered/filtered",
+                   ref_count_transfer_root_level_reordered_filtered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/root-level/filter",
+                   ref_count_transfer_root_level_filter);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/insert",
+                   ref_count_transfer_child_level_insert);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/remove",
+                   ref_count_transfer_child_level_remove);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/remove/filtered",
+                   ref_count_transfer_child_level_remove_filtered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/reordered",
+                   ref_count_transfer_child_level_reordered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/reordered/filtered",
+                   ref_count_transfer_child_level_reordered_filtered);
+  g_test_add_func ("/TreeModelFilter/ref-count/transfer/child-level/filter",
+                   ref_count_transfer_child_level_filter);
 
   g_test_add_func ("/TreeModelFilter/specific/path-dependent-filter",
                    specific_path_dependent_filter);
@@ -3694,6 +7068,12 @@ register_filter_model_tests (void)
                    specific_root_mixed_visibility);
   g_test_add_func ("/TreeModelFilter/specific/has-child-filter",
                    specific_has_child_filter);
+  g_test_add_func ("/TreeModelFilter/specific/has-child-filter-on-sort-model",
+                   specific_has_child_filter_on_sort_model);
+  g_test_add_func ("/TreeModelFilter/specific/at-least-2-children-filter",
+                   specific_at_least_2_children_filter);
+  g_test_add_func ("/TreeModelFilter/specific/at-least-2-children-filter-on-sort-model",
+                   specific_at_least_2_children_filter_on_sort_model);
   g_test_add_func ("/TreeModelFilter/specific/root-has-child-filter",
                    specific_root_has_child_filter);
   g_test_add_func ("/TreeModelFilter/specific/filter-add-child",
@@ -3711,6 +7091,8 @@ register_filter_model_tests (void)
                    specific_bug_301558);
   g_test_add_func ("/TreeModelFilter/specific/bug-311955",
                    specific_bug_311955);
+  g_test_add_func ("/TreeModelFilter/specific/bug-311955-clean",
+                   specific_bug_311955_clean);
   g_test_add_func ("/TreeModelFilter/specific/bug-346800",
                    specific_bug_346800);
   g_test_add_func ("/TreeModelFilter/specific/bug-464173",
@@ -3721,4 +7103,16 @@ register_filter_model_tests (void)
                    specific_bug_549287);
   g_test_add_func ("/TreeModelFilter/specific/bug-621076",
                    specific_bug_621076);
+  g_test_add_func ("/TreeModelFilter/specific/bug-657353-related",
+                   specific_bug_657353_related);
+  g_test_add_func ("/TreeModelFilter/specific/bug-657353",
+                   specific_bug_657353);
+  g_test_add_func ("/TreeModelFilter/specific/bug-658696",
+                   specific_bug_658696);
+  g_test_add_func ("/TreeModelFilter/specific/bug-659022/row-changed-emission",
+                   specific_bug_659022_row_changed_emission);
+  g_test_add_func ("/TreeModelFilter/specific/bug-659022/row-deleted-node-invisible",
+                   specific_bug_659022_row_deleted_node_invisible);
+  g_test_add_func ("/TreeModelFilter/specific/bug-659022/row-deleted-free-level",
+                   specific_bug_659022_row_deleted_free_level);
 }